import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/features/attachments/blocs/attachments_bloc.dart'; import 'package:flux/features/operations/blocs/operation_form_cubit.dart'; import 'package:flux/features/operations/models/operation_model.dart'; import 'package:flux/core/widgets/shared_forms/customer_section.dart'; import 'package:flux/features/operations/ui/widgets/details_section.dart'; import 'package:flux/core/widgets/shared_forms/shared_files_section.dart'; // <- Cambiato ad un file unico per coerenza col ticket class OperationFormScreen extends StatefulWidget { final String? operationId; final OperationModel? existingOperation; const OperationFormScreen({ super.key, this.operationId, this.existingOperation, }); @override State createState() => _OperationFormScreenState(); } class _OperationFormScreenState extends State { final _formKey = GlobalKey(); final _referenceController = TextEditingController(); final _noteController = TextEditingController(); final _freeTextSubtypeController = TextEditingController(); final _freeTextDescriptionController = TextEditingController(); final List _availableTypes = [ 'AL', 'MNP', 'NIP', 'UNICA', 'FWA', 'TELEPASS', 'Energy', 'Fin', 'Entertainment', 'Custom', ]; bool _isInitialized = false; @override void initState() { super.initState(); // 1. Lanciamo l'inizializzazione sincrona/asincrona context.read().initForm( existingOperation: widget.existingOperation, operationId: widget.operationId, ); // 2. Lettura immediata dello stato (come fatto per il customer!) final currentState = context.read().state; if (currentState.status == OperationFormStatus.ready && !_isInitialized) { _syncTextControllers(currentState.operation); } } @override void dispose() { _referenceController.dispose(); _noteController.dispose(); _freeTextSubtypeController.dispose(); _freeTextDescriptionController.dispose(); super.dispose(); } void _syncTextControllers(OperationModel model) { if (_referenceController.text.isEmpty) { _referenceController.text = model.reference; } if (_noteController.text.isEmpty) { _noteController.text = model.note; } if (_freeTextSubtypeController.text.isEmpty) { _freeTextSubtypeController.text = model.subtype ?? ''; } if (_freeTextDescriptionController.text.isEmpty) { _freeTextDescriptionController.text = model.description ?? ''; } _isInitialized = true; } void _flushControllersToCubit() { context.read().updateFields( reference: _referenceController.text, note: _noteController.text, subtype: _freeTextSubtypeController.text, description: _freeTextDescriptionController.text, ); } void _saveOperation({ required OperationStatus targetStatus, required bool keepAdding, }) { if (_formKey.currentState!.validate()) { _flushControllersToCubit(); // Aggiorniamo prima lo stato bersaglio nel cubit context.read().updateFields(status: targetStatus); // Poi chiamiamo il salvataggio context.read().saveOperation( targetStatus: targetStatus, keepAdding: keepAdding, ); } } Future _generateIdForQr() async { if (!_formKey.currentState!.validate()) return null; _flushControllersToCubit(); final attachmentsBloc = context.read(); // Assicurati che questo metodo esista nel Cubit (come per il Ticket) final newId = await context.read().saveOperationDraft(); if (newId != null && context.mounted) { attachmentsBloc.add(ParentEntitySavedEvent(newId)); } return newId; } Color _getStatusColor(OperationStatus status) { switch (status) { case OperationStatus.success: return Colors.green; case OperationStatus.waitingForAction: return Colors.orange; case OperationStatus.waitingForSupport: return Colors.blue; case OperationStatus.failure: return Colors.red; case OperationStatus.draft: return Colors.grey; } } @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 == OperationFormStatus.ready && !_isInitialized) { _syncTextControllers(state.operation); } if (state.status == OperationFormStatus.success) { Navigator.of(context).pop(); } else if (state.status == OperationFormStatus.successAndAddAnother) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Operazione salvata! Inserisci la prossima'), ), ); _freeTextSubtypeController.clear(); _freeTextDescriptionController.clear(); _referenceController.clear(); } else if (state.status == OperationFormStatus.failure) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.errorMessage ?? 'Errore di salvataggio'), backgroundColor: Colors.red, ), ); } }, builder: (context, state) { if (!_isInitialized && (widget.operationId != null || widget.existingOperation != null) && state.status == OperationFormStatus.loading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } final displayStatus = state.operation.status == OperationStatus.draft && state.operation.id == null ? OperationStatus.success : state.operation.status; return Scaffold( appBar: AppBar( title: Text( state.operation.id == null ? 'Nuova Pratica - Operatore: ${state.operation.staffDisplayName}' : 'Modifica Pratica - Operatore: ${state.operation.staffDisplayName}', ), actions: displayStatus != OperationStatus.success && displayStatus != OperationStatus.draft ? [ Padding( padding: const EdgeInsets.only(right: 16.0), child: Chip( label: Text( displayStatus.displayName, style: const TextStyle( color: Colors.white, fontSize: 12, ), ), backgroundColor: _getStatusColor(displayStatus), ), ), ] : null, ), body: Form( key: _formKey, child: FocusTraversalGroup( policy: WidgetOrderTraversalPolicy(), child: LayoutBuilder( builder: (context, constraints) { final isUltraWide = constraints.maxWidth > 1400; final isDesktop = constraints.maxWidth > 900; return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Center( child: ConstrainedBox( constraints: BoxConstraints( maxWidth: isUltraWide ? 1600 : (isDesktop ? 1200 : 800), ), child: _buildResponsiveLayout( isUltraWide, isDesktop, state, displayStatus, ), ), ), ); }, ), ), ), bottomNavigationBar: SafeArea( child: Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: theme.scaffoldBackgroundColor, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, -3), ), ], ), child: Row( children: [ Expanded( flex: 1, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: displayStatus != OperationStatus.success && displayStatus != OperationStatus.draft ? _getStatusColor(displayStatus) : null, foregroundColor: displayStatus != OperationStatus.success && displayStatus != OperationStatus.draft ? Colors.white : null, ), onPressed: state.status == OperationFormStatus.saving ? null : () => _saveOperation( keepAdding: false, targetStatus: displayStatus, ), child: state.status == OperationFormStatus.saving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Text('Salva ed Esci'), ), ), const SizedBox(width: 12), Expanded( flex: 1, child: OutlinedButton( onPressed: state.status == OperationFormStatus.saving ? null : () => _saveOperation( keepAdding: true, targetStatus: displayStatus, ), child: const Text( 'Salva e Aggiungi Altro', textAlign: TextAlign.center, ), ), ), ], ), ), ), ); }, ); } // --- LOGICA DI IMPAGINAZIONE RESPONSIVE --- Widget _buildResponsiveLayout( bool isUltraWide, bool isDesktop, OperationFormState state, OperationStatus displayStatus, ) { if (isUltraWide) { // 3 COLONNE return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( children: [_cardAnagrafica(state), _cardEsito(state)], ), ), const SizedBox(width: 24), Expanded(child: Column(children: [_cardDettagli(state)])), const SizedBox(width: 24), Expanded( child: Column(children: [_cardNote(state), _cardAllegati(state)]), ), ], ); } else if (isDesktop) { // 2 COLONNE return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( children: [ _cardAnagrafica(state), _cardEsito(state), _cardAllegati(state), ], ), ), const SizedBox(width: 24), Expanded( child: Column(children: [_cardDettagli(state), _cardNote(state)]), ), ], ); } else { // 1 COLONNA (Mobile) return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _cardAnagrafica(state), _cardEsito(state), _cardDettagli(state), _cardNote(state), _cardAllegati(state), ], ); } } // --- LE CARD MODULARIZZATE E COLORATE --- Widget _cardAnagrafica(OperationFormState state) { return _buildCard( title: 'Cliente e Riferimento', icon: Icons.person, themeColor: Colors.indigo, children: [ SharedCustomerSection( customer: state.operation.customer, onCustomerSelected: (customer) => context .read() .updateFields(customer: customer), ), const SizedBox(height: 16), TextFormField( controller: _referenceController, decoration: const InputDecoration( labelText: 'Riferimento (es. Telefono, Targa...)', prefixIcon: Icon(Icons.tag), ), validator: (v) => v == null || v.isEmpty ? 'Inserisci un riferimento' : null, ), ], ); } Widget _cardEsito(OperationFormState state) { return _buildCard( title: 'Esito Pratica', icon: Icons.fact_check, themeColor: _getStatusColor(state.operation.status), children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), decoration: BoxDecoration( color: _getStatusColor( state.operation.status, ).withValues(alpha: 0.1), border: Border.all( color: _getStatusColor( state.operation.status, ).withValues(alpha: 0.3), ), borderRadius: BorderRadius.circular(12), ), child: DropdownButtonHideUnderline( child: DropdownButton( isExpanded: true, value: state.operation.status, icon: Icon( Icons.arrow_drop_down, color: _getStatusColor(state.operation.status), ), items: OperationStatus.values.map((status) { return DropdownMenuItem( value: status, child: Row( children: [ Icon( status == OperationStatus.success ? Icons.check_circle : Icons.error_outline, color: _getStatusColor(status), size: 20, ), const SizedBox(width: 12), Text( status.displayName, style: TextStyle( fontWeight: FontWeight.w600, color: _getStatusColor(status), ), ), ], ), ); }).toList(), onChanged: (newStatus) { if (newStatus != null) context.read().updateFields( status: newStatus, ); }, ), ), ), const SizedBox(height: 8), Text( state.operation.status == OperationStatus.success ? 'Lascia OK se caricata con successo.' : 'Attenzione: pratica salvata in stato anomalo.', style: TextStyle(fontSize: 12, color: Colors.grey.shade600), ), ], ); } Widget _cardDettagli(OperationFormState state) { return _buildCard( title: 'Dettagli Servizio', icon: Icons.design_services, themeColor: Colors.deepOrange, children: [ Wrap( spacing: 8.0, runSpacing: 8.0, children: _availableTypes.map((type) { return ChoiceChip( label: Text(type), selected: state.operation.type == type, onSelected: (selected) { if (selected) context.read().setTypeWithSmartDefault( type, ); }, ); }).toList(), ), const Divider(height: 32), Row( children: [ const Text( 'Quantità:', style: TextStyle(fontWeight: FontWeight.bold), ), const Spacer(), IconButton( icon: const Icon(Icons.remove_circle_outline), onPressed: state.operation.quantity > 1 ? () => context.read().updateFields( quantity: state.operation.quantity - 1, ) : null, ), Text( '${state.operation.quantity}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), IconButton( icon: const Icon(Icons.add_circle_outline), onPressed: () => context.read().updateFields( quantity: state.operation.quantity + 1, ), ), ], ), const Divider(height: 32), OperationDetailsSection( currentOp: state.operation, currentType: state.operation.type, freeTextSubtypeController: _freeTextSubtypeController, freeTextDescriptionController: _freeTextDescriptionController, durationQuickPicks: _buildDurationQuickPicks(), ), ], ); } Widget _cardNote(OperationFormState state) { return _buildCard( title: 'Note Interne', icon: Icons.notes, themeColor: Colors.teal, children: [ TextFormField( controller: _noteController, maxLines: 5, decoration: InputDecoration( hintText: 'Incolla seriali, ICCID, IBAN...', alignLabelWithHint: true, fillColor: Colors.teal.withValues(alpha: 0.05), filled: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), ), ), ], ); } Widget _cardAllegati(OperationFormState state) { return _buildCard( title: 'Allegati e Documenti', icon: Icons.attach_file, themeColor: Colors.deepPurple, children: [ SharedFilesSection( titleNameForUpload: state.operation.customer?.name ?? 'Nuova Pratica', onGenerateIdForQr: _generateIdForQr, ), ], ); } // --- WIDGET BASE PER LA CARD --- Widget _buildCard({ required String title, required IconData icon, required Color themeColor, required List children, }) { return Card( margin: const EdgeInsets.only(bottom: 24), elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide(color: themeColor.withValues(alpha: 0.3), width: 1), ), child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: themeColor.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: themeColor), ), const SizedBox(width: 12), Text( title, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, color: themeColor, ), ), ], ), const Divider(height: 32), ...children, ], ), ), ); } Widget _buildDurationQuickPicks() { final durations = [3, 6, 12, 24, 30, 36, 48]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Imposta durata rapida (mesi):", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.grey, ), ), const SizedBox(height: 8), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: durations.map((months) { return Padding( padding: const EdgeInsets.only(right: 8.0), child: ActionChip( label: Text("$months m"), backgroundColor: Colors.blue.withValues(alpha: 0.05), onPressed: () { final now = DateTime.now(); context.read().updateFields( expirationDate: DateTime( now.year, now.month + months, now.day, ), ); }, ), ); }).toList(), ), ), ], ); } }