diff --git a/lib/data/database/app_database.dart b/lib/data/database/app_database.dart index 848a608..137362f 100644 --- a/lib/data/database/app_database.dart +++ b/lib/data/database/app_database.dart @@ -13,7 +13,7 @@ class AppDatabase extends _$AppDatabase { AppDatabase() : super(_openConnection()); @override - int get schemaVersion => 3; + int get schemaVersion => 4; @override MigrationStrategy get migration => MigrationStrategy( @@ -23,11 +23,10 @@ class AppDatabase extends _$AppDatabase { await customStatement( 'INSERT INTO accounts (name, is_main, currency, sort_order, created_at) ' 'VALUES (?, ?, ?, ?, ?)', - ['Main', 1, 'USD', 0, DateTime.now().millisecondsSinceEpoch], + ['main', 1, 'USD', 0, DateTime.now().millisecondsSinceEpoch], ); } if (from == 2) { - // Add currency column to existing accounts table await customStatement( 'ALTER TABLE accounts ADD COLUMN currency TEXT NOT NULL DEFAULT "USD"', ); diff --git a/lib/data/database/app_database.g.dart b/lib/data/database/app_database.g.dart index 6bc3153..c8b00ea 100644 --- a/lib/data/database/app_database.g.dart +++ b/lib/data/database/app_database.g.dart @@ -113,6 +113,17 @@ class $TransactionsTable extends Transactions requiredDuringInsert: false, defaultValue: const Constant('USD'), ); + static const VerificationMeta _accountIdMeta = const VerificationMeta( + 'accountId', + ); + @override + late final GeneratedColumn accountId = GeneratedColumn( + 'account_id', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); static const VerificationMeta _createdAtMeta = const VerificationMeta( 'createdAt', ); @@ -137,6 +148,7 @@ class $TransactionsTable extends Transactions lastOccurrence, currency, currencyCode, + accountId, createdAt, ]; @override @@ -224,6 +236,14 @@ class $TransactionsTable extends Transactions ), ); } + if (data.containsKey('account_id')) { + context.handle( + _accountIdMeta, + accountId.isAcceptableOrUnknown(data['account_id']!, _accountIdMeta), + ); + } else if (isInserting) { + context.missing(_accountIdMeta); + } if (data.containsKey('created_at')) { context.handle( _createdAtMeta, @@ -279,6 +299,10 @@ class $TransactionsTable extends Transactions DriftSqlType.string, data['${effectivePrefix}currency_code'], )!, + accountId: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}account_id'], + )!, createdAt: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}created_at'], @@ -303,6 +327,7 @@ class Transaction extends DataClass implements Insertable { final DateTime? lastOccurrence; final String currency; final String currencyCode; + final int accountId; final DateTime createdAt; const Transaction({ required this.id, @@ -315,6 +340,7 @@ class Transaction extends DataClass implements Insertable { this.lastOccurrence, required this.currency, required this.currencyCode, + required this.accountId, required this.createdAt, }); @override @@ -334,6 +360,7 @@ class Transaction extends DataClass implements Insertable { } map['currency'] = Variable(currency); map['currency_code'] = Variable(currencyCode); + map['account_id'] = Variable(accountId); map['created_at'] = Variable(createdAt); return map; } @@ -352,6 +379,7 @@ class Transaction extends DataClass implements Insertable { : Value(lastOccurrence), currency: Value(currency), currencyCode: Value(currencyCode), + accountId: Value(accountId), createdAt: Value(createdAt), ); } @@ -372,6 +400,7 @@ class Transaction extends DataClass implements Insertable { lastOccurrence: serializer.fromJson(json['lastOccurrence']), currency: serializer.fromJson(json['currency']), currencyCode: serializer.fromJson(json['currencyCode']), + accountId: serializer.fromJson(json['accountId']), createdAt: serializer.fromJson(json['createdAt']), ); } @@ -389,6 +418,7 @@ class Transaction extends DataClass implements Insertable { 'lastOccurrence': serializer.toJson(lastOccurrence), 'currency': serializer.toJson(currency), 'currencyCode': serializer.toJson(currencyCode), + 'accountId': serializer.toJson(accountId), 'createdAt': serializer.toJson(createdAt), }; } @@ -404,6 +434,7 @@ class Transaction extends DataClass implements Insertable { Value lastOccurrence = const Value.absent(), String? currency, String? currencyCode, + int? accountId, DateTime? createdAt, }) => Transaction( id: id ?? this.id, @@ -418,6 +449,7 @@ class Transaction extends DataClass implements Insertable { : this.lastOccurrence, currency: currency ?? this.currency, currencyCode: currencyCode ?? this.currencyCode, + accountId: accountId ?? this.accountId, createdAt: createdAt ?? this.createdAt, ); Transaction copyWithCompanion(TransactionsCompanion data) { @@ -438,6 +470,7 @@ class Transaction extends DataClass implements Insertable { currencyCode: data.currencyCode.present ? data.currencyCode.value : this.currencyCode, + accountId: data.accountId.present ? data.accountId.value : this.accountId, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, ); } @@ -455,6 +488,7 @@ class Transaction extends DataClass implements Insertable { ..write('lastOccurrence: $lastOccurrence, ') ..write('currency: $currency, ') ..write('currencyCode: $currencyCode, ') + ..write('accountId: $accountId, ') ..write('createdAt: $createdAt') ..write(')')) .toString(); @@ -472,6 +506,7 @@ class Transaction extends DataClass implements Insertable { lastOccurrence, currency, currencyCode, + accountId, createdAt, ); @override @@ -488,6 +523,7 @@ class Transaction extends DataClass implements Insertable { other.lastOccurrence == this.lastOccurrence && other.currency == this.currency && other.currencyCode == this.currencyCode && + other.accountId == this.accountId && other.createdAt == this.createdAt); } @@ -502,6 +538,7 @@ class TransactionsCompanion extends UpdateCompanion { final Value lastOccurrence; final Value currency; final Value currencyCode; + final Value accountId; final Value createdAt; final Value rowid; const TransactionsCompanion({ @@ -515,6 +552,7 @@ class TransactionsCompanion extends UpdateCompanion { this.lastOccurrence = const Value.absent(), this.currency = const Value.absent(), this.currencyCode = const Value.absent(), + this.accountId = const Value.absent(), this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }); @@ -529,13 +567,15 @@ class TransactionsCompanion extends UpdateCompanion { this.lastOccurrence = const Value.absent(), this.currency = const Value.absent(), this.currencyCode = const Value.absent(), + required int accountId, this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), amount = Value(amount), category = Value(category), type = Value(type), - date = Value(date); + date = Value(date), + accountId = Value(accountId); static Insertable custom({ Expression? id, Expression? amount, @@ -547,6 +587,7 @@ class TransactionsCompanion extends UpdateCompanion { Expression? lastOccurrence, Expression? currency, Expression? currencyCode, + Expression? accountId, Expression? createdAt, Expression? rowid, }) { @@ -561,6 +602,7 @@ class TransactionsCompanion extends UpdateCompanion { if (lastOccurrence != null) 'last_occurrence': lastOccurrence, if (currency != null) 'currency': currency, if (currencyCode != null) 'currency_code': currencyCode, + if (accountId != null) 'account_id': accountId, if (createdAt != null) 'created_at': createdAt, if (rowid != null) 'rowid': rowid, }); @@ -577,6 +619,7 @@ class TransactionsCompanion extends UpdateCompanion { Value? lastOccurrence, Value? currency, Value? currencyCode, + Value? accountId, Value? createdAt, Value? rowid, }) { @@ -591,6 +634,7 @@ class TransactionsCompanion extends UpdateCompanion { lastOccurrence: lastOccurrence ?? this.lastOccurrence, currency: currency ?? this.currency, currencyCode: currencyCode ?? this.currencyCode, + accountId: accountId ?? this.accountId, createdAt: createdAt ?? this.createdAt, rowid: rowid ?? this.rowid, ); @@ -629,6 +673,9 @@ class TransactionsCompanion extends UpdateCompanion { if (currencyCode.present) { map['currency_code'] = Variable(currencyCode.value); } + if (accountId.present) { + map['account_id'] = Variable(accountId.value); + } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } @@ -651,6 +698,7 @@ class TransactionsCompanion extends UpdateCompanion { ..write('lastOccurrence: $lastOccurrence, ') ..write('currency: $currency, ') ..write('currencyCode: $currencyCode, ') + ..write('accountId: $accountId, ') ..write('createdAt: $createdAt, ') ..write('rowid: $rowid') ..write(')')) @@ -2276,6 +2324,7 @@ typedef $$TransactionsTableCreateCompanionBuilder = Value lastOccurrence, Value currency, Value currencyCode, + required int accountId, Value createdAt, Value rowid, }); @@ -2291,6 +2340,7 @@ typedef $$TransactionsTableUpdateCompanionBuilder = Value lastOccurrence, Value currency, Value currencyCode, + Value accountId, Value createdAt, Value rowid, }); @@ -2354,6 +2404,11 @@ class $$TransactionsTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get accountId => $composableBuilder( + column: $table.accountId, + builder: (column) => ColumnFilters(column), + ); + ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column), @@ -2419,6 +2474,11 @@ class $$TransactionsTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get accountId => $composableBuilder( + column: $table.accountId, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column), @@ -2470,6 +2530,9 @@ class $$TransactionsTableAnnotationComposer builder: (column) => column, ); + GeneratedColumn get accountId => + $composableBuilder(column: $table.accountId, builder: (column) => column); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); } @@ -2515,6 +2578,7 @@ class $$TransactionsTableTableManager Value lastOccurrence = const Value.absent(), Value currency = const Value.absent(), Value currencyCode = const Value.absent(), + Value accountId = const Value.absent(), Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => TransactionsCompanion( @@ -2528,6 +2592,7 @@ class $$TransactionsTableTableManager lastOccurrence: lastOccurrence, currency: currency, currencyCode: currencyCode, + accountId: accountId, createdAt: createdAt, rowid: rowid, ), @@ -2543,6 +2608,7 @@ class $$TransactionsTableTableManager Value lastOccurrence = const Value.absent(), Value currency = const Value.absent(), Value currencyCode = const Value.absent(), + required int accountId, Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => TransactionsCompanion.insert( @@ -2556,6 +2622,7 @@ class $$TransactionsTableTableManager lastOccurrence: lastOccurrence, currency: currency, currencyCode: currencyCode, + accountId: accountId, createdAt: createdAt, rowid: rowid, ), diff --git a/lib/data/database/tables.dart b/lib/data/database/tables.dart index a305251..f6fd03e 100644 --- a/lib/data/database/tables.dart +++ b/lib/data/database/tables.dart @@ -12,6 +12,7 @@ class Transactions extends Table { DateTimeColumn get lastOccurrence => dateTime().nullable()(); TextColumn get currency => text().withDefault(const Constant('\$'))(); TextColumn get currencyCode => text().withDefault(const Constant('USD'))(); + IntColumn get accountId => integer()(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); @override diff --git a/lib/data/repositories/account_repository.dart b/lib/data/repositories/account_repository.dart index bbe1f42..620beea 100644 --- a/lib/data/repositories/account_repository.dart +++ b/lib/data/repositories/account_repository.dart @@ -64,11 +64,8 @@ class AccountRepository { ..orderBy([(a) => OrderingTerm.asc(a.sortOrder)])) .get(); - print('AccountRepository.getAll(): rows.length = ${rows.length}'); - // Fallback: insert default account if none exists if (rows.isEmpty) { - print('AccountRepository.getAll(): inserting default account'); try { await _db.into(_db.accounts).insert( AccountsCompanion.insert( @@ -78,18 +75,16 @@ class AccountRepository { sortOrder: const Value(0), ), ); - print('AccountRepository.getAll(): default account inserted'); } catch (e) { - print('AccountRepository.getAll(): insert error: $e'); + // Ignore if already exists } // Re-query after insert rows = await (_db.select(_db.accounts) ..orderBy([(a) => OrderingTerm.asc(a.sortOrder)])) .get(); - print('AccountRepository.getAll(): after insert, rows.length = ${rows.length}'); } - final accounts = rows + return rows .map((row) => model.Account( id: row.id, name: row.name, @@ -99,12 +94,7 @@ class AccountRepository { createdAt: row.createdAt, )) .toList(); - - print('AccountRepository.getAll(): returning ${accounts.length} accounts'); - return accounts; - } catch (e, stack) { - print('AccountRepository.getAll(): error: $e'); - print('Stack: $stack'); + } catch (e) { // Return empty list on error return []; } diff --git a/lib/data/repositories/transaction_repository.dart b/lib/data/repositories/transaction_repository.dart index b2ca1b6..a6ea7c3 100644 --- a/lib/data/repositories/transaction_repository.dart +++ b/lib/data/repositories/transaction_repository.dart @@ -148,6 +148,7 @@ class TransactionRepository { lastOccurrence: dbTransaction.lastOccurrence as DateTime?, currency: dbTransaction.currency as String, currencyCode: dbTransaction.currencyCode as String, + accountId: dbTransaction.accountId as int, ); } @@ -165,6 +166,7 @@ class TransactionRepository { lastOccurrence: Value(transaction.lastOccurrence), currency: Value(transaction.currency), currencyCode: Value(transaction.currencyCode), + accountId: Value(transaction.accountId), createdAt: Value(DateTime.now()), ); } @@ -182,6 +184,7 @@ class TransactionRepository { lastOccurrence: Value(transaction.lastOccurrence), currency: Value(transaction.currency), currencyCode: Value(transaction.currencyCode), + accountId: Value(transaction.accountId), ); } diff --git a/lib/features/add_transaction/screen.dart b/lib/features/add_transaction/screen.dart index 0c07013..ce2669b 100644 --- a/lib/features/add_transaction/screen.dart +++ b/lib/features/add_transaction/screen.dart @@ -15,11 +15,18 @@ import 'provider.dart'; const _uuid = Uuid(); -// Provider to get main account name -final mainAccountNameProvider = FutureProvider((ref) async { - final repository = ref.watch(accountRepositoryProvider); - final mainAccount = await repository.getMain(); - return mainAccount.name; +// Provider to get the account for new transactions +final transactionAccountProvider = Provider<({int id, String name})>((ref) { + final activeAccount = ref.watch(activeAccountProvider); + + if (activeAccount != null) { + // User is on a specific account page + return (id: activeAccount.id, name: activeAccount.name); + } + + // User is on Total Balance page, use Main account + // This will be resolved in the widget + return (id: 0, name: ''); // Placeholder, will be replaced }); class AddTransactionScreen extends ConsumerStatefulWidget { @@ -146,6 +153,11 @@ class _AddTransactionScreenState extends ConsumerState ? null : _noteController.text.trim(); + // Get account ID: use active account or fallback to main + final activeAccount = ref.read(activeAccountProvider); + final accountId = activeAccount?.id ?? + (await ref.read(accountRepositoryProvider).getMain()).id; + final tx = Transaction( id: state.editingId ?? _uuid.v4(), amount: amount, @@ -155,6 +167,7 @@ class _AddTransactionScreenState extends ConsumerState note: note, currency: state.overrideCurrency, currencyCode: state.overrideCurrencyCode, + accountId: accountId, ); if (state.isEditing) { @@ -226,7 +239,10 @@ class _AddTransactionScreenState extends ConsumerState final categories = ref.watch(availableCategoriesProvider(widget.initial)); final overrideCurrency = state.overrideCurrency; final isDark = Theme.of(context).brightness == Brightness.dark; - final accountNameAsync = ref.watch(mainAccountNameProvider); + + // Get active account or fallback to main + final activeAccount = ref.watch(activeAccountProvider); + final accountRepository = ref.watch(accountRepositoryProvider); return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, @@ -278,18 +294,24 @@ class _AddTransactionScreenState extends ConsumerState child: ListView( padding: const EdgeInsets.all(20), children: [ - accountNameAsync.when( - data: (accountName) => Padding( - padding: const EdgeInsets.only(bottom: 16), - child: Text( - 'Account: $accountName', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), - ), - ), - ), - loading: () => const SizedBox.shrink(), - error: (_, __) => const SizedBox.shrink(), + FutureBuilder( + future: activeAccount != null + ? Future.value(activeAccount.name) + : accountRepository.getMain().then((acc) => acc.name), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text( + 'Account: ${snapshot.data}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5), + ), + ), + ); + } + return const SizedBox.shrink(); + }, ), _TypeToggle( selected: state.type, diff --git a/lib/features/dashboard/provider.dart b/lib/features/dashboard/provider.dart index f415c58..da7e1df 100644 --- a/lib/features/dashboard/provider.dart +++ b/lib/features/dashboard/provider.dart @@ -117,9 +117,23 @@ final timeFilterProvider = StateProvider( (ref) => TimeFilter.lastMonth, ); -final totalBalanceProvider = Provider((ref) { +// Base filtered transactions by active account +final accountFilteredTransactionsProvider = Provider>((ref) { final txsAsync = ref.watch(transactionsProvider); final txs = txsAsync.valueOrNull ?? []; + final activeAccount = ref.watch(activeAccountProvider); + + // If activeAccount is null (Total Balance page), return all transactions + if (activeAccount == null) { + return txs; + } + + // Filter by account ID + return txs.where((t) => t.accountId == activeAccount.id).toList(); +}); + +final totalBalanceProvider = Provider((ref) { + final txs = ref.watch(accountFilteredTransactionsProvider); final exchangeService = ref.watch(exchangeRateServiceProvider); final targetCurrency = ref.watch(currencyProvider).code; @@ -134,8 +148,7 @@ final totalBalanceProvider = Provider((ref) { }); final totalIncomeProvider = Provider((ref) { - final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = ref.watch(accountFilteredTransactionsProvider); final filtered = txs.where((t) => t.type == TransactionType.income); final exchangeService = ref.watch(exchangeRateServiceProvider); final targetCurrency = ref.watch(currencyProvider).code; @@ -147,8 +160,7 @@ final totalIncomeProvider = Provider((ref) { }); final totalExpenseProvider = Provider((ref) { - final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = ref.watch(accountFilteredTransactionsProvider); final filtered = txs.where((t) => t.type == TransactionType.expense); final exchangeService = ref.watch(exchangeRateServiceProvider); final targetCurrency = ref.watch(currencyProvider).code; @@ -161,8 +173,7 @@ final totalExpenseProvider = Provider((ref) { final currentMonthExpenseProvider = Provider((ref) { final now = DateTime.now(); - final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = ref.watch(accountFilteredTransactionsProvider); final filtered = txs.where( (t) => t.type == TransactionType.expense && @@ -179,8 +190,7 @@ final currentMonthExpenseProvider = Provider((ref) { }); final filteredTransactionsProvider = Provider>((ref) { - final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = ref.watch(accountFilteredTransactionsProvider); final query = ref.watch(searchQueryProvider).toLowerCase(); final typeFilter = ref.watch(transactionFilterProvider); final timeFilter = ref.watch(timeFilterProvider); @@ -236,6 +246,25 @@ final accountsProvider = StreamProvider>((ref) async* { // Ephemeral UI state — active carousel index, starts at 0, not persisted final activeAccountIndexProvider = StateProvider((ref) => 0); +// Returns the currently active Account based on carousel index +final activeAccountProvider = Provider((ref) { + final index = ref.watch(activeAccountIndexProvider); + final accountsAsync = ref.watch(accountsProvider); + + if (index == 0) return null; // 0 means "Total Balance" + + return accountsAsync.when( + data: (accounts) { + if (index > 0 && index <= accounts.length) { + return accounts[index - 1]; + } + return null; + }, + loading: () => null, + error: (_, __) => null, + ); +}); + class CardColors { final Color primary; final Color secondary; diff --git a/lib/features/dashboard/widgets/balance_card.dart b/lib/features/dashboard/widgets/balance_card.dart index 96edb22..f40eb90 100644 --- a/lib/features/dashboard/widgets/balance_card.dart +++ b/lib/features/dashboard/widgets/balance_card.dart @@ -34,6 +34,7 @@ class BalanceCard extends ConsumerStatefulWidget { final Color? previewPrimary; final Color? previewSecondary; final GradientType? previewGradientType; + final String? accountName; const BalanceCard({ super.key, @@ -43,6 +44,7 @@ class BalanceCard extends ConsumerStatefulWidget { this.previewPrimary, this.previewSecondary, this.previewGradientType, + this.accountName, }); @override @@ -181,6 +183,25 @@ class BalanceCardState extends ConsumerState borderRadius: BorderRadius.circular(20), child: Stack( children: [ + if (widget.accountName != null) + Positioned( + top: 20, + left: 24, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + widget.accountName!, + style: TextStyle( + color: Colors.white.withOpacity(0.7), + fontSize: 12, + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + ), + ], + ), + ), Padding( padding: const EdgeInsets.symmetric( horizontal: 24, @@ -196,15 +217,17 @@ class BalanceCardState extends ConsumerState mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - s.totalBalance, - style: TextStyle( - fontSize: 11, - letterSpacing: 1.5, - color: Colors.white.withOpacity(0.6), + if (widget.accountName == null) + Text( + s.totalBalance, + style: TextStyle( + fontSize: 11, + letterSpacing: 1.5, + color: Colors.white.withOpacity(0.6), + ), ), - ), - const SizedBox(height: 6), + if (widget.accountName == null) + const SizedBox(height: 6), FittedBox( fit: BoxFit.scaleDown, alignment: Alignment.center, diff --git a/lib/features/dashboard/widgets/balance_card_carousel.dart b/lib/features/dashboard/widgets/balance_card_carousel.dart index dddaa6d..baf6b84 100644 --- a/lib/features/dashboard/widgets/balance_card_carousel.dart +++ b/lib/features/dashboard/widgets/balance_card_carousel.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/services/card_color_service.dart'; +import '../../../core/services/haptic_service.dart'; +import '../../../shared/models/transaction.dart'; import '../../settings/provider.dart'; import '../provider.dart'; import 'balance_card.dart'; @@ -34,7 +36,8 @@ class _BalanceCardCarouselState extends ConsumerState { @override void initState() { super.initState(); - _pageController = PageController(viewportFraction: 1.0); + // 0.92 позволяет видеть края предыдущей/следующей карточки + _pageController = PageController(viewportFraction: 0.92); } @override @@ -50,63 +53,59 @@ class _BalanceCardCarouselState extends ConsumerState { return accountsAsync.when( data: (accounts) { - // Debug logging - debugPrint('BalanceCardCarousel: accounts.length = ${accounts.length}'); - if (accounts.isNotEmpty) { - debugPrint('BalanceCardCarousel: first account = ${accounts[0].name}'); - } - - // Page 0: Total balance - // Pages 1..N: Account cards - // Page N+1: AddAccountCard (if < 5 accounts) final totalPages = 1 + accounts.length + (accounts.length < 5 ? 1 : 0); return Column( children: [ SizedBox( - height: 220, - child: PageView.builder( - clipBehavior: Clip.none, - controller: _pageController, - itemCount: totalPages, - onPageChanged: (index) { - ref.read(activeAccountIndexProvider.notifier).state = index; - }, - itemBuilder: (context, index) { - Widget pageContent; - - if (index == 0) { - // Page 0: Total balance card - pageContent = BalanceCard( - balance: widget.balance, - currencyInfo: widget.currencyInfo, - onLongPress: widget.onLongPress, - previewPrimary: widget.previewPrimary, - previewSecondary: widget.previewSecondary, - previewGradientType: widget.previewGradientType, + height: 230, + // OverflowBox позволяет PageView игнорировать паддинги родителя (DashboardScreen) + // и растянуться на всю ширину экрана + child: OverflowBox( + maxWidth: MediaQuery.of(context).size.width, + child: PageView.builder( + controller: _pageController, + clipBehavior: Clip.none, // Не обрезает карточку при 3D-наклоне + itemCount: totalPages, + onPageChanged: (index) { + ref.read(activeAccountIndexProvider.notifier).state = index; + if (ref.read(hapticEnabledProvider)) { + HapticService.light(); + } + }, + itemBuilder: (context, index) { + Widget cardWidget; + + if (index == 0) { + cardWidget = BalanceCard( + balance: widget.balance, + currencyInfo: widget.currencyInfo, + onLongPress: widget.onLongPress, + previewPrimary: widget.previewPrimary, + previewSecondary: widget.previewSecondary, + previewGradientType: widget.previewGradientType, + ); + } else if (index <= accounts.length) { + final account = accounts[index - 1]; + cardWidget = BalanceCard( + balance: widget.balance, // TODO: Calculate per-account balance + currencyInfo: widget.currencyInfo, + onLongPress: null, + accountName: account.name, + ); + } else { + cardWidget = AddAccountCard( + onTap: () {}, + ); + } + + // Отступ между карточками во время свайпа + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: cardWidget, ); - } else if (index <= accounts.length) { - // Pages 1..N: Account cards - final account = accounts[index - 1]; - debugPrint('BalanceCardCarousel: building account card at index $index for ${account.name}'); - pageContent = _AccountBalanceCard( - account: account, - balance: widget.balance, // TODO: Calculate per-account balance - currencyInfo: widget.currencyInfo, - ); - } else { - // Page N+1: AddAccountCard - pageContent = AddAccountCard( - onTap: () {}, - ); - } - - // Add horizontal padding to create gap between cards - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: pageContent, - ); - }, + }, + ), ), ), const SizedBox(height: 12), @@ -122,27 +121,21 @@ class _BalanceCardCarouselState extends ConsumerState { child: Center(child: CircularProgressIndicator()), ), error: (error, stack) { - debugPrint('BalanceCardCarousel error: $error'); - debugPrint('Stack: $stack'); - // Show total balance card on error return Column( children: [ SizedBox( height: 220, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 6), - child: BalanceCard( - balance: widget.balance, - currencyInfo: widget.currencyInfo, - onLongPress: widget.onLongPress, - previewPrimary: widget.previewPrimary, - previewSecondary: widget.previewSecondary, - previewGradientType: widget.previewGradientType, - ), + child: BalanceCard( + balance: widget.balance, + currencyInfo: widget.currencyInfo, + onLongPress: widget.onLongPress, + previewPrimary: widget.previewPrimary, + previewSecondary: widget.previewSecondary, + previewGradientType: widget.previewGradientType, ), ), const SizedBox(height: 12), - _DotIndicators( + const _DotIndicators( count: 1, activeIndex: 0, ), @@ -153,48 +146,6 @@ class _BalanceCardCarouselState extends ConsumerState { } } -class _AccountBalanceCard extends ConsumerWidget { - final dynamic account; - final double balance; - final CurrencyInfo currencyInfo; - - const _AccountBalanceCard({ - required this.account, - required this.balance, - required this.currencyInfo, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Stack( - clipBehavior: Clip.none, - children: [ - BalanceCard( - balance: balance, - currencyInfo: currencyInfo, - onLongPress: null, // No long press for account cards - previewPrimary: null, - previewSecondary: null, - previewGradientType: null, - ), - Positioned( - top: 16, - left: 24, - child: Text( - account.name, - style: TextStyle( - fontSize: 11, - letterSpacing: 1.5, - color: Colors.white.withOpacity(0.6), - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ); - } -} - class AddAccountCard extends StatelessWidget { final VoidCallback? onTap; @@ -306,4 +257,4 @@ class _DotIndicators extends StatelessWidget { }), ); } -} +} \ No newline at end of file diff --git a/lib/shared/models/transaction.dart b/lib/shared/models/transaction.dart index aae4b01..503dea4 100644 --- a/lib/shared/models/transaction.dart +++ b/lib/shared/models/transaction.dart @@ -16,6 +16,7 @@ class Transaction { final DateTime? lastOccurrence; final String currency; final String currencyCode; + final int accountId; const Transaction({ required this.id, @@ -28,6 +29,7 @@ class Transaction { this.lastOccurrence, this.currency = '\$', this.currencyCode = 'USD', + required this.accountId, }); factory Transaction.fromJson(Map json) => Transaction( @@ -50,6 +52,7 @@ class Transaction { : null, currency: json['currency'] as String? ?? '\$', currencyCode: json['currencyCode'] as String? ?? 'USD', + accountId: json['accountId'] as int, ); Map toJson() => { @@ -63,6 +66,7 @@ class Transaction { 'lastOccurrence': lastOccurrence?.toIso8601String(), 'currency': currency, 'currencyCode': currencyCode, + 'accountId': accountId, }; Transaction copyWith({ @@ -76,6 +80,7 @@ class Transaction { DateTime? lastOccurrence, String? currency, String? currencyCode, + int? accountId, }) => Transaction( id: id ?? this.id, @@ -88,5 +93,6 @@ class Transaction { lastOccurrence: lastOccurrence ?? this.lastOccurrence, currency: currency ?? this.currency, currencyCode: currencyCode ?? this.currencyCode, + accountId: accountId ?? this.accountId, ); } diff --git a/lib/shared/services/storage_service.dart b/lib/shared/services/storage_service.dart index 648e7e7..721c475 100644 --- a/lib/shared/services/storage_service.dart +++ b/lib/shared/services/storage_service.dart @@ -178,6 +178,7 @@ class StorageService { note: tx.note, recurrence: tx.recurrence, lastOccurrence: today, + accountId: tx.accountId, ); transactions.add(newTx); @@ -248,6 +249,7 @@ class StorageService { lastOccurrence: today, currency: tx.currency, currencyCode: tx.currencyCode, + accountId: tx.accountId, ); transactions.add(newTx);