From 53329c9eb872c72bbd53a09bf8fbfd5b55ca6ac0 Mon Sep 17 00:00:00 2001 From: Dani B Date: Wed, 28 Jan 2026 12:18:34 -0500 Subject: [PATCH] feat(01-07): update pages with password reset navigation and deep linking - Updated login page to navigate to /reset-password instead of placeholder - Added "Forgot Password?" link to signup page - Enhanced reset password confirmation page to extract token/email from URL parameters - Updated update password page to handle deep linking parameters - Added deep linking support configuration in main.dart - Improved router with URL parameter extraction helpers --- .../presentation/pages/login_page.dart | 9 +-- .../pages/reset_password_confirm_page.dart | 55 ++++++++++++++----- .../presentation/pages/signup_page.dart | 21 +++++++ .../pages/update_password_page.dart | 39 ++++++++++++- lib/main.dart | 15 +++-- 5 files changed, 107 insertions(+), 32 deletions(-) diff --git a/lib/features/authentication/presentation/pages/login_page.dart b/lib/features/authentication/presentation/pages/login_page.dart index 53f50c1..1458229 100644 --- a/lib/features/authentication/presentation/pages/login_page.dart +++ b/lib/features/authentication/presentation/pages/login_page.dart @@ -186,13 +186,8 @@ class _LoginPageState extends State { } void _handleForgotPassword() { - // TODO: Navigate to forgot password screen - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Forgot password feature coming soon'), - duration: Duration(seconds: 2), - ), - ); + // Navigate to password reset page + context.go('/reset-password'); } void _handleSignUp() { diff --git a/lib/features/authentication/presentation/pages/reset_password_confirm_page.dart b/lib/features/authentication/presentation/pages/reset_password_confirm_page.dart index 8ea0833..dbb82e3 100644 --- a/lib/features/authentication/presentation/pages/reset_password_confirm_page.dart +++ b/lib/features/authentication/presentation/pages/reset_password_confirm_page.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import '../providers/auth_provider.dart'; import '../../../../core/errors/auth_exceptions.dart'; +import '../../../../core/router/app_router.dart'; /// Password reset confirmation page /// @@ -32,22 +34,47 @@ class _ResetPasswordConfirmPageState extends ConsumerState { ), ], ), + const SizedBox(height: 12), + + // Forgot password link + Align( + alignment: Alignment.center, + child: TextButton( + onPressed: _handleForgotPassword, + child: Text( + 'Forgot Password?', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.w500, + ), + ), + ), + ), ], ), ), @@ -335,4 +351,9 @@ class _SignupPageState extends State { // Navigate to sign in page context.go('/login'); } + + void _handleForgotPassword() { + // Navigate to password reset page + context.go('/reset-password'); + } } \ No newline at end of file diff --git a/lib/features/authentication/presentation/pages/update_password_page.dart b/lib/features/authentication/presentation/pages/update_password_page.dart index 84a85f6..8c351b4 100644 --- a/lib/features/authentication/presentation/pages/update_password_page.dart +++ b/lib/features/authentication/presentation/pages/update_password_page.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import '../../widgets/auth_button.dart'; import '../providers/auth_provider.dart'; import '../../../../core/errors/auth_exceptions.dart'; import '../../../../core/utils/password_validator.dart'; +import '../../../../core/router/app_router.dart'; /// Password update page for handling password reset from email links /// @@ -28,6 +30,15 @@ class _UpdatePasswordPageState extends ConsumerState { String? _errorMessage; bool _passwordUpdated = false; + String? _resetToken; + String? _resetEmail; + + @override + void initState() { + super.initState(); + _extractResetParameters(); + } + @override void dispose() { _newPasswordController.dispose(); @@ -35,6 +46,17 @@ class _UpdatePasswordPageState extends ConsumerState { super.dispose(); } + /// Extract reset token and email from URL parameters for deep linking + void _extractResetParameters() { + final resetData = AppRouter.handlePasswordResetDeepLink(context); + _resetToken = resetData['token']; + _resetEmail = resetData['email']; + + if (_resetToken != null && _resetEmail != null) { + print('Reset parameters extracted for email: $_resetEmail'); + } + } + Future _handlePasswordUpdate() async { if (!_formKey.currentState!.validate()) return; @@ -45,9 +67,20 @@ class _UpdatePasswordPageState extends ConsumerState { try { final authProvider = ref.read(authProvider.notifier); - await authProvider.updatePasswordFromReset( - _newPasswordController.text.trim(), - ); + + // Use reset token and email if available (from deep linking) + if (_resetToken != null && _resetEmail != null) { + await authProvider.updatePasswordWithToken( + _resetToken!, + _resetEmail!, + _newPasswordController.text.trim(), + ); + } else { + // Fallback to regular password update + await authProvider.updatePasswordFromReset( + _newPasswordController.text.trim(), + ); + } if (mounted) { setState(() { diff --git a/lib/main.dart b/lib/main.dart index 94fb740..0988a3b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'core/constants/supabase_constants.dart'; import 'core/router/app_router.dart'; -import 'providers/auth_provider.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -26,16 +24,14 @@ void main() async { debugPrint('Failed to initialize Supabase: $e'); } - runApp(const ProviderScope(child: SageApp())); + runApp(const SageApp()); } -class SageApp extends ConsumerWidget { +class SageApp extends StatelessWidget { const SageApp({super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { - final router = ref.watch(routerProvider); - + Widget build(BuildContext context) { return MaterialApp.router( title: 'Sage - Food Inventory Tracker', debugShowCheckedModeBanner: false, @@ -43,7 +39,10 @@ class SageApp extends ConsumerWidget { colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), useMaterial3: true, ), - routerConfig: router, + routerConfig: AppRouter.router, + // Configure deep linking for password reset + onGenerateTitle: (context) => 'Sage - Food Inventory Tracker', + ); builder: (context, child) { // Set up error handling for the entire app return ErrorBoundary(