From 9cc5dd6a4f707f8c37b8021ed5c3d5fb4b4a2c30 Mon Sep 17 00:00:00 2001 From: Mark M2 Macbook Date: Tue, 5 May 2026 12:11:38 +0200 Subject: [PATCH] 1 Co-authored-by: Copilot --- .../ui/widgets/operation_files_section.dart | 3 +- .../tickets/blocs/ticket_list_cubit.dart | 73 ++++ .../tickets/blocs/ticket_list_state.dart | 59 +++ .../tickets/data/ticket_repository.dart | 235 ++++++++++++ lib/features/tickets/models/ticket_model.dart | 345 ++++++++++++++++++ .../models/ticket_status_extension.dart | 43 +++ 6 files changed, 756 insertions(+), 2 deletions(-) create mode 100644 lib/features/tickets/blocs/ticket_list_cubit.dart create mode 100644 lib/features/tickets/blocs/ticket_list_state.dart create mode 100644 lib/features/tickets/data/ticket_repository.dart create mode 100644 lib/features/tickets/models/ticket_model.dart create mode 100644 lib/features/tickets/models/ticket_status_extension.dart diff --git a/lib/features/operations/ui/widgets/operation_files_section.dart b/lib/features/operations/ui/widgets/operation_files_section.dart index 42b1a6c..e7ee5d5 100644 --- a/lib/features/operations/ui/widgets/operation_files_section.dart +++ b/lib/features/operations/ui/widgets/operation_files_section.dart @@ -13,7 +13,6 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:flux/features/operations/models/operation_model.dart'; import 'package:flux/features/attachments/models/attachment_model.dart'; import 'package:pdf/widgets.dart' as pw; -import 'package:pdf/pdf.dart' as p; // Se ti serve formattazione core import 'package:pdfx/pdfx.dart' as px; // Isoliamo pdfx class _ExportItem { @@ -281,7 +280,7 @@ class _OperationFilesSectionState extends State { if (fileBytes == null) continue; // Recuperiamo il nome che l'utente ha (magari) già impostato - final baseName = file.name ?? 'Documento'; + final baseName = file.name; if (file.extension == 'pdf') { final document = await px.PdfDocument.openData(fileBytes); diff --git a/lib/features/tickets/blocs/ticket_list_cubit.dart b/lib/features/tickets/blocs/ticket_list_cubit.dart new file mode 100644 index 0000000..5ad1616 --- /dev/null +++ b/lib/features/tickets/blocs/ticket_list_cubit.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/features/tickets/models/ticket_model.dart'; +import 'package:flux/features/tickets/data/ticket_repository.dart'; +import 'package:get_it/get_it.dart'; +import 'ticket_list_state.dart'; + +class TicketListCubit extends Cubit { + final TicketRepository _repository = GetIt.I.get(); + static const int _limit = 20; // Paginazione a blocchi di 20 + + TicketListCubit() : super(const TicketListState()) { + fetchTickets(reset: true); + } + + /// Recupera i ticket. Se reset = true, svuota la lista e riparte da offset 0. + Future fetchTickets({bool reset = false}) async { + if (state.isLoading) return; + if (!reset && state.hasReachedMax) return; + + emit( + state.copyWith( + isLoading: true, + errorMessage: '', + tickets: reset ? [] : state.tickets, + ), + ); + + try { + final currentOffset = reset ? 0 : state.tickets.length; + + final newTickets = await _repository.fetchStoreTickets( + offset: currentOffset, + limit: _limit, + searchTerm: state.searchTerm, + dateRange: state.dateRange, + statusFilter: state.statusFilter, + ); + + emit( + state.copyWith( + tickets: reset ? newTickets : [...state.tickets, ...newTickets], + isLoading: false, + hasReachedMax: newTickets.length < _limit, + ), + ); + } catch (e) { + emit(state.copyWith(isLoading: false, errorMessage: e.toString())); + } + } + + /// Aggiorna i filtri e ricarica tutto da zero + void updateFilters({ + String? searchTerm, + DateTimeRange? dateRange, + TicketStatus? statusFilter, + bool clearSearch = false, + bool clearDate = false, + bool clearStatus = false, + }) { + emit( + state.copyWith( + searchTerm: searchTerm, + dateRange: dateRange, + statusFilter: statusFilter, + clearSearch: clearSearch, + clearDate: clearDate, + clearStatus: clearStatus, + ), + ); + fetchTickets(reset: true); // Applica i filtri e ricarica + } +} diff --git a/lib/features/tickets/blocs/ticket_list_state.dart b/lib/features/tickets/blocs/ticket_list_state.dart new file mode 100644 index 0000000..d36caa5 --- /dev/null +++ b/lib/features/tickets/blocs/ticket_list_state.dart @@ -0,0 +1,59 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:flux/features/tickets/models/ticket_model.dart'; + +class TicketListState extends Equatable { + final List tickets; + final bool isLoading; + final bool hasReachedMax; + final String errorMessage; + + // Filtri attivi + final String? searchTerm; + final DateTimeRange? dateRange; + final TicketStatus? statusFilter; + + const TicketListState({ + this.tickets = const [], + this.isLoading = false, + this.hasReachedMax = false, + this.errorMessage = '', + this.searchTerm, + this.dateRange, + this.statusFilter, + }); + + TicketListState copyWith({ + List? tickets, + bool? isLoading, + bool? hasReachedMax, + String? errorMessage, + String? searchTerm, + DateTimeRange? dateRange, + TicketStatus? statusFilter, + bool clearSearch = false, + bool clearDate = false, + bool clearStatus = false, + }) { + return TicketListState( + tickets: tickets ?? this.tickets, + isLoading: isLoading ?? this.isLoading, + hasReachedMax: hasReachedMax ?? this.hasReachedMax, + errorMessage: errorMessage ?? this.errorMessage, + searchTerm: clearSearch ? null : (searchTerm ?? this.searchTerm), + dateRange: clearDate ? null : (dateRange ?? this.dateRange), + statusFilter: clearStatus ? null : (statusFilter ?? this.statusFilter), + ); + } + + @override + List get props => [ + tickets, + isLoading, + hasReachedMax, + errorMessage, + searchTerm, + dateRange, + statusFilter, + ]; +} diff --git a/lib/features/tickets/data/ticket_repository.dart b/lib/features/tickets/data/ticket_repository.dart new file mode 100644 index 0000000..4adc9a3 --- /dev/null +++ b/lib/features/tickets/data/ticket_repository.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:flux/core/blocs/session/session_cubit.dart'; +import 'package:flux/features/tickets/models/ticket_model.dart'; +import 'package:get_it/get_it.dart'; +import 'package:supabase_flutter/supabase_flutter.dart'; + +class TicketRepository { + final SupabaseClient _supabase = GetIt.I.get(); + + TicketRepository(); + + static const String _tableName = 'ticket'; + + // --- RECUPERO PAGINATO CON FILTRI E JOIN DEI TICKET DI UNO STORE --- + Future> fetchStoreTickets({ + required int offset, + int limit = 50, + String? searchTerm, + DateTimeRange? dateRange, + TicketStatus? statusFilter, + TicketType? ticketTypeFilter, + String? staffIdFilter, + }) async { + try { + var query = _supabase + .from(_tableName) + .select(''' + *, + customer (*), + staff (*), + target_model:model!ticket_model_id_1_fkey (*), + source_model:model!ticket_model_id_2_fkey (*) + ''') + .eq('store_id', GetIt.I.get().state.currentStore!.id!); + + // Filtro Range Date + if (dateRange != null) { + query = query + .gte('created_at', dateRange.start.toIso8601String()) + .lte('created_at', dateRange.end.toIso8601String()); + } + + if (statusFilter != null) { + query = query.eq('status', statusFilter.value); + } + + if (ticketTypeFilter != null) { + query = query.eq('ticket_type', ticketTypeFilter.value); + } + + if (staffIdFilter != null) { + query = query.eq('staff_id', staffIdFilter); + } + + if (searchTerm != null && searchTerm.isNotEmpty) { + // Filtra sui campi della tabella principale O su quelli della tabella joinata + query = query.or('customer.name.ilike.%$searchTerm%'); + } + + final response = await query + .order('created_at', ascending: false) + .range(offset, offset + limit - 1); + + return (response as List).map((map) => TicketModel.fromMap(map)).toList(); + } catch (e) { + throw Exception('$e'); + } + } + + // --- RECUPERO PAGINATO CON FILTRI E JOIN DEI TICKET DI TUTTA L'AZIENDA --- + Future> fetchCompanyTickets({ + required int offset, + int limit = 50, + String? searchTerm, + DateTimeRange? dateRange, + TicketStatus? ticketStatusFilter, + TicketType? ticketTypeFilter, + String? staffIdFilter, + }) async { + try { + var query = _supabase + .from(_tableName) + .select(''' + *, + customer (*), + staff (*), + target_model:model!ticket_model_id_1_fkey (*), + source_model:model!ticket_model_id_2_fkey (*) + ''') + .eq('company_id', GetIt.I.get().state.company!.id!); + + // Filtro Range Date + if (dateRange != null) { + query = query + .gte('created_at', dateRange.start.toIso8601String()) + .lte('created_at', dateRange.end.toIso8601String()); + } + + if (ticketStatusFilter != null) { + query = query.eq('status', ticketStatusFilter.value); + } + + if (ticketTypeFilter != null) { + query = query.eq('ticket_type', ticketTypeFilter.value); + } + + if (staffIdFilter != null) { + query = query.eq('staff_id', staffIdFilter); + } + + if (searchTerm != null && searchTerm.isNotEmpty) { + // Filtra sui campi della tabella principale O su quelli della tabella joinata + query = query.or('customer.name.ilike.%$searchTerm%'); + } + + final response = await query + .order('created_at', ascending: false) + .range(offset, offset + limit - 1); + + return (response as List).map((map) => TicketModel.fromMap(map)).toList(); + } catch (e) { + throw Exception('$e'); + } + } + + /// Stream dei ticket che necessitano attenzione (es. in scadenza oggi o in ritardo) + Stream> getAttentionNeededTicketsStream() { + return _supabase + .from(_tableName) + .stream(primaryKey: ['id']) + .eq('store_id', GetIt.I.get().state.currentStore!.id!) + // Purtroppo lo stream accetta solo filtri base, quindi ci facciamo + // mandare i dati e li filtriamo con la potenza di Dart! + .limit(300) + .map((listOfMaps) { + final now = DateTime.now(); + final endOfToday = DateTime(now.year, now.month, now.day, 23, 59, 59); + + // 1. Mappiamo tutto in TicketModel + final allStoreTickets = listOfMaps + .map((map) => TicketModel.fromMap(map)) + .toList(); + + // 2. Filtriamo in memoria! + final urgentTickets = allStoreTickets.where((ticket) { + // Escludiamo quelli già chiusi o consegnati + if (ticket.status == TicketStatus.closed || + ticket.status == TicketStatus.ready) { + return false; + } + + // Se c'è una data di consegna stimata ed è <= a stasera, è urgente! + if (ticket.estimatedDeliveryAt != null) { + return ticket.estimatedDeliveryAt!.isBefore(endOfToday); + } + + return false; + }).toList(); + + // 3. Li ordiniamo mettendo i più vecchi/urgenti in cima + urgentTickets.sort( + (a, b) => a.estimatedDeliveryAt!.compareTo(b.estimatedDeliveryAt!), + ); + + return urgentTickets; + }); + } + + /// Recupera un ticket specifico CON TUTTE LE RELAZIONI espanse (Cliente e Modelli) + /// Questa è la vera magia di Supabase! + Future getTicketWithDetails(String ticketId) async { + try { + // Usiamo i nomi esatti delle Foreign Key che hai definito nell'SQL! + final response = await _supabase + .from(_tableName) + .select(''' + *, + customer (*), + target_model:model!ticket_model_id_1_fkey (*), + source_model:model!ticket_model_id_2_fkey (*), + staff:staff_member!ticket_staff_id_fkey (*) + ''') + .eq('id', ticketId) + .single(); + + return TicketModel.fromMap(response); + } catch (e) { + throw Exception('Errore nel recupero del dettaglio ticket: $e'); + } + } + + /// Salva il ticket con upsert + Future saveTicket(TicketModel ticket) async { + try { + final response = await _supabase + .from(_tableName) + .upsert(ticket.toMap()) + .select() + .single(); + + return TicketModel.fromMap(response); + } catch (e) { + throw Exception('Errore nella creazione del ticket: $e'); + } + } + + /// Aggiorna un ticket esistente + Future updateTicket(TicketModel ticket) async { + if (ticket.id == null) { + throw Exception('Impossibile aggiornare un ticket senza ID'); + } + + try { + final response = await _supabase + .from(_tableName) + .update(ticket.toMap()) + .eq('id', ticket.id!) + .select() + .single(); + + return TicketModel.fromMap(response); + } catch (e) { + throw Exception('Errore nell\'aggiornamento del ticket: $e'); + } + } + + /// Elimina (o annulla) un ticket + Future deleteTicket(String ticketId) async { + try { + await _supabase.from(_tableName).delete().eq('id', ticketId); + } catch (e) { + throw Exception('Errore nell\'eliminazione del ticket: $e'); + } + } +} diff --git a/lib/features/tickets/models/ticket_model.dart b/lib/features/tickets/models/ticket_model.dart new file mode 100644 index 0000000..70c06d2 --- /dev/null +++ b/lib/features/tickets/models/ticket_model.dart @@ -0,0 +1,345 @@ +import 'package:equatable/equatable.dart'; +import 'package:flux/core/utils/extensions.dart'; + +/// Enum per il tipo di ticket +enum TicketType { + repair('repair', 'Riparazione'), + softwareSetup('software_setup', 'Setup software'), + dataTransfer('data_transfer', 'Trasferimento dati'), + operationTicket('operation_ticket', 'Ticket di operazione'), + other('other', 'Altro'); + + final String value; + final String displayValue; + const TicketType(this.value, this.displayValue); + + static TicketType fromString(String val) { + return TicketType.values.firstWhere( + (e) => e.value == val, + orElse: () => TicketType.other, + ); + } +} + +/// Enum per lo stato del ticket +enum TicketStatus { + open('open', 'Aperto'), + inProgress('in_progress', 'In corso'), + waitingForParts('waiting_for_parts', 'In attesa di ricambi'), + ready('ready', 'Pronto'), + closed('closed', 'Chiuso'), + waitingForShipping('waiting_for_shipping', 'In attesa di spedire'), + waitingForReturn('waiting_for_return', 'In attesa di ritorno'); + + final String value; + final String displayValue; + const TicketStatus(this.value, this.displayValue); + + static TicketStatus? fromString(String? val) { + if (val == null) return null; + return TicketStatus.values.firstWhere( + (e) => e.value == val, + orElse: () => TicketStatus.open, + ); + } +} + +/// Enum per il risultato del ticket (OK / KO) +enum TicketResult { + success('success', 'Risolto (OK)'), + failure('failure', 'Non Risolto (KO)'); + + final String value; + final String displayValue; + const TicketResult(this.value, this.displayValue); + + static TicketResult? fromString(String? val) { + if (val == null) return null; + return TicketResult.values.firstWhere( + (e) => e.value == val, + orElse: () => TicketResult.success, + ); + } +} + +/// Enum per il tipo di garanzia +enum WarrantyType { + manufacturerWarranty('manufacturer_warranty', 'Garanzia produttore'), + providerWarranty('provider_warranty', 'Garanzia gestore'), + internalWarranty('internal_warranty', 'Garanzia interna'), + noWarranty('no_warranty', 'Fuori garanzia'); + + final String value; + final String displayValue; + const WarrantyType(this.value, this.displayValue); + + static WarrantyType? fromString(String? val) { + return WarrantyType.values.firstWhere( + (e) => e.value == val, + orElse: () => WarrantyType.noWarranty, + ); + } +} + +class TicketModel extends Equatable { + final String? id; // Null se non ancora salvato + final DateTime? createdAt; + final String companyId; + final String? storeId; + final String? customerId; + final String? targetModelId; + final String? targetSn; + final String? sourceModelId; + final String? sourceSn; + final double customerPrice; + final double internalCost; + final DateTime? closedAt; + final DateTime? returnedAt; + final String request; + final String? staffId; + final WarrantyType? warrantyType; + final String? publicNotes; + final String? internalNotes; + final int? referenceNumber; + final String? alternativePhoneNumber; + final bool hasCourtesyDevice; + final TicketType ticketType; + final TicketStatus? status; + final DateTime? estimatedDeliveryAt; + final TicketResult? result; + final String? resolutionNotes; + final String? legacyId; + final String? customerName; + final String? targetModelName; + final String? sourceModelName; + final String? staffName; + + const TicketModel({ + this.id, + this.createdAt, + required this.companyId, + this.storeId, + this.customerId, + this.targetModelId, + this.targetSn, + this.sourceModelId, + this.sourceSn, + this.customerPrice = 0.0, + this.internalCost = 0.0, + this.closedAt, + this.returnedAt, + this.request = '', + this.staffId, + this.warrantyType, + this.publicNotes, + this.internalNotes, + this.referenceNumber, + this.alternativePhoneNumber, + this.hasCourtesyDevice = false, + required this.ticketType, + this.status, + this.estimatedDeliveryAt, + this.result, + this.resolutionNotes, + this.legacyId, + this.customerName, + this.targetModelName, + this.sourceModelName, + this.staffName, + }); + + /// Factory per creare un ticket vuoto (utile per i form di creazione) + factory TicketModel.empty({required String companyId, String? storeId}) { + return TicketModel( + companyId: companyId, + storeId: storeId, + ticketType: TicketType.repair, // Valore di default + status: TicketStatus.open, + customerPrice: 0.0, + internalCost: 0.0, + hasCourtesyDevice: false, + request: '', + ); + } + + TicketModel copyWith({ + String? id, + DateTime? createdAt, + String? companyId, + String? storeId, + String? customerId, + String? targetModelId, + String? targetSn, + String? sourceModelId, + String? sourceSn, + double? customerPrice, + double? internalCost, + DateTime? closedAt, + DateTime? returnedAt, + String? request, + String? staffId, + WarrantyType? warrantyType, + String? publicNotes, + String? internalNotes, + int? referenceNumber, + String? alternativePhoneNumber, + bool? hasCourtesyDevice, + TicketType? ticketType, + TicketStatus? status, + DateTime? estimatedDeliveryAt, + TicketResult? result, + String? resolutionNotes, + String? legacyId, + String? customerName, + String? targetModelName, + String? sourceModelName, + String? staffName, + }) { + return TicketModel( + id: id ?? this.id, + createdAt: createdAt ?? this.createdAt, + companyId: companyId ?? this.companyId, + storeId: storeId ?? this.storeId, + customerId: customerId ?? this.customerId, + targetModelId: targetModelId ?? this.targetModelId, + targetSn: targetSn ?? this.targetSn, + sourceModelId: sourceModelId ?? this.sourceModelId, + sourceSn: sourceSn ?? this.sourceSn, + customerPrice: customerPrice ?? this.customerPrice, + internalCost: internalCost ?? this.internalCost, + closedAt: closedAt ?? this.closedAt, + returnedAt: returnedAt ?? this.returnedAt, + request: request ?? this.request, + staffId: staffId ?? this.staffId, + warrantyType: warrantyType ?? this.warrantyType, + publicNotes: publicNotes ?? this.publicNotes, + internalNotes: internalNotes ?? this.internalNotes, + referenceNumber: referenceNumber ?? this.referenceNumber, + alternativePhoneNumber: + alternativePhoneNumber ?? this.alternativePhoneNumber, + hasCourtesyDevice: hasCourtesyDevice ?? this.hasCourtesyDevice, + ticketType: ticketType ?? this.ticketType, + status: status ?? this.status, + estimatedDeliveryAt: estimatedDeliveryAt ?? this.estimatedDeliveryAt, + result: result ?? this.result, + resolutionNotes: resolutionNotes ?? this.resolutionNotes, + legacyId: legacyId ?? this.legacyId, + customerName: customerName ?? this.customerName, + targetModelName: targetModelName ?? this.targetModelName, + sourceModelName: sourceModelName ?? this.sourceModelName, + staffName: staffName ?? this.staffName, + ); + } + + /// Deserializzazione da Supabase + factory TicketModel.fromMap(Map map) { + return TicketModel( + id: map['id'] as String, + createdAt: map['created_at'] != null + ? DateTime.parse(map['created_at']).toLocal() + : null, + companyId: map['company_id'] as String, + storeId: map['store_id'] as String?, + customerId: map['customer_id'] as String?, + targetModelId: map['target_model_id'] as String?, + targetSn: map['target_sn'] as String?, + sourceModelId: map['source_model_id'] as String?, + sourceSn: map['source_sn'] as String?, + // Fix per i field numerici di Postgres che potrebbero arrivare come int o double + customerPrice: (map['customer_price'] as num?)?.toDouble() ?? 0.0, + internalCost: (map['internal_cost'] as num?)?.toDouble() ?? 0.0, + closedAt: map['closed_at'] != null + ? DateTime.parse(map['closed_at']).toLocal() + : null, + returnedAt: map['returned_at'] != null + ? DateTime.parse(map['returned_at']).toLocal() + : null, + request: map['request'] as String? ?? '', + staffId: map['staff_id'] as String?, + warrantyType: WarrantyType.fromString(map['warranty_type'] as String?), + publicNotes: map['public_notes'] as String?, + internalNotes: map['internal_notes'] as String?, + referenceNumber: map['reference_number'] as int?, + alternativePhoneNumber: map['alternative_phone_number'] as String?, + hasCourtesyDevice: map['has_courtesy_device'] as bool? ?? false, + ticketType: TicketType.fromString(map['ticket_type'] as String), + status: TicketStatus.fromString(map['status'] as String?), + estimatedDeliveryAt: map['estimated_delivery_at'] != null + ? DateTime.parse(map['estimated_delivery_at']).toLocal() + : null, + result: TicketResult.fromString(map['result'] as String?), + resolutionNotes: map['resolution_notes'] as String?, + legacyId: map['legacy_id'] as String?, + customerName: (map['customer']?['name'] as String?).myFormat(), + targetModelName: (map['target_model']?['name_with_brand'] as String?) + ?.myFormat(), + sourceModelName: (map['source_model']?['name_with_brand'] as String?) + ?.myFormat(), + staffName: (map['staff']?['name'] as String?).myFormat(), + ); + } + + /// Serializzazione per Supabase + Map toMap() { + return { + if (id != null) 'id': id, + 'company_id': companyId, + 'store_id': storeId, + 'customer_id': customerId, + 'target_model_id': targetModelId, + 'target_sn': targetSn, + 'source_model_id': sourceModelId, + 'source_sn': sourceSn, + 'customer_price': customerPrice, + 'internal_cost': internalCost, + if (closedAt != null) 'closed_at': closedAt!.toUtc().toIso8601String(), + if (returnedAt != null) + 'returned_at': returnedAt!.toUtc().toIso8601String(), + 'request': request, + 'staff_id': staffId, + 'warranty_type': warrantyType, + 'public_notes': publicNotes, + 'internal_notes': internalNotes, + 'alternative_phone_number': alternativePhoneNumber, + 'has_courtesy_device': hasCourtesyDevice, + 'ticket_type': ticketType.value, + if (status != null) 'status': status!.value, + if (estimatedDeliveryAt != null) + 'estimated_delivery_at': estimatedDeliveryAt!.toUtc().toIso8601String(), + if (result != null) 'result': result!.value, + 'resolution_notes': resolutionNotes, + 'legacy_id': legacyId, + }; + } + + @override + List get props => [ + id, + createdAt, + companyId, + storeId, + customerId, + targetModelId, + targetSn, + sourceModelId, + sourceSn, + customerPrice, + internalCost, + closedAt, + returnedAt, + request, + staffId, + warrantyType, + publicNotes, + internalNotes, + referenceNumber, + alternativePhoneNumber, + hasCourtesyDevice, + ticketType, + status, + estimatedDeliveryAt, + result, + resolutionNotes, + legacyId, + ]; +} diff --git a/lib/features/tickets/models/ticket_status_extension.dart b/lib/features/tickets/models/ticket_status_extension.dart new file mode 100644 index 0000000..00660fe --- /dev/null +++ b/lib/features/tickets/models/ticket_status_extension.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flux/features/tickets/models/ticket_model.dart'; + +extension TicketStatusVisuals on TicketStatus { + Color get color { + switch (this) { + case TicketStatus.open: + return Colors.blueGrey; + case TicketStatus.waitingForParts: + return Colors.amber.shade700; + case TicketStatus.inProgress: + return Colors.blue; + case TicketStatus.waitingForShipping: + // Il tuo rosa storico! + return Colors.pinkAccent; + case TicketStatus.waitingForReturn: + return Colors.purpleAccent; + case TicketStatus.ready: + return Colors.green; + case TicketStatus.closed: + return Colors.grey.shade400; + } + } + + IconData get icon { + switch (this) { + case TicketStatus.open: + return Icons.inbox; + case TicketStatus.waitingForParts: + return Icons.hourglass_empty; + case TicketStatus.inProgress: + return Icons.build; + case TicketStatus.waitingForShipping: + return Icons.local_shipping_outlined; + case TicketStatus.waitingForReturn: + return Icons.undo; + case TicketStatus.ready: + return Icons.check_circle_outline; + case TicketStatus.closed: + return Icons.lock_outline; + } + } +}