import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:relationship_saver/core/config/app_config.dart'; import 'package:relationship_saver/core/config/app_theme.dart'; import 'package:relationship_saver/features/auth/session_controller.dart'; import 'package:relationship_saver/features/shared/frosted_card.dart'; import 'package:relationship_saver/integrations/backend/models/backend_models.dart'; class SignInView extends ConsumerStatefulWidget { const SignInView({super.key}); @override ConsumerState createState() => _SignInViewState(); } class _SignInViewState extends ConsumerState { SignInMethod _method = SignInMethod.password; final TextEditingController _emailController = TextEditingController( text: 'demo@example.com', ); final TextEditingController _passwordController = TextEditingController( text: 'password123', ); final TextEditingController _idTokenController = TextEditingController(); @override void dispose() { _emailController.dispose(); _passwordController.dispose(); _idTokenController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final AsyncValue session = ref.watch( sessionControllerProvider, ); final bool busy = session.isLoading; final Object? error = session.asError?.error; return Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 620), child: Padding( padding: const EdgeInsets.all(24), child: FrostedCard( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Sign in to continue', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 8), Text( 'Your local data remains available offline. Backend unlocks sync and signals.', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: AppTheme.textSecondary, ), ), const SizedBox(height: 18), SegmentedButton( segments: const >[ ButtonSegment( value: SignInMethod.password, label: Text('Password'), ), ButtonSegment( value: SignInMethod.emailMagicLink, label: Text('Magic Link'), ), ButtonSegment( value: SignInMethod.oidc, label: Text('OIDC'), ), ], selected: {_method}, onSelectionChanged: busy ? null : (Set value) { setState(() { _method = value.first; }); }, ), const SizedBox(height: 14), if (_method == SignInMethod.password || _method == SignInMethod.emailMagicLink) TextField( controller: _emailController, keyboardType: TextInputType.emailAddress, enabled: !busy, decoration: const InputDecoration(labelText: 'Email'), ), if (_method == SignInMethod.password) TextField( controller: _passwordController, obscureText: true, enabled: !busy, decoration: const InputDecoration(labelText: 'Password'), ), if (_method == SignInMethod.oidc) TextField( controller: _idTokenController, enabled: !busy, decoration: const InputDecoration(labelText: 'ID Token'), ), if (error != null) Padding( padding: const EdgeInsets.only(top: 10), child: Text( 'Sign-in failed: $error', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: const Color(0xFFC83030), ), ), ), const SizedBox(height: 16), Row( children: [ FilledButton.icon( onPressed: busy ? null : _submit, icon: busy ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Icon(Icons.login_rounded), label: const Text('Sign In'), ), const SizedBox(width: 12), Text( AppConfig.useFakeBackend ? 'Fake mode enabled' : 'REST mode (${AppConfig.backendBaseUrl})', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: AppTheme.textSecondary, ), ), ], ), ], ), ), ), ), ); } Future _submit() async { final String? email = _emailController.text.trim().isEmpty ? null : _emailController.text.trim(); final String? password = _passwordController.text.trim().isEmpty ? null : _passwordController.text.trim(); final String? idToken = _idTokenController.text.trim().isEmpty ? null : _idTokenController.text.trim(); await ref .read(sessionControllerProvider.notifier) .signIn( method: _method, email: email, password: password, idToken: idToken, ); } }