From 29790a7a361fed33493556e4fa3bb9ee7c5b0721 Mon Sep 17 00:00:00 2001 From: mark-cachy Date: Wed, 15 Apr 2026 19:31:08 +0200 Subject: [PATCH] feat: service set repos and cubit --- .../services/blocs/services_cubit.dart | 111 ++++++++++++++++++ .../services/data/services_repository.dart | 51 ++++++-- 2 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 lib/features/services/blocs/services_cubit.dart diff --git a/lib/features/services/blocs/services_cubit.dart b/lib/features/services/blocs/services_cubit.dart new file mode 100644 index 0000000..81b4034 --- /dev/null +++ b/lib/features/services/blocs/services_cubit.dart @@ -0,0 +1,111 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flux/features/services/data/services_repository.dart'; +import 'package:flux/features/services/models/service_model.dart'; +import 'package:get_it/get_it.dart'; + +class ServicesState extends Equatable { + final List 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? 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 get props => [ + allServices, + isLoading, + hasReachedMax, + errorMessage, + query, + dateRange, + ]; +} + +class ServicesCubit extends Cubit { + final ServicesRepository _repository = GetIt.I(); + + ServicesCubit() : super(const ServicesState()); + + // Carica tutto il pacchetto + Future loadServices({bool refresh = false}) async { + if (state.isLoading) return; + + // Se facciamo refresh, resettiamo tutto + final currentOffset = refresh ? 0 : state.allServices.length; + + emit( + state.copyWith( + isLoading: true, + allServices: refresh ? [] : state.allServices, + hasReachedMax: refresh ? false : state.hasReachedMax, + ), + ); + + try { + final newServices = await _repository.fetchServices( + offset: currentOffset, + searchTerm: state.query, + dateRange: state.dateRange, + ); + + 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 + ), + ); + } catch (e) { + emit(state.copyWith(isLoading: false, errorMessage: e.toString())); + } + } + + void updateFilters({String? query, DateTimeRange? range}) { + emit(state.copyWith(query: query, dateRange: range)); + loadServices(refresh: true); // Applica i filtri e riparte da zero + } + + // Salva e ricarica + Future addService(ServiceModel service) async { + emit(state.copyWith(isLoading: true)); + try { + await _repository.saveFullService(service); + await loadServices(); // Ricarichiamo la lista aggiornata + } catch (e) { + emit(state.copyWith(isLoading: false, errorMessage: e.toString())); + } + } +} diff --git a/lib/features/services/data/services_repository.dart b/lib/features/services/data/services_repository.dart index aae513f..157f2df 100644 --- a/lib/features/services/data/services_repository.dart +++ b/lib/features/services/data/services_repository.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../models/service_model.dart'; // Importa gli altri modelli se sono in file separati @@ -6,24 +7,48 @@ class ServicesRepository { final _supabase = Supabase.instance.client; // --- RECUPERO TUTTI I SERVIZI --- - Future> fetchAllServices() async { + Future> fetchServices({ + required int offset, + int limit = 50, + String? searchTerm, + DateTimeRange? dateRange, + }) async { try { - // La stringa di selezione tira giù il padre e TUTTI i record correlati dalle tabelle figlie - final response = await _supabase - .from('service') - .select(''' - *, - energy_service(*), - fin_service(*), - entertainment_service(*) - ''') - .order('created_at', ascending: false); + var query = _supabase.from('service').select(''' + *, + energy_service(*), + fin_service(*), + entertainment_service(*) + '''); - return (response as List) + // Filtro per range di date + if (dateRange != null) { + query = query + .gte('created_at', dateRange.start.toIso8601String()) + .lte('created_at', dateRange.end.toIso8601String()); + } + + // Ordinamento e Paginazione + final response = await query + .order('created_at', ascending: false) + .range(offset, offset + limit - 1); + + final List services = (response as List) .map((map) => ServiceModel.fromMap(map)) .toList(); + + // Filtro testuale lato client per semplicità (o potresti farlo in SQL se preferisci) + if (searchTerm != null && searchTerm.isNotEmpty) { + return services.where((s) { + // Qui cercheremo per numero pratica o note (il nome cliente lo vedremo poi con le Join) + return s.number.toLowerCase().contains(searchTerm.toLowerCase()) || + s.note.toLowerCase().contains(searchTerm.toLowerCase()); + }).toList(); + } + + return services; } catch (e) { - throw Exception('Errore nel recupero servizi: $e'); + throw Exception('Errore fetch: $e'); } }