Prechádzať zdrojové kódy

WIP: initial redesign

Fabio Dalle Ave 2 mesiacov pred
rodič
commit
22fe66b571

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
.flutter-plugins-dependencies


+ 2 - 2
lib/app.dart

@@ -11,11 +11,11 @@ class RistorisApp extends ConsumerWidget {
     final router = ref.watch(routerProvider);
 
     return MaterialApp.router(
-      title: 'Ristoris',
+      title: 'PowerAge',
       debugShowCheckedModeBanner: false,
       theme: AppTheme.light,
       darkTheme: AppTheme.dark,
-      themeMode: ThemeMode.system,
+      themeMode: ThemeMode.light, //ThemeMode.system,
       routerConfig: router,
     );
   }

+ 167 - 16
lib/core/theme.dart

@@ -1,26 +1,128 @@
 import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
 
 class AppTheme {
   static ThemeData get light {
-    final colorScheme = ColorScheme.fromSeed(
-      seedColor: const Color(0xFF1A6B4C),
-      brightness: Brightness.light,
-    );
-    return _buildTheme(colorScheme);
+    return _buildTheme(_lightColorScheme, Brightness.light);
   }
 
   static ThemeData get dark {
-    final colorScheme = ColorScheme.fromSeed(
-      seedColor: const Color(0xFF1A6B4C),
-      brightness: Brightness.dark,
-    );
-    return _buildTheme(colorScheme);
+    return _buildTheme(_darkColorScheme, Brightness.dark);
   }
 
-  static ThemeData _buildTheme(ColorScheme colorScheme) {
+  static const ColorScheme _lightColorScheme = ColorScheme(
+    brightness: Brightness.light,
+    primary: Color(0xFF000000),
+    onPrimary: Color(0xFFFFFFFF),
+    primaryContainer: Color(0xFF131B2E),
+    onPrimaryContainer: Color(0xFF7C839B),
+    secondary: Color(0xFF0058BE),
+    onSecondary: Color(0xFFFFFFFF),
+    secondaryContainer: Color(0xFF2170E4),
+    onSecondaryContainer: Color(0xFFFEFCFF),
+    tertiary: Color(0xFF000000),
+    onTertiary: Color(0xFFFFFFFF),
+    tertiaryContainer: Color(0xFF191C1E),
+    onTertiaryContainer: Color(0xFF818486),
+    error: Color(0xFFBA1A1A),
+    onError: Color(0xFFFFFFFF),
+    errorContainer: Color(0xFFFFDAD6),
+    onErrorContainer: Color(0xFF93000A),
+    background: Color(0xFFF8F9FF),
+    onBackground: Color(0xFF0B1C30),
+    surface: Color(0xFFF8F9FF),
+    onSurface: Color(0xFF0B1C30),
+    surfaceVariant: Color(0xFFD3E4FE),
+    onSurfaceVariant: Color(0xFF45464D),
+    outline: Color(0xFF76777D),
+    outlineVariant: Color(0xFFC6C6CD),
+    inverseSurface: Color(0xFF213145),
+    //inverseOnSurface: Color(0xFFEAF1FF),
+    inversePrimary: Color(0xFFBEC6E0),
+    surfaceTint: Color(0xFF565E74),
+  );
+
+  static const ColorScheme _darkColorScheme = ColorScheme(
+    brightness: Brightness.dark,
+    primary: Color(0xFFBEC6E0),
+    onPrimary: Color(0xFF131B2E),
+    primaryContainer: Color(0xFF3F465C),
+    onPrimaryContainer: Color(0xFFDAE2FD),
+    secondary: Color(0xFFADC6FF),
+    onSecondary: Color(0xFF001A42),
+    secondaryContainer: Color(0xFF004395),
+    onSecondaryContainer: Color(0xFFD8E2FF),
+    tertiary: Color(0xFFC4C7C9),
+    onTertiary: Color(0xFF191C1E),
+    tertiaryContainer: Color(0xFF444749),
+    onTertiaryContainer: Color(0xFFE0E3E5),
+    error: Color(0xFFFFB4AB),
+    onError: Color(0xFF690005),
+    errorContainer: Color(0xFF93000A),
+    onErrorContainer: Color(0xFFFFDAD6),
+    background: Color(0xFF111318),
+    onBackground: Color(0xFFEAF1FF),
+    surface: Color(0xFF111318),
+    onSurface: Color(0xFFEAF1FF),
+    surfaceVariant: Color(0xFF2D3138),
+    onSurfaceVariant: Color(0xFFC6C6CD),
+    outline: Color(0xFF8E9099),
+    outlineVariant: Color(0xFF45464D),
+    inverseSurface: Color(0xFFEAF1FF),
+    //inverseOnSurface: Color(0xFF213145),
+    inversePrimary: Color(0xFF000000),
+    surfaceTint: Color(0xFF565E74),
+  );
+
+  static ThemeData _buildTheme(ColorScheme colorScheme, Brightness brightness) {
+    final textTheme = GoogleFonts.interTextTheme(
+      brightness == Brightness.dark
+          ? ThemeData.dark().textTheme
+          : ThemeData.light().textTheme,
+    );
+
     return ThemeData(
       useMaterial3: true,
       colorScheme: colorScheme,
+      textTheme: textTheme.copyWith(
+        displayLarge: textTheme.displayLarge?.copyWith(
+          fontSize: 48,
+          height: 56 / 48,
+          letterSpacing: -0.02,
+          fontWeight: FontWeight.w700,
+        ),
+        headlineLarge: textTheme.headlineLarge?.copyWith(
+          fontSize: 32,
+          height: 40 / 32,
+          letterSpacing: -0.02,
+          fontWeight: FontWeight.w700,
+        ),
+        headlineMedium: textTheme.headlineMedium?.copyWith(
+          fontSize: 24,
+          height: 32 / 24,
+          fontWeight: FontWeight.w600,
+        ),
+        titleLarge: textTheme.titleLarge?.copyWith(
+          fontSize: 20,
+          height: 28 / 20,
+          fontWeight: FontWeight.w600,
+        ),
+        bodyLarge: textTheme.bodyLarge?.copyWith(
+          fontSize: 16,
+          height: 24 / 16,
+          fontWeight: FontWeight.w400,
+        ),
+        bodyMedium: textTheme.bodyMedium?.copyWith(
+          fontSize: 14,
+          height: 20 / 14,
+          fontWeight: FontWeight.w400,
+        ),
+        labelMedium: textTheme.labelMedium?.copyWith(
+          fontSize: 12,
+          height: 16 / 12,
+          fontWeight: FontWeight.w600,
+        ),
+      ),
       appBarTheme: AppBarTheme(
         centerTitle: false,
         elevation: 0,
@@ -37,16 +139,50 @@ class AppTheme {
       ),
       inputDecorationTheme: InputDecorationTheme(
         filled: true,
+        fillColor: colorScheme.surface,
         border: OutlineInputBorder(
-          borderRadius: BorderRadius.circular(12),
+          borderRadius: BorderRadius.circular(8),
+          borderSide: BorderSide(color: colorScheme.outlineVariant),
+        ),
+        enabledBorder: OutlineInputBorder(
+          borderRadius: BorderRadius.circular(8),
+          borderSide: BorderSide(color: colorScheme.outlineVariant),
+        ),
+        focusedBorder: OutlineInputBorder(
+          borderRadius: BorderRadius.circular(8),
+          borderSide: BorderSide(color: colorScheme.secondary, width: 1),
+        ),
+        errorBorder: OutlineInputBorder(
+          borderRadius: BorderRadius.circular(8),
+          borderSide: BorderSide(color: colorScheme.error),
+        ),
+        contentPadding: const EdgeInsets.symmetric(
+          horizontal: 16,
+          vertical: 14,
+        ),
+        prefixIconColor: colorScheme.onSurfaceVariant,
+        labelStyle: TextStyle(color: colorScheme.onSurfaceVariant),
+      ),
+      filledButtonTheme: FilledButtonThemeData(
+        style: FilledButton.styleFrom(
+          backgroundColor: colorScheme.primary,
+          foregroundColor: colorScheme.onPrimary,
+          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
+          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
+          textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
+        ),
+      ),
+      outlinedButtonTheme: OutlinedButtonThemeData(
+        style: OutlinedButton.styleFrom(
+          foregroundColor: colorScheme.secondary,
+          side: BorderSide(color: colorScheme.secondary.withValues(alpha: 0.2)),
+          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
+          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
         ),
-        contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
       ),
       floatingActionButtonTheme: FloatingActionButtonThemeData(
         elevation: 2,
-        shape: RoundedRectangleBorder(
-          borderRadius: BorderRadius.circular(16),
-        ),
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
       ),
       navigationBarTheme: NavigationBarThemeData(
         elevation: 1,
@@ -60,6 +196,21 @@ class AppTheme {
           borderRadius: BorderRadius.circular(12),
         ),
       ),
+      dividerTheme: DividerThemeData(
+        color: colorScheme.outlineVariant,
+        thickness: 1,
+      ),
+      checkboxTheme: CheckboxThemeData(
+        fillColor: WidgetStateProperty.resolveWith((states) {
+          if (states.contains(WidgetState.selected)) {
+            return colorScheme.secondary;
+          }
+          return Colors.transparent;
+        }),
+        checkColor: WidgetStateProperty.all(Colors.white),
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
+        side: BorderSide(color: colorScheme.outlineVariant),
+      ),
     );
   }
 }

+ 28 - 28
lib/database/database.g.dart

@@ -7732,7 +7732,7 @@ class $ListiniArticoloTable extends ListiniArticolo
   );
   @override
   late final GeneratedColumn<int> idListinoArticoli = GeneratedColumn<int>(
-    'idListino_articoli',
+    'idListinoArticoli',
     aliasedName,
     false,
     hasAutoIncrement: true,
@@ -7839,11 +7839,11 @@ class $ListiniArticoloTable extends ListiniArticolo
   }) {
     final context = VerificationContext();
     final data = instance.toColumns(true);
-    if (data.containsKey('idListino_articoli')) {
+    if (data.containsKey('idListinoArticoli')) {
       context.handle(
         _idListinoArticoliMeta,
         idListinoArticoli.isAcceptableOrUnknown(
-          data['idListino_articoli']!,
+          data['idListinoArticoli']!,
           _idListinoArticoliMeta,
         ),
       );
@@ -7901,7 +7901,7 @@ class $ListiniArticoloTable extends ListiniArticolo
     return ListinoArticolo(
       idListinoArticoli: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
-        data['${effectivePrefix}idListino_articoli'],
+        data['${effectivePrefix}idListinoArticoli'],
       )!,
       idListino: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
@@ -7962,7 +7962,7 @@ class ListinoArticolo extends DataClass implements Insertable<ListinoArticolo> {
   @override
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
-    map['idListino_articoli'] = Variable<int>(idListinoArticoli);
+    map['idListinoArticoli'] = Variable<int>(idListinoArticoli);
     if (!nullToAbsent || idListino != null) {
       map['idListino'] = Variable<int>(idListino);
     }
@@ -8157,7 +8157,7 @@ class ListiniArticoloCompanion extends UpdateCompanion<ListinoArticolo> {
     Expression<int>? attivo,
   }) {
     return RawValuesInsertable({
-      if (idListinoArticoli != null) 'idListino_articoli': idListinoArticoli,
+      if (idListinoArticoli != null) 'idListinoArticoli': idListinoArticoli,
       if (idListino != null) 'idListino': idListino,
       if (idArticolo != null) 'idArticolo': idArticolo,
       if (prezzo != null) 'prezzo': prezzo,
@@ -8194,7 +8194,7 @@ class ListiniArticoloCompanion extends UpdateCompanion<ListinoArticolo> {
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
     if (idListinoArticoli.present) {
-      map['idListino_articoli'] = Variable<int>(idListinoArticoli.value);
+      map['idListinoArticoli'] = Variable<int>(idListinoArticoli.value);
     }
     if (idListino.present) {
       map['idListino'] = Variable<int>(idListino.value);
@@ -8247,7 +8247,7 @@ class $ListiniClienteTable extends ListiniCliente
   );
   @override
   late final GeneratedColumn<int> idListinoCliente = GeneratedColumn<int>(
-    'idListino_cliente',
+    'id_listino_cliente',
     aliasedName,
     false,
     hasAutoIncrement: true,
@@ -8401,11 +8401,11 @@ class $ListiniClienteTable extends ListiniCliente
   }) {
     final context = VerificationContext();
     final data = instance.toColumns(true);
-    if (data.containsKey('idListino_cliente')) {
+    if (data.containsKey('id_listino_cliente')) {
       context.handle(
         _idListinoClienteMeta,
         idListinoCliente.isAcceptableOrUnknown(
-          data['idListino_cliente']!,
+          data['id_listino_cliente']!,
           _idListinoClienteMeta,
         ),
       );
@@ -8490,7 +8490,7 @@ class $ListiniClienteTable extends ListiniCliente
     return ListinoCliente(
       idListinoCliente: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
-        data['${effectivePrefix}idListino_cliente'],
+        data['${effectivePrefix}id_listino_cliente'],
       )!,
       idListino: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
@@ -8575,7 +8575,7 @@ class ListinoCliente extends DataClass implements Insertable<ListinoCliente> {
   @override
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
-    map['idListino_cliente'] = Variable<int>(idListinoCliente);
+    map['id_listino_cliente'] = Variable<int>(idListinoCliente);
     if (!nullToAbsent || idListino != null) {
       map['idListino'] = Variable<int>(idListino);
     }
@@ -8844,7 +8844,7 @@ class ListiniClienteCompanion extends UpdateCompanion<ListinoCliente> {
     Expression<String>? stato,
   }) {
     return RawValuesInsertable({
-      if (idListinoCliente != null) 'idListino_cliente': idListinoCliente,
+      if (idListinoCliente != null) 'id_listino_cliente': idListinoCliente,
       if (idListino != null) 'idListino': idListino,
       if (idCliente != null) 'idCliente': idCliente,
       if (idArticolo != null) 'idArticolo': idArticolo,
@@ -8893,7 +8893,7 @@ class ListiniClienteCompanion extends UpdateCompanion<ListinoCliente> {
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
     if (idListinoCliente.present) {
-      map['idListino_cliente'] = Variable<int>(idListinoCliente.value);
+      map['id_listino_cliente'] = Variable<int>(idListinoCliente.value);
     }
     if (idListino.present) {
       map['idListino'] = Variable<int>(idListino.value);
@@ -9481,7 +9481,7 @@ class $ScontiClienteTable extends ScontiCliente
   );
   @override
   late final GeneratedColumn<int> idScontoCliente = GeneratedColumn<int>(
-    'idSconto_cliente',
+    'idScontoCliente',
     aliasedName,
     false,
     hasAutoIncrement: true,
@@ -9599,11 +9599,11 @@ class $ScontiClienteTable extends ScontiCliente
   }) {
     final context = VerificationContext();
     final data = instance.toColumns(true);
-    if (data.containsKey('idSconto_cliente')) {
+    if (data.containsKey('idScontoCliente')) {
       context.handle(
         _idScontoClienteMeta,
         idScontoCliente.isAcceptableOrUnknown(
-          data['idSconto_cliente']!,
+          data['idScontoCliente']!,
           _idScontoClienteMeta,
         ),
       );
@@ -9670,7 +9670,7 @@ class $ScontiClienteTable extends ScontiCliente
     return ScontoCliente(
       idScontoCliente: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
-        data['${effectivePrefix}idSconto_cliente'],
+        data['${effectivePrefix}idScontoCliente'],
       )!,
       idCliente: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
@@ -9737,7 +9737,7 @@ class ScontoCliente extends DataClass implements Insertable<ScontoCliente> {
   @override
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
-    map['idSconto_cliente'] = Variable<int>(idScontoCliente);
+    map['idScontoCliente'] = Variable<int>(idScontoCliente);
     if (!nullToAbsent || idCliente != null) {
       map['idCliente'] = Variable<int>(idCliente);
     }
@@ -9956,7 +9956,7 @@ class ScontiClienteCompanion extends UpdateCompanion<ScontoCliente> {
     Expression<String>? dataModifica,
   }) {
     return RawValuesInsertable({
-      if (idScontoCliente != null) 'idSconto_cliente': idScontoCliente,
+      if (idScontoCliente != null) 'idScontoCliente': idScontoCliente,
       if (idCliente != null) 'idCliente': idCliente,
       if (idArticolo != null) 'idArticolo': idArticolo,
       if (linea != null) 'linea': linea,
@@ -9996,7 +9996,7 @@ class ScontiClienteCompanion extends UpdateCompanion<ScontoCliente> {
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
     if (idScontoCliente.present) {
-      map['idSconto_cliente'] = Variable<int>(idScontoCliente.value);
+      map['idScontoCliente'] = Variable<int>(idScontoCliente.value);
     }
     if (idCliente.present) {
       map['idCliente'] = Variable<int>(idCliente.value);
@@ -13563,7 +13563,7 @@ class $ScadenzeTable extends Scadenze with TableInfo<$ScadenzeTable, Scadenza> {
   );
   @override
   late final GeneratedColumn<int> idScadenza = GeneratedColumn<int>(
-    'idScadenza',
+    'id_scadenza',
     aliasedName,
     false,
     hasAutoIncrement: true,
@@ -13803,10 +13803,10 @@ class $ScadenzeTable extends Scadenze with TableInfo<$ScadenzeTable, Scadenza> {
   }) {
     final context = VerificationContext();
     final data = instance.toColumns(true);
-    if (data.containsKey('idScadenza')) {
+    if (data.containsKey('id_scadenza')) {
       context.handle(
         _idScadenzaMeta,
-        idScadenza.isAcceptableOrUnknown(data['idScadenza']!, _idScadenzaMeta),
+        idScadenza.isAcceptableOrUnknown(data['id_scadenza']!, _idScadenzaMeta),
       );
     }
     if (data.containsKey('idCliente')) {
@@ -13955,7 +13955,7 @@ class $ScadenzeTable extends Scadenze with TableInfo<$ScadenzeTable, Scadenza> {
     return Scadenza(
       idScadenza: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
-        data['${effectivePrefix}idScadenza'],
+        data['${effectivePrefix}id_scadenza'],
       )!,
       idCliente: attachedDatabase.typeMapping.read(
         DriftSqlType.int,
@@ -14088,7 +14088,7 @@ class Scadenza extends DataClass implements Insertable<Scadenza> {
   @override
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
-    map['idScadenza'] = Variable<int>(idScadenza);
+    map['id_scadenza'] = Variable<int>(idScadenza);
     if (!nullToAbsent || idCliente != null) {
       map['idCliente'] = Variable<int>(idCliente);
     }
@@ -14523,7 +14523,7 @@ class ScadenzeCompanion extends UpdateCompanion<Scadenza> {
     Expression<String>? stato,
   }) {
     return RawValuesInsertable({
-      if (idScadenza != null) 'idScadenza': idScadenza,
+      if (idScadenza != null) 'id_scadenza': idScadenza,
       if (idCliente != null) 'idCliente': idCliente,
       if (idAgente != null) 'idAgente': idAgente,
       if (chiave != null) 'chiave': chiave,
@@ -14596,7 +14596,7 @@ class ScadenzeCompanion extends UpdateCompanion<Scadenza> {
   Map<String, Expression> toColumns(bool nullToAbsent) {
     final map = <String, Expression>{};
     if (idScadenza.present) {
-      map['idScadenza'] = Variable<int>(idScadenza.value);
+      map['id_scadenza'] = Variable<int>(idScadenza.value);
     }
     if (idCliente.present) {
       map['idCliente'] = Variable<int>(idCliente.value);

+ 5 - 3
lib/database/tables/agenti.dart

@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Agente')
 class Agenti extends Table {
-  IntColumn get idAgente => integer().autoIncrement()();
+  IntColumn get idAgente => integer().named('idAgente').autoIncrement()();
   TextColumn get codAgente => text().named('codAgente')();
   TextColumn get ragioneSociale => text().named('ragioneSociale').nullable()();
   TextColumn get pwd => text().named('pwd').nullable()();
@@ -14,8 +14,10 @@ class Agenti extends Table {
   TextColumn get cap => text().named('cap').nullable()();
   TextColumn get provincia => text().named('provincia').nullable()();
   TextColumn get frazione => text().named('frazione').nullable()();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
-  IntColumn get modificaPrezzi => integer().named('modificaPrezzi').nullable()();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
+  IntColumn get modificaPrezzi =>
+      integer().named('modificaPrezzi').nullable()();
   TextColumn get capoArea => text().named('capoArea').nullable()();
 
   @override

+ 9 - 5
lib/database/tables/articoli.dart

@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Articolo')
 class Articoli extends Table {
-  IntColumn get idArticolo => integer().autoIncrement()();
+  IntColumn get idArticolo => integer().named('idArticolo').autoIncrement()();
   IntColumn get posizione => integer().named('posizione').nullable()();
   TextColumn get codArticolo => text().named('codArticolo')();
   TextColumn get descrizione => text().named('descrizione').nullable()();
@@ -15,9 +15,12 @@ class Articoli extends Table {
   RealColumn get peso => real().named('peso').nullable()();
   RealColumn get giacenza => real().named('giacenza').nullable()();
   TextColumn get unitaMisura => text().named('unitaMisura').nullable()();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
-  IntColumn get nonDisponibile => integer().named('nonDisponibile').nullable()();
-  RealColumn get pezziConfezione => real().named('pezziConfezione').nullable()();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
+  IntColumn get nonDisponibile =>
+      integer().named('nonDisponibile').nullable()();
+  RealColumn get pezziConfezione =>
+      real().named('pezziConfezione').nullable()();
   TextColumn get fileFoto => text().named('fileFoto').nullable()();
   TextColumn get schedaProdotto => text().named('schedaProdotto').nullable()();
   TextColumn get prezzoBase => text().named('prezzoBase').nullable()();
@@ -26,7 +29,8 @@ class Articoli extends Table {
   TextColumn get gruppo => text().named('gruppo').nullable()();
   TextColumn get categoria => text().named('categoria').nullable()();
   TextColumn get colliPerStrato => text().named('colliPerStrato').nullable()();
-  TextColumn get stratiPerPallet => text().named('stratiPerPallet').nullable()();
+  TextColumn get stratiPerPallet =>
+      text().named('stratiPerPallet').nullable()();
 
   @override
   String get tableName => 'Articoli';

+ 7 - 4
lib/database/tables/clienti.dart

@@ -2,8 +2,9 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Cliente')
 class Clienti extends Table {
-  IntColumn get idCliente => integer().autoIncrement()();
-  IntColumn get idClienteRemoto => integer().named('idClienteRemoto').nullable()();
+  IntColumn get idCliente => integer().named('idCliente').autoIncrement()();
+  IntColumn get idClienteRemoto =>
+      integer().named('idClienteRemoto').nullable()();
   TextColumn get codCliente => text().named('codCliente').nullable()();
   TextColumn get ragioneSociale => text().named('ragioneSociale').nullable()();
   TextColumn get indirizzo => text().named('indirizzo').nullable()();
@@ -20,8 +21,10 @@ class Clienti extends Table {
   TextColumn get codiceFiscale => text().named('codiceFiscale').nullable()();
   TextColumn get note => text().named('note').nullable()();
   TextColumn get noteOrdine => text().named('noteOrdine').nullable()();
-  TextColumn get stato => text().named('stato').withDefault(const Constant('N'))();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
+  TextColumn get stato =>
+      text().named('stato').withDefault(const Constant('N'))();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
   IntColumn get idPagamento => integer().named('idPagamento').nullable()();
   IntColumn get idListino => integer().named('idListino').nullable()();
   TextColumn get dataModifica => text().named('dataModifica').nullable()();

+ 4 - 2
lib/database/tables/clienti_agente.dart

@@ -2,10 +2,12 @@ import 'package:drift/drift.dart';
 
 @DataClassName('ClientiAgenteData')
 class ClientiAgente extends Table {
-  IntColumn get idClientiAgente => integer().autoIncrement()();
+  IntColumn get idClientiAgente =>
+      integer().named('idClientiAgente').autoIncrement()();
   IntColumn get idCliente => integer().named('idCliente')();
   IntColumn get idAgente => integer().named('idAgente')();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
 
   @override
   String get tableName => 'Clienti_Agente';

+ 5 - 3
lib/database/tables/filiali.dart

@@ -2,8 +2,9 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Filiale')
 class Filiali extends Table {
-  IntColumn get idFiliale => integer().autoIncrement()();
-  IntColumn get idFilialeRemoto => integer().named('idFilialeRemoto').nullable()();
+  IntColumn get idFiliale => integer().named('idFiliale').autoIncrement()();
+  IntColumn get idFilialeRemoto =>
+      integer().named('idFilialeRemoto').nullable()();
   IntColumn get idCliente => integer().named('idCliente').nullable()();
   IntColumn get idAgente => integer().named('idAgente').nullable()();
   TextColumn get codFiliale => text().named('codFiliale').nullable()();
@@ -19,7 +20,8 @@ class Filiali extends Table {
   TextColumn get fax => text().named('fax').nullable()();
   TextColumn get email => text().named('email').nullable()();
   TextColumn get stato => text().named('stato').nullable()();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
   TextColumn get note => text().named('note').nullable()();
 
   @override

+ 3 - 2
lib/database/tables/listini.dart

@@ -2,12 +2,13 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Listino')
 class Listini extends Table {
-  IntColumn get idListino => integer().autoIncrement()();
+  IntColumn get idListino => integer().named('idListino').autoIncrement()();
   TextColumn get codListino => text().named('codListino').nullable()();
   TextColumn get descrizione => text().named('descrizione').nullable()();
   TextColumn get validoDa => text().named('validoDa').nullable()();
   TextColumn get validoA => text().named('validoA').nullable()();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
   TextColumn get dataModifica => text().named('dataModifica').nullable()();
 
   @override

+ 4 - 2
lib/database/tables/listini_articolo.dart

@@ -2,14 +2,16 @@ import 'package:drift/drift.dart';
 
 @DataClassName('ListinoArticolo')
 class ListiniArticolo extends Table {
-  IntColumn get idListinoArticoli => integer().autoIncrement()();
+  IntColumn get idListinoArticoli =>
+      integer().named('idListinoArticoli').autoIncrement()();
   IntColumn get idListino => integer().named('idListino').nullable()();
   IntColumn get idArticolo => integer().named('idArticolo').nullable()();
   RealColumn get prezzo => real().named('prezzo').nullable()();
   IntColumn get idSconto => integer().named('idSconto').nullable()();
   TextColumn get validoDa => text().named('validoDa').nullable()();
   TextColumn get validoA => text().named('validoA').nullable()();
-  IntColumn get attivo => integer().named('attivo').withDefault(const Constant(1))();
+  IntColumn get attivo =>
+      integer().named('attivo').withDefault(const Constant(1))();
 
   @override
   String get tableName => 'Listini_articolo';

+ 3 - 2
lib/database/tables/ordini.dart

@@ -2,10 +2,11 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Ordine')
 class Ordini extends Table {
-  IntColumn get idOrdine => integer().autoIncrement()();
+  IntColumn get idOrdine => integer().named('idOrdine').autoIncrement()();
   IntColumn get idOrdineSede => integer().named('idOrdineSede').nullable()();
   IntColumn get numeroOrdine => integer().named('numeroOrdine').nullable()();
-  TextColumn get numeroOrdineSede => text().named('numeroOrdineSede').nullable()();
+  TextColumn get numeroOrdineSede =>
+      text().named('numeroOrdineSede').nullable()();
   TextColumn get dataOrdine => text().named('dataOrdine').nullable()();
   TextColumn get dataConsegna => text().named('dataConsegna').nullable()();
   TextColumn get dataModifica => text().named('dataModifica').nullable()();

+ 1 - 1
lib/database/tables/pagamenti.dart

@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Pagamento')
 class Pagamenti extends Table {
-  IntColumn get idPagamento => integer().autoIncrement()();
+  IntColumn get idPagamento => integer().named('idPagamento').autoIncrement()();
   TextColumn get codPagamento => text().named('codPagamento').nullable()();
   TextColumn get descrizione => text().named('descrizione').nullable()();
   TextColumn get dataModifica => text().named('dataModifica').nullable()();

+ 4 - 2
lib/database/tables/righe_ordine.dart

@@ -2,7 +2,8 @@ import 'package:drift/drift.dart';
 
 @DataClassName('RigaOrdine')
 class RigheOrdine extends Table {
-  IntColumn get idRigaOrdine => integer().autoIncrement()();
+  IntColumn get idRigaOrdine =>
+      integer().named('idRigaOrdine').autoIncrement()();
   IntColumn get idOrdine => integer().named('idOrdine').nullable()();
   IntColumn get idArticolo => integer().named('idArticolo').nullable()();
   IntColumn get idListino => integer().named('idListino').nullable()();
@@ -22,7 +23,8 @@ class RigheOrdine extends Table {
   TextColumn get stato => text().named('stato').nullable()();
   RealColumn get colli => real().named('colli').nullable()();
   IntColumn get promozione => integer().named('promozione').nullable()();
-  IntColumn get rigaPromozione => integer().named('rigaPromozione').nullable()();
+  IntColumn get rigaPromozione =>
+      integer().named('rigaPromozione').nullable()();
   IntColumn get prezzoManuale => integer().named('prezzoManuale').nullable()();
 
   @override

+ 1 - 1
lib/database/tables/sconti.dart

@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Sconto')
 class Sconti extends Table {
-  IntColumn get idSconto => integer().autoIncrement()();
+  IntColumn get idSconto => integer().named('idSconto').autoIncrement()();
   TextColumn get codSconto => text().named('codSconto').nullable()();
   TextColumn get descrizione => text().named('descrizione').nullable()();
   RealColumn get sconto1 => real().named('sconto1').nullable()();

+ 2 - 1
lib/database/tables/sconti_cliente.dart

@@ -2,7 +2,8 @@ import 'package:drift/drift.dart';
 
 @DataClassName('ScontoCliente')
 class ScontiCliente extends Table {
-  IntColumn get idScontoCliente => integer().autoIncrement()();
+  IntColumn get idScontoCliente =>
+      integer().named('idScontoCliente').autoIncrement()();
   IntColumn get idCliente => integer().named('idCliente').nullable()();
   IntColumn get idArticolo => integer().named('idArticolo').nullable()();
   TextColumn get linea => text().named('linea').nullable()();

+ 1 - 1
lib/database/tables/tabelle.dart

@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
 
 @DataClassName('Tabella')
 class Tabelle extends Table {
-  IntColumn get idTabella => integer().autoIncrement()();
+  IntColumn get idTabella => integer().named('idTabella').autoIncrement()();
   TextColumn get tipoTabella => text().named('tipoTabella')();
   TextColumn get codTabella => text().named('codTabella').nullable()();
   TextColumn get descrizione => text().named('descrizione').nullable()();

+ 13 - 3
lib/providers/auth_provider.dart

@@ -11,7 +11,12 @@ class AuthState {
   final bool isInitialized;
   final String? error;
 
-  const AuthState({this.agente, this.isLoading = false, this.isInitialized = false, this.error});
+  const AuthState({
+    this.agente,
+    this.isLoading = false,
+    this.isInitialized = false,
+    this.error,
+  });
 
   bool get isLoggedIn => agente != null;
 }
@@ -46,7 +51,10 @@ class AuthNotifier extends Notifier<AuthState> {
       if (agente != null) {
         state = AuthState(agente: agente, isInitialized: true);
       } else {
-        state = const AuthState(error: 'Codice agente o password errati', isInitialized: true);
+        state = const AuthState(
+          error: 'Codice agente o password errati',
+          isInitialized: true,
+        );
       }
     } catch (e) {
       state = AuthState(error: e.toString(), isInitialized: true);
@@ -84,4 +92,6 @@ class AuthNotifier extends Notifier<AuthState> {
   }
 }
 
-final authProvider = NotifierProvider<AuthNotifier, AuthState>(AuthNotifier.new);
+final authProvider = NotifierProvider<AuthNotifier, AuthState>(
+  AuthNotifier.new,
+);

+ 43 - 21
lib/ui/pages/customers/customer_list_page.dart

@@ -28,9 +28,14 @@ class _CustomerListPageState extends ConsumerState<CustomerListPage> {
   Widget build(BuildContext context) {
     final agente = ref.watch(authProvider).agente;
     final customersAsync = agente != null
-        ? ref.watch(_filter.isEmpty
-            ? customerListProvider(agente.idAgente)
-            : customerSearchProvider((idAgente: agente.idAgente, filter: _filter)))
+        ? ref.watch(
+            _filter.isEmpty
+                ? customerListProvider(agente.idAgente)
+                : customerSearchProvider((
+                    idAgente: agente.idAgente,
+                    filter: _filter,
+                  )),
+          )
         : null;
 
     return Scaffold(
@@ -41,26 +46,43 @@ class _CustomerListPageState extends ConsumerState<CustomerListPage> {
             controller: _searchCtrl,
             hint: 'Cerca cliente...',
             onChanged: (v) => setState(() => _filter = v),
-            onClear: () { _searchCtrl.clear(); setState(() => _filter = ''); },
+            onClear: () {
+              _searchCtrl.clear();
+              setState(() => _filter = '');
+            },
           ),
           Expanded(
-            child: customersAsync?.when(
-              data: (customers) {
-                if (customers.isEmpty) return const EmptyState(icon: Icons.people_outline, title: 'Nessun cliente');
-                return ListView.builder(
-                  itemCount: customers.length,
-                  itemBuilder: (_, i) => ListTile(
-                    onTap: () => context.go('/dashboard/customers/${customers[i].idCliente}'),
-                    leading: CircleAvatar(child: Text(customers[i].codCliente?.substring(0, 2) ?? '??')),
-                    title: Text(customers[i].ragioneSociale ?? ''),
-                    subtitle: Text(customers[i].codCliente ?? ''),
-                    trailing: const Icon(Icons.chevron_right),
-                  ),
-                );
-              },
-              loading: () => const Center(child: CircularProgressIndicator()),
-              error: (e, _) => Center(child: Text('Errore: $e')),
-            ) ?? const Center(child: CircularProgressIndicator()),
+            child:
+                customersAsync?.when(
+                  data: (customers) {
+                    if (customers.isEmpty)
+                      return const EmptyState(
+                        icon: Icons.people_outline,
+                        title: 'Nessun cliente',
+                      );
+                    return ListView.builder(
+                      itemCount: customers.length,
+                      itemBuilder: (_, i) => ListTile(
+                        onTap: () => context.go(
+                          '/dashboard/customers/${customers[i].idCliente}',
+                        ),
+                        leading: CircleAvatar(
+                          child: Text(
+                            customers[i].ragioneSociale?.substring(0, 2) ??
+                                '??',
+                          ),
+                        ),
+                        title: Text(customers[i].ragioneSociale ?? ''),
+                        subtitle: Text(customers[i].codCliente ?? ''),
+                        trailing: const Icon(Icons.chevron_right),
+                      ),
+                    );
+                  },
+                  loading: () =>
+                      const Center(child: CircularProgressIndicator()),
+                  error: (e, _) => Center(child: Text('Errore: $e')),
+                ) ??
+                const Center(child: CircularProgressIndicator()),
           ),
         ],
       ),

+ 262 - 68
lib/ui/pages/login/init_page.dart

@@ -16,6 +16,7 @@ class _InitPageState extends ConsumerState<InitPage> {
   final _ragsocCtrl = TextEditingController();
   final _urlCtrl = TextEditingController();
   final _formKey = GlobalKey<FormState>();
+  bool _obscurePassword = true;
 
   @override
   void initState() {
@@ -41,84 +42,277 @@ class _InitPageState extends ConsumerState<InitPage> {
   Widget build(BuildContext context) {
     final authState = ref.watch(authProvider);
     final theme = Theme.of(context);
+    final cs = theme.colorScheme;
 
     return Scaffold(
-      body: Center(
-        child: SingleChildScrollView(
-          padding: const EdgeInsets.all(24),
-          child: ConstrainedBox(
-            constraints: const BoxConstraints(maxWidth: 400),
-            child: Form(
-              key: _formKey,
-              child: Column(
-                mainAxisSize: MainAxisSize.min,
-                crossAxisAlignment: CrossAxisAlignment.stretch,
-                children: [
-                  Icon(Icons.store, size: 72, color: theme.colorScheme.primary),
-                  const SizedBox(height: 16),
-                  Text(
-                    'Benvenuto in Ristoris',
-                    textAlign: TextAlign.center,
-                    style: theme.textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
-                  ),
-                  const SizedBox(height: 8),
-                  Text(
-                    'Configura il tuo account agente per iniziare',
-                    textAlign: TextAlign.center,
-                    style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.outline),
-                  ),
-                  const SizedBox(height: 32),
-                  TextFormField(
-                    controller: _codiceCtrl,
-                    decoration: const InputDecoration(labelText: 'Codice Agente', prefixIcon: Icon(Icons.badge)),
-                    validator: (v) => v == null || v.isEmpty ? 'Campo obbligatorio' : null,
-                  ),
-                  const SizedBox(height: 16),
-                  TextFormField(
-                    controller: _passwordCtrl,
-                    obscureText: true,
-                    decoration: const InputDecoration(labelText: 'Password', prefixIcon: Icon(Icons.lock)),
-                    validator: (v) => v == null || v.isEmpty ? 'Campo obbligatorio' : null,
-                  ),
-                  const SizedBox(height: 16),
-                  TextFormField(
-                    controller: _ragsocCtrl,
-                    decoration: const InputDecoration(labelText: 'Ragione Sociale (opzionale)', prefixIcon: Icon(Icons.person)),
-                  ),
-                  const SizedBox(height: 16),
-                  TextFormField(
-                    controller: _urlCtrl,
-                    decoration: const InputDecoration(labelText: 'URL Backend', prefixIcon: Icon(Icons.link)),
-                    validator: (v) => v == null || v.isEmpty ? 'Campo obbligatorio' : null,
-                  ),
-                  const SizedBox(height: 24),
-                  FilledButton.icon(
-                    onPressed: authState.isLoading
-                        ? null
-                        : () {
-                            if (_formKey.currentState!.validate()) {
-                              ref.read(authProvider.notifier).initialize(
+      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),
+                    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.store, color: Colors.white, size: 40),
+        ),
+        const SizedBox(height: 16),
+        Text(
+          'Benvenuto in PowerAge',
+          style: theme.textTheme.headlineLarge?.copyWith(
+            color: cs.primary,
+            letterSpacing: -0.02,
+          ),
+        ),
+        const SizedBox(height: 8),
+        Text(
+          'Configura il tuo account agente per iniziare',
+          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: 'Codice Agente',
+                icon: Icons.badge_outlined,
+                controller: _codiceCtrl,
+                hintText: 'Inserisci il codice agente',
+                validator: (v) =>
+                    v == null || v.isEmpty ? 'Campo obbligatorio' : null,
+              ),
+              const SizedBox(height: 16),
+              _buildPasswordField(cs),
+              const SizedBox(height: 16),
+              _buildLabeledField(
+                label: 'Ragione Sociale (opzionale)',
+                icon: Icons.person_outline,
+                controller: _ragsocCtrl,
+                hintText: 'Inserisci la ragione sociale',
+              ),
+              const SizedBox(height: 16),
+              _buildLabeledField(
+                label: 'URL Backend',
+                icon: Icons.link,
+                controller: _urlCtrl,
+                hintText: 'https://api.powerage.cloud',
+                validator: (v) =>
+                    v == null || v.isEmpty ? 'Campo obbligatorio' : null,
+              ),
+              const SizedBox(height: 24),
+              FilledButton.icon(
+                onPressed: authState.isLoading
+                    ? null
+                    : () {
+                        if (_formKey.currentState!.validate()) {
+                          ref.read(authProvider.notifier).initialize(
                                 codAgente: _codiceCtrl.text,
                                 password: _passwordCtrl.text,
-                                ragioneSociale: _ragsocCtrl.text.isEmpty ? null : _ragsocCtrl.text,
+                                ragioneSociale: _ragsocCtrl.text.isEmpty
+                                    ? null
+                                    : _ragsocCtrl.text,
                                 url: _urlCtrl.text,
                               );
-                            }
-                          },
-                    icon: authState.isLoading
-                        ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2))
-                        : const Icon(Icons.arrow_forward),
-                    label: Text(authState.isLoading ? 'Configurazione...' : 'Configura'),
-                  ),
-                  if (authState.error != null) ...[
-                    const SizedBox(height: 16),
-                    Text(authState.error!, style: TextStyle(color: theme.colorScheme.error)),
-                  ],
-                ],
+                        }
+                      },
+                icon: authState.isLoading
+                    ? const SizedBox(
+                        width: 20,
+                        height: 20,
+                        child: CircularProgressIndicator(
+                          strokeWidth: 2,
+                          color: Colors.white,
+                        ),
+                      )
+                    : const Icon(Icons.arrow_forward),
+                label: Text(
+                    authState.isLoading ? 'Configurazione...' : 'Configura'),
+              ),
+              if (authState.error != null) ...[
+                const SizedBox(height: 16),
+                _buildErrorCard(authState.error!, cs),
+              ],
+            ],
+          ),
+        ),
+      ),
+    );
+  }
+
+  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;
+                });
+              },
             ),
           ),
+          validator: (v) =>
+              v == null || v.isEmpty ? 'Campo obbligatorio' : null,
         ),
+      ],
+    );
+  }
+
+  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),
+            ),
+          ),
+        ],
       ),
     );
   }

+ 400 - 107
lib/ui/pages/login/login_page.dart

@@ -1,5 +1,6 @@
 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';
 
@@ -15,6 +16,8 @@ class _LoginPageState extends ConsumerState<LoginPage> {
   final _passwordCtrl = TextEditingController();
   final _urlCtrl = TextEditingController();
   final _formKey = GlobalKey<FormState>();
+  bool _obscurePassword = true;
+  bool _rememberMe = false;
 
   @override
   void initState() {
@@ -41,124 +44,414 @@ class _LoginPageState extends ConsumerState<LoginPage> {
   Widget build(BuildContext context) {
     final authState = ref.watch(authProvider);
     final theme = Theme.of(context);
+    final cs = theme.colorScheme;
 
     return Scaffold(
-      body: Center(
-        child: SingleChildScrollView(
-          padding: const EdgeInsets.all(24),
-          child: ConstrainedBox(
-            constraints: const BoxConstraints(maxWidth: 400),
-            child: Form(
-              key: _formKey,
-              child: Column(
-                mainAxisSize: MainAxisSize.min,
-                crossAxisAlignment: CrossAxisAlignment.stretch,
-                children: [
-                  Icon(Icons.login, size: 72, color: theme.colorScheme.primary),
-                  const SizedBox(height: 16),
-                  Text(
-                    'Ristoris',
-                    textAlign: TextAlign.center,
-                    style: theme.textTheme.headlineMedium?.copyWith(
-                      fontWeight: FontWeight.bold,
-                    ),
-                  ),
-                  const SizedBox(height: 8),
-                  Text(
-                    'Accedi con le tue credenziali',
-                    textAlign: TextAlign.center,
-                    style: theme.textTheme.bodyMedium?.copyWith(
-                      color: theme.colorScheme.outline,
-                    ),
-                  ),
-                  const SizedBox(height: 32),
-                  TextFormField(
-                    controller: _codiceCtrl,
-                    decoration: const InputDecoration(
-                      labelText: 'Codice Agente',
-                      prefixIcon: Icon(Icons.badge_outlined),
-                    ),
-                    validator: (v) => v == null || v.isEmpty
-                        ? 'Inserisci il codice agente'
-                        : null,
-                  ),
-                  const SizedBox(height: 16),
-                  TextFormField(
-                    controller: _passwordCtrl,
-                    obscureText: true,
-                    decoration: const InputDecoration(
-                      labelText: 'Password',
-                      prefixIcon: Icon(Icons.lock_outlined),
-                    ),
-                    // validator: (v) => v == null || v.isEmpty ? 'Inserisci la password' : null,
-                  ),
-                  const SizedBox(height: 16),
-                  TextFormField(
-                    controller: _urlCtrl,
-                    decoration: const InputDecoration(
-                      labelText: 'URL Backend',
-                      prefixIcon: Icon(Icons.link),
-                    ),
-                    validator: (v) => v == null || v.isEmpty
-                        ? 'Inserisci l\'URL del backend'
-                        : null,
-                  ),
-                  const SizedBox(height: 24),
-                  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),
-                          )
-                        : const Icon(Icons.arrow_forward),
-                    label: Text(authState.isLoading ? 'Accesso...' : 'Accedi'),
-                  ),
-                  if (authState.error != null) ...[
-                    const SizedBox(height: 16),
-                    Card(
-                      color: theme.colorScheme.errorContainer,
-                      child: Padding(
-                        padding: const EdgeInsets.all(12),
-                        child: Row(
-                          children: [
-                            Icon(
-                              Icons.error_outline,
-                              color: theme.colorScheme.error,
-                            ),
-                            const SizedBox(width: 8),
-                            Expanded(
-                              child: Text(
-                                authState.error!,
-                                style: TextStyle(
-                                  color: theme.colorScheme.onErrorContainer,
-                                ),
-                              ),
-                            ),
-                          ],
+      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,
+            ),
+          ),
+        ),
+      ],
     );
   }
 }

+ 49 - 12
lib/ui/routes.dart

@@ -24,8 +24,16 @@ final routerProvider = Provider<GoRouter>((ref) {
     redirect: (context, state) {
       final isLogin = state.matchedLocation == '/login';
       final isInit = state.matchedLocation == '/init';
-      if (!authState.isInitialized && !isInit) return '/init';
-      if (authState.isInitialized && !authState.isLoggedIn && !isLogin) return '/login';
+      final isSyncSettings = state.matchedLocation == '/sync-settings';
+      if (!authState.isInitialized && !isInit && !isSyncSettings) {
+        return '/init';
+      }
+      if (authState.isInitialized &&
+          !authState.isLoggedIn &&
+          !isLogin &&
+          !isSyncSettings) {
+        return '/login';
+      }
       if (authState.isLoggedIn && isLogin) return '/dashboard';
       if (authState.isLoggedIn && isInit) return '/dashboard';
       return null;
@@ -33,24 +41,53 @@ final routerProvider = Provider<GoRouter>((ref) {
     routes: [
       GoRoute(path: '/init', builder: (_, __) => const InitPage()),
       GoRoute(path: '/login', builder: (_, __) => const LoginPage()),
+      GoRoute(
+        path: '/sync-settings',
+        builder: (_, __) => const SyncSettingsPage(),
+      ),
       GoRoute(
         path: '/dashboard',
         builder: (_, __) => const DashboardPage(),
         routes: [
           GoRoute(path: 'orders', builder: (_, __) => const OrderListPage()),
-          GoRoute(path: 'orders/new', builder: (_, state) {
-            final idCliente = int.tryParse(state.uri.queryParameters['cliente'] ?? '');
-            return OrderEditPage(idCliente: idCliente);
-          }),
-          GoRoute(path: 'orders/:id', builder: (_, state) => OrderEditPage(id: int.parse(state.pathParameters['id']!))),
-          GoRoute(path: 'customers', builder: (_, __) => const CustomerListPage()),
-          GoRoute(path: 'customers/:id', builder: (_, state) => CustomerDetailPage(id: int.parse(state.pathParameters['id']!))),
+          GoRoute(
+            path: 'orders/new',
+            builder: (_, state) {
+              final idCliente = int.tryParse(
+                state.uri.queryParameters['cliente'] ?? '',
+              );
+              return OrderEditPage(idCliente: idCliente);
+            },
+          ),
+          GoRoute(
+            path: 'orders/:id',
+            builder: (_, state) =>
+                OrderEditPage(id: int.parse(state.pathParameters['id']!)),
+          ),
+          GoRoute(
+            path: 'customers',
+            builder: (_, __) => const CustomerListPage(),
+          ),
+          GoRoute(
+            path: 'customers/:id',
+            builder: (_, state) =>
+                CustomerDetailPage(id: int.parse(state.pathParameters['id']!)),
+          ),
           GoRoute(path: 'catalog', builder: (_, __) => const CatalogPage()),
-          GoRoute(path: 'promotions', builder: (_, __) => const PromotionListPage()),
+          GoRoute(
+            path: 'promotions',
+            builder: (_, __) => const PromotionListPage(),
+          ),
           GoRoute(path: 'payments', builder: (_, __) => const PaymentsPage()),
-          GoRoute(path: 'statistics', builder: (_, __) => const StatisticsPage()),
+          GoRoute(
+            path: 'statistics',
+            builder: (_, __) => const StatisticsPage(),
+          ),
           GoRoute(path: 'sync', builder: (_, __) => const SyncPage()),
-          GoRoute(path: 'sync/settings', builder: (_, __) => const SyncSettingsPage()),
+          GoRoute(
+            path: 'sync/settings',
+            builder: (_, __) => const SyncSettingsPage(),
+          ),
         ],
       ),
     ],

+ 1 - 0
pubspec.yaml

@@ -37,6 +37,7 @@ dependencies:
   intl: ^0.19.0
   fl_chart: ^0.69.0
   connectivity_plus: ^6.0.0
+  google_fonts: ^6.1.0
 
 dev_dependencies:
   flutter_test:

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov