service #2
111
lib/features/services/blocs/services_cubit.dart
Normal file
111
lib/features/services/blocs/services_cubit.dart
Normal 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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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')
|
|
||||||
.select('''
|
|
||||||
*,
|
*,
|
||||||
energy_service(*),
|
energy_service(*),
|
||||||
fin_service(*),
|
fin_service(*),
|
||||||
entertainment_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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user