Refactor service management: streamline service form, enhance state management, and improve loading logic

This commit is contained in:
2026-04-17 19:19:01 +02:00
parent 667bbf6404
commit a06be4bf7a
13 changed files with 715 additions and 261 deletions

View File

@@ -3,55 +3,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flux/core/blocs/session/session_bloc.dart';
import 'package:flux/features/services/data/services_repository.dart';
import 'package:flux/features/services/models/energy_service_model.dart';
import 'package:flux/features/services/models/entertainment_service_model.dart';
import 'package:flux/features/services/models/fin_service_model.dart';
import 'package:flux/features/services/models/service_model.dart';
import 'package:get_it/get_it.dart';
class ServicesState extends Equatable {
final List<ServiceModel> allServices;
final bool isLoading;
final bool hasReachedMax; // Per lo scroll infinito
final String? errorMessage;
// Parametri di ricerca
final String query;
final DateTimeRange? dateRange;
const ServicesState({
this.allServices = const [],
this.isLoading = false,
this.hasReachedMax = false,
this.errorMessage,
this.query = '',
this.dateRange,
});
ServicesState copyWith({
List<ServiceModel>? allServices,
bool? isLoading,
String? errorMessage,
bool? hasReachedMax,
String? query,
DateTimeRange? dateRange,
}) {
return ServicesState(
allServices: allServices ?? this.allServices,
isLoading: isLoading ?? this.isLoading,
errorMessage:
errorMessage, // Se non lo passiamo, torna null (pulisce l'errore)
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
query: query ?? this.query,
dateRange: dateRange ?? this.dateRange,
);
}
@override
List<Object?> get props => [
allServices,
isLoading,
hasReachedMax,
errorMessage,
query,
dateRange,
];
}
part 'services_state.dart';
class ServicesCubit extends Cubit<ServicesState> {
final ServicesRepository _repository = GetIt.I<ServicesRepository>();
@@ -59,58 +16,178 @@ class ServicesCubit extends Cubit<ServicesState> {
ServicesCubit(this._sessionBloc) : super(const ServicesState());
// Carica tutto il pacchetto
// --- CARICAMENTO E PAGINAZIONE ---
Future<void> loadServices({bool refresh = false}) async {
// Se non è un refresh e abbiamo già dati, non disturbare Supabase
if (!refresh && state.allServices.isNotEmpty) return;
// Se stiamo già caricando, evitiamo chiamate doppie
if (state.isLoading) return;
// Se facciamo refresh, resettiamo tutto
final currentOffset = refresh ? 0 : state.allServices.length;
// Se non è un refresh e abbiamo già raggiunto la fine dei dati, ci fermiamo
if (!refresh && state.hasReachedMax) return;
emit(
state.copyWith(
isLoading: true,
errorMessage: null,
// Se è un refresh, svuotiamo la lista attuale per mostrare lo shimmer/loading
allServices: refresh ? [] : state.allServices,
hasReachedMax: refresh ? false : state.hasReachedMax,
),
);
try {
final currentOffset = refresh ? 0 : state.allServices.length;
final companyId = _sessionBloc.state.company?.id;
if (companyId == null) {
throw Exception("Company ID non trovato nella sessione");
}
final newServices = await _repository.fetchServices(
companyId: _sessionBloc.state.company!.id,
companyId: companyId,
offset: currentOffset,
limit: 50,
searchTerm: state.query,
dateRange: state.dateRange,
);
// Se ricevi meno record del limite, significa che non ce ne sono altri sul DB
final bool reachedMax = newServices.length < 50;
emit(
state.copyWith(
isLoading: false,
allServices: List.from(state.allServices)..addAll(newServices),
hasReachedMax:
newServices.length <
50, // Se ne arrivano meno di 50, siamo alla fine
allServices: refresh
? newServices
: [...state.allServices, ...newServices],
hasReachedMax: reachedMax,
),
);
} catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
emit(
state.copyWith(
isLoading: false,
errorMessage: "Errore nel caricamento servizi: $e",
),
);
}
}
// --- GESTIONE FILTRI ---
/// Aggiorna i parametri di ricerca e ricarica da zero
void updateFilters({String? query, DateTimeRange? range}) {
emit(state.copyWith(query: query, dateRange: range));
loadServices(refresh: true); // Applica i filtri e riparte da zero
emit(
state.copyWith(
query: query ?? state.query,
dateRange: range ?? state.dateRange,
),
);
loadServices(refresh: true);
}
// Salva e ricarica
Future<void> addService(ServiceModel service) async {
emit(state.copyWith(isLoading: true));
/// Pulisce tutti i filtri
void clearFilters() {
emit(state.copyWith(query: '', dateRange: null));
loadServices(refresh: true);
}
// --- GESTIONE BOZZA (DRAFT) ---
/// Inizializza un nuovo servizio o ne carica uno esistente per la modifica
void initServiceForm(ServiceModel? existingService) {
if (existingService != null) {
emit(state.copyWith(currentService: existingService));
} else {
// Crea un template vuoto con lo store di default (se disponibile)
emit(
state.copyWith(
currentService: ServiceModel(
storeId: _sessionBloc.state.selectedStore?.id ?? '',
number: '', // Sarà compilato dall'utente
createdAt: DateTime.now(),
),
),
);
}
}
/// Metodo generico per aggiornare i campi base (AL, MNP, Note, ecc.)
void updateField({
int? al,
int? mnp,
int? nip,
int? unica,
int? telepass,
String? note,
String? number,
bool? isBozza,
bool? resultOk,
String? customerId,
}) {
if (state.currentService == null) return;
final updated = state.currentService!.copyWith(
al: al,
mnp: mnp,
nip: nip,
unica: unica,
telepass: telepass,
note: note,
number: number,
isBozza: isBozza,
resultOk: resultOk,
customerId: customerId,
);
emit(state.copyWith(currentService: updated));
}
// --- GESTIONE MODULI COMPLESSI ---
void updateEnergyServices(List<EnergyServiceModel> energyList) {
emit(
state.copyWith(
currentService: state.currentService?.copyWith(
energyServices: energyList,
),
),
);
}
void updateFinServices(List<FinServiceModel> finList) {
emit(
state.copyWith(
currentService: state.currentService?.copyWith(finServices: finList),
),
);
}
void updateEntertainmentServices(List<EntertainmentServiceModel> entList) {
emit(
state.copyWith(
currentService: state.currentService?.copyWith(
entertainmentServices: entList,
),
),
);
}
// --- PERSISTENZA ---
Future<void> saveCurrentService() async {
if (state.currentService == null) return;
emit(state.copyWith(isSaving: true, errorMessage: null));
try {
await _repository.saveFullService(service);
await loadServices(); // Ricarichiamo la lista aggiornata
// Usiamo il repository corazzato che abbiamo scritto prima
await _repository.saveFullService(state.currentService!);
// Reset della bozza e ricaricamento lista
emit(state.copyWith(isSaving: false, currentService: null));
await loadServices(refresh: true);
} catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
emit(state.copyWith(isSaving: false, errorMessage: e.toString()));
}
}
}