diff --git a/lib/features/master_data/providers/blocs/provider_cubit.dart b/lib/features/master_data/providers/blocs/provider_cubit.dart index 07ea2d7..517ddb1 100644 --- a/lib/features/master_data/providers/blocs/provider_cubit.dart +++ b/lib/features/master_data/providers/blocs/provider_cubit.dart @@ -1,6 +1,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/blocs/session/session_bloc.dart'; import 'package:flux/features/master_data/providers/data/provider_repository.dart'; +import 'package:flux/features/master_data/store/models/store_model.dart'; import 'package:get_it/get_it.dart'; import '../models/provider_model.dart'; @@ -43,18 +45,21 @@ class ProvidersState extends Equatable { class ProvidersCubit extends Cubit { final ProviderRepository _repository = GetIt.I(); + final SessionBloc _sessionBloc; - ProvidersCubit() : super(const ProvidersState()); + ProvidersCubit(this._sessionBloc) : super(const ProvidersState()); // Carica i provider della company e quelli associati a uno store specifico - Future loadProviders(String companyId, String? storeId) async { + Future loadProviders(StoreModel? store) async { emit(state.copyWith(isLoading: true)); try { - final all = await _repository.fetchAllCompanyProviders(companyId); + final all = await _repository.fetchAllCompanyProviders( + _sessionBloc.state.company!.id, + ); List associated = []; - if (storeId != null) { - associated = await _repository.fetchAssociatedProviderIds(storeId); + if (store != null) { + associated = await _repository.fetchAssociatedProviderIds(store.id!); } emit( @@ -105,7 +110,7 @@ class ProvidersCubit extends Cubit { try { await _repository.saveProvider(provider); // Ricarichiamo la lista per vedere le modifiche - await loadProviders(provider.companyId, null); + await loadProviders(null); } catch (e) { emit(state.copyWith(isLoading: false, errorMessage: e.toString())); } diff --git a/lib/features/master_data/providers/models/provider_model.dart b/lib/features/master_data/providers/models/provider_model.dart index 55cb4f8..62d60f2 100644 --- a/lib/features/master_data/providers/models/provider_model.dart +++ b/lib/features/master_data/providers/models/provider_model.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; class ProviderModel extends Equatable { - final String id; + final String? id; final String nome; final bool telefoniaFissa; final bool telefoniaMobile; @@ -13,7 +13,7 @@ class ProviderModel extends Equatable { final String companyId; const ProviderModel({ - required this.id, + this.id, required this.nome, required this.telefoniaFissa, required this.telefoniaMobile, @@ -41,7 +41,7 @@ class ProviderModel extends Equatable { } Map toMap() { - return { + final map = { 'nome': nome, 'telefonia_fissa': telefoniaFissa, 'telefonia_mobile': telefoniaMobile, @@ -52,6 +52,12 @@ class ProviderModel extends Equatable { 'is_active': isActive, 'company_id': companyId, }; + // AGGIUNGIAMO L'ID SOLO SE NON È NULLO + // Senza questo, l'upsert non sa dove andare a parare + if (id != null) { + map['id'] = id!; + } + return map; } @override diff --git a/lib/features/master_data/providers/ui/provider_form_sheet.dart b/lib/features/master_data/providers/ui/provider_form_sheet.dart new file mode 100644 index 0000000..35a41b7 --- /dev/null +++ b/lib/features/master_data/providers/ui/provider_form_sheet.dart @@ -0,0 +1,159 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/features/master_data/providers/blocs/provider_cubit.dart'; +import 'package:flux/features/master_data/providers/models/provider_model.dart'; + +class ProviderFormSheet extends StatefulWidget { + final ProviderModel? initialProvider; + + const ProviderFormSheet({super.key, this.initialProvider}); + + @override + State createState() => _ProviderFormSheetState(); +} + +class _ProviderFormSheetState extends State { + late TextEditingController _nameController; + late bool _telefoniaFissa; + late bool _telefoniaMobile; + late bool _energia; + late bool _assicurazioni; + late bool _intrattenimento; + late bool _altro; + late bool _isActive; + + @override + void initState() { + super.initState(); + final p = widget.initialProvider; + _nameController = TextEditingController(text: p?.nome ?? ''); + _telefoniaFissa = p?.telefoniaFissa ?? false; + _telefoniaMobile = p?.telefoniaMobile ?? false; + _energia = p?.energia ?? false; + _assicurazioni = p?.assicurazioni ?? false; + _intrattenimento = p?.intrattenimento ?? false; + _altro = p?.altro ?? false; + _isActive = p?.isActive ?? true; + } + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); + } + + void _save() { + if (_nameController.text.trim().isEmpty) return; + + final provider = ProviderModel( + id: widget.initialProvider?.id, // Se nullo, Supabase farà insert + nome: _nameController.text.trim(), + telefoniaFissa: _telefoniaFissa, + telefoniaMobile: _telefoniaMobile, + energia: _energia, + assicurazioni: _assicurazioni, + intrattenimento: _intrattenimento, + altro: _altro, + isActive: _isActive, + companyId: + '', // Verrà ignorato dal toMap e gestito dal Cubit/SessionBloc se hai messo la logica lì + ); + + context.read().saveProvider(provider); + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.of( + context, + ).viewInsets.bottom, // Gestisce la tastiera + left: 16, + right: 16, + top: 16, + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + widget.initialProvider == null + ? "Nuovo Provider" + : "Modifica Provider", + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 16), + TextField( + controller: _nameController, + decoration: const InputDecoration( + labelText: "Nome Gestore/Brand", + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 16), + const Text( + "Servizi Abilitati", + style: TextStyle(fontWeight: FontWeight.bold), + ), + _buildSwitch( + "Energia (Luce/Gas)", + _energia, + (v) => setState(() => _energia = v), + ), + _buildSwitch( + "Telefonia Fissa", + _telefoniaFissa, + (v) => setState(() => _telefoniaFissa = v), + ), + _buildSwitch( + "Telefonia Mobile", + _telefoniaMobile, + (v) => setState(() => _telefoniaMobile = v), + ), + _buildSwitch( + "Assicurazioni", + _assicurazioni, + (v) => setState(() => _assicurazioni = v), + ), + _buildSwitch( + "Intrattenimento", + _intrattenimento, + (v) => setState(() => _intrattenimento = v), + ), + _buildSwitch( + "Altro/Accessori", + _altro, + (v) => setState(() => _altro = v), + ), + const Divider(), + _buildSwitch( + "Stato Attivo", + _isActive, + (v) => setState(() => _isActive = v), + ), + const SizedBox(height: 24), + ElevatedButton( + style: ElevatedButton.styleFrom( + minimumSize: const Size.fromHeight(50), + ), + onPressed: _save, + child: const Text("SALVA ANAGRAFICA"), + ), + const SizedBox(height: 24), + ], + ), + ), + ); + } + + Widget _buildSwitch(String title, bool value, Function(bool) onChanged) { + return SwitchListTile( + title: Text(title), + value: value, + onChanged: onChanged, + contentPadding: EdgeInsets.zero, + ); + } +} diff --git a/lib/features/master_data/providers/ui/providers_master_data_screen.dart b/lib/features/master_data/providers/ui/providers_master_data_screen.dart new file mode 100644 index 0000000..7ea2325 --- /dev/null +++ b/lib/features/master_data/providers/ui/providers_master_data_screen.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/features/master_data/providers/blocs/provider_cubit.dart'; +import 'package:flux/features/master_data/providers/models/provider_model.dart'; +import 'package:flux/features/master_data/providers/ui/provider_form_sheet.dart'; + +class ProvidersMasterDataScreen extends StatefulWidget { + const ProvidersMasterDataScreen({super.key}); + + @override + State createState() => + _ProvidersMasterDataScreenState(); +} + +class _ProvidersMasterDataScreenState extends State { + @override + void initState() { + super.initState(); + // Carichiamo i provider della company (senza store specifico per ora) + context.read().loadProviders(null); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Anagrafica Provider")), + body: BlocBuilder( + builder: (context, state) { + if (state.isLoading && state.allProviders.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + return ListView.separated( + itemCount: state.allProviders.length, + separatorBuilder: (context, index) => const Divider(height: 1), + itemBuilder: (context, index) { + final provider = state.allProviders[index]; + return ListTile( + leading: CircleAvatar( + backgroundColor: provider.isActive + ? Colors.green.shade100 + : Colors.grey.shade300, + child: Icon( + Icons.business, + color: provider.isActive ? Colors.green : Colors.grey, + ), + ), + title: Text( + provider.nome, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: _buildProviderBadges(provider), + trailing: const Icon(Icons.edit_outlined), + onTap: () => _showProviderForm(context, provider), + ); + }, + ); + }, + ), + floatingActionButton: FloatingActionButton( + onPressed: () => _showProviderForm(context, null), + child: const Icon(Icons.add), + ), + ); + } + + // Visualizza i servizi abilitati per quel provider nella lista + Widget _buildProviderBadges(ProviderModel p) { + return Wrap( + spacing: 4, + children: [ + if (p.telefoniaFissa || p.telefoniaMobile) + _smallTag("📞 Tel", Colors.blue), + if (p.energia) _smallTag("⚡ Energy", Colors.orange), + if (p.assicurazioni) _smallTag("🛡️ Assic", Colors.teal), + if (p.intrattenimento) _smallTag("📺 Ent", Colors.red), + if (p.altro) _smallTag("📦 Altro", Colors.grey), + ], + ); + } + + Widget _smallTag(String label, Color color) { + return Text( + label, + style: TextStyle(color: color, fontSize: 10, fontWeight: FontWeight.w600), + ); + } + + // DIALOG PER INSERIMENTO/MODIFICA + void _showProviderForm(BuildContext context, ProviderModel? provider) { + // Implementeremo qui il form con i vari SwitchListTile + // Per ora facciamo un segnaposto o passiamo a scriverlo seriamente + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (_) => ProviderFormSheet(initialProvider: provider), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 147acc4..2a92bdf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,8 @@ import 'package:flux/features/customers/blocs/customer_bloc.dart'; import 'package:flux/features/customers/data/customer_repository.dart'; import 'package:flux/features/master_data/products/blocs/product_cubit.dart'; import 'package:flux/features/master_data/products/data/product_repository.dart'; +import 'package:flux/features/master_data/providers/blocs/provider_cubit.dart'; +import 'package:flux/features/master_data/providers/data/provider_repository.dart'; import 'package:flux/features/master_data/staff/blocs/staff_cubit.dart'; import 'package:flux/features/master_data/staff/data/staff_repository.dart'; import 'package:flux/features/master_data/store/bloc/store_cubit.dart'; @@ -64,6 +66,7 @@ Future setupLocator() async { getIt.registerLazySingleton(() => ProductRepository()); getIt.registerLazySingleton(() => StaffRepository()); getIt.registerLazySingleton(() => ServicesRepository()); + getIt.registerLazySingleton(() => ProviderRepository()); } class FluxApp extends StatefulWidget { @@ -103,6 +106,9 @@ class _FluxAppState extends State { BlocProvider( create: (_) => ServicesCubit(context.read()), ), + BlocProvider( + create: (_) => ProvidersCubit(context.read()), + ), ], child: BlocBuilder( builder: (context, state) {