import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/features/tasks/blocs/task_form_cubit.dart'; import 'package:flux/features/tasks/models/task_status.dart'; import 'package:go_router/go_router.dart'; class TaskFormScreen extends StatefulWidget { const TaskFormScreen({super.key}); @override State createState() => _TaskFormScreenState(); } class _TaskFormScreenState extends State { late final TextEditingController _titleController; late final TextEditingController _descController; @override void initState() { super.initState(); // Leggiamo lo stato iniziale dal Cubit (che ha già i dati del task esistente) final initialState = context.read().state; _titleController = TextEditingController(text: initialState.title); _descController = TextEditingController(text: initialState.description); } @override void dispose() { _titleController.dispose(); _descController.dispose(); super.dispose(); } void _showAddReminderDialog(BuildContext context, TaskFormCubit cubit) { int minutes = 15; String channel = 'push'; showDialog( context: context, builder: (dialogContext) { return AlertDialog( title: const Text('Aggiungi Promemoria'), content: Column( mainAxisSize: MainAxisSize.min, children: [ DropdownButtonFormField( initialValue: minutes, decoration: const InputDecoration(labelText: 'Preavviso'), items: const [ DropdownMenuItem(value: 5, child: Text('5 minuti prima')), DropdownMenuItem(value: 15, child: Text('15 minuti prima')), DropdownMenuItem(value: 60, child: Text('1 ora prima')), DropdownMenuItem(value: 1440, child: Text('1 giorno prima')), ], onChanged: (v) => {if (v != null) minutes = v}, ), const SizedBox(height: 16), DropdownButtonFormField( initialValue: channel, decoration: const InputDecoration(labelText: 'Canale'), items: const [ DropdownMenuItem(value: 'push', child: Text('Notifica Push')), DropdownMenuItem(value: 'email', child: Text('Email')), ], onChanged: (v) => {if (v != null) channel = v}, ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext), child: const Text('Annulla'), ), TextButton( onPressed: () { cubit.addReminderRule(minutes, channel); Navigator.pop(dialogContext); }, child: const Text( 'Inserisci', style: TextStyle(color: Colors.orange), ), ), ], ); }, ); } @override Widget build(BuildContext context) { return BlocConsumer( listenWhen: (previous, current) => previous.status != current.status, listener: (context, state) { // GESTIONE DEEP LINK: Se eravamo in caricamento e ora siamo pronti, popoliamo i controller! if (state.status == TaskFormStatus.initial) { if (_titleController.text != state.title) { _titleController.text = state.title; } if (_descController.text != state.description) { _descController.text = state.description; } } if (state.status == TaskFormStatus.success) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Task salvato con successo! 🎉')), ); context.pop(); } else if (state.status == TaskFormStatus.failure) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.errorMessage ?? 'Errore di salvataggio'), backgroundColor: Theme.of(context).colorScheme.error, ), ); } }, builder: (context, state) { final cubit = context.read(); final isEditing = state.id != null; return Scaffold( appBar: AppBar( title: Text(isEditing ? 'Modifica Task' : 'Nuovo Task'), actions: [ // 🥷 1. BOTTONE COMPLETAMENTO RAPIDO (Solo se in edit e non già completato) if (isEditing && state.taskStatus != TaskStatus.completed) Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 8.0, ), child: ElevatedButton.icon( onPressed: () { context.read().updateTaskStatus( TaskStatus.completed, ); }, icon: const Icon(Icons.check_circle_outline), label: const Text('Completa'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green.shade600, foregroundColor: Colors.white, elevation: 0, ), ), ), // 🥷 2. LOADER O BOTTONE SALVA if (state.status == TaskFormStatus.submitting) const Padding( padding: EdgeInsets.all(16.0), child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ), ) else Padding( padding: const EdgeInsets.only(right: 8.0), child: TextButton.icon( onPressed: state.isFormValid ? () => cubit.saveTask() : null, icon: const Icon(Icons.save), label: const Text('Salva'), style: TextButton.styleFrom( foregroundColor: Colors.orange, disabledForegroundColor: Colors.grey, ), ), ), ], ), body: state.status == TaskFormStatus.loading // Se sta scaricando i dati dal Deep Link, mostriamo un bel loader centrato ? const Center(child: CircularProgressIndicator()) : LayoutBuilder( builder: (context, constraints) { final isWideScreen = constraints.maxWidth > 800; if (isWideScreen) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 6, child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: _buildFormFields(context, state, cubit), ), ), VerticalDivider( color: Theme.of(context).dividerColor, ), Expanded( flex: 4, child: _buildStaffSelectorInline( context, state, cubit, ), ), ], ); } return SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildFormFields(context, state, cubit), const SizedBox(height: 30), ElevatedButton.icon( style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: () => _showStaffBottomSheet(context, cubit), icon: const Icon(Icons.group_add), label: Text( state.selectedStaffIds.isEmpty ? 'Assegna Staff' : 'Assegnato a ${state.selectedStaffIds.length} persone', ), ), ], ), ); }, ), ); }, ); } // --- I CAMPI DEL FORM (Aggiornati con i Controller) --- Widget _buildFormFields( BuildContext context, TaskFormState state, TaskFormCubit cubit, ) { return FocusTraversalGroup( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Card( elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: Theme.of(context).dividerColor.withValues(alpha: 0.2), ), ), child: SwitchListTile( title: const Text( 'Task Globale Aziendale', style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: const Text( 'Visibile a tutta l\'azienda, non legato a un negozio specifico.', ), value: state.isGlobal, activeThumbColor: Colors.orange, onChanged: (val) => cubit.toggleGlobalScope(val), ), ), const SizedBox(height: 24), // Addio initialValue, benvenuto controller! TextFormField( controller: _titleController, decoration: const InputDecoration( labelText: 'Titolo del Task*', border: OutlineInputBorder(), prefixIcon: Icon(Icons.title), ), onChanged: cubit.updateTitle, ), const SizedBox(height: 16), TextFormField( controller: _descController, maxLines: 4, decoration: const InputDecoration( labelText: 'Descrizione (opzionale)', border: OutlineInputBorder(), alignLabelWithHint: true, ), onChanged: cubit.updateDescription, ), if (state.id != null) ...[ const SizedBox(height: 16), DropdownButtonFormField( // Leggiamo lo stato attuale dal Cubit (o usiamo un default per i nuovi task) initialValue: state.taskStatus, decoration: InputDecoration( labelText: 'Stato Attuale', prefixIcon: const Icon(Icons.flag_outlined), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), filled: true, fillColor: Theme.of(context).colorScheme.surface, ), // Mappiamo tutti i valori dell'enum in elementi della tendina items: TaskStatus.values.map((TaskStatus status) { return DropdownMenuItem( value: status, child: Text( status .displayName, // Usa la property displayName del tuo enum! style: TextStyle( fontWeight: status == state.taskStatus ? FontWeight.bold : FontWeight.normal, ), ), ); }).toList(), onChanged: (TaskStatus? newStatus) { if (newStatus != null && newStatus != state.taskStatus) { context.read().updateTaskStatus(newStatus); } }, ), ], const SizedBox(height: 24), // --- SCADENZA --- ListTile( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: Theme.of(context).dividerColor), ), leading: const Icon(Icons.calendar_today, color: Colors.orange), title: Text( state.dueDate != null // Formattiamo aggiungendo gli zeri (es. 05/09/2026 alle 09:05) ? 'Scadenza: ${state.dueDate!.day.toString().padLeft(2, '0')}/${state.dueDate!.month.toString().padLeft(2, '0')}/${state.dueDate!.year} alle ${state.dueDate!.hour.toString().padLeft(2, '0')}:${state.dueDate!.minute.toString().padLeft(2, '0')}' : 'Nessuna scadenza impostata', ), trailing: state.dueDate != null ? IconButton( icon: const Icon(Icons.close), onPressed: () => cubit.updateDueDate(null), ) : null, onTap: () async { // 1. Chiediamo prima la Data final date = await showDatePicker( context: context, initialDate: state.dueDate ?? DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime(2100), ); // Se l'utente chiude il calendario senza scegliere, ci fermiamo if (date == null || !context.mounted) return; // 2. Chiediamo subito dopo l'Orario final time = await showTimePicker( context: context, initialTime: state.dueDate != null ? TimeOfDay.fromDateTime(state.dueDate!) : const TimeOfDay(hour: 9, minute: 0), // Default ore 09:00 ); // Se l'utente chiude l'orologio senza scegliere, ci fermiamo if (time == null) return; // 3. Fondiamo Data e Ora in un nuovo oggetto DateTime final finalDateTime = DateTime( date.year, date.month, date.day, time.hour, time.minute, ); // Aggiorniamo lo stato tramite il Cubit cubit.updateDueDate(finalDateTime); }, ), if (state.dueDate != null) ...[ const SizedBox(height: 24), Text( 'Promemoria del Task', style: Theme.of( context, ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Card( elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: Theme.of(context).dividerColor.withValues(alpha: 0.3), ), ), child: Padding( padding: const EdgeInsets.all(12.0), child: Column( children: [ // Elenco dei promemoria attuali del form ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: state.reminders.length, itemBuilder: (context, index) { final reminder = state.reminders[index]; final isPush = reminder.channel == 'push'; return ListTile( dense: true, leading: Icon( isPush ? Icons.notifications_active_outlined : Icons.mail_outline, color: isPush ? Colors.orange : Colors.blue, ), title: Text( reminder.friendlyTime, style: const TextStyle(fontWeight: FontWeight.w600), ), trailing: IconButton( icon: const Icon( Icons.close, size: 18, color: Colors.redAccent, ), onPressed: () => cubit.removeReminderRule(index), ), ); }, ), const Divider(), // Tasto di aggiunta rapida promemoria TextButton.icon( onPressed: () => _showAddReminderDialog(context, cubit), icon: const Icon(Icons.add, size: 18), label: const Text('Aggiungi un promemoria a questo task'), style: TextButton.styleFrom( foregroundColor: Colors.orange, ), ), ], ), ), ), ], ], ), ); } // ========================================================================= // 2. SELEZIONE STAFF INLINE (PER DESKTOP/WIDE) // ========================================================================= Widget _buildStaffSelectorInline( BuildContext context, TaskFormState state, TaskFormCubit cubit, ) { return Container( color: Theme.of(context).cardColor, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Padding( padding: EdgeInsets.all(24.0), child: Text( 'Assegnazione Staff', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), Expanded( child: ListView( padding: const EdgeInsets.symmetric(horizontal: 16.0), children: _buildGroupedStaffList(context, state, cubit), ), ), ], ), ); } // ========================================================================= // 3. BOTTOM SHEET SELEZIONE STAFF (PER MOBILE) // ========================================================================= void _showStaffBottomSheet(BuildContext context, TaskFormCubit cubit) { showModalBottomSheet( context: context, isScrollControlled: true, useSafeArea: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (bottomSheetContext) { return DraggableScrollableSheet( expand: false, initialChildSize: 0.7, // Occupa il 70% dello schermo in altezza minChildSize: 0.5, maxChildSize: 0.9, builder: (_, controller) { return BlocBuilder( bloc: cubit, // Passiamo il cubit esistente per mantenere lo stato! builder: (context, state) { return Column( children: [ const Padding( padding: EdgeInsets.all(16.0), child: Text( 'Assegna Staff', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), ), const Divider(height: 1), Expanded( child: ListView( controller: controller, padding: const EdgeInsets.only(bottom: 24), children: _buildGroupedStaffList(context, state, cubit), ), ), ], ); }, ); }, ); }, ); } // ========================================================================= // 4. GENERATORE DELLA LISTA RAGGRUPPATA (RIUTILIZZABILE) // ========================================================================= List _buildGroupedStaffList( BuildContext context, TaskFormState state, TaskFormCubit cubit, ) { final widgets = []; if (state.groupedAvailableStaff.isEmpty) { return [ const Padding( padding: EdgeInsets.all(32.0), child: Center( child: Text( 'Nessun membro dello staff trovato.', style: TextStyle(fontStyle: FontStyle.italic), ), ), ), ]; } // Iteriamo sulla mappa { "Nome Negozio" : [Lista Dipendenti] } for (final entry in state.groupedAvailableStaff.entries) { final storeName = entry.key; final staffList = entry.value; // Verifichiamo se TUTTI i membri di questo negozio sono selezionati final allSelectedInStore = staffList.every( (staff) => state.selectedStaffIds.contains(staff.id), ); widgets.add( Padding( padding: const EdgeInsets.only( top: 24.0, bottom: 8.0, left: 16, right: 8, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( storeName.toUpperCase(), style: TextStyle( color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold, letterSpacing: 1.2, ), ), // IL MAGICO BOTTONE "SELEZIONA TUTTI" DEL NEGOZIO TextButton.icon( onPressed: () => cubit.toggleStoreSelection(storeName, !allSelectedInStore), icon: Icon( allSelectedInStore ? Icons.deselect : Icons.select_all, size: 18, ), label: Text( allSelectedInStore ? 'Deseleziona' : 'Seleziona Tutti', ), style: TextButton.styleFrom( visualDensity: VisualDensity.compact, foregroundColor: allSelectedInStore ? Colors.grey : Colors.orange, ), ), ], ), ), ); // Renderizziamo i dipendenti di questo negozio usando dei Wrap con FilterChip widgets.add( Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Wrap( spacing: 8.0, runSpacing: 8.0, children: staffList.map((staff) { final isSelected = state.selectedStaffIds.contains(staff.id); return FilterChip( label: Text(staff.name), selected: isSelected, selectedColor: Colors.orange.withValues(alpha: 0.2), checkmarkColor: Colors.orange, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), onSelected: (_) => cubit.toggleStaffSelection(staff.id!), ); }).toList(), ), ), ); } return widgets; } }