import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/features/tickets/blocs/ticket_form_cubit.dart'; import 'package:flux/features/tickets/blocs/ticket_form_state.dart'; import 'package:flux/features/tickets/models/ticket_model.dart'; import 'package:flux/core/widgets/shared_forms/shared_customer_section.dart'; import 'package:flux/core/widgets/shared_forms/shared_model_section.dart'; import 'package:flux/core/widgets/shared_forms/shared_staff_section.dart'; // Il tuo widget agnostico dello staff class TicketFormScreen extends StatefulWidget { final TicketModel? existingTicket; const TicketFormScreen({super.key, this.existingTicket}); @override State createState() => _TicketFormScreenState(); } class _TicketFormScreenState extends State { final _formKey = GlobalKey(); // Controllers testuali final _altPhoneCtrl = TextEditingController(); final _serialCtrl = TextEditingController(); final _requestCtrl = TextEditingController(); final _accessoriesCtrl = TextEditingController(); final _publicNotesCtrl = TextEditingController(); final _internalNotesCtrl = TextEditingController(); final _priceCtrl = TextEditingController(); final _costCtrl = TextEditingController(); bool _isInitialized = false; @override void initState() { super.initState(); // Inizializziamo il Cubit context.read().initForm(widget.existingTicket); } @override void dispose() { _altPhoneCtrl.dispose(); _serialCtrl.dispose(); _requestCtrl.dispose(); _accessoriesCtrl.dispose(); _publicNotesCtrl.dispose(); _internalNotesCtrl.dispose(); _priceCtrl.dispose(); _costCtrl.dispose(); super.dispose(); } // Sincronizza i controller con lo stato iniziale senza sovrascrivere se l'utente sta digitando void _syncTextControllers(TicketModel model) { if (_altPhoneCtrl.text.isEmpty) _altPhoneCtrl.text = model.alternativePhoneNumber ?? ''; if (_serialCtrl.text.isEmpty) _serialCtrl.text = model.targetSn ?? ''; if (_requestCtrl.text.isEmpty) _requestCtrl.text = model.request ?? ''; if (_accessoriesCtrl.text.isEmpty) _accessoriesCtrl.text = model.includedAccessories ?? ''; if (_publicNotesCtrl.text.isEmpty) _publicNotesCtrl.text = model.publicNotes ?? ''; if (_internalNotesCtrl.text.isEmpty) _internalNotesCtrl.text = model.internalNotes ?? ''; if (_priceCtrl.text.isEmpty && model.customerPrice > 0) _priceCtrl.text = model.customerPrice.toString(); if (_costCtrl.text.isEmpty && model.internalCost > 0) _costCtrl.text = model.internalCost.toString(); _isInitialized = true; } // Chiamato prima del salvataggio per pushare i testi nei campi del Cubit void _flushControllersToCubit() { context.read().updateFields( alternativePhoneNumber: _altPhoneCtrl.text, targetSn: _serialCtrl.text, request: _requestCtrl.text, includedAccessories: _accessoriesCtrl.text, publicNotes: _publicNotesCtrl.text, internalNotes: _internalNotesCtrl.text, customerPrice: double.tryParse(_priceCtrl.text) ?? 0.0, internalCost: double.tryParse(_costCtrl.text) ?? 0.0, ); } void _saveTicket({required bool keepAdding}) { if (_formKey.currentState!.validate()) { _flushControllersToCubit(); context.read().saveTicket(keepAdding: keepAdding); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); return BlocConsumer( listenWhen: (previous, current) => previous.status != current.status, listener: (context, state) { if (state.status == TicketFormStatus.ready && !_isInitialized) { _syncTextControllers(state.ticket); } if (state.status == TicketFormStatus.success) { Navigator.of(context).pop(); } else if (state.status == TicketFormStatus.successAndAddAnother) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Scheda salvata! Inserisci la prossima.'), ), ); // Svuotiamo i controller per il nuovo ticket _altPhoneCtrl.clear(); _serialCtrl.clear(); _requestCtrl.clear(); _accessoriesCtrl.clear(); _publicNotesCtrl.clear(); _internalNotesCtrl.clear(); _priceCtrl.clear(); _costCtrl.clear(); _isInitialized = false; } else if (state.status == TicketFormStatus.failure) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.errorMessage ?? 'Errore di salvataggio'), backgroundColor: theme.colorScheme.error, ), ); } }, builder: (context, state) { final ticket = state.ticket; return Scaffold( appBar: AppBar( title: Text( ticket.id == null ? 'Nuova Scheda Assistenza' : 'Modifica Scheda', ), actions: [ // PICCOLO BADGE DI STATO IN ALTO A DESTRA Padding( padding: const EdgeInsets.only(right: 16.0), child: Chip( label: Text( ticket.status.name.toUpperCase(), style: const TextStyle(color: Colors.white, fontSize: 10), ), backgroundColor: ticket.status.color, ), ), ], ), body: Form( key: _formKey, child: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Center( child: ConstrainedBox( // Limitiamo la larghezza su schermi grandi per non renderlo illeggibile constraints: const BoxConstraints(maxWidth: 800), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // --- CARD 1: INTESTAZIONE & CLIENTE --- _buildCard( title: 'Anagrafica', icon: Icons.person, children: [ // Qui usiamo la StaffSection per scegliere "A nome di chi" apriamo la scheda StaffSection( label: 'Creato Da', staffId: ticket.createdById, staffName: ticket.createdByName, onStaffSelected: (staff) => context.read().updateCreator( staffId: staff.id, staffName: staff.name, ), ), const Divider(height: 32), SharedCustomerSection( customerId: ticket.customerId, customerName: ticket.customerName, onCustomerSelected: (customer) => context .read() .updateCustomer(customer), ), const SizedBox(height: 16), TextFormField( controller: _altPhoneCtrl, decoration: const InputDecoration( labelText: 'Recapito Alternativo (es. se lascia il telefono principale)', prefixIcon: Icon(Icons.phone), ), ), ], ), // --- CARD 2: DISPOSITIVO --- _buildCard( title: 'Dispositivo', icon: Icons.devices, children: [ SharedModelSection( label: 'Modello da Riparare', modelId: ticket.targetModelId, modelName: ticket.targetModelName, onModelSelected: (id, name) => context .read() .updateModel(modelId: id, modelName: name), ), const SizedBox(height: 16), TextFormField( controller: _serialCtrl, decoration: const InputDecoration( labelText: 'Seriale / IMEI', prefixIcon: Icon(Icons.qr_code), ), ), ], ), // --- CARD 3: PROBLEMA E LAVORAZIONE --- _buildCard( title: 'Dettagli Riparazione', icon: Icons.build, children: [ // Tipo Lavorazione e Tipo Garanzia Row( children: [ Expanded( child: DropdownButtonFormField( value: ticket.ticketType, decoration: const InputDecoration( labelText: 'Tipo Lavorazione', ), items: TicketType.values .map( (t) => DropdownMenuItem( value: t, child: Text( t.name, ), // Se hai estensioni, usa t.displayName ), ) .toList(), onChanged: (val) => context .read() .updateFields(ticketType: val), ), ), const SizedBox(width: 16), Expanded( child: DropdownButtonFormField( value: ticket.status, decoration: const InputDecoration( labelText: 'Stato Attuale', ), items: TicketStatus.values .map( (s) => DropdownMenuItem( value: s, child: Text(s.name), // Idem qui ), ) .toList(), onChanged: (val) => context .read() .updateFields(status: val), ), ), ], ), const SizedBox(height: 16), TextFormField( controller: _requestCtrl, maxLines: 4, decoration: const InputDecoration( labelText: 'Difetto dichiarato o Richiesta del cliente', alignLabelWithHint: true, ), ), const SizedBox(height: 16), TextFormField( controller: _accessoriesCtrl, decoration: const InputDecoration( labelText: 'Accessori Consegnati (es. cover, caricatore...)', prefixIcon: Icon(Icons.cable), ), ), const SizedBox(height: 16), SwitchListTile( title: const Text('Prestato Telefono di Cortesia?'), value: ticket.hasCourtesyDevice, onChanged: (val) => context .read() .updateFields(hasCourtesyDevice: val), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: theme.dividerColor), ), ), ], ), // --- CARD 4: COSTI E NOTE --- _buildCard( title: 'Costi & Note', icon: Icons.euro, children: [ Row( children: [ Expanded( child: TextFormField( controller: _priceCtrl, keyboardType: const TextInputType.numberWithOptions( decimal: true, ), decoration: const InputDecoration( labelText: 'Preventivo Cliente (€)', prefixIcon: Icon(Icons.sell_outlined), ), ), ), const SizedBox(width: 16), Expanded( child: TextFormField( controller: _costCtrl, keyboardType: const TextInputType.numberWithOptions( decimal: true, ), decoration: const InputDecoration( labelText: 'Nostro Costo (€)', prefixIcon: Icon( Icons.shopping_cart_outlined, ), ), ), ), ], ), const SizedBox(height: 16), TextFormField( controller: _publicNotesCtrl, maxLines: 2, decoration: const InputDecoration( labelText: 'Note Pubbliche (Visibili su ricevuta)', ), ), const SizedBox(height: 16), TextFormField( controller: _internalNotesCtrl, maxLines: 3, decoration: InputDecoration( labelText: 'Note Interne (Solo per lo Staff)', fillColor: Colors.amber.withValues(alpha: 0.1), filled: true, ), ), ], ), // --- CARD 5: ASSEGNAZIONE & FILE --- _buildCard( title: 'Assegnazione Tecnico', icon: Icons.engineering, children: [ StaffSection( label: 'Assegnato A', staffId: ticket.assignedToId, staffName: ticket.assignedToName, onStaffSelected: (staff) => context.read().updateFields( assignedToId: staff.id, assignedToName: staff.name, ), ), // TODO: Inserire qui il tuo SharedAttachmentsSection per foto pre-riparazione ], ), const SizedBox(height: 80), // Spazio per il bottom nav ], ), ), ), ), ), bottomNavigationBar: SafeArea( child: Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ Expanded( flex: 1, child: OutlinedButton( onPressed: state.status == TicketFormStatus.saving ? null : () => _saveTicket(keepAdding: true), child: const Text( 'Salva e Aggiungi Altro', textAlign: TextAlign.center, ), ), ), const SizedBox(width: 12), Expanded( flex: 1, child: ElevatedButton( onPressed: state.status == TicketFormStatus.saving ? null : () => _saveTicket(keepAdding: false), child: state.status == TicketFormStatus.saving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Text('Salva ed Esci'), ), ), ], ), ), ), ); }, ); } // Helper per creare le Card esteticamente coerenti Widget _buildCard({ required String title, required IconData icon, required List children, }) { return Card( margin: const EdgeInsets.only(bottom: 24), elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, color: Theme.of(context).colorScheme.primary), const SizedBox(width: 12), Text( title, style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), ], ), const Divider(height: 32), ...children, ], ), ), ); } }