diff --git a/lib/core/l10n/app_strings.dart b/lib/core/l10n/app_strings.dart index da1e4a3..79f95a7 100644 --- a/lib/core/l10n/app_strings.dart +++ b/lib/core/l10n/app_strings.dart @@ -167,6 +167,7 @@ class AppStrings { String get colorPrimary => _ru ? 'Основной' : 'Primary'; String get colorSecondary => _ru ? 'Второй' : 'Secondary'; + String get colorSecond => _ru ? 'Второй' : 'Second'; String get colorSolid => _ru ? 'Однотон' : 'Solid'; String get gradientLinear => _ru ? 'Линейный' : 'Linear'; String get gradientReverse => _ru ? 'Обратный' : 'Reverse'; diff --git a/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart b/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart index 553af00..cc9ccfb 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart @@ -85,7 +85,7 @@ class _AccountEditorOverlayState extends State { Widget build(BuildContext context) { final mq = MediaQuery.of(widget.context); final cardTop = mq.padding.top + kToolbarHeight + 16; - const cardHeight = 230.0; + const cardHeight = 190.0; const editorPanelHeight = 102.0; final editorPanelTop = cardTop + cardHeight + 20; final colorPanelTop = editorPanelTop + editorPanelHeight + 12; diff --git a/lib/features/dashboard/widgets/account_editor_overlay/editor_panel.dart b/lib/features/dashboard/widgets/account_editor_overlay/editor_panel.dart index 90e5ad2..c832d50 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay/editor_panel.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay/editor_panel.dart @@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/constants.dart'; import '../../../../shared/widgets/byn_sign.dart'; -class AccountEditorPanel extends ConsumerWidget { +class AccountEditorPanel extends ConsumerStatefulWidget { final TextEditingController nameController; final String selectedCurrency; final bool showCurrencyDropdown; @@ -26,15 +26,29 @@ class AccountEditorPanel extends ConsumerWidget { }); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => _AccountEditorPanelState(); +} + +class _AccountEditorPanelState extends ConsumerState { + bool _showNameError = false; + + void _triggerNameError() { + setState(() => _showNameError = true); + Future.delayed(const Duration(seconds: 2), () { + if (mounted) setState(() => _showNameError = false); + }); + } + + @override + Widget build(BuildContext context) { return Container( - height: panelHeight, + height: widget.panelHeight, decoration: BoxDecoration( - color: Theme.of(dashboardContext).colorScheme.surface, + color: Theme.of(widget.dashboardContext).colorScheme.surface, borderRadius: BorderRadius.circular(20), border: Border.all( color: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.1), width: 1.5, ), @@ -58,7 +72,7 @@ class AccountEditorPanel extends ConsumerWidget { fontSize: 11, fontWeight: FontWeight.w600, color: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.6), ), ), @@ -70,7 +84,7 @@ class AccountEditorPanel extends ConsumerWidget { Expanded( flex: 3, child: TextField( - controller: nameController, + controller: widget.nameController, buildCounter: ( context, { @@ -84,23 +98,23 @@ class AccountEditorPanel extends ConsumerWidget { hintStyle: TextStyle( fontSize: 13, color: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.4), ), filled: true, fillColor: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.05), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: - (showLimitError || - showDuplicateError || - nameController.text.trim().isEmpty) + (widget.showLimitError || + widget.showDuplicateError || + _showNameError) ? Colors.red : Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.15), width: 1.5, ), @@ -109,12 +123,12 @@ class AccountEditorPanel extends ConsumerWidget { borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: - (showLimitError || - showDuplicateError || - nameController.text.trim().isEmpty) + (widget.showLimitError || + widget.showDuplicateError || + _showNameError) ? Colors.red : Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.15), width: 1.5, ), @@ -123,9 +137,9 @@ class AccountEditorPanel extends ConsumerWidget { borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: - (showLimitError || - showDuplicateError || - nameController.text.trim().isEmpty) + (widget.showLimitError || + widget.showDuplicateError || + _showNameError) ? Colors.red : const Color(0xFF7C6DED), width: 1.5, @@ -141,19 +155,19 @@ class AccountEditorPanel extends ConsumerWidget { const SizedBox(width: 8), Expanded( child: GestureDetector( - onTap: onCurrencyDropdownToggle, + onTap: widget.onCurrencyDropdownToggle, child: Container( height: double.infinity, decoration: BoxDecoration( color: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.05), borderRadius: BorderRadius.circular(12), border: Border.all( - color: showCurrencyDropdown + color: widget.showCurrencyDropdown ? const Color(0xFF7C6DED) : Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.15), width: 1.5, ), @@ -162,17 +176,17 @@ class AccountEditorPanel extends ConsumerWidget { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - selectedCurrency == 'BYN' + widget.selectedCurrency == 'BYN' ? BynSign( fontSize: 15, color: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface, ) : Text( kDisplayCurrencies .firstWhere( - (c) => c.$1 == selectedCurrency, + (c) => c.$1 == widget.selectedCurrency, ) .$2, style: const TextStyle( @@ -182,12 +196,12 @@ class AccountEditorPanel extends ConsumerWidget { ), const SizedBox(width: 4), Icon( - showCurrencyDropdown + widget.showCurrencyDropdown ? Icons.arrow_drop_up : Icons.arrow_drop_down, size: 20, color: Theme.of( - dashboardContext, + widget.dashboardContext, ).colorScheme.onSurface.withOpacity(0.6), ), ], diff --git a/lib/features/dashboard/widgets/balance_card.dart b/lib/features/dashboard/widgets/balance_card.dart index ea9a08f..2bd4673 100644 --- a/lib/features/dashboard/widgets/balance_card.dart +++ b/lib/features/dashboard/widgets/balance_card.dart @@ -171,7 +171,7 @@ class BalanceCardState extends ConsumerState ..rotateY(_tiltY * 0.42), child: Container( width: double.infinity, - height: 220, + height: 180, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: _buildGradient(primary, secondary, gradientType), diff --git a/lib/features/dashboard/widgets/balance_card_carousel.dart b/lib/features/dashboard/widgets/balance_card_carousel.dart index 56c1de5..45f02f6 100644 --- a/lib/features/dashboard/widgets/balance_card_carousel.dart +++ b/lib/features/dashboard/widgets/balance_card_carousel.dart @@ -66,7 +66,7 @@ class _BalanceCardCarouselState extends ConsumerState { return Column( children: [ SizedBox( - height: 230, + height: 190, child: OverflowBox( maxWidth: MediaQuery.of(context).size.width, child: PageView.builder( @@ -154,14 +154,14 @@ class _BalanceCardCarouselState extends ConsumerState { ); }, loading: () => const SizedBox( - height: 220, + height: 180, child: Center(child: CircularProgressIndicator()), ), error: (error, stack) { return Column( children: [ SizedBox( - height: 220, + height: 180, child: BalanceCard( balance: widget.balance, currencyInfo: widget.currencyInfo, @@ -198,7 +198,7 @@ class AddAccountCard extends StatelessWidget { painter: _DashedBorderPainter(), child: Container( width: double.infinity, - height: 205, + height: 165, decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface.withOpacity(0.4), borderRadius: BorderRadius.circular(20), diff --git a/lib/main.dart b/lib/main.dart index a278d03..84c7e50 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,10 +2,188 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uuid/uuid.dart'; import 'app/app.dart'; import 'core/services/haptic_service.dart'; -import 'data/database/app_database.dart'; +import 'data/database/app_database.dart' hide Account, Transaction; +import 'data/repositories/account_repository.dart'; +import 'data/repositories/transaction_repository.dart'; import 'features/dashboard/provider.dart'; +import 'shared/models/account.dart'; +import 'shared/models/transaction.dart'; + +Future seedTestData(AppDatabase database) async { + final accountRepo = AccountRepository(database); + final transactionRepo = TransactionRepository(database); + + final existingAccounts = await accountRepo.getAll(); + if (existingAccounts.length > 1) { + return; + } + + final now = DateTime.now(); + final uuid = const Uuid(); + + final cashId = await accountRepo.add( + Account( + id: 0, + name: 'Cash', + isMain: false, + sortOrder: 1, + currency: 'USD', + createdAt: now, + ), + ); + + final cardId = await accountRepo.add( + Account( + id: 0, + name: 'Card', + isMain: false, + sortOrder: 2, + currency: 'USD', + createdAt: now, + ), + ); + + final savingsId = await accountRepo.add( + Account( + id: 0, + name: 'Savings', + isMain: false, + sortOrder: 3, + currency: 'USD', + createdAt: now, + ), + ); + + final transactions = [ + Transaction( + id: uuid.v4(), + amount: 3500.0, + category: 'Salary', + type: TransactionType.income, + date: now.subtract(const Duration(days: 28)), + note: 'Monthly salary', + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 85.50, + category: 'Groceries', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 27)), + note: 'Weekly shopping', + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 45.00, + category: 'Transportation', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 25)), + accountId: cashId, + ), + Transaction( + id: uuid.v4(), + amount: 120.00, + category: 'Utilities', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 22)), + note: 'Electricity bill', + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 200.0, + category: 'Freelance', + type: TransactionType.income, + date: now.subtract(const Duration(days: 20)), + note: 'Side project', + accountId: cashId, + ), + Transaction( + id: uuid.v4(), + amount: 500.0, + category: 'Savings', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 18)), + note: 'Monthly savings', + accountId: savingsId, + ), + Transaction( + id: uuid.v4(), + amount: 65.25, + category: 'Dining', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 15)), + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 30.00, + category: 'Entertainment', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 12)), + note: 'Movie tickets', + accountId: cashId, + ), + Transaction( + id: uuid.v4(), + amount: 95.00, + category: 'Groceries', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 10)), + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 150.0, + category: 'Bonus', + type: TransactionType.income, + date: now.subtract(const Duration(days: 8)), + note: 'Performance bonus', + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 75.00, + category: 'Shopping', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 6)), + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 40.00, + category: 'Transportation', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 4)), + accountId: cashId, + ), + Transaction( + id: uuid.v4(), + amount: 55.80, + category: 'Dining', + type: TransactionType.expense, + date: now.subtract(const Duration(days: 2)), + note: 'Dinner with friends', + accountId: cardId, + ), + Transaction( + id: uuid.v4(), + amount: 100.0, + category: 'Gift', + type: TransactionType.income, + date: now.subtract(const Duration(days: 1)), + accountId: cashId, + ), + ]; + + for (final transaction in transactions) { + await transactionRepo.add(transaction); + } +} void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -19,6 +197,8 @@ void main() async { await HapticService.init(); final database = AppDatabase(); + + await seedTestData(database); runApp( ProviderScope(