/// Providers: Quotes /// /// Riverpod providers for managing quotes state. library; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:worker/core/database/models/enums.dart'; import 'package:worker/features/quotes/data/datasources/quotes_local_datasource.dart'; import 'package:worker/features/quotes/data/models/quote_model.dart'; part 'quotes_provider.g.dart'; /// Quotes Local Data Source Provider @riverpod QuotesLocalDataSource quotesLocalDataSource(Ref ref) { return QuotesLocalDataSource(); } /// Quotes Provider /// /// Provides list of all quotes from local data source. @riverpod class Quotes extends _$Quotes { @override Future> build() async { final datasource = ref.read(quotesLocalDataSourceProvider); // Seed mock data on first load final quotes = await datasource.getAllQuotes(); if (quotes.isEmpty) { await datasource.seedMockQuotes(); return await datasource.getAllQuotes(); } return quotes; } /// Refresh quotes Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { return await ref.read(quotesLocalDataSourceProvider).getAllQuotes(); }); } } /// Quote Search Query Provider @riverpod class QuoteSearchQuery extends _$QuoteSearchQuery { @override String build() { return ''; } void updateQuery(String query) { state = query; } void clearQuery() { state = ''; } } /// Selected Quote Status Filter Provider @riverpod class SelectedQuoteStatus extends _$SelectedQuoteStatus { @override QuoteStatus? build() { return null; // null means show all statuses } void selectStatus(QuoteStatus? status) { state = status; } void clearFilter() { state = null; } } /// Filtered Quotes Provider /// /// Filters quotes by search query and status. @riverpod Future> filteredQuotes(Ref ref) async { final quotesAsync = ref.watch(quotesProvider); final searchQuery = ref.watch(quoteSearchQueryProvider); final selectedStatus = ref.watch(selectedQuoteStatusProvider); return quotesAsync.when( data: (quotes) { var filtered = quotes; // Filter by search query if (searchQuery.isNotEmpty) { final lowerQuery = searchQuery.toLowerCase(); filtered = filtered.where((quote) { final matchesNumber = quote.quoteNumber.toLowerCase().contains(lowerQuery); final matchesProject = quote.projectName?.toLowerCase().contains(lowerQuery) ?? false; return matchesNumber || matchesProject; }).toList(); } // Filter by status if (selectedStatus != null) { filtered = filtered.where((quote) => quote.status == selectedStatus).toList(); } // Sort by creation date (newest first) filtered.sort((a, b) => b.createdAt.compareTo(a.createdAt)); return filtered; }, loading: () => [], error: (error, stack) => [], ); } /// Quotes Count by Status Provider @riverpod Future> quotesCountByStatus(Ref ref) async { final quotesAsync = ref.watch(quotesProvider); return quotesAsync.when( data: (quotes) { final counts = {}; for (final status in QuoteStatus.values) { counts[status] = quotes.where((quote) => quote.status == status).length; } return counts; }, loading: () => {}, error: (error, stack) => {}, ); }