feat-insert-service #5

Merged
brontomark merged 11 commits from feat-insert-service into main 2026-04-20 16:52:20 +02:00
7 changed files with 776 additions and 98 deletions
Showing only changes of commit 60a8b00bcd - Show all commits

View File

@@ -7,15 +7,17 @@ import 'package:get_it/get_it.dart';
import '../models/provider_model.dart'; import '../models/provider_model.dart';
class ProvidersState extends Equatable { class ProvidersState extends Equatable {
final List<ProviderModel> allProviders; // Tutti i provider della company final List<ProviderModel> allProviders;
final List<String> final List<String> associatedIds;
associatedIds; // ID dei provider attivi nello store selezionato // NUOVO CAMPO: Lista dei provider pronti per essere usati nel form pratiche
final List<ProviderModel> activeProviders;
final bool isLoading; final bool isLoading;
final String? errorMessage; final String? errorMessage;
const ProvidersState({ const ProvidersState({
this.allProviders = const [], this.allProviders = const [],
this.associatedIds = const [], this.associatedIds = const [],
this.activeProviders = const [], // Inizializza
this.isLoading = false, this.isLoading = false,
this.errorMessage, this.errorMessage,
}); });
@@ -23,14 +25,18 @@ class ProvidersState extends Equatable {
ProvidersState copyWith({ ProvidersState copyWith({
List<ProviderModel>? allProviders, List<ProviderModel>? allProviders,
List<String>? associatedIds, List<String>? associatedIds,
List<ProviderModel>? activeProviders, // Aggiungi qui
bool? isLoading, bool? isLoading,
String? errorMessage, String? errorMessage,
}) { }) {
return ProvidersState( return ProvidersState(
allProviders: allProviders ?? this.allProviders, allProviders: allProviders ?? this.allProviders,
associatedIds: associatedIds ?? this.associatedIds, associatedIds: associatedIds ?? this.associatedIds,
activeProviders: activeProviders ?? this.activeProviders, // Aggiungi qui
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage, errorMessage:
errorMessage ??
this.errorMessage, // Correzione bug: mancava "?? this.errorMessage" nel tuo originale
); );
} }
@@ -38,6 +44,7 @@ class ProvidersState extends Equatable {
List<Object?> get props => [ List<Object?> get props => [
allProviders, allProviders,
associatedIds, associatedIds,
activeProviders, // Aggiungi qui
isLoading, isLoading,
errorMessage, errorMessage,
]; ];
@@ -74,6 +81,23 @@ class ProvidersCubit extends Cubit<ProvidersState> {
} }
} }
Future<void> loadActiveProvidersForStore(String storeId) async {
emit(state.copyWith(isLoading: true));
try {
final activeList = await _repository.fetchActiveProvidersForStore(
storeId,
);
emit(state.copyWith(activeProviders: activeList, isLoading: false));
} catch (e) {
emit(
state.copyWith(
isLoading: false,
errorMessage: "Errore caricamento gestori: $e",
),
);
}
}
// Aggiunge o rimuove l'associazione con lo store // Aggiunge o rimuove l'associazione con lo store
Future<void> toggleProviderAssociation({ Future<void> toggleProviderAssociation({
required String providerId, required String providerId,

View File

@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
class ActionCard extends StatelessWidget {
final String label;
final int count;
final IconData icon;
final Color color;
final VoidCallback onTap;
const ActionCard({
super.key,
required this.label,
required this.count,
required this.icon,
required this.color,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final isActive = count > 0;
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 110, // Larghezza fissa per avere una griglia ordinata
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
decoration: BoxDecoration(
color: isActive
? color.withValues(alpha: 0.15)
: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isActive ? color : Colors.grey.withValues(alpha: 0.3),
width: isActive ? 2 : 1,
),
boxShadow: isActive
? [
BoxShadow(
color: color.withValues(alpha: 0.2),
blurRadius: 8,
spreadRadius: 1,
),
]
: [],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: isActive ? color : Colors.grey, size: 28),
const SizedBox(height: 8),
Text(
label,
style: TextStyle(
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? color : Colors.grey.shade700,
),
textAlign: TextAlign.center,
),
if (isActive) ...[
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(12),
),
child: Text(
count.toString(),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
),
],
],
),
),
);
}
}

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/customers/ui/customer_search_sheet.dart'; import 'package:flux/features/customers/ui/customer_search_sheet.dart';
import 'package:flux/features/services/models/service_model.dart'; import 'package:flux/features/services/models/service_model.dart';

View File

@@ -0,0 +1,333 @@
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/services/models/energy_service_model.dart'; // Assicurati degli import
class EnergyServiceDialog extends StatefulWidget {
final List<EnergyServiceModel> initialServices;
final String
currentStoreId; // Ci serve per sapere per quale negozio caricare i gestori
const EnergyServiceDialog({
super.key,
required this.initialServices,
required this.currentStoreId,
});
@override
State<EnergyServiceDialog> createState() => _EnergyServiceDialogState();
}
class _EnergyServiceDialogState extends State<EnergyServiceDialog> {
// Lista temporanea per non "sporcare" il cubit finché non si preme Conferma
late List<EnergyServiceModel> _tempList;
bool _isAddingNew = false;
@override
void initState() {
super.initState();
_tempList = List.from(widget.initialServices);
// Al caricamento della modale, chiediamo al Cubit di recuperare i gestori veri!
context.read<ProvidersCubit>().loadActiveProvidersForStore(
widget.currentStoreId,
);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Row(
children: [
Icon(Icons.bolt, color: Theme.of(context).colorScheme.primary),
const SizedBox(width: 8),
Text(_isAddingNew ? "Nuovo Contratto" : "Servizi Energia"),
],
),
content: AnimatedSize(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
child: SizedBox(
width: double.maxFinite,
// Cambia vista in base al flag
child: _isAddingNew
? _EnergyForm(
onSave: (newService) {
setState(() {
_tempList.add(newService);
_isAddingNew = false; // Torna alla lista
});
},
onCancel: () {
setState(() => _isAddingNew = false);
},
)
: _EnergyList(
services: _tempList,
onDelete: (index) {
setState(() => _tempList.removeAt(index));
},
onAddTap: () {
setState(() => _isAddingNew = true); // Passa al form
},
activeProviders: [
// Passiamo i provider attivi filtrati per tipo Energia
...context
.read<ProvidersCubit>()
.state
.activeProviders
.where((p) => p.energia == true),
],
),
),
),
actions: [
if (!_isAddingNew) ...[
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Annulla"),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, _tempList),
child: const Text("Conferma Tutti"),
),
],
],
);
}
}
// ==========================================
// VISTA 1: LA LISTA DEI CONTRATTI
// ==========================================
class _EnergyList extends StatelessWidget {
final List<EnergyServiceModel> services;
final List<ProviderModel>
activeProviders; // <--- NUOVO: La lista vera dal Cubit
final Function(int) onDelete;
final VoidCallback onAddTap;
const _EnergyList({
required this.services,
required this.activeProviders, // <--- Richiesto
required this.onDelete,
required this.onAddTap,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (services.isEmpty)
const Padding(
padding: EdgeInsets.symmetric(vertical: 32.0),
child: Text(
"Nessun contratto energia inserito.",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
)
else
Flexible(
child: ListView.separated(
shrinkWrap: true,
itemCount: services.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (context, index) {
final s = services[index];
final isLuce = s.type == EnergyType.luce;
// LA MAGIA: Troviamo il nome partendo dall'ID salvato nel servizio
final providerIndex = activeProviders.indexWhere(
(p) => p.id == s.providerId,
);
final providerName = providerIndex >= 0
? (activeProviders[providerIndex].nome ?? 'Sconosciuto')
: 'Gestore Rimosso/Sconosciuto';
// Formattazione data pulita (es. 04/09/2025)
final day = s.expiration.day.toString().padLeft(2, '0');
final month = s.expiration.month.toString().padLeft(2, '0');
final formattedDate = "$day/$month/${s.expiration.year}";
return ListTile(
contentPadding: EdgeInsets.zero,
leading: CircleAvatar(
backgroundColor: isLuce
? Colors.orange.shade100
: Colors.blue.shade100,
child: Icon(
isLuce
? Icons.lightbulb_outline
: Icons.local_fire_department,
color: isLuce ? Colors.orange : Colors.blue,
),
),
title: Text(
providerName,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text("Scadenza: $formattedDate"),
trailing: IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.red),
onPressed: () => onDelete(index),
),
);
},
),
),
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: onAddTap,
icon: const Icon(Icons.add),
label: const Text("Aggiungi Contratto"),
),
],
);
}
}
// ==========================================
// VISTA 2: IL FORM DI INSERIMENTO
// ==========================================
class _EnergyForm extends StatefulWidget {
final Function(EnergyServiceModel) onSave;
final VoidCallback onCancel;
const _EnergyForm({required this.onSave, required this.onCancel});
@override
State<_EnergyForm> createState() => _EnergyFormState();
}
class _EnergyFormState extends State<_EnergyForm> {
EnergyType _selectedType = EnergyType.luce;
String? _selectedProviderId;
DateTime? _selectedExpiration;
Future<void> _pickDate() async {
final picked = await showDatePicker(
context: context,
initialDate: DateTime.now().add(
const Duration(days: 365),
), // Default 1 anno
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 365 * 10)),
);
if (picked != null) {
setState(() => _selectedExpiration = picked);
}
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 1. Tipo (Luce o Gas) - Segmented Button stile M3
SegmentedButton<EnergyType>(
segments: const [
ButtonSegment(
value: EnergyType.luce,
label: Text("Luce"),
icon: Icon(Icons.lightbulb_outline),
),
ButtonSegment(
value: EnergyType.gas,
label: Text("Gas"),
icon: Icon(Icons.local_fire_department),
),
],
selected: {_selectedType},
onSelectionChanged: (Set<EnergyType> newSelection) {
setState(() => _selectedType = newSelection.first);
},
),
const SizedBox(height: 16),
// 2. Provider Dropdown
BlocBuilder<ProvidersCubit, ProvidersState>(
builder: (context, state) {
if (state.isLoading) {
return const Center(
child: LinearProgressIndicator(),
); // Mostra una barretta di caricamento
}
if (state.activeProviders.isEmpty) {
return const Text(
"Nessun gestore associato a questo negozio.",
style: TextStyle(color: Colors.red),
);
}
// Filtra solo i provider di tipo Energia (Se hai una categoria nel modello)
// Se non hai una categoria nel ProviderModel, puoi rimuovere il .where
final energyProviders = state.activeProviders;
return DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: "Gestore / Provider",
border: OutlineInputBorder(),
),
initialValue: _selectedProviderId,
items: energyProviders.map((p) {
return DropdownMenuItem(
value: p.id,
child: Text(p.nome ?? 'Sconosciuto'),
);
}).toList(),
onChanged: (val) => setState(() => _selectedProviderId = val),
);
},
),
const SizedBox(height: 16),
// 3. Scadenza (DatePicker integrato in un TextField)
TextFormField(
readOnly: true,
onTap: _pickDate,
decoration: InputDecoration(
labelText: "Data Scadenza",
border: const OutlineInputBorder(),
suffixIcon: const Icon(Icons.calendar_month),
),
// Mostra la data se selezionata, altrimenti vuoto
controller: TextEditingController(
text: _selectedExpiration != null
? "${_selectedExpiration!.day}/${_selectedExpiration!.month}/${_selectedExpiration!.year}"
: "",
),
),
const SizedBox(height: 24),
// 4. Pulsanti Interni al Form
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: widget.onCancel,
child: const Text("Indietro"),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed:
(_selectedProviderId == null || _selectedExpiration == null)
? null // Disabilitato se mancano dati obbligatori
: () {
final newService = EnergyServiceModel(
type: _selectedType,
expiration: _selectedExpiration!,
providerId: _selectedProviderId!,
);
widget.onSave(newService);
},
child: const Text("Salva Contratto"),
),
],
),
],
);
}
}

View File

@@ -0,0 +1,158 @@
import 'dart:async'; // Necessario per il Timer
import 'package:flutter/material.dart';
Future<void> updateCountDialog(
BuildContext context,
String title,
int currentValue,
Function(int) onSave,
) async {
int tempValue =
currentValue; // Variabile locale per gestire il conteggio nella dialog
final result = await showDialog<int>(
context: context,
builder: (context) => AlertDialog(
title: Text("Imposta $title"),
content: QuickCounter(
initialValue: tempValue,
onChanged: (val) => tempValue =
val, // Aggiorna il valore locale quando il counter cambia
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Annulla"),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, tempValue),
child: const Text("Conferma"),
),
],
),
);
if (result != null) {
onSave(result);
}
}
// --- Widget Interno Specifico per il Counter Veloce ---
class QuickCounter extends StatefulWidget {
final int initialValue;
final ValueChanged<int>
onChanged; // Callback per notificare il padre dei cambiamenti
const QuickCounter({
super.key,
required this.initialValue,
required this.onChanged,
});
@override
State<QuickCounter> createState() => _QuickCounterState();
}
class _QuickCounterState extends State<QuickCounter> {
late int _value;
Timer? _longPressTimer; // Il timer per l'auto-incremento
@override
void initState() {
super.initState();
_value = widget.initialValue;
}
@override
void dispose() {
_longPressTimer
?.cancel(); // IMPORTANTE: Annulla sempre il timer alla distruzione
super.dispose();
}
// Logica comune per incremento/decremento singolo o rapido
void _update(int delta) {
setState(() {
_value += delta;
if (_value < 0) _value = 0; // Impedisci numeri negativi
});
widget.onChanged(_value); // Notifica il padre
}
// Gestione dell'inizio della pressione prolungata
void _startLongPress(int delta) {
_update(delta); // Esegui subito il primo aggiornamento al tocco iniziale
_longPressTimer = Timer.periodic(const Duration(milliseconds: 100), (
timer,
) {
_update(delta); // Aggiorna velocemente finché la pressione continua
});
}
// Gestione della fine della pressione prolungata
void _stopLongPress() {
_longPressTimer?.cancel();
}
@override
Widget build(BuildContext context) {
final canDecrement = _value > 0;
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// --- Pulsante MENO ---
GestureDetector(
onLongPressStart: canDecrement ? (_) => _startLongPress(-1) : null,
onLongPressEnd: (_) => _stopLongPress(),
onLongPressCancel: () => _stopLongPress(),
onTap: canDecrement ? () => _update(-1) : null,
child: Opacity(
// Visivamente disabilitato se < 0
opacity: canDecrement ? 1.0 : 0.4,
child: const ActionButton(icon: Icons.remove, color: Colors.red),
),
),
// --- Valore Centrale ---
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Text(
_value.toString(),
style: const TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
),
),
// --- Pulsante PIU' ---
GestureDetector(
onLongPressStart: (_) => _startLongPress(1),
onLongPressEnd: (_) => _stopLongPress(),
onLongPressCancel: () => _stopLongPress(),
onTap: () => _update(1),
child: const ActionButton(icon: Icons.add, color: Colors.green),
),
],
);
}
}
// Piccolo widget di utilità per l'aspetto del pulsante
class ActionButton extends StatelessWidget {
final IconData icon;
final Color color;
const ActionButton({super.key, required this.icon, required this.color});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
shape: BoxShape.circle,
border: Border.all(color: color, width: 2),
),
child: Icon(icon, color: color, size: 30),
);
}
}

View File

@@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/services/blocs/services_cubit.dart'; import 'package:flux/features/services/blocs/services_cubit.dart';
import 'package:flux/features/services/ui/service_form_screen/customer_section.dart';
import 'package:flux/features/services/ui/service_form_screen/general_info_section.dart'; import 'package:flux/features/services/ui/service_form_screen/general_info_section.dart';
import 'package:flux/features/services/ui/service_form_screen/services_grid.dart';
class ServiceFormScreen extends StatelessWidget { class ServiceFormScreen extends StatelessWidget {
const ServiceFormScreen({super.key}); const ServiceFormScreen({super.key});
@@ -30,7 +32,7 @@ class ServiceFormScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// SEZIONE 1: CLIENTE // SEZIONE 1: CLIENTE
const _CustomerSection(), CustomerSection(service: service),
const SizedBox(height: 24), const SizedBox(height: 24),
// SEZIONE 2: INFO GENERALI (Da fare) // SEZIONE 2: INFO GENERALI (Da fare)
@@ -38,13 +40,7 @@ class ServiceFormScreen extends StatelessWidget {
const SizedBox(height: 24), const SizedBox(height: 24),
// SEZIONE 3: I MODULI (Da fare) // SEZIONE 3: I MODULI (Da fare)
Text( ServicesGrid(service: service),
"Servizi e Accessori",
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
// const _ServicesGrid(),
const SizedBox(height: 32), const SizedBox(height: 32),
// SEZIONE 4: ALLEGATI (Da fare) // SEZIONE 4: ALLEGATI (Da fare)
@@ -90,88 +86,3 @@ class _SaveButton extends StatelessWidget {
); );
} }
} }
class _CustomerSection extends StatelessWidget {
const _CustomerSection();
@override
Widget build(BuildContext context) {
return BlocBuilder<ServicesCubit, ServicesState>(
builder: (context, state) {
final service = state.currentService!;
final hasCustomer = service.customerId != null;
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.person,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
Text(
"Dati Cliente",
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 16),
// Se non c'è il cliente, mostriamo il tastone per cercarlo
if (!hasCustomer)
Center(
child: ElevatedButton.icon(
onPressed: () {
// TODO: Aprire modale/dialog per ricerca clienti
print("Apro ricerca clienti...");
},
icon: const Icon(Icons.search),
label: const Text("Seleziona o Crea Cliente"),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
),
)
// Se c'è, mostriamo chi è e diamo la possibilità di cambiarlo
else
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
service.customerDisplayName ?? "Cliente Selezionato",
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
TextButton.icon(
onPressed: () {
// TODO: Aprire modale/dialog per ricerca clienti
},
icon: const Icon(Icons.edit, size: 18),
label: const Text("Cambia"),
),
],
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,168 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/features/services/blocs/services_cubit.dart';
import 'package:flux/features/services/models/energy_service_model.dart';
import 'package:flux/features/services/models/service_model.dart';
import 'package:flux/features/services/ui/service_form_screen/action_card.dart';
import 'package:flux/features/services/ui/service_form_screen/energy_service_dialog.dart';
import 'package:flux/features/services/ui/service_form_screen/int_dialogs.dart'; // Assicurati di importare il modello
class ServicesGrid extends StatelessWidget {
final ServiceModel service;
const ServicesGrid({super.key, required this.service});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.layers_outlined,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 8),
Text(
"Servizi e Accessori",
style: Theme.of(context).textTheme.titleMedium,
),
],
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: Wrap(
spacing: 16,
runSpacing: 16,
alignment: WrapAlignment.center,
children: [
// --- CONTATORI SEMPLICI ---
ActionCard(
label: "AL",
count: service.al,
icon: Icons.sim_card,
color: Colors.blue,
onTap: () => updateCountDialog(
context,
"AL",
service.al,
(val) =>
context.read<ServicesCubit>().updateField(al: val),
),
),
ActionCard(
label: "MNP",
count: service.mnp,
icon: Icons.phone_android,
color: Colors.indigo,
onTap: () => updateCountDialog(
context,
"MNP",
service.mnp,
(val) =>
context.read<ServicesCubit>().updateField(mnp: val),
),
),
ActionCard(
label: "NIP",
count: service.nip,
icon: Icons.compare_arrows,
color: Colors.cyan,
onTap: () => updateCountDialog(
context,
"NIP",
service.nip,
(val) =>
context.read<ServicesCubit>().updateField(nip: val),
),
),
ActionCard(
label: "Unica",
count: service.unica,
icon: Icons.all_inclusive,
color: Colors.purple,
onTap: () => updateCountDialog(
context,
"Unica",
service.unica,
(val) =>
context.read<ServicesCubit>().updateField(unica: val),
),
),
ActionCard(
label: "Telepass",
count: service.telepass,
icon: Icons.directions_car,
color: Colors.amber.shade700,
onTap: () => updateCountDialog(
context,
"Telepass",
service.telepass,
(val) => context.read<ServicesCubit>().updateField(
telepass: val,
),
),
),
// --- MODULI COMPLESSI (Le liste) ---
ActionCard(
label: "Energia",
count: service.energyServices.length,
icon: Icons.bolt,
color: Colors.green,
onTap: () async {
// Apriamo la modale e aspettiamo il risultato
final result = await showDialog<List<EnergyServiceModel>>(
context: context,
builder: (context) => EnergyServiceDialog(
currentStoreId: service.storeId,
initialServices: service
.energyServices, // Passiamo la lista attuale
),
);
// Se l'utente ha premuto "Conferma" e non "Annulla" o tap fuori
if (result != null && context.mounted) {
context.read<ServicesCubit>().updateEnergyServices(
result,
);
}
},
),
ActionCard(
label: "Finanziam.",
count: service.finServices.length,
icon: Icons.euro_symbol,
color: Colors.teal,
onTap: () {
// TODO: Aprire la Dialog Finanziamenti complessa
print("Apri Fin Dialog");
},
),
ActionCard(
label: "Contenuti",
count: service.entertainmentServices.length,
icon: Icons.tv,
color: Colors.redAccent,
onTap: () {
// TODO: Aprire la Dialog Contenuti complessa
print("Apri Contenuti Dialog");
},
),
],
),
),
],
),
),
);
}
}