import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/features/tickets/blocs/ticket_shipping_cubit.dart'; import 'package:flux/features/tickets/models/ticket_model.dart'; class TicketShippingModal extends StatefulWidget { final List ticketIds; const TicketShippingModal({super.key, required this.ticketIds}); @override State createState() => _TicketShippingModalState(); } class _TicketShippingModalState extends State { final _formKey = GlobalKey(); @override void initState() { super.initState(); // Appena si apre la modale, carichiamo la lista dei laboratori context.read().loadRepairCenters(); } @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom + 24, top: 24, left: 24, right: 24, ), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.vertical(top: Radius.circular(28)), ), child: BlocConsumer( listener: (context, state) { if (state.status == TicketShippingStatus.success) { final provider = state.availableProviders.firstWhere( (p) => p.id == state.document.providerId, ); final location = state.availableLocations.firstWhere( (l) => l.id == state.document.destinationLocationId, ); // Creiamo un Dart Record elegante e lo "spariamo" fuori final ddtData = ( document: state.document, provider: provider, location: location, ); Navigator.pop(context, ddtData); } if (state.status == TicketShippingStatus.failure && state.errorMessage != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.errorMessage!), backgroundColor: Colors.red, ), ); } }, builder: (context, state) { if (state.status == TicketShippingStatus.loading && state.availableProviders.isEmpty) { return const SizedBox( height: 200, child: Center(child: CircularProgressIndicator()), ); } final doc = state.document; // Scorciatoia comoda per il nostro modello return Form( key: _formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ _buildHeader(context, state), const Divider(height: 32), // 1. DESTINAZIONE _buildSectionTitle(Icons.business, "Destinatario"), _buildProviderDropdown(context, state), const SizedBox(height: 16), if (doc.providerId.isNotEmpty) _buildLocationDropdown(context, state), const SizedBox(height: 24), // 2. DATI DOCUMENTO _buildSectionTitle(Icons.description, "Dati Documento"), _buildNumberingSection(context, state), const SizedBox(height: 16), _buildDatePicker(context, state), const SizedBox(height: 24), // 3. DETTAGLI MERCE E TRASPORTO _buildSectionTitle(Icons.inventory_2, "Dettagli Trasporto"), _buildShippingDetails(context, state), const SizedBox(height: 32), // BOTTONE CONFERMA SizedBox( width: double.infinity, height: 54, child: FilledButton.icon( onPressed: state.status == TicketShippingStatus.loading ? null : () { if (_formKey.currentState!.validate()) { // Assicurati che lo stato qui coincida con l'Enum del tuo TicketStatus context .read() .confirmShipment( newTicketStatus: TicketStatus.waitingForReturn, ); } }, icon: state.status == TicketShippingStatus.loading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Icon(Icons.local_shipping), label: const Text( "GENERA DDT E SPEDISCI", style: TextStyle(fontWeight: FontWeight.bold), ), ), ), ], ), ), ); }, ), ); } Widget _buildHeader(BuildContext context, TicketShippingState state) { return Row( children: [ CircleAvatar( backgroundColor: Theme.of(context).colorScheme.primaryContainer, child: Icon( Icons.share_location, color: Theme.of(context).colorScheme.primary, ), ), const SizedBox(width: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Spedizione Multipla", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), Text( "${widget.ticketIds.length} ticket pronti per il laboratorio", style: const TextStyle(color: Colors.grey), ), ], ), ], ); } Widget _buildProviderDropdown( BuildContext context, TicketShippingState state, ) { return DropdownButtonFormField( initialValue: state.document.providerId.isEmpty ? null : state.document.providerId, decoration: const InputDecoration( labelText: "Seleziona Centro Riparazioni", prefixIcon: Icon(Icons.store), ), items: state.availableProviders .map((p) => DropdownMenuItem(value: p.id, child: Text(p.name))) .toList(), onChanged: (val) => val != null ? context.read().selectProvider(val) : null, validator: (v) => v == null || v.isEmpty ? 'Campo obbligatorio' : null, ); } Widget _buildLocationDropdown( BuildContext context, TicketShippingState state, ) { return DropdownButtonFormField( initialValue: state.document.destinationLocationId.isEmpty ? null : state.document.destinationLocationId, decoration: const InputDecoration( labelText: "Sede di destinazione", prefixIcon: Icon(Icons.location_on), ), items: state.availableLocations .map((l) => DropdownMenuItem(value: l.id, child: Text(l.name))) .toList(), onChanged: (val) => val != null ? context.read().selectLocation(val) : null, validator: (v) => v == null || v.isEmpty ? 'Campo obbligatorio' : null, ); } Widget _buildNumberingSection( BuildContext context, TicketShippingState state, ) { return Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( flex: 2, child: state.isAutoNumber ? const Text('Numero auto-generato alla conferma') : TextFormField( // Key è fondamentale per far aggiornare il campo quando cambia da auto a manuale key: ValueKey('docNum_${state.isAutoNumber}'), initialValue: state.document.docNumber, readOnly: state.isAutoNumber, // Bloccato se automatico decoration: InputDecoration( labelText: "Numero DDT", helperText: state.isAutoNumber ? "Generato automaticamente" : "Inserimento manuale", ), onChanged: (val) => context .read() .updateDocument(docNumber: val), ), ), const SizedBox(width: 16), // Switch per modalità automatica Column( children: [ const Text( "Auto", style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold), ), Switch( value: state.isAutoNumber, onChanged: (val) => context.read().toggleAutoNumber(val), ), ], ), ], ); } Widget _buildDatePicker(BuildContext context, TicketShippingState state) { final date = state.document.docDate; return ListTile( contentPadding: EdgeInsets.zero, title: const Text("Data Documento"), subtitle: Text( "${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}", ), trailing: const Icon(Icons.calendar_month), onTap: () async { final ticketShippingCubit = context.read(); final newDate = await showDatePicker( context: context, initialDate: date, firstDate: DateTime.now().subtract(const Duration(days: 365)), lastDate: DateTime.now().add(const Duration(days: 365)), ); if (newDate != null) { ticketShippingCubit.updateDocument(docDate: newDate); } }, ); } Widget _buildShippingDetails( BuildContext context, TicketShippingState state, ) { return Column( children: [ Row( children: [ Expanded( child: TextFormField( initialValue: state.document.packageCount.toString(), decoration: const InputDecoration(labelText: "N. Colli"), keyboardType: TextInputType.number, onChanged: (val) => context .read() .updateDocument(packageCount: int.tryParse(val) ?? 1), ), ), const SizedBox(width: 16), Expanded( child: TextFormField( initialValue: state.document.weight?.toString() ?? '', decoration: const InputDecoration( labelText: "Peso", suffixText: "Kg", ), keyboardType: const TextInputType.numberWithOptions( decimal: true, ), onChanged: (val) => context .read() .updateDocument(weight: double.tryParse(val)), ), ), ], ), const SizedBox(height: 16), TextFormField( initialValue: state.document.shippingReason, decoration: const InputDecoration(labelText: "Causale Trasporto"), onChanged: (val) => context .read() .updateDocument(shippingReason: val), ), const SizedBox(height: 16), TextFormField( initialValue: state.document.notes ?? '', decoration: const InputDecoration( labelText: "Aspetto Beni (es. Scatola, Busta)", ), onChanged: (val) => context.read().updateDocument(notes: val), ), ], ); } Widget _buildSectionTitle(IconData icon, String title) { return Padding( padding: const EdgeInsets.only(bottom: 12.0), child: Row( children: [ Icon(icon, size: 18, color: Colors.grey), const SizedBox(width: 8), Text( title.toUpperCase(), style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.grey, letterSpacing: 1.1, ), ), ], ), ); } }