import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flux/core/routes/routes.dart'; import 'package:flux/features/operations/blocs/operation_list_cubit.dart'; import 'package:flux/features/operations/models/operation_model.dart'; import 'package:go_router/go_router.dart'; class OperationListScreen extends StatefulWidget { const OperationListScreen({super.key}); @override State createState() => _OperationListScreenState(); } class _OperationListScreenState extends State { final ScrollController _scrollController = ScrollController(); // 🥷 1. LO STATO PER LE BULK ACTIONS final Set _selectedOperationIds = {}; bool get _isSelectionMode => _selectedOperationIds.isNotEmpty; @override void initState() { super.initState(); _scrollController.addListener(_onScroll); } void _onScroll() { if (_isBottom) { context.read().loadOperations(); } } bool get _isBottom { if (!_scrollController.hasClients) return false; final maxScroll = _scrollController.position.maxScrollExtent; final currentScroll = _scrollController.offset; return currentScroll >= (maxScroll * 0.9); } @override void dispose() { _scrollController.dispose(); super.dispose(); } void _toggleSelection(String id) { setState(() { if (_selectedOperationIds.contains(id)) { _selectedOperationIds.remove(id); } else { _selectedOperationIds.add(id); } }); } void _clearSelection() { setState(() { _selectedOperationIds.clear(); }); } @override Widget build(BuildContext context) { return Scaffold( // 🥷 2. APPBAR DINAMICA (Standard o Modalità Selezione) appBar: _isSelectionMode ? AppBar( backgroundColor: Theme.of(context).colorScheme.primaryContainer, leading: IconButton( icon: const Icon(Icons.close), onPressed: _clearSelection, ), title: Text("${_selectedOperationIds.length} selezionate"), actions: [ IconButton( icon: const Icon(Icons.edit_note), tooltip: 'Cambia Stato Massivo', onPressed: () { // TODO: Apri BottomSheet per cambiare stato a tutte le selezionate }, ), ], ) : AppBar( title: const Text("Gestione Servizi"), elevation: 0, actions: [ IconButton( icon: const Icon(Icons.filter_list), onPressed: () { // TODO: Apri drawer laterale o modal per i filtri avanzati }, ), IconButton(icon: const Icon(Icons.search), onPressed: () {}), ], ), body: BlocBuilder( builder: (context, state) { if (state.status == OperationListStatus.loading && state.operations.isEmpty) { return const Center(child: CircularProgressIndicator()); } if (state.operations.isEmpty) { return const Center(child: Text("Nessuna pratica trovata.")); } // 🥷 3. IL MOTORE RESPONSIVO return RefreshIndicator( onRefresh: () => context.read().loadOperations( refresh: true, ), child: LayoutBuilder( builder: (context, constraints) { // Se lo schermo è largo (Desktop/Tablet), usiamo la griglia final isDesktop = constraints.maxWidth > 700; return GridView.builder( controller: _scrollController, padding: const EdgeInsets.all(12).copyWith(bottom: 80), // Magia della griglia: si adatta! gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 450, // Larghezza massima della singola card mainAxisExtent: 180, // Altezza fissa della card (da aggiustare in base ai tuoi font) crossAxisSpacing: 12, mainAxisSpacing: 12, ), itemCount: state.hasReachedMax ? state.operations.length : state.operations.length + 1, itemBuilder: (context, index) { if (index >= state.operations.length) { return const Center( child: CircularProgressIndicator(strokeWidth: 2), ); } final operation = state.operations[index]; final isSelected = _selectedOperationIds.contains( operation.id, ); return _RichOperationCard( operation: operation, isSelected: isSelected, isSelectionMode: _isSelectionMode, onTap: () { if (_isSelectionMode) { _toggleSelection(operation.id!); } else { context.pushNamed( Routes.operationForm, extra: (createdBy: null, operation: operation), pathParameters: {'id': operation.id!}, ); } }, onLongPress: () => _toggleSelection(operation.id!), ); }, ); }, ), ); }, ), floatingActionButton: _isSelectionMode ? null // Nascondi il FAB se stai selezionando : FloatingActionButton( onPressed: () { /* Tuo codice per nuova operazione */ }, child: const Icon(Icons.add), ), ); } } // 🥷 4. LA SUPER CARD ESTRATTA class _RichOperationCard extends StatelessWidget { final OperationModel operation; final bool isSelected; final bool isSelectionMode; final VoidCallback onTap; final VoidCallback onLongPress; const _RichOperationCard({ required this.operation, required this.isSelected, required this.isSelectionMode, required this.onTap, required this.onLongPress, }); // 🥷 1. IL COLORE DELLO STATO: Centralizzato per usarlo ovunque Color _getStatusColor(OperationStatus status) { switch (status) { case OperationStatus.success: return Colors.green; case OperationStatus.waitingForAction: case OperationStatus.draft: return Colors.orange; case OperationStatus.waitingForSupport: return Colors.blue; case OperationStatus.failure: return Colors.grey.shade800; // O Colors.red se preferisci } } // 🥷 2. IL COLORE DEL TIPO: Per farlo risaltare Color _getTypeColor(String type) { switch (type) { case 'FIN': return Colors.deepPurple; case 'TELEPASS': return Colors.yellow.shade700; case 'ENERGY': return Colors.amber.shade700; case 'ENTERTAINMENT': return Colors.pinkAccent; case 'AL': case 'MNP': return Colors.indigo; case 'NIP': case 'FWA': return Colors.cyan; default: return Colors.blueGrey; } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final statusColor = _getStatusColor(operation.status); final typeColor = _getTypeColor(operation.type); return Card( elevation: isSelected ? 4 : 1, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: isSelected ? theme.colorScheme.primary : Colors.transparent, width: 2, ), ), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: onTap, onLongPress: onLongPress, child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Container( decoration: BoxDecoration( color: isSelected ? theme.colorScheme.primaryContainer.withValues(alpha: 0.2) : null, // BANDA LATERALE LEGATA ALLO STATO (Stilosissima) border: Border(left: BorderSide(color: statusColor, width: 6)), ), padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // --- HEADER --- Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (isSelectionMode) SizedBox( height: 24, width: 24, child: Checkbox( value: isSelected, onChanged: (_) => onTap(), ), ), Expanded( child: Text( operation.reference ?? 'Senza Riferimento', style: theme.textTheme.labelSmall?.copyWith( color: Colors.grey[600], ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), Text( "${operation.createdAt?.day.toString().padLeft(2, '0')}/${operation.createdAt?.month.toString().padLeft(2, '0')}/${operation.createdAt?.year}", style: theme.textTheme.labelSmall?.copyWith( color: Colors.grey[600], ), ), ], ), const SizedBox(height: 8), // --- CLIENTE E TIPO OPERAZIONE --- Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Text( operation.customer?.name ?? "Cliente sconosciuto", style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 8), // IL TIPO DI OPERAZIONE CHE SPICCA Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), decoration: BoxDecoration( color: typeColor.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), border: Border.all( color: typeColor.withValues(alpha: 0.3), ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (_getIconForType( operation.type, operation.subType, ) != null) ...[ Icon( _getIconForType( operation.type, operation.subType, ), size: 14, color: typeColor, ), const SizedBox(width: 4), ], Text( operation.subType?.isNotEmpty == true ? operation.subType! : operation.type, style: TextStyle( color: typeColor, fontWeight: FontWeight.bold, fontSize: 12, ), ), ], ), ), ], ), const SizedBox(height: 12), // --- I TAG COMPATTI (Business/Privato, Provider, Device) --- Wrap( spacing: 6, runSpacing: 6, children: [ // Espanso in "Business" e "Privato" _MiniChip( label: operation.isBusiness ? 'Business' : 'Privato', icon: operation.isBusiness ? Icons.business : Icons.person, color: operation.isBusiness ? Colors.indigo : Colors.teal, ), // Tag Provider con il suo colore personalizzato dal DB if (operation.providerId != null) _MiniChip( label: operation.providerDisplayName ?? 'Gestore', // Se hai popolato il campo colorHex, qui puoi usare: operation.provider?.displayColor ?? Colors.grey color: Colors.redAccent, ), if (operation.type == 'Fin' && operation.modelId != null) _MiniChip( label: operation.modelDisplayName ?? 'Modello', icon: Icons.devices, color: Colors.deepPurple, ), ], ), const Spacer(), // --- FOOTER: Staff e Stato --- Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ const Icon( Icons.support_agent, size: 14, color: Colors.grey, ), const SizedBox(width: 4), Text( operation.staffDisplayName ?? 'Staff', style: theme.textTheme.labelSmall?.copyWith( color: Colors.grey[700], ), ), ], ), _buildOperationStatus(operation.status, statusColor), ], ), ], ), ), ), ), ); } IconData? _getIconForType(String type, String? subtype) { if (type == 'Energy') { if (subtype?.toLowerCase() == 'luce') return Icons.bolt; if (subtype?.toLowerCase() == 'gas') return Icons.local_fire_department; } return null; } Widget _buildOperationStatus(OperationStatus status, Color statusColor) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: statusColor, borderRadius: BorderRadius.circular(8), ), child: Text( status.displayName, style: const TextStyle( fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold, ), ), ); } } class _MiniChip extends StatelessWidget { final String label; final IconData? icon; final Color color; const _MiniChip({required this.label, this.icon, required this.color}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), border: Border.all(color: color.withValues(alpha: 0.3)), borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (icon != null) ...[ Icon(icon, size: 12, color: color), const SizedBox(width: 4), ], Text( label, style: TextStyle( fontSize: 11, color: color, fontWeight: FontWeight.bold, ), ), ], ), ); } }