From 1a40770390ae9dffcd19c7cceeb08f3e00a641f4 Mon Sep 17 00:00:00 2001 From: Mark M2 Macbook Date: Sun, 12 Apr 2026 21:32:20 +0200 Subject: [PATCH] mamma mia sistemata rail e bottom navigation, bellissime ora --- lib/core/routes/app_router.dart | 6 + .../anagrafiche/master_data_hub_content.dart | 113 +++++++++++++++ .../ui/dashboard_action_card.dart | 44 ++++++ .../ui/dashboard_adaptive_grid.dart | 74 ++++++++++ .../ui/dashboard_content.dart | 97 +------------ .../home_and_dashboard/ui/home_screen.dart | 136 ++++++++++-------- .../products/blocs/product_cubit.dart | 3 +- lib/features/products/ui/brand_selector.dart | 58 ++++++++ lib/features/products/ui/models_list.dart | 79 ++++++++++ lib/features/products/ui/product_dialogs.dart | 102 +++++++++++++ lib/features/products/ui/products_screen.dart | 74 ++++++++++ .../products/ui/round_action_button.dart | 23 +++ 12 files changed, 655 insertions(+), 154 deletions(-) create mode 100644 lib/features/anagrafiche/master_data_hub_content.dart create mode 100644 lib/features/home_and_dashboard/ui/dashboard_action_card.dart create mode 100644 lib/features/home_and_dashboard/ui/dashboard_adaptive_grid.dart create mode 100644 lib/features/products/ui/brand_selector.dart create mode 100644 lib/features/products/ui/models_list.dart create mode 100644 lib/features/products/ui/product_dialogs.dart create mode 100644 lib/features/products/ui/products_screen.dart create mode 100644 lib/features/products/ui/round_action_button.dart diff --git a/lib/core/routes/app_router.dart b/lib/core/routes/app_router.dart index 6b4d2a1..f581242 100644 --- a/lib/core/routes/app_router.dart +++ b/lib/core/routes/app_router.dart @@ -5,6 +5,7 @@ import 'package:flux/features/company/ui/create_company_screen.dart'; import 'package:flux/features/customers/models/customer_model.dart'; import 'package:flux/features/customers/ui/customer_detail_screen.dart'; import 'package:flux/features/home_and_dashboard/ui/home_screen.dart'; +import 'package:flux/features/products/ui/products_screen.dart'; import 'package:flux/features/store/ui/create_store_screen.dart'; import 'package:go_router/go_router.dart'; import 'dart:async'; @@ -68,6 +69,11 @@ class AppRouter { return CustomerDetailScreen(customer: customer); }, ), + GoRoute( + path: '/products', + name: 'products', + builder: (context, state) => const ProductsScreen(), + ), ], ); } diff --git a/lib/features/anagrafiche/master_data_hub_content.dart b/lib/features/anagrafiche/master_data_hub_content.dart new file mode 100644 index 0000000..2a85516 --- /dev/null +++ b/lib/features/anagrafiche/master_data_hub_content.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flux/core/theme/theme.dart'; +import 'package:flux/features/customers/ui/customers_content.dart'; +import 'package:flux/features/products/ui/products_screen.dart'; + +class MasterDataHubContent extends StatelessWidget { + final Function(Widget) onOpenPage; + + const MasterDataHubContent({super.key, required this.onOpenPage}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Anagrafiche", + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: context.accent, + ), + ), + const SizedBox(height: 8), + Text( + "Gestisci i dati fondamentali del tuo business", + style: TextStyle(color: context.secondaryText), + ), + const SizedBox(height: 32), + + Expanded( + child: GridView.count( + crossAxisCount: MediaQuery.of(context).size.width > 600 ? 3 : 2, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + children: [ + _HubCard( + label: 'Prodotti', + icon: Icons.inventory_2_outlined, + color: Colors.blue, + onTap: () => onOpenPage( + const ProductsScreen(), + ), // Apre ProductsScreen, // Indice per ProductsScreen + ), + _HubCard( + label: 'Clienti', + icon: Icons.people_outlined, + color: Colors.orange, + onTap: () => onOpenPage( + const CustomersContent(), + ), // Indice per CustomersContent + ), + _HubCard( + label: 'Commessi', + icon: Icons.badge_outlined, + color: Colors.teal, + onTap: () {}, // Coming soon + ), + _HubCard( + label: 'Negozi', + icon: Icons.storefront_outlined, + color: Colors.purple, + onTap: () {}, // Coming soon + ), + ], + ), + ), + ], + ), + ); + } +} + +// Widget semplice per le card dell'hub +class _HubCard extends StatelessWidget { + final String label; + final IconData icon; + final Color color; + final VoidCallback onTap; + + const _HubCard({ + required this.label, + required this.icon, + required this.color, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Card( + elevation: 0, + color: context.background, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: context.accent.withValues(alpha: 0.1)), + ), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, color: color, size: 40), + const SizedBox(height: 12), + Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ), + ), + ); + } +} diff --git a/lib/features/home_and_dashboard/ui/dashboard_action_card.dart b/lib/features/home_and_dashboard/ui/dashboard_action_card.dart new file mode 100644 index 0000000..46446c2 --- /dev/null +++ b/lib/features/home_and_dashboard/ui/dashboard_action_card.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:flux/core/theme/theme.dart'; + +class DashboardActionCard extends StatelessWidget { + final String label; + final IconData icon; + final Color color; + final VoidCallback onTap; + + const DashboardActionCard({ + super.key, + required this.label, + required this.icon, + required this.color, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + // CAMBIA QUI: da Border.all a BorderSide + side: BorderSide( + color: context.accent.withValues(alpha: 0.1), + width: 1, + ), + ), + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, color: color, size: 32), + const SizedBox(height: 8), + Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ), + ), + ); + } +} diff --git a/lib/features/home_and_dashboard/ui/dashboard_adaptive_grid.dart b/lib/features/home_and_dashboard/ui/dashboard_adaptive_grid.dart new file mode 100644 index 0000000..25831c0 --- /dev/null +++ b/lib/features/home_and_dashboard/ui/dashboard_adaptive_grid.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flux/core/theme/theme.dart'; +import 'package:flux/features/home_and_dashboard/ui/dashboard_action_card.dart'; +import 'package:go_router/go_router.dart'; + +class DashboardAdaptiveGrid extends StatelessWidget { + final bool isLargeScreen; + final Function(int)? onTabRequested; + const DashboardAdaptiveGrid({ + super.key, + this.isLargeScreen = false, + this.onTabRequested, + }); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + // Logica Colonne: Mobile 2, Tablet 3, Desktop 4+ + int crossAxisCount = 2; + if (constraints.maxWidth > 1000) { + crossAxisCount = 5; + } else if (constraints.maxWidth > 700) { + crossAxisCount = 3; + } + + return GridView.count( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: crossAxisCount, + mainAxisSpacing: 16, + crossAxisSpacing: 16, + childAspectRatio: isLargeScreen ? 1.3 : 1.5, + children: [ + DashboardActionCard( + label: 'Nuova Op', + icon: Icons.add_task, + color: context.accent, + onTap: () {}, + ), + DashboardActionCard( + label: 'Clienti', + icon: Icons.people, + color: Colors.orange, + onTap: () => onTabRequested?.call(1), + ), + DashboardActionCard( + label: 'Prodotti', + icon: Icons + .phone_android_outlined, // Icona "comoda" e professionale + color: context + .accent, // O un colore a tua scelta, magari Indigo o Blue + onTap: () => context.push( + '/products', + ), // Apre la schermata sopra la Dashboard + ), + DashboardActionCard( + label: 'Campagne', + icon: Icons.campaign, + color: Colors.purple, + onTap: () {}, + ), + DashboardActionCard( + label: 'Report', + icon: Icons.analytics, + color: Colors.teal, + onTap: () {}, + ), + ], + ); + }, + ); + } +} diff --git a/lib/features/home_and_dashboard/ui/dashboard_content.dart b/lib/features/home_and_dashboard/ui/dashboard_content.dart index 9b49d34..a701205 100644 --- a/lib/features/home_and_dashboard/ui/dashboard_content.dart +++ b/lib/features/home_and_dashboard/ui/dashboard_content.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/core/blocs/session/session_bloc.dart'; import 'package:flux/core/theme/theme.dart'; +import 'package:flux/features/home_and_dashboard/ui/dashboard_adaptive_grid.dart'; class DashboardContent extends StatelessWidget { final bool isLargeScreen; @@ -53,7 +54,10 @@ class DashboardContent extends StatelessWidget { const SizedBox(height: 32), const _SectionTitle(title: 'AZIONI RAPIDE'), const SizedBox(height: 16), - _buildAdaptiveGrid(context), + DashboardAdaptiveGrid( + isLargeScreen: isLargeScreen, + onTabRequested: onTabRequested, + ), const SizedBox(height: 40), const _SectionTitle(title: 'INFO PUNTO VENDITA'), const SizedBox(height: 16), @@ -86,55 +90,6 @@ class DashboardContent extends StatelessWidget { ); } - Widget _buildAdaptiveGrid(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - // Logica Colonne: Mobile 2, Tablet 3, Desktop 4+ - int crossAxisCount = 2; - if (constraints.maxWidth > 1000) { - crossAxisCount = 5; - } else if (constraints.maxWidth > 700) { - crossAxisCount = 3; - } - - return GridView.count( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: crossAxisCount, - mainAxisSpacing: 16, - crossAxisSpacing: 16, - childAspectRatio: isLargeScreen ? 1.3 : 1.5, - children: [ - _ActionCard( - label: 'Nuova Op', - icon: Icons.add_task, - color: context.accent, - onTap: () {}, - ), - _ActionCard( - label: 'Clienti', - icon: Icons.people, - color: Colors.orange, - onTap: () => onTabRequested?.call(1), - ), - _ActionCard( - label: 'Campagne', - icon: Icons.campaign, - color: Colors.purple, - onTap: () {}, - ), - _ActionCard( - label: 'Report', - icon: Icons.analytics, - color: Colors.teal, - onTap: () {}, - ), - ], - ); - }, - ); - } - Widget _buildStoreCard(BuildContext context, dynamic store) { return Container( padding: const EdgeInsets.all(20), @@ -154,48 +109,6 @@ class DashboardContent extends StatelessWidget { } } -// Widget di supporto rimasti invariati (ActionCard e SectionTitle) -class _ActionCard extends StatelessWidget { - final String label; - final IconData icon; - final Color color; - final VoidCallback onTap; - - const _ActionCard({ - required this.label, - required this.icon, - required this.color, - required this.onTap, - }); - - @override - Widget build(BuildContext context) { - return Card( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - // CAMBIA QUI: da Border.all a BorderSide - side: BorderSide( - color: context.accent.withValues(alpha: 0.1), - width: 1, - ), - ), - child: InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, color: color, size: 32), - const SizedBox(height: 8), - Text(label, style: const TextStyle(fontWeight: FontWeight.bold)), - ], - ), - ), - ); - } -} - class _SectionTitle extends StatelessWidget { final String title; const _SectionTitle({required this.title}); diff --git a/lib/features/home_and_dashboard/ui/home_screen.dart b/lib/features/home_and_dashboard/ui/home_screen.dart index 443c3b0..ac9f44f 100644 --- a/lib/features/home_and_dashboard/ui/home_screen.dart +++ b/lib/features/home_and_dashboard/ui/home_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/core/blocs/session/session_bloc.dart'; import 'package:flux/core/theme/theme.dart'; -import 'package:flux/features/customers/ui/customers_content.dart'; +import 'package:flux/features/anagrafiche/master_data_hub_content.dart'; import 'dashboard_content.dart'; // Importiamo il contenuto della dashboard class HomeScreen extends StatefulWidget { @@ -14,6 +14,7 @@ class HomeScreen extends StatefulWidget { class _HomeScreenState extends State { int _selectedIndex = 0; + bool _isRailHovered = false; @override Widget build(BuildContext context) { @@ -27,53 +28,9 @@ class _HomeScreenState extends State { return Scaffold( body: Row( children: [ - // --- SIDEBAR (Solo Desktop/Tablet) --- + // --- SIDEBAR (Desktop) --- if (isLargeScreen) - NavigationRail( - selectedIndex: _selectedIndex, - onDestinationSelected: (index) => - setState(() => _selectedIndex = index), - extended: constraints.maxWidth > 1200, - backgroundColor: context.background, - // 1. Definiamo il colore dell'ellisse (l'indicatore) - indicatorColor: context.accent.withValues(alpha: 0.2), - - selectedIconTheme: IconThemeData( - color: context - .accent, // O un colore che stacchi bene dall'ellisse - size: 28, - ), - selectedLabelTextStyle: TextStyle( - color: context.accent, - fontWeight: FontWeight.bold, - ), - // 3. Forza il colore delle ICONE quando NON selezionate - unselectedIconTheme: IconThemeData( - color: context.secondaryText, - size: 24, - ), - unselectedLabelTextStyle: TextStyle( - color: context.secondaryText, - ), - leading: _buildRailHeader(constraints.maxWidth > 1200), - destinations: const [ - NavigationRailDestination( - icon: Icon(Icons.dashboard_outlined), - selectedIcon: Icon(Icons.dashboard), - label: Text('Dashboard'), - ), - NavigationRailDestination( - icon: Icon(Icons.people_outlined), - selectedIcon: Icon(Icons.people), - label: Text('Clienti'), - ), - NavigationRailDestination( - icon: Icon(Icons.receipt_long_outlined), - selectedIcon: Icon(Icons.receipt_long), - label: Text('Operazioni'), - ), - ], - ), + _buildNavigationRail(constraints.maxWidth > 1200), // --- CONTENUTO DINAMICO --- Expanded( @@ -92,15 +49,15 @@ class _HomeScreenState extends State { items: const [ BottomNavigationBarItem( icon: Icon(Icons.dashboard), - label: 'Home', - ), - BottomNavigationBarItem( - icon: Icon(Icons.people), - label: 'Clienti', + label: 'Dashboard', ), BottomNavigationBarItem( icon: Icon(Icons.receipt_long), - label: 'Ops', + label: 'Operazioni', + ), + BottomNavigationBarItem( + icon: Icon(Icons.folder_shared), + label: 'Anagrafiche', ), ], ), @@ -111,6 +68,58 @@ class _HomeScreenState extends State { ); } + // --- NAVIGATION RAIL (Desktop) --- + Widget _buildNavigationRail(bool railAlwaysExtended) { + return MouseRegion( + onEnter: (_) => setState(() => _isRailHovered = true), + onExit: (_) => setState(() => _isRailHovered = false), + child: NavigationRail( + // Manteniamo 'extended' dinamico in base alla larghezza per un look Pro + extended: railAlwaysExtended ? true : _isRailHovered, + selectedIndex: _selectedIndex, + onDestinationSelected: (index) => + setState(() => _selectedIndex = index), + backgroundColor: context.background, + indicatorColor: context.accent.withValues(alpha: 0.2), + + // Header con il logo FLUX o l'icona bolt + leading: _buildRailHeader(railAlwaysExtended ? true : _isRailHovered), + + selectedIconTheme: IconThemeData(color: context.accent, size: 28), + unselectedIconTheme: IconThemeData( + color: context.secondaryText, + size: 24, + ), + + selectedLabelTextStyle: TextStyle( + color: context.accent, + fontWeight: FontWeight.bold, + ), + unselectedLabelTextStyle: TextStyle(color: context.secondaryText), + + destinations: const [ + NavigationRailDestination( + icon: Icon(Icons.dashboard_outlined), + selectedIcon: Icon(Icons.dashboard), + label: Text('Dashboard'), + ), + NavigationRailDestination( + icon: Icon(Icons.receipt_long_outlined), + selectedIcon: Icon(Icons.receipt_long), + label: Text('Operazioni'), + ), + NavigationRailDestination( + icon: Icon(Icons.folder_shared_outlined), + selectedIcon: Icon(Icons.folder_shared), + label: Text( + 'Anagrafiche', + ), // Questo caricherĂ  il MasterDataHubContent + ), + ], + ), + ); + } + Widget _buildRailHeader(bool isExtended) { return Padding( padding: const EdgeInsets.symmetric(vertical: 24), @@ -133,17 +142,22 @@ class _HomeScreenState extends State { case 0: return DashboardContent( isLargeScreen: isLargeScreen, - // Questo pezzo DEVE puntare al setState dello State della HomeScreen - onTabRequested: (targetIndex) { - setState(() { - _selectedIndex = targetIndex; - }); - }, + // Se dalla Dashboard clicchi "Clienti", ti porto all'Hub + onTabRequested: (idx) => setState(() => _selectedIndex = 2), ); case 1: - return const CustomersContent(); + return const Center(child: Text('Operazioni')); case 2: - return const Center(child: Text('Pagina Operazioni (Coming Soon)')); + // L'unico punto di ingresso per tutte le anagrafiche + return MasterDataHubContent( + // Qui gestiamo la navigazione "interna" all'hub + onOpenPage: (widget) { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => widget), + ); + }, + ); default: return DashboardContent(isLargeScreen: isLargeScreen); } diff --git a/lib/features/products/blocs/product_cubit.dart b/lib/features/products/blocs/product_cubit.dart index 2243953..e566657 100644 --- a/lib/features/products/blocs/product_cubit.dart +++ b/lib/features/products/blocs/product_cubit.dart @@ -55,8 +55,9 @@ class ProductCubit extends Cubit { name: name, companyId: _sessionBloc.state.company!.id, ); - await _repository.upsertBrand(brand); + final newBrand = await _repository.upsertBrand(brand); await loadBrands(); // Ricarichiamo la lista aggiornata + selectBrand(newBrand); } catch (e) { emit( state.copyWith(status: ProductStatus.error, errorMessage: e.toString()), diff --git a/lib/features/products/ui/brand_selector.dart b/lib/features/products/ui/brand_selector.dart new file mode 100644 index 0000000..5743587 --- /dev/null +++ b/lib/features/products/ui/brand_selector.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/theme/theme.dart'; +import 'package:flux/features/products/blocs/product_cubit.dart'; +import 'package:flux/features/products/models/brand_model.dart'; +import 'package:flux/features/products/ui/product_dialogs.dart'; +import 'package:flux/features/products/ui/round_action_button.dart'; + +class BrandSelector extends StatelessWidget { + final ProductState state; + const BrandSelector(this.state, {super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.background, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: context.accent.withValues(alpha: 0.1)), + ), + child: Row( + children: [ + Expanded( + child: DropdownButtonFormField( + initialValue: state.selectedBrand, + //value: state.selectedBrand, + decoration: const InputDecoration( + labelText: "Seleziona Brand", + prefixIcon: Icon(Icons.branding_watermark_outlined), + ), + items: state.brands.map((brand) { + return DropdownMenuItem(value: brand, child: Text(brand.name)); + }).toList(), + onChanged: (brand) => + context.read().selectBrand(brand), + ), + ), + const SizedBox(width: 16), + // Pulsanti rapidi Brand + RoundActionButton( + icon: Icons.add, + onTap: () => showBrandDialog(context), + tooltip: "Nuovo Brand", + ), + if (state.selectedBrand != null) ...[ + const SizedBox(width: 8), + RoundActionButton( + icon: Icons.edit_outlined, + onTap: () => showBrandDialog(context, brand: state.selectedBrand), + tooltip: "Modifica Brand", + ), + ], + ], + ), + ); + } +} diff --git a/lib/features/products/ui/models_list.dart b/lib/features/products/ui/models_list.dart new file mode 100644 index 0000000..9d7a72f --- /dev/null +++ b/lib/features/products/ui/models_list.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/theme/theme.dart'; +import 'package:flux/features/products/blocs/product_cubit.dart'; +import 'package:flux/features/products/ui/product_dialogs.dart'; + +class ModelsList extends StatelessWidget { + final ProductState state; + const ModelsList(this.state, {super.key}); + + @override + Widget build(BuildContext context) { + if (state.selectedBrand == null) { + return const Center( + child: Text("Seleziona un brand per gestire i modelli"), + ); + } + + if (state.status == ProductStatus.loading && state.models.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Modelli di ${state.selectedBrand!.name}", + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ElevatedButton.icon( + onPressed: () => showModelDialog(context), + icon: const Icon(Icons.add), + label: const Text("NUOVO MODELLO"), + ), + ], + ), + const SizedBox(height: 16), + Expanded( + child: ListView.separated( + itemCount: state.models.length, + separatorBuilder: (_, __) => const Divider(height: 1), + itemBuilder: (context, index) { + final model = state.models[index]; + return ListTile( + title: Text(model.name), + subtitle: Text( + model.nameWithBrand, + ), // Quello gestito dal trigger! + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit_outlined), + onPressed: () => showModelDialog(context, model: model), + ), + IconButton( + icon: Icon( + model.isActive + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + color: model.isActive ? context.accent : Colors.grey, + ), + onPressed: () => context + .read() + .toggleStatus('model', model.id!, model.isActive), + ), + ], + ), + ); + }, + ), + ), + ], + ); + } +} diff --git a/lib/features/products/ui/product_dialogs.dart b/lib/features/products/ui/product_dialogs.dart new file mode 100644 index 0000000..4d4c419 --- /dev/null +++ b/lib/features/products/ui/product_dialogs.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/features/products/blocs/product_cubit.dart'; +import 'package:flux/features/products/models/brand_model.dart'; +import 'package:flux/features/products/models/model_model.dart'; + +void showBrandDialog(BuildContext context, {BrandModel? brand}) { + final controller = TextEditingController(text: brand?.name); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(brand == null ? "Nuovo Brand" : "Modifica Brand"), + content: TextField( + controller: controller, + autofocus: true, + decoration: const InputDecoration( + labelText: "Nome Brand", + hintText: "es. Apple, Samsung...", + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text("Annulla"), + ), + ElevatedButton( + onPressed: () { + if (controller.text.isNotEmpty) { + context.read().saveBrand( + controller.text, + id: brand?.id, + ); + Navigator.pop(context); + } + }, + child: const Text("Salva"), + ), + ], + ), + ); +} + +void showModelDialog(BuildContext context, {ModelModel? model}) { + final controller = TextEditingController(text: model?.name); + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(model == null ? "Nuovo Modello" : "Modifica Modello"), + content: TextField( + controller: controller, + autofocus: true, + decoration: const InputDecoration( + labelText: "Nome Modello", + hintText: "es. iPhone 15, Galaxy S24...", + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text("Annulla"), + ), + ElevatedButton( + onPressed: () { + if (controller.text.isNotEmpty) { + context.read().saveModel( + controller.text, + id: model?.id, + ); + Navigator.pop(context); + } + }, + child: const Text("Salva"), + ), + ], + ), + ); +} + +void confirmToggle(BuildContext context, String title, VoidCallback onConfirm) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text("Sei sicuro?"), + content: Text("Stai per cambiare lo stato di: $title"), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text("Annulla"), + ), + TextButton( + onPressed: () { + onConfirm(); + Navigator.pop(context); + }, + child: const Text("Conferma", style: TextStyle(color: Colors.red)), + ), + ], + ), + ); +} diff --git a/lib/features/products/ui/products_screen.dart b/lib/features/products/ui/products_screen.dart new file mode 100644 index 0000000..03fa345 --- /dev/null +++ b/lib/features/products/ui/products_screen.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/theme/theme.dart'; +import 'package:flux/features/products/blocs/product_cubit.dart'; +import 'package:flux/features/products/ui/brand_selector.dart'; +import 'package:flux/features/products/ui/models_list.dart'; +import 'package:go_router/go_router.dart'; + +class ProductsScreen extends StatelessWidget { + const ProductsScreen({super.key}); + + @override + Widget build(BuildContext context) { + // Carichiamo i brand appena la pagina viene creata + context.read().loadBrands(); + + return Scaffold( + backgroundColor: context.background, + appBar: AppBar( + backgroundColor: context.background, + elevation: 0, + centerTitle: false, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => context.pop(), // Torna alla Dashboard + ), + title: Text( + "Anagrafica Prodotti", + style: TextStyle( + color: context.primaryText, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + ), + body: BlocConsumer( + listener: (context, state) { + if (state.status == ProductStatus.error) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(state.errorMessage ?? 'Errore')), + ); + } + }, + builder: (context, state) { + return Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Marche e Modelli", + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: context.accent, + ), + ), + const SizedBox(height: 24), + + // SEZIONE BRAND + BrandSelector(state), + + const SizedBox(height: 32), + + // SEZIONE MODELLI + Expanded(child: ModelsList(state)), + ], + ), + ); + }, + ), + ); + } +} diff --git a/lib/features/products/ui/round_action_button.dart b/lib/features/products/ui/round_action_button.dart new file mode 100644 index 0000000..b174be0 --- /dev/null +++ b/lib/features/products/ui/round_action_button.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RoundActionButton extends StatelessWidget { + final IconData icon; + final VoidCallback onTap; + final String tooltip; + + const RoundActionButton({ + super.key, + required this.icon, + required this.onTap, + required this.tooltip, + }); + + @override + Widget build(BuildContext context) { + return IconButton.filledTonal( + onPressed: onTap, + icon: Icon(icon), + tooltip: tooltip, + ); + } +}