feat: service set repos and cubit

This commit is contained in:
2026-04-15 19:31:08 +02:00
parent 61442339fe
commit 29790a7a36
2 changed files with 149 additions and 13 deletions

View File

@@ -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<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,
];
}
class ServicesCubit extends Cubit<ServicesState> {
final ServicesRepository _repository = GetIt.I<ServicesRepository>();
ServicesCubit() : super(const ServicesState());
// Carica tutto il pacchetto
Future<void> 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<void> 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()));
}
}
}

View File

@@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:supabase_flutter/supabase_flutter.dart';
import '../models/service_model.dart'; import '../models/service_model.dart';
// Importa gli altri modelli se sono in file separati // Importa gli altri modelli se sono in file separati
@@ -6,24 +7,48 @@ class ServicesRepository {
final _supabase = Supabase.instance.client; final _supabase = Supabase.instance.client;
// --- RECUPERO TUTTI I SERVIZI --- // --- RECUPERO TUTTI I SERVIZI ---
Future<List<ServiceModel>> fetchAllServices() async { Future<List<ServiceModel>> fetchServices({
required int offset,
int limit = 50,
String? searchTerm,
DateTimeRange? dateRange,
}) async {
try { try {
// La stringa di selezione tira giù il padre e TUTTI i record correlati dalle tabelle figlie var query = _supabase.from('service').select('''
final response = await _supabase *,
.from('service') energy_service(*),
.select(''' fin_service(*),
*, entertainment_service(*)
energy_service(*), ''');
fin_service(*),
entertainment_service(*)
''')
.order('created_at', ascending: false);
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<ServiceModel> services = (response as List)
.map((map) => ServiceModel.fromMap(map)) .map((map) => ServiceModel.fromMap(map))
.toList(); .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) { } catch (e) {
throw Exception('Errore nel recupero servizi: $e'); throw Exception('Errore fetch: $e');
} }
} }