| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- 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<LoginPage> createState() => _LoginPageState();
- }
- class _LoginPageState extends ConsumerState<LoginPage> {
- final _codiceCtrl = TextEditingController();
- final _passwordCtrl = TextEditingController();
- final _urlCtrl = TextEditingController();
- final _formKey = GlobalKey<FormState>();
- bool _obscurePassword = true;
- bool _rememberMe = false;
- @override
- void initState() {
- super.initState();
- _loadUrl();
- }
- Future<void> _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,
- ),
- ),
- ),
- ],
- );
- }
- }
|