mirror of
https://github.com/koloideal/Casha.git
synced 2026-06-10 18:35:28 +03:00
update
This commit is contained in:
@@ -53,9 +53,13 @@ final searchQueryProvider = StateProvider<String>((ref) => '');
|
||||
|
||||
enum TransactionFilter { all, income, expense }
|
||||
|
||||
enum TimeFilter { allTime, lastMonth }
|
||||
|
||||
final transactionFilterProvider =
|
||||
StateProvider<TransactionFilter>((ref) => TransactionFilter.all);
|
||||
|
||||
final timeFilterProvider = StateProvider<TimeFilter>((ref) => TimeFilter.lastMonth);
|
||||
|
||||
final totalBalanceProvider = Provider<double>((ref) {
|
||||
final txs = ref.watch(transactionsProvider);
|
||||
final exchangeService = ref.watch(exchangeRateServiceProvider);
|
||||
@@ -104,16 +108,30 @@ final currentMonthExpenseProvider = Provider<double>((ref) {
|
||||
final filteredTransactionsProvider = Provider<List<Transaction>>((ref) {
|
||||
final txs = ref.watch(transactionsProvider);
|
||||
final query = ref.watch(searchQueryProvider).toLowerCase();
|
||||
final filter = ref.watch(transactionFilterProvider);
|
||||
final typeFilter = ref.watch(transactionFilterProvider);
|
||||
final timeFilter = ref.watch(timeFilterProvider);
|
||||
|
||||
var filtered = txs;
|
||||
|
||||
if (filter == TransactionFilter.income) {
|
||||
// Apply time filter first
|
||||
if (timeFilter == TimeFilter.lastMonth) {
|
||||
final now = DateTime.now();
|
||||
final start = DateTime(now.year, now.month, 1);
|
||||
final end = DateTime(now.year, now.month + 1, 1);
|
||||
filtered = filtered.where((t) =>
|
||||
t.date.isAfter(start.subtract(const Duration(seconds: 1))) &&
|
||||
t.date.isBefore(end)
|
||||
).toList();
|
||||
}
|
||||
|
||||
// Apply type filter
|
||||
if (typeFilter == TransactionFilter.income) {
|
||||
filtered = filtered.where((t) => t.type == TransactionType.income).toList();
|
||||
} else if (filter == TransactionFilter.expense) {
|
||||
} else if (typeFilter == TransactionFilter.expense) {
|
||||
filtered = filtered.where((t) => t.type == TransactionType.expense).toList();
|
||||
}
|
||||
|
||||
// Apply search query
|
||||
if (query.isNotEmpty) {
|
||||
filtered = filtered.where((t) {
|
||||
final matchesCategory = t.category.toLowerCase().contains(query);
|
||||
|
||||
@@ -76,7 +76,6 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
final monthExpense = ref.watch(currentMonthExpenseProvider);
|
||||
final budget = ref.watch(budgetProvider);
|
||||
final recent = ref.watch(recentTransactionsProvider);
|
||||
final filter = ref.watch(transactionFilterProvider);
|
||||
final currencyInfo = ref.watch(currencyProvider);
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
@@ -152,7 +151,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
ref: ref,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_FilterChips(selected: filter, ref: ref),
|
||||
const FilterChips(),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Transactions',
|
||||
@@ -254,36 +253,61 @@ class _SearchBar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _FilterChips extends StatelessWidget {
|
||||
final TransactionFilter selected;
|
||||
final WidgetRef ref;
|
||||
const _FilterChips({required this.selected, required this.ref});
|
||||
class FilterChips extends ConsumerWidget {
|
||||
const FilterChips({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final typeFilter = ref.watch(transactionFilterProvider);
|
||||
final timeFilter = ref.watch(timeFilterProvider);
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
// TIME GROUP
|
||||
_FilterChip(
|
||||
label: 'All Time',
|
||||
isSelected: timeFilter == TimeFilter.allTime,
|
||||
onTap: () => ref.read(timeFilterProvider.notifier).state = TimeFilter.allTime,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
_FilterChip(
|
||||
label: 'Month',
|
||||
isSelected: timeFilter == TimeFilter.lastMonth,
|
||||
onTap: () => ref.read(timeFilterProvider.notifier).state = TimeFilter.lastMonth,
|
||||
),
|
||||
|
||||
// VISUAL DIVIDER
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Container(
|
||||
width: 1,
|
||||
height: 20,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(
|
||||
isDark ? 0.15 : 0.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// TYPE GROUP
|
||||
_FilterChip(
|
||||
label: 'All',
|
||||
isSelected: selected == TransactionFilter.all,
|
||||
onTap: () => ref.read(transactionFilterProvider.notifier).state =
|
||||
TransactionFilter.all,
|
||||
isSelected: typeFilter == TransactionFilter.all,
|
||||
onTap: () => ref.read(transactionFilterProvider.notifier).state = TransactionFilter.all,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: 6),
|
||||
_FilterChip(
|
||||
label: 'Income',
|
||||
isSelected: selected == TransactionFilter.income,
|
||||
isSelected: typeFilter == TransactionFilter.income,
|
||||
color: AppColors.income,
|
||||
onTap: () => ref.read(transactionFilterProvider.notifier).state =
|
||||
TransactionFilter.income,
|
||||
onTap: () => ref.read(transactionFilterProvider.notifier).state = TransactionFilter.income,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(width: 6),
|
||||
_FilterChip(
|
||||
label: 'Expense',
|
||||
isSelected: selected == TransactionFilter.expense,
|
||||
isSelected: typeFilter == TransactionFilter.expense,
|
||||
color: AppColors.expense,
|
||||
onTap: () => ref.read(transactionFilterProvider.notifier).state =
|
||||
TransactionFilter.expense,
|
||||
onTap: () => ref.read(transactionFilterProvider.notifier).state = TransactionFilter.expense,
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -295,22 +319,24 @@ class _FilterChip extends StatelessWidget {
|
||||
final bool isSelected;
|
||||
final Color? color;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _FilterChip({
|
||||
required this.label,
|
||||
required this.isSelected,
|
||||
this.color,
|
||||
required this.onTap,
|
||||
this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final chipColor = color ?? AppColors.accent;
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? chipColor.withOpacity(0.2)
|
||||
@@ -318,17 +344,19 @@ class _FilterChip extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: isSelected
|
||||
? Border.all(color: chipColor, width: 1.5)
|
||||
: (isDark
|
||||
? null
|
||||
: Border.all(color: const Color(0xFFDDDDEE), width: 1)),
|
||||
: isDark
|
||||
? null
|
||||
: Border.all(color: const Color(0xFFDDDDEE), width: 1),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: isSelected
|
||||
? chipColor
|
||||
: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
|
||||
style: const TextStyle(fontSize: 12).merge(
|
||||
Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: isSelected
|
||||
? chipColor
|
||||
: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user