2026-04-20 16:52:20 +02:00
|
|
|
import 'dart:async'; // Serve per il Timer del debounce
|
|
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
|
import 'package:equatable/equatable.dart';
|
2026-04-22 11:06:02 +02:00
|
|
|
import 'package:flux/core/blocs/session/session_cubit.dart';
|
2026-04-20 16:52:20 +02:00
|
|
|
import 'package:flux/features/customers/data/customer_repository.dart';
|
|
|
|
|
import 'package:flux/features/customers/models/customer_model.dart';
|
|
|
|
|
import 'package:get_it/get_it.dart';
|
|
|
|
|
|
2026-05-02 12:19:04 +02:00
|
|
|
part 'customers_state.dart';
|
2026-04-20 16:52:20 +02:00
|
|
|
|
2026-05-02 12:19:04 +02:00
|
|
|
class CustomersCubit extends Cubit<CustomersState> {
|
2026-04-20 16:52:20 +02:00
|
|
|
final CustomerRepository _repository = GetIt.I<CustomerRepository>();
|
2026-04-22 11:06:02 +02:00
|
|
|
final SessionCubit _sessionCubit = GetIt.I<SessionCubit>();
|
2026-04-20 16:52:20 +02:00
|
|
|
|
|
|
|
|
// Variabile per gestire il debounce della ricerca
|
|
|
|
|
Timer? _searchDebounce;
|
|
|
|
|
|
2026-05-02 12:19:04 +02:00
|
|
|
CustomersCubit() : super(const CustomersState());
|
2026-04-20 16:52:20 +02:00
|
|
|
|
|
|
|
|
// --- LETTURA ---
|
|
|
|
|
Future<void> loadCustomers() async {
|
2026-05-02 12:19:04 +02:00
|
|
|
emit(state.copyWith(status: CustomersStatus.loading));
|
2026-04-20 16:52:20 +02:00
|
|
|
try {
|
|
|
|
|
final customers = await _repository.getCustomers(
|
2026-04-22 11:06:02 +02:00
|
|
|
_sessionCubit.state.company!.id!,
|
2026-04-20 16:52:20 +02:00
|
|
|
);
|
|
|
|
|
emit(
|
2026-05-02 12:19:04 +02:00
|
|
|
state.copyWith(status: CustomersStatus.success, customers: customers),
|
2026-04-20 16:52:20 +02:00
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
emit(
|
|
|
|
|
state.copyWith(
|
2026-05-02 12:19:04 +02:00
|
|
|
status: CustomersStatus.failure,
|
2026-04-20 16:52:20 +02:00
|
|
|
errorMessage: e.toString(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- CREAZIONE ---
|
|
|
|
|
Future<void> createCustomer(CustomerModel customer) async {
|
2026-05-02 12:19:04 +02:00
|
|
|
emit(state.copyWith(status: CustomersStatus.loading));
|
2026-04-20 16:52:20 +02:00
|
|
|
try {
|
|
|
|
|
final newCustomer = await _repository.saveCustomer(customer);
|
|
|
|
|
|
|
|
|
|
// Aggiorniamo la lista locale aggiungendo il nuovo cliente in cima
|
|
|
|
|
final updatedList = List<CustomerModel>.from(state.customers)
|
|
|
|
|
..insert(0, newCustomer);
|
|
|
|
|
|
|
|
|
|
emit(
|
|
|
|
|
state.copyWith(
|
2026-05-02 12:19:04 +02:00
|
|
|
status: CustomersStatus.success,
|
2026-04-20 16:52:20 +02:00
|
|
|
customers: updatedList,
|
|
|
|
|
lastCreatedCustomer: newCustomer,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
emit(
|
|
|
|
|
state.copyWith(
|
2026-05-02 12:19:04 +02:00
|
|
|
status: CustomersStatus.failure,
|
2026-04-20 16:52:20 +02:00
|
|
|
errorMessage: e.toString(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- AGGIORNAMENTO ---
|
|
|
|
|
Future<void> updateCustomer(CustomerModel customer) async {
|
2026-05-02 12:19:04 +02:00
|
|
|
emit(state.copyWith(status: CustomersStatus.loading));
|
2026-04-20 16:52:20 +02:00
|
|
|
try {
|
|
|
|
|
final updatedCustomer = await _repository.updateCustomer(customer);
|
|
|
|
|
|
|
|
|
|
final updatedList = List<CustomerModel>.from(state.customers);
|
|
|
|
|
final index = updatedList.indexWhere((c) => c.id == updatedCustomer.id);
|
|
|
|
|
|
|
|
|
|
if (index != -1) {
|
|
|
|
|
updatedList[index] = updatedCustomer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit(
|
|
|
|
|
state.copyWith(
|
2026-05-02 12:19:04 +02:00
|
|
|
status: CustomersStatus.success,
|
2026-04-20 16:52:20 +02:00
|
|
|
customers: updatedList,
|
|
|
|
|
lastCreatedCustomer:
|
|
|
|
|
updatedCustomer, // Utile se modifichi un cliente appena creato
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
emit(
|
|
|
|
|
state.copyWith(
|
2026-05-02 12:19:04 +02:00
|
|
|
status: CustomersStatus.failure,
|
2026-04-20 16:52:20 +02:00
|
|
|
errorMessage: e.toString(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- RICERCA CON DEBOUNCE ---
|
|
|
|
|
void searchCustomers(String query) {
|
|
|
|
|
// 1. Se c'è già una ricerca in attesa (l'utente sta digitando veloce), la annulliamo
|
|
|
|
|
if (_searchDebounce?.isActive ?? false) _searchDebounce!.cancel();
|
|
|
|
|
|
|
|
|
|
// 2. Facciamo partire un timer di 400 millisecondi
|
|
|
|
|
_searchDebounce = Timer(const Duration(milliseconds: 300), () async {
|
|
|
|
|
// Se cancella tutto e la query è vuota, ricarichiamo la lista base
|
|
|
|
|
if (query.trim().isEmpty) {
|
|
|
|
|
await loadCustomers();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Nessun "loading" state qui, per evitare sfarfallii visivi mentre si scrive
|
|
|
|
|
try {
|
|
|
|
|
final results = await _repository.searchCustomers(
|
2026-04-22 11:06:02 +02:00
|
|
|
_sessionCubit.state.company!.id!,
|
2026-04-20 16:52:20 +02:00
|
|
|
query,
|
|
|
|
|
);
|
|
|
|
|
emit(
|
2026-05-02 12:19:04 +02:00
|
|
|
state.copyWith(status: CustomersStatus.success, customers: results),
|
2026-04-20 16:52:20 +02:00
|
|
|
);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
emit(
|
|
|
|
|
state.copyWith(
|
2026-05-02 12:19:04 +02:00
|
|
|
status: CustomersStatus.failure,
|
2026-04-20 16:52:20 +02:00
|
|
|
errorMessage: e.toString(),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<CustomerModel?> quickCreateCustomer({
|
|
|
|
|
required String name,
|
|
|
|
|
String? phone,
|
|
|
|
|
String? email,
|
|
|
|
|
}) async {
|
|
|
|
|
final newCustomer = CustomerModel(
|
2026-05-01 11:54:39 +02:00
|
|
|
name: name,
|
|
|
|
|
phoneNumber: phone ?? '',
|
2026-04-20 16:52:20 +02:00
|
|
|
email: email ?? '',
|
2026-04-22 11:06:02 +02:00
|
|
|
companyId: _sessionCubit.state.company!.id!,
|
2026-04-20 16:52:20 +02:00
|
|
|
note: '',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final saved = await _repository.saveCustomer(newCustomer);
|
|
|
|
|
// Lo aggiungiamo in cima ai suggerimenti
|
|
|
|
|
emit(state.copyWith(customers: [saved, ...state.customers]));
|
|
|
|
|
return saved;
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pulizia della memoria quando il Cubit viene distrutto
|
|
|
|
|
@override
|
|
|
|
|
Future<void> close() {
|
|
|
|
|
_searchDebounce?.cancel();
|
|
|
|
|
return super.close();
|
|
|
|
|
}
|
|
|
|
|
}
|