From 864560ef2eaf7aa3ac1a0ded15bac47b2253526e Mon Sep 17 00:00:00 2001 From: m3mo Date: Mon, 2 Feb 2026 21:07:27 +0100 Subject: [PATCH] Fix calendar navigation, task deletion, and language defaults - Add date parameter support to DailyAgendaPage for calendar navigation - Add popup menu to task tile with delete and reschedule options - Set default locale to English when none is saved - Update API base URL comment for desktop vs Android --- .../presentation/pages/settings_page.dart | 48 ++++++++--------- .../viewmodels/settings_viewmodel.dart | 2 +- .../datasources/task_remote_datasource.dart | 3 +- .../presentation/pages/daily_agenda_page.dart | 17 +++++- .../tasks/presentation/widgets/task_tile.dart | 53 +++++++++++++++++-- lib/routing/app_router.dart | 5 +- 6 files changed, 93 insertions(+), 35 deletions(-) diff --git a/lib/features/settings/presentation/pages/settings_page.dart b/lib/features/settings/presentation/pages/settings_page.dart index 6415a48..a1d9da9 100644 --- a/lib/features/settings/presentation/pages/settings_page.dart +++ b/lib/features/settings/presentation/pages/settings_page.dart @@ -27,11 +27,7 @@ class SettingsPage extends StatelessWidget { ListTile( leading: const Icon(Icons.language), title: Text(l10n.language), - subtitle: Text( - settingsVm.locale != null - ? settingsVm.getLanguageName(settingsVm.locale!) - : l10n.systemDefault, - ), + subtitle: Text(settingsVm.getLanguageName(settingsVm.locale!)), trailing: const Icon(Icons.chevron_right), onTap: () => _showLanguageDialog(context, settingsVm, l10n), ), @@ -107,12 +103,12 @@ class _LanguageDialog extends StatefulWidget { } class _LanguageDialogState extends State<_LanguageDialog> { - late Locale? _selectedLocale; + late String _selectedLanguage; @override void initState() { super.initState(); - _selectedLocale = widget.vm.locale; + _selectedLanguage = widget.vm.locale!.languageCode; } @override @@ -121,27 +117,27 @@ class _LanguageDialogState extends State<_LanguageDialog> { title: Text(widget.l10n.language), content: Column( mainAxisSize: MainAxisSize.min, - children: SettingsViewModel.supportedLocales.map((locale) { - return ListTile( - title: Text(widget.vm.getLanguageName(locale)), - leading: Radio( - value: locale, - groupValue: _selectedLocale, - onChanged: (value) { - setState(() => _selectedLocale = value); - if (value != null) { - widget.vm.setLocale(value); + children: [ + ...SettingsViewModel.supportedLocales.map((locale) { + return ListTile( + title: Text(widget.vm.getLanguageName(locale)), + leading: Radio( + value: locale.languageCode, + groupValue: _selectedLanguage, + onChanged: (value) { + setState(() => _selectedLanguage = value!); + widget.vm.setLocale(locale); Navigator.pop(context); - } + }, + ), + onTap: () { + setState(() => _selectedLanguage = locale.languageCode); + widget.vm.setLocale(locale); + Navigator.pop(context); }, - ), - onTap: () { - setState(() => _selectedLocale = locale); - widget.vm.setLocale(locale); - Navigator.pop(context); - }, - ); - }).toList(), + ); + }), + ], ), actions: [ TextButton( diff --git a/lib/features/settings/presentation/viewmodels/settings_viewmodel.dart b/lib/features/settings/presentation/viewmodels/settings_viewmodel.dart index 7341ead..b95bce0 100644 --- a/lib/features/settings/presentation/viewmodels/settings_viewmodel.dart +++ b/lib/features/settings/presentation/viewmodels/settings_viewmodel.dart @@ -26,7 +26,7 @@ class SettingsViewModel extends ChangeNotifier { ]; void _loadSettings() { - _locale = dataSource.getLocale(); + _locale = dataSource.getLocale() ?? const Locale('en'); _themeMode = dataSource.getThemeMode(); logger.info('Settings loaded: locale=$_locale, themeMode=$_themeMode'); } diff --git a/lib/features/tasks/data/datasources/task_remote_datasource.dart b/lib/features/tasks/data/datasources/task_remote_datasource.dart index 42dcde6..f550081 100644 --- a/lib/features/tasks/data/datasources/task_remote_datasource.dart +++ b/lib/features/tasks/data/datasources/task_remote_datasource.dart @@ -21,7 +21,8 @@ class TaskRemoteDataSourceImpl implements TaskRemoteDataSource { final AppLogger logger; final http.Client _client; - static const String _baseUrl = 'http://10.0.2.2:8000'; + // Use 10.0.2.2 for Android emulator, localhost for desktop/web + static const String _baseUrl = 'http://localhost:8000'; TaskRemoteDataSourceImpl({ required this.logger, diff --git a/lib/features/tasks/presentation/pages/daily_agenda_page.dart b/lib/features/tasks/presentation/pages/daily_agenda_page.dart index bbcef20..13e39ae 100644 --- a/lib/features/tasks/presentation/pages/daily_agenda_page.dart +++ b/lib/features/tasks/presentation/pages/daily_agenda_page.dart @@ -10,12 +10,25 @@ import '../widgets/task_tile.dart'; import '../widgets/filter_chips.dart'; class DailyAgendaPage extends StatelessWidget { - const DailyAgendaPage({super.key}); + final String? initialDate; + + const DailyAgendaPage({super.key, this.initialDate}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( - create: (_) => getIt()..loadTasks(), + create: (_) { + final vm = getIt(); + if (initialDate != null) { + final date = DateTime.tryParse(initialDate!); + if (date != null) { + vm.setSelectedDate(date); + return vm; + } + } + vm.loadTasks(); + return vm; + }, child: const _DailyAgendaView(), ); } diff --git a/lib/features/tasks/presentation/widgets/task_tile.dart b/lib/features/tasks/presentation/widgets/task_tile.dart index a039299..84c4618 100644 --- a/lib/features/tasks/presentation/widgets/task_tile.dart +++ b/lib/features/tasks/presentation/widgets/task_tile.dart @@ -118,10 +118,55 @@ class TaskTile extends StatelessWidget { ], ), ), - IconButton( - icon: const Icon(Icons.schedule), - onPressed: onReschedule, - tooltip: l10n.rescheduleToTomorrow, + PopupMenuButton( + onSelected: (value) async { + if (value == 'reschedule') { + onReschedule(); + } else if (value == 'delete') { + final confirm = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(l10n.deleteTask), + content: Text(l10n.deleteTaskConfirm), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: Text(l10n.cancel), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: Text(l10n.delete), + ), + ], + ), + ); + if (confirm == true) { + onDelete(); + } + } + }, + itemBuilder: (context) => [ + PopupMenuItem( + value: 'reschedule', + child: Row( + children: [ + const Icon(Icons.schedule), + const SizedBox(width: 8), + Text(l10n.rescheduleToTomorrow), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + Icon(Icons.delete, color: Theme.of(context).colorScheme.error), + const SizedBox(width: 8), + Text(l10n.delete, style: TextStyle(color: Theme.of(context).colorScheme.error)), + ], + ), + ), + ], ), ], ), diff --git a/lib/routing/app_router.dart b/lib/routing/app_router.dart index 6c37af8..fdc2a7a 100644 --- a/lib/routing/app_router.dart +++ b/lib/routing/app_router.dart @@ -12,7 +12,10 @@ class AppRouter { GoRoute( path: '/', name: 'daily', - builder: (context, state) => const DailyAgendaPage(), + builder: (context, state) { + final dateStr = state.uri.queryParameters['date']; + return DailyAgendaPage(initialDate: dateStr); + }, ), GoRoute( path: '/calendar',