From e0fa55c6f595c26a4b0a53fdf7583bea4bbee91b Mon Sep 17 00:00:00 2001 From: kolo Date: Tue, 24 Mar 2026 16:40:00 +0300 Subject: [PATCH] update --- lib/data/repositories/account_repository.dart | 16 + lib/features/dashboard/screen.dart | 30 +- .../widgets/account_editor_overlay.dart | 278 ++++++++++++------ 3 files changed, 213 insertions(+), 111 deletions(-) diff --git a/lib/data/repositories/account_repository.dart b/lib/data/repositories/account_repository.dart index cab3bed..dd336c6 100644 --- a/lib/data/repositories/account_repository.dart +++ b/lib/data/repositories/account_repository.dart @@ -171,4 +171,20 @@ class AccountRepository { createdAt: Value(account.createdAt), )); } + + Future add(model.Account account) async { + await _db.into(_db.accounts).insert( + AccountsCompanion.insert( + id: Value(account.id), + name: account.name, + isMain: Value(account.isMain), + currency: Value(account.currency), + sortOrder: Value(account.sortOrder), + ), + ); + } + + Future delete(int id) async { + await (_db.delete(_db.accounts)..where((a) => a.id.equals(id))).go(); + } } diff --git a/lib/features/dashboard/screen.dart b/lib/features/dashboard/screen.dart index bb23b37..3e31135 100644 --- a/lib/features/dashboard/screen.dart +++ b/lib/features/dashboard/screen.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; -import 'package:drift/drift.dart' as drift; import '../../core/l10n/locale_provider.dart'; import '../../core/services/card_color_service.dart'; import '../../core/services/haptic_service.dart'; -import '../../data/database/app_database.dart' hide Account; import '../../shared/models/account.dart'; import '../settings/provider.dart'; import 'provider.dart'; @@ -163,23 +161,23 @@ class _DashboardScreenState extends ConsumerState { if (isAddingAccount) { // Create new account - final newId = DateTime.now().millisecondsSinceEpoch; - - // Insert the new account using Drift's insert method - final db = ref.read(appDatabaseProvider); - await db.into(db.accounts).insert( - AccountsCompanion.insert( - id: drift.Value(newId), - name: tempAccountName.trim(), - isMain: const drift.Value(false), - currency: drift.Value(tempAccountCurrency), - sortOrder: const drift.Value(99), - ), + final newAccount = Account( + id: DateTime.now().millisecondsSinceEpoch, + name: tempAccountName.trim(), + isMain: false, + sortOrder: 99, + currency: tempAccountCurrency, + createdAt: DateTime.now(), ); - // Save the chosen colors for the newly created account + await ref.read(accountRepositoryProvider).add(newAccount); + + // FIX: Fetch the actual ID assigned by the database to save colors correctly + final accounts = await ref.read(accountRepositoryProvider).getAll(); + final actualNewAccount = accounts.lastWhere((a) => a.name == tempAccountName.trim()); + await ref - .read(accountCardColorsProvider(newId).notifier) + .read(accountCardColorsProvider(actualNewAccount.id).notifier) .save(tempPrimary, tempSecondary, tempGradientType); } else if (editingAccount != null) { // Existing edit logic diff --git a/lib/features/dashboard/widgets/account_editor_overlay.dart b/lib/features/dashboard/widgets/account_editor_overlay.dart index 07093ad..57fa696 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay.dart @@ -36,6 +36,7 @@ class _AccountEditorOverlayState extends State { late String _selectedCurrency; bool _showCurrencyDropdown = false; bool _showLimitError = false; + bool _showDeleteDialog = false; @override void initState() { @@ -260,36 +261,117 @@ class _AccountEditorOverlayState extends State { ), ), ), - // Close Button - Top Right + // Top Right Buttons (Delete & Close) Positioned( top: mq.padding.top + 8, right: 20, child: SafeArea( - child: GestureDetector( - onTap: () => dash.closeAccountOverlay(apply: false), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: Theme.of(widget.context).colorScheme.surface, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.3), - blurRadius: 8, - offset: const Offset(0, 2), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (!dash.isAddingAccount && (ref.watch(accountsProvider).valueOrNull?.length ?? 0) > 1) ...[ + GestureDetector( + onTap: () => setState(() => _showDeleteDialog = true), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Theme.of(widget.context).colorScheme.surface, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.red.withOpacity(0.2), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: const Icon( + Icons.delete_outline_rounded, + size: 22, + color: Colors.red, + ), ), - ], + ), + const SizedBox(width: 12), + ], + GestureDetector( + onTap: () => dash.closeAccountOverlay(apply: false), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Theme.of(widget.context).colorScheme.surface, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Icon( + Icons.close_rounded, + size: 24, + color: Theme.of(widget.context).colorScheme.onSurface, + ), + ), ), - child: Icon( - Icons.close_rounded, - size: 24, - color: Theme.of(widget.context).colorScheme.onSurface, + ], + ), + ), + ), + // Custom Dialog Overlay + if (_showDeleteDialog) + Positioned.fill( + child: Container( + color: Colors.black.withOpacity(0.4), + child: Center( + child: Material( + color: Colors.transparent, + child: AlertDialog( + backgroundColor: Theme.of(widget.context).colorScheme.surface, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + title: const Text('Delete Account?'), + content: const Text( + 'Are you sure you want to delete this account? All associated transactions will also be permanently deleted.', + ), + actions: [ + TextButton( + onPressed: () => setState(() => _showDeleteDialog = false), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () async { + if (dash.editingAccount == null) return; + + final accountId = dash.editingAccount!.id; + + dash.closeAccountOverlay(apply: false); + + final txs = ref.read(transactionsProvider).valueOrNull ?? []; + final accountTxs = txs.where((t) => t.accountId == accountId).toList(); + for (final t in accountTxs) { + await ref.read(transactionsProvider.notifier).delete(t.id); + } + + await ref.read(accountRepositoryProvider).delete(accountId); + + if (ref.read(hapticEnabledProvider)) { + HapticService.medium(); + } + }, + child: const Text('Delete', style: TextStyle(color: Colors.red)), + ), + ], + ), ), ), ), ), - ), ], ), ); @@ -298,45 +380,47 @@ class _AccountEditorOverlayState extends State { } Widget _buildEditorPanel(double panelHeight) { - return Container( - height: panelHeight, - decoration: BoxDecoration( - color: Theme.of(widget.context).colorScheme.surface, - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.1), - width: 1.5, - ), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.3), - blurRadius: 24, - offset: const Offset(0, 8), - ), - ], - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Account Settings', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.6), - ), + return Consumer( + builder: (context, ref, _) { + return Container( + height: panelHeight, + decoration: BoxDecoration( + color: Theme.of(widget.context).colorScheme.surface, + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.1), + width: 1.5, ), - const SizedBox(height: 8), - IntrinsicHeight( - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 3, - child: TextField( + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + blurRadius: 24, + offset: const Offset(0, 8), + ), + ], + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Account Settings', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.6), + ), + ), + const SizedBox(height: 8), + IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: TextField( controller: _nameController, buildCounter: (context, {required currentLength, required isFocused, maxLength}) => null, style: const TextStyle(fontSize: 13), @@ -423,6 +507,8 @@ class _AccountEditorOverlayState extends State { ), ), ); + }, + ); } Widget _buildColorPanel(double panelHeight) { @@ -521,48 +607,50 @@ class _AccountEditorOverlayState extends State { margin: const EdgeInsets.symmetric(vertical: 4), ), ), - GestureDetector( - onTap: isSolid ? null : () { - dash.setState(() { - dash.tempGradientType = GradientType.solid; - dash.editingPrimary = true; - }); - setPanelState(() {}); - dash.overlayEntry?.markNeedsBuild(); - }, - child: Container( - height: double.infinity, - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 6), - decoration: BoxDecoration( - color: isSolid - ? const Color(0xFF7C6DED).withOpacity(0.15) - : Colors.transparent, - borderRadius: BorderRadius.circular(10), - border: Border.all( + Expanded( + child: GestureDetector( + onTap: isSolid ? null : () { + dash.setState(() { + dash.tempGradientType = GradientType.solid; + dash.editingPrimary = true; + }); + setPanelState(() {}); + dash.overlayEntry?.markNeedsBuild(); + }, + child: Container( + height: double.infinity, + padding: const EdgeInsets.symmetric( + horizontal: 4, vertical: 6), + decoration: BoxDecoration( color: isSolid - ? const Color(0xFF7C6DED) - : Theme.of(widget.context) - .colorScheme - .onSurface - .withOpacity(0.2), - width: 1.5, - ), - ), - child: Center( - child: Text( - s.colorSolid, - style: TextStyle( - fontSize: 11, - fontWeight: isSolid - ? FontWeight.w600 - : FontWeight.normal, + ? const Color(0xFF7C6DED).withOpacity(0.15) + : Colors.transparent, + borderRadius: BorderRadius.circular(10), + border: Border.all( color: isSolid ? const Color(0xFF7C6DED) : Theme.of(widget.context) .colorScheme .onSurface - .withOpacity(0.5), + .withOpacity(0.2), + width: 1.5, + ), + ), + child: Center( + child: Text( + s.colorSolid, + style: TextStyle( + fontSize: 11, + fontWeight: isSolid + ? FontWeight.w600 + : FontWeight.normal, + color: isSolid + ? const Color(0xFF7C6DED) + : Theme.of(widget.context) + .colorScheme + .onSurface + .withOpacity(0.5), + ), ), ), ), @@ -937,7 +1025,7 @@ class PanelTab extends StatelessWidget { child: AnimatedContainer( duration: const Duration(milliseconds: 150), width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8), decoration: BoxDecoration( color: isSelected ? const Color(0xFF7C6DED).withOpacity(0.15) : Colors.transparent, borderRadius: BorderRadius.circular(10),