From bdde092976ecf75c58ad121ccd1ca88109d471de Mon Sep 17 00:00:00 2001 From: Mark M2 Macbook Date: Wed, 6 May 2026 12:20:26 +0200 Subject: [PATCH] refactot ticket model + cubit --- .../tickets/blocs/ticket_form_cubit.dart | 169 ++++++++++++++++++ .../tickets/blocs/ticket_form_state.dart | 40 +++++ lib/features/tickets/models/ticket_model.dart | 41 +++-- 3 files changed, 236 insertions(+), 14 deletions(-) create mode 100644 lib/features/tickets/blocs/ticket_form_cubit.dart create mode 100644 lib/features/tickets/blocs/ticket_form_state.dart diff --git a/lib/features/tickets/blocs/ticket_form_cubit.dart b/lib/features/tickets/blocs/ticket_form_cubit.dart new file mode 100644 index 0000000..24c1cf8 --- /dev/null +++ b/lib/features/tickets/blocs/ticket_form_cubit.dart @@ -0,0 +1,169 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/core/blocs/session/session_cubit.dart'; +import 'package:flux/features/customers/models/customer_model.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_form_state.dart'; + +class TicketFormCubit extends Cubit { + final TicketRepository _repository = GetIt.I.get(); + final SessionCubit _sessionCubit = GetIt.I.get(); + + TicketFormCubit() + : super( + // Inizializziamo con un ticket vuoto di default + TicketFormState( + ticket: TicketModel.empty( + companyId: GetIt.I.get().state.company!.id!, + ), + ), + ); + + /// 1. INIZIALIZZAZIONE (Se stiamo modificando un ticket esistente) + void initForm(TicketModel? existingTicket) { + if (existingTicket != null) { + emit( + state.copyWith(ticket: existingTicket, status: TicketFormStatus.ready), + ); + } else { + // È un nuovo ticket! Inseriamo i default base (Azienda, Negozio, Creatore) + final currentUser = _sessionCubit.state.currentStaffMember; + final currentStore = _sessionCubit.state.currentStore; + final companyId = _sessionCubit.state.company?.id ?? ''; + + final newTicket = + TicketModel.empty( + companyId: _sessionCubit.state.company!.id!, + ).copyWith( + companyId: companyId, + storeId: currentStore?.id, + staffId: currentUser?.id, + createdById: currentUser?.name, + // Impostiamo lo stato iniziale + status: TicketStatus.open, + ticketType: TicketType.repair, // Default + ); + + emit(state.copyWith(ticket: newTicket, status: TicketFormStatus.ready)); + } + } + + /// 2. AGGIORNAMENTO CLIENTE (Usato dal nostro SharedCustomerSection!) + void updateCustomer(CustomerModel customer) { + emit( + state.copyWith( + ticket: state.ticket.copyWith( + customerId: customer.id, + customerName: customer.name, + alternativePhoneNumber: customer.phoneNumber, // Comodo come fallback! + ), + ), + ); + } + + /// 3. AGGIORNAMENTO MODELLO (Usato dal nostro SharedModelSection!) + void updateModel({required String modelId, required String modelName}) { + emit( + state.copyWith( + ticket: state.ticket.copyWith( + targetModelId: modelId, + targetModelName: modelName, + ), + ), + ); + } + + void updateCreator({required String staffId, required String staffName}) { + emit( + state.copyWith( + ticket: state.ticket.copyWith(staffId: staffId, createdById: staffName), + ), + ); + } + + /// 4. AGGIORNAMENTO GENERICO DEI CAMPI + void updateFields({ + TicketType? ticketType, + TicketStatus? status, + String? request, + String? targetSn, + String? alternativePhoneNumber, + bool? hasCourtesyDevice, + String? includedAccessories, + String? publicNotes, + String? internalNotes, + double? customerPrice, + double? internalCost, + String? assignedToId, + String? assignedToName, + }) { + emit( + state.copyWith( + ticket: state.ticket.copyWith( + ticketType: ticketType ?? state.ticket.ticketType, + status: status ?? state.ticket.status, + request: request ?? state.ticket.request, + targetSn: targetSn ?? state.ticket.targetSn, + alternativePhoneNumber: + alternativePhoneNumber ?? state.ticket.alternativePhoneNumber, + hasCourtesyDevice: + hasCourtesyDevice ?? state.ticket.hasCourtesyDevice, + includedAccessories: + includedAccessories ?? state.ticket.includedAccessories, + publicNotes: publicNotes ?? state.ticket.publicNotes, + internalNotes: internalNotes ?? state.ticket.internalNotes, + customerPrice: customerPrice ?? state.ticket.customerPrice, + internalCost: internalCost ?? state.ticket.internalCost, + assignedToId: assignedToId ?? state.ticket.assignedToId, + assignedToName: assignedToName ?? state.ticket.assignedToName, + ), + ), + ); + } + + /// 5. SALVATAGGIO + Future saveTicket({required bool keepAdding}) async { + emit(state.copyWith(status: TicketFormStatus.saving)); + + try { + final ticketToSave = state.ticket; + + // Validazione base + if (ticketToSave.customerId == null || ticketToSave.customerId!.isEmpty) { + throw Exception("Seleziona un cliente prima di salvare."); + } + + final savedTicket = await _repository.saveTicket(ticketToSave); + + if (keepAdding) { + emit( + state.copyWith( + status: TicketFormStatus.successAndAddAnother, + // Svuotiamo il form per il prossimo, mantenendo Store e Creatore ATTUALI + ticket: TicketModel.empty().copyWith( + companyId: savedTicket.companyId, + storeId: savedTicket.storeId, + createdById: ticketToSave + .createdById, // Manteniamo quello selezionato nella tendina! + createdByName: ticketToSave.createdByName, + status: TicketStatus.open, + ticketType: TicketType.repair, + ), + ), + ); + } else { + emit( + state.copyWith(status: TicketFormStatus.success, ticket: savedTicket), + ); + } + } catch (e) { + emit( + state.copyWith( + status: TicketFormStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } +} diff --git a/lib/features/tickets/blocs/ticket_form_state.dart b/lib/features/tickets/blocs/ticket_form_state.dart new file mode 100644 index 0000000..ecf277d --- /dev/null +++ b/lib/features/tickets/blocs/ticket_form_state.dart @@ -0,0 +1,40 @@ +import 'package:equatable/equatable.dart'; +import 'package:flux/features/tickets/models/ticket_model.dart'; +// Adatta gli import al tuo progetto! + +enum TicketFormStatus { + initial, + ready, + loading, + saving, + success, + successAndAddAnother, + failure, +} + +class TicketFormState extends Equatable { + final TicketModel ticket; + final TicketFormStatus status; + final String? errorMessage; + + const TicketFormState({ + required this.ticket, + this.status = TicketFormStatus.initial, + this.errorMessage, + }); + + @override + List get props => [ticket, status, errorMessage]; + + TicketFormState copyWith({ + TicketModel? ticket, + TicketFormStatus? status, + String? errorMessage, + }) { + return TicketFormState( + ticket: ticket ?? this.ticket, + status: status ?? this.status, + errorMessage: errorMessage, + ); + } +} diff --git a/lib/features/tickets/models/ticket_model.dart b/lib/features/tickets/models/ticket_model.dart index 74a7b2b..2ad59e1 100644 --- a/lib/features/tickets/models/ticket_model.dart +++ b/lib/features/tickets/models/ticket_model.dart @@ -96,7 +96,6 @@ class TicketModel extends Equatable { final DateTime? closedAt; final DateTime? returnedAt; final String request; - final String? staffId; final WarrantyType? warrantyType; final String? publicNotes; final String? internalNotes; @@ -112,7 +111,10 @@ class TicketModel extends Equatable { final String? customerName; final String? targetModelName; final String? sourceModelName; - final String? staffName; + final String? createdById; + final String? createdByName; + final String? assignedToId; + final String? assignedToName; final String? includedAccessories; const TicketModel({ @@ -130,7 +132,6 @@ class TicketModel extends Equatable { this.closedAt, this.returnedAt, this.request = '', - this.staffId, this.warrantyType, this.publicNotes, this.internalNotes, @@ -146,14 +147,17 @@ class TicketModel extends Equatable { this.customerName, this.targetModelName, this.sourceModelName, - this.staffName, + this.createdById, + this.createdByName, + this.assignedToId, + this.assignedToName, this.includedAccessories, }); /// Factory per creare un ticket vuoto (utile per i form di creazione) - factory TicketModel.empty({required String companyId, String? storeId}) { + factory TicketModel.empty({String? companyId, String? storeId}) { return TicketModel( - companyId: companyId, + companyId: companyId ?? '', storeId: storeId, ticketType: TicketType.repair, // Valore di default status: TicketStatus.open, @@ -195,7 +199,10 @@ class TicketModel extends Equatable { String? customerName, String? targetModelName, String? sourceModelName, - String? staffName, + String? createdById, + String? createdByName, + String? assignedToId, + String? assignedToName, String? includedAccessories, }) { return TicketModel( @@ -213,7 +220,6 @@ class TicketModel extends Equatable { 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, @@ -230,7 +236,10 @@ class TicketModel extends Equatable { customerName: customerName ?? this.customerName, targetModelName: targetModelName ?? this.targetModelName, sourceModelName: sourceModelName ?? this.sourceModelName, - staffName: staffName ?? this.staffName, + createdById: createdById ?? this.createdById, + createdByName: createdByName ?? this.createdByName, + assignedToId: assignedToId ?? this.assignedToId, + assignedToName: assignedToName ?? this.assignedToName, includedAccessories: includedAccessories ?? this.includedAccessories, ); } @@ -259,7 +268,6 @@ class TicketModel extends Equatable { ? 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?, @@ -279,7 +287,10 @@ class TicketModel extends Equatable { ?.myFormat(), sourceModelName: (map['source_model']?['name_with_brand'] as String?) ?.myFormat(), - staffName: (map['staff']?['name'] as String?).myFormat(), + createdById: map['staff_id'] as String?, + createdByName: (map['staff']?['name'] as String?).myFormat(), + assignedToId: map['assigned_to_id'] as String?, + assignedToName: (map['assigned_to']?['name'] as String?).myFormat(), includedAccessories: map['included_accessories'] as String?, ); } @@ -301,7 +312,7 @@ class TicketModel extends Equatable { if (returnedAt != null) 'returned_at': returnedAt!.toUtc().toIso8601String(), 'request': request, - 'staff_id': staffId, + 'created_by_id': createdById, 'warranty_type': warrantyType, 'public_notes': publicNotes, 'internal_notes': internalNotes, @@ -334,7 +345,6 @@ class TicketModel extends Equatable { closedAt, returnedAt, request, - staffId, warrantyType, publicNotes, internalNotes, @@ -351,6 +361,9 @@ class TicketModel extends Equatable { customerName, targetModelName, sourceModelName, - staffName, + createdById, + createdByName, + assignedToId, + assignedToName, ]; }