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 '../../../providers/database_provider.dart'; class LoginPage extends ConsumerStatefulWidget { const LoginPage({super.key}); @override ConsumerState createState() => _LoginPageState(); } class _LoginPageState extends ConsumerState { final _codiceCtrl = TextEditingController(); final _passwordCtrl = TextEditingController(); final _urlCtrl = TextEditingController(); final _formKey = GlobalKey(); bool _obscurePassword = true; bool _rememberMe = false; @override void initState() { super.initState(); _loadUrl(); } Future _loadUrl() async { final url = await ref.read(parametriDaoProvider).read('URL'); if (url != null && url.isNotEmpty) { _urlCtrl.text = url; } } @override void dispose() { _codiceCtrl.dispose(); _passwordCtrl.dispose(); _urlCtrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final authState = ref.watch(authProvider); final theme = Theme.of(context); final cs = theme.colorScheme; return Scaffold( body: Stack( children: [ Positioned( top: -MediaQuery.of(context).size.height * 0.08, right: -MediaQuery.of(context).size.width * 0.05, child: Container( width: 320, height: 320, decoration: BoxDecoration( color: cs.secondaryContainer.withValues(alpha: 0.15), shape: BoxShape.circle, ), ), ), Positioned( bottom: -MediaQuery.of(context).size.height * 0.08, left: -MediaQuery.of(context).size.width * 0.05, child: Container( width: 320, height: 320, decoration: BoxDecoration( color: cs.primaryContainer.withValues(alpha: 0.08), shape: BoxShape.circle, ), ), ), Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 16), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 32), _buildBrandHeader(theme, cs), const SizedBox(height: 32), _buildFormCard(authState, theme, cs), const SizedBox(height: 32), _buildFooterLinks(theme, cs), const SizedBox(height: 24), ], ), ), ), ), ], ), ); } Widget _buildBrandHeader(ThemeData theme, ColorScheme cs) { return Column( children: [ Container( width: 64, height: 64, decoration: BoxDecoration( color: cs.primary, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.15), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: const Icon(Icons.bolt, color: Colors.white, size: 40), ), const SizedBox(height: 16), Text( 'PowerAge', style: theme.textTheme.headlineLarge?.copyWith( color: cs.primary, letterSpacing: -0.02, ), ), const SizedBox(height: 8), Text( 'Accedi alla tua piattaforma di vendita ad alte prestazioni.', textAlign: TextAlign.center, style: theme.textTheme.bodyMedium?.copyWith( color: cs.onSurfaceVariant, ), ), ], ); } Widget _buildFormCard(AuthState authState, ThemeData theme, ColorScheme cs) { return Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: cs.outlineVariant), boxShadow: [ BoxShadow( color: const Color(0xFF0F172A).withValues(alpha: 0.05), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Padding( padding: const EdgeInsets.all(24), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildLabeledField( label: 'Username', icon: Icons.person_outline, controller: _codiceCtrl, hintText: 'Inserisci il tuo username', validator: (v) => v == null || v.isEmpty ? 'Inserisci il codice agente' : null, ), const SizedBox(height: 16), _buildPasswordField(cs), const SizedBox(height: 16), _buildLabeledField( label: 'URL di Connessione', icon: Icons.link, controller: _urlCtrl, hintText: 'https://api.powerage.cloud', validator: (v) => v == null || v.isEmpty ? "Inserisci l'URL del backend" : null, ), const SizedBox(height: 8), _buildRememberRow(theme, cs), const SizedBox(height: 20), FilledButton.icon( onPressed: authState.isLoading ? null : () { if (_formKey.currentState!.validate()) { ref .read(authProvider.notifier) .login( _codiceCtrl.text, _passwordCtrl.text, _urlCtrl.text, ); } }, icon: authState.isLoading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Icon(Icons.login), label: Text( authState.isLoading ? 'Verifica in corso...' : 'Accedi', ), ), if (authState.error != null) ...[ const SizedBox(height: 16), _buildErrorCard(authState.error!, cs), ], Padding( padding: const EdgeInsets.symmetric(vertical: 24), child: Row( children: [ Expanded(child: Divider(color: cs.outlineVariant)), Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: Text( 'Configurazione Sistema', style: theme.textTheme.labelMedium?.copyWith( color: cs.onSurfaceVariant, ), ), ), Expanded(child: Divider(color: cs.outlineVariant)), ], ), ), Text( 'Inizializza gli archivi di PowerAge', textAlign: TextAlign.center, style: theme.textTheme.bodyMedium?.copyWith( color: cs.onSurfaceVariant, ), ), const SizedBox(height: 12), OutlinedButton.icon( onPressed: () => context.go('/sync-settings'), icon: const Icon(Icons.settings_backup_restore, size: 18), label: const Text('Inizializza'), ), ], ), ), ), ); } Widget _buildLabeledField({ required String label, required IconData icon, required TextEditingController controller, required String hintText, String? Function(String?)? validator, }) { final cs = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 12, height: 16 / 12, fontWeight: FontWeight.w600, color: cs.onSurface, ), ), const SizedBox(height: 8), TextFormField( controller: controller, decoration: InputDecoration( hintText: hintText, prefixIcon: Icon(icon, size: 20), ), validator: validator, ), ], ); } Widget _buildPasswordField(ColorScheme cs) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Password', style: TextStyle( fontSize: 12, height: 16 / 12, fontWeight: FontWeight.w600, color: cs.onSurface, ), ), const SizedBox(height: 8), TextFormField( controller: _passwordCtrl, obscureText: _obscurePassword, decoration: InputDecoration( hintText: '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022', prefixIcon: const Icon(Icons.lock_outline, size: 20), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, size: 20, ), onPressed: () { setState(() { _obscurePassword = !_obscurePassword; }); }, ), ), ), ], ); } Widget _buildRememberRow(ThemeData theme, ColorScheme cs) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( height: 24, width: 24, child: Checkbox( value: _rememberMe, onChanged: (v) { setState(() { _rememberMe = v ?? false; }); }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), const SizedBox(width: 8), Text( 'Ricordami', style: theme.textTheme.labelMedium?.copyWith( color: cs.onSurfaceVariant, ), ), ], ), TextButton( onPressed: () {}, style: TextButton.styleFrom( padding: EdgeInsets.zero, minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: Text( 'Password dimenticata?', style: theme.textTheme.labelMedium?.copyWith(color: cs.secondary), ), ), ], ); } Widget _buildErrorCard(String error, ColorScheme cs) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: cs.errorContainer, borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon(Icons.error_outline, color: cs.error, size: 20), const SizedBox(width: 8), Expanded( child: Text( error, style: TextStyle(color: cs.onErrorContainer, fontSize: 14), ), ), ], ), ); } Widget _buildFooterLinks(ThemeData theme, ColorScheme cs) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () {}, style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: Text( 'Termini di Servizio', style: theme.textTheme.labelMedium?.copyWith( color: cs.onSurfaceVariant, ), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Text( '\u00B7', style: TextStyle(color: cs.onSurfaceVariant, fontSize: 12), ), ), TextButton( onPressed: () {}, style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: Text( 'Privacy Policy', style: theme.textTheme.labelMedium?.copyWith( color: cs.onSurfaceVariant, ), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: Text( '\u00B7', style: TextStyle(color: cs.onSurfaceVariant, fontSize: 12), ), ), TextButton( onPressed: () {}, style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: Text( 'Supporto', style: theme.textTheme.labelMedium?.copyWith( color: cs.onSurfaceVariant, ), ), ), ], ); } }