feat: refactorisation majeure - DataLoadingService + UserRepository simplifié
✅ NOUVEAU SERVICE CRÉÉ: - DataLoadingService: gère tout le chargement des données au login - Sépare les responsabilités: UserRepository se concentre sur l'auth - Simplification massive du code de connexion ✅ USERREPOSITORY REFACTORISÉ: - Suppression de toute la logique de chargement de données (déplacée vers DataLoadingService) - Délégation complète aux services singleton (CurrentUserService, CurrentAmicaleService) - Constructeur ultra-simplifié (plus d'injection ApiService) - Méthodes d'auth optimisées et clarifiées ✅ REPOSITORIES SIMPLIFIÉS: - AmicaleRepository: constructeur sans paramètres, ApiService.instance - ClientRepository: même pattern de simplification - MembreRepository: suppression injection, getters sécurisés - OperationRepository: utilisation ApiService.instance - PassageRepository: simplification massive, nouveau pattern - SectorRepository: optimisation et nouvelle structure ✅ ARCHITECTURE SINGLETONS: - ApiService: pattern singleton thread-safe - CurrentUserService: gestion utilisateur connecté + persistence Hive (Box user) - CurrentAmicaleService: gestion amicale courante + auto-sync - Box Hive 'users' renommée en 'user' avec migration automatique ✅ APP.DART & MAIN.DART: - Suppression injections multiples dans repositories - Intégration des services singleton dans main.dart - Router simplifié avec CurrentUserService État: Architecture singleton opérationnelle, prêt pour tests et widgets
This commit is contained in:
@@ -1,7 +1,209 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
|
||||
class AmicaleRepository extends ChangeNotifier {
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
AmicaleRepository();
|
||||
|
||||
// Utiliser un getter lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
// et vérifier qu'elle est ouverte avant accès
|
||||
Box<AmicaleModel> get _amicaleBox {
|
||||
_ensureBoxIsOpen();
|
||||
return Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
|
||||
}
|
||||
|
||||
bool _isLoading = false;
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
List<AmicaleModel> get amicales => getAllAmicales();
|
||||
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
const boxName = AppKeys.amicaleBoxName;
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName dans AmicaleRepository...');
|
||||
await Hive.openBox<AmicaleModel>(boxName);
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer toutes les amicales
|
||||
List<AmicaleModel> getAllAmicales() {
|
||||
return _amicaleBox.values.toList();
|
||||
}
|
||||
|
||||
// Récupérer une amicale par son ID
|
||||
AmicaleModel? getAmicaleById(int id) {
|
||||
return _amicaleBox.get(id);
|
||||
}
|
||||
|
||||
// Récupérer les amicales par type
|
||||
List<AmicaleModel> getAmicalesByType(int type) {
|
||||
return _amicaleBox.values.where((amicale) => amicale.fkType == type).toList();
|
||||
}
|
||||
|
||||
// Récupérer uniquement les clients (type 1)
|
||||
List<AmicaleModel> getClients() {
|
||||
return getAmicalesByType(1);
|
||||
}
|
||||
|
||||
// Sauvegarder une amicale
|
||||
Future<void> saveAmicale(AmicaleModel amicale) async {
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Supprimer une amicale
|
||||
Future<void> deleteAmicale(int id) async {
|
||||
await _amicaleBox.delete(id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Créer une amicale via l'API
|
||||
Future<bool> createAmicale(AmicaleModel amicale) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
final data = amicale.toJson();
|
||||
|
||||
// Appeler l'API pour créer l'amicale
|
||||
final response = await ApiService.instance.post('/amicales', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Récupérer l'ID de la nouvelle amicale
|
||||
final amicaleId = response.data['id'] is String ? int.parse(response.data['id']) : response.data['id'] as int;
|
||||
|
||||
// Créer l'amicale localement avec l'ID retourné par l'API
|
||||
final newAmicale = amicale.copyWith(
|
||||
id: amicaleId,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await saveAmicale(newAmicale);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création de l\'amicale: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour une amicale via l'API
|
||||
Future<bool> updateAmicale(AmicaleModel amicale) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
final data = amicale.toJson();
|
||||
|
||||
// Appeler l'API pour mettre à jour l'amicale
|
||||
final response = await ApiService.instance.put('/amicales/${amicale.id}', data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Mettre à jour l'amicale localement
|
||||
final updatedAmicale = amicale.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await saveAmicale(updatedAmicale);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'amicale: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer une amicale via l'API
|
||||
Future<bool> deleteAmicaleViaApi(int id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Appeler l'API pour supprimer l'amicale
|
||||
final response = await ApiService.instance.delete('/amicales/$id');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer l'amicale localement
|
||||
await deleteAmicale(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la suppression de l\'amicale: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Traitement des données d'amicales depuis l'API
|
||||
Future<void> processAmicalesData(dynamic amicalesData) async {
|
||||
try {
|
||||
debugPrint('Traitement des données des amicales...');
|
||||
|
||||
// Vérifier que les données sont au bon format
|
||||
if (amicalesData == null) {
|
||||
debugPrint('Aucune donnée d\'amicale à traiter');
|
||||
return;
|
||||
}
|
||||
|
||||
List<dynamic> amicalesList;
|
||||
if (amicalesData is List) {
|
||||
amicalesList = amicalesData;
|
||||
} else if (amicalesData is Map && amicalesData.containsKey('data')) {
|
||||
amicalesList = amicalesData['data'] as List<dynamic>;
|
||||
} else {
|
||||
debugPrint('Format de données d\'amicales non reconnu');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouvelles données
|
||||
await _amicaleBox.clear();
|
||||
|
||||
// Traiter chaque amicale
|
||||
int count = 0;
|
||||
for (final amicaleData in amicalesList) {
|
||||
try {
|
||||
final amicale = AmicaleModel.fromJson(amicaleData);
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
count++;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'une amicale: $e');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$count amicales traitées et stockées');
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des amicales: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
|
||||
|
||||
@@ -1,81 +1,151 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:geosector_app/core/data/models/client_model.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
|
||||
class ClientRepository extends ChangeNotifier {
|
||||
// Utilisation de getters lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
Box<ClientModel> get _clientBox =>
|
||||
Hive.box<ClientModel>(AppKeys.clientsBoxName);
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
ClientRepository();
|
||||
// Utiliser un getter lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
// et vérifier qu'elle est ouverte avant accès
|
||||
Box<ClientModel> get _clientBox {
|
||||
_ensureBoxIsOpen();
|
||||
return Hive.box<ClientModel>(AppKeys.clientsBoxName);
|
||||
}
|
||||
|
||||
final ApiService _apiService;
|
||||
bool _isLoading = false;
|
||||
|
||||
ClientRepository(this._apiService);
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
List<ClientModel> get clients => getAllClients();
|
||||
|
||||
// Méthode pour vérifier si une boîte est ouverte et l'ouvrir si nécessaire
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
try {
|
||||
if (!Hive.isBoxOpen(AppKeys.clientsBoxName)) {
|
||||
debugPrint('Ouverture de la boîte clients...');
|
||||
await Hive.openBox<ClientModel>(AppKeys.clientsBoxName);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'ouverture de la boîte clients: $e');
|
||||
throw Exception('Impossible d\'ouvrir la boîte clients: $e');
|
||||
const boxName = AppKeys.clientsBoxName;
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName dans ClientRepository...');
|
||||
await Hive.openBox<ClientModel>(boxName);
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer tous les clients
|
||||
List<ClientModel> getAllClients() {
|
||||
try {
|
||||
_ensureBoxIsOpen();
|
||||
return _clientBox.values.toList();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des clients: $e');
|
||||
return [];
|
||||
}
|
||||
return _clientBox.values.toList();
|
||||
}
|
||||
|
||||
// Récupérer un client par son ID
|
||||
ClientModel? getClientById(int id) {
|
||||
try {
|
||||
_ensureBoxIsOpen();
|
||||
return _clientBox.get(id);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération du client: $e');
|
||||
return null;
|
||||
}
|
||||
return _clientBox.get(id);
|
||||
}
|
||||
|
||||
// Créer ou mettre à jour un client localement
|
||||
Future<ClientModel> saveClient(ClientModel client) async {
|
||||
await _ensureBoxIsOpen();
|
||||
// Sauvegarder un client
|
||||
Future<void> saveClient(ClientModel client) async {
|
||||
await _clientBox.put(client.id, client);
|
||||
notifyListeners(); // Notifier les changements pour mettre à jour l'UI
|
||||
return client;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Supprimer un client localement
|
||||
// Supprimer un client
|
||||
Future<void> deleteClient(int id) async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _clientBox.delete(id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Vider la boîte des clients
|
||||
Future<void> clearClients() async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _clientBox.clear();
|
||||
// Créer un client via l'API
|
||||
Future<bool> createClient(ClientModel client) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
final data = client.toJson();
|
||||
|
||||
// Appeler l'API pour créer le client
|
||||
final response = await ApiService.instance.post('/clients', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Récupérer l'ID du nouveau client
|
||||
final clientId = response.data['id'] is String ? int.parse(response.data['id']) : response.data['id'] as int;
|
||||
|
||||
// Créer le client localement avec l'ID retourné par l'API
|
||||
final newClient = client.copyWith(
|
||||
id: clientId,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
await saveClient(newClient);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création du client: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Traiter les données des clients reçues de l'API
|
||||
// Mettre à jour un client via l'API
|
||||
Future<bool> updateClient(ClientModel client) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
final data = client.toJson();
|
||||
|
||||
// Appeler l'API pour mettre à jour le client
|
||||
final response = await ApiService.instance.put('/clients/${client.id}', data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Mettre à jour le client localement
|
||||
final updatedClient = client.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await saveClient(updatedClient);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour du client: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer un client via l'API
|
||||
Future<bool> deleteClientViaApi(int id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Appeler l'API pour supprimer le client
|
||||
final response = await ApiService.instance.delete('/clients/$id');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer le client localement
|
||||
await deleteClient(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la suppression du client: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Traitement des données de clients depuis l'API
|
||||
Future<void> processClientsData(dynamic clientsData) async {
|
||||
try {
|
||||
debugPrint('Traitement des données des clients...');
|
||||
@@ -97,7 +167,6 @@ class ClientRepository extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouvelles données
|
||||
await _ensureBoxIsOpen();
|
||||
await _clientBox.clear();
|
||||
|
||||
// Traiter chaque client
|
||||
@@ -107,7 +176,6 @@ class ClientRepository extends ChangeNotifier {
|
||||
final client = ClientModel.fromJson(clientData);
|
||||
await _clientBox.put(client.id, client);
|
||||
count++;
|
||||
debugPrint('Client traité: ${client.name} (ID: ${client.id})');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'un client: $e');
|
||||
}
|
||||
@@ -120,21 +188,27 @@ class ClientRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Vider la boîte des clients
|
||||
Future<void> clearClients() async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _clientBox.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Récupérer les clients depuis l'API
|
||||
Future<List<ClientModel>> fetchClientsFromApi() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _apiService.get('/clients');
|
||||
final response = await ApiService.instance.get('/clients');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final clientsData = response.data;
|
||||
await processClientsData(clientsData);
|
||||
return getAllClients();
|
||||
} else {
|
||||
debugPrint(
|
||||
'Erreur lors de la récupération des clients: ${response.statusCode}');
|
||||
debugPrint('Erreur lors de la récupération des clients: ${response.statusCode}');
|
||||
return [];
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -153,9 +227,7 @@ class ClientRepository extends ChangeNotifier {
|
||||
}
|
||||
|
||||
final lowercaseQuery = query.toLowerCase();
|
||||
return _clientBox.values
|
||||
.where((client) => client.name.toLowerCase().contains(lowercaseQuery))
|
||||
.toList();
|
||||
return _clientBox.values.where((client) => client.name.toLowerCase().contains(lowercaseQuery)).toList();
|
||||
}
|
||||
|
||||
// Filtrer les clients par type
|
||||
@@ -165,15 +237,11 @@ class ClientRepository extends ChangeNotifier {
|
||||
|
||||
// Filtrer les clients par région
|
||||
List<ClientModel> getClientsByRegion(int regionId) {
|
||||
return _clientBox.values
|
||||
.where((client) => client.fkRegion == regionId)
|
||||
.toList();
|
||||
return _clientBox.values.where((client) => client.fkRegion == regionId).toList();
|
||||
}
|
||||
|
||||
// Filtrer les clients actifs
|
||||
List<ClientModel> getActiveClients() {
|
||||
return _clientBox.values
|
||||
.where((client) => client.chkActive == true)
|
||||
.toList();
|
||||
return _clientBox.values.where((client) => client.chkActive == true).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:geosector_app/core/data/models/membre_model.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
|
||||
class MembreRepository extends ChangeNotifier {
|
||||
// Utilisation de getters lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
Box<MembreModel> get _membreBox => Hive.box<MembreModel>(AppKeys.membresBoxName);
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
MembreRepository();
|
||||
// Utiliser un getter lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
// et vérifier qu'elle est ouverte avant accès
|
||||
Box<MembreModel> get _membreBox {
|
||||
_ensureBoxIsOpen();
|
||||
return Hive.box<MembreModel>(AppKeys.membresBoxName);
|
||||
}
|
||||
|
||||
final ApiService _apiService;
|
||||
bool _isLoading = false;
|
||||
|
||||
MembreRepository(this._apiService);
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
List<MembreModel> get membres => getAllMembres();
|
||||
@@ -32,17 +34,12 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour vérifier si une boîte est ouverte et l'ouvrir si nécessaire
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
try {
|
||||
if (!Hive.isBoxOpen(AppKeys.membresBoxName)) {
|
||||
debugPrint('Ouverture de la boîte ${AppKeys.membresBoxName}...');
|
||||
await Hive.openBox<MembreModel>(AppKeys.membresBoxName);
|
||||
debugPrint('Boîte ${AppKeys.membresBoxName} ouverte avec succès');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'ouverture de la boîte ${AppKeys.membresBoxName}: $e');
|
||||
throw Exception('Impossible d\'ouvrir la boîte ${AppKeys.membresBoxName}: $e');
|
||||
const boxName = AppKeys.membresBoxName;
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName dans MembreRepository...');
|
||||
await Hive.openBox<MembreModel>(boxName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,12 +92,11 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Créer ou mettre à jour un membre
|
||||
Future<MembreModel> saveMembre(MembreModel membre) async {
|
||||
// Sauvegarder un membre
|
||||
Future<void> saveMembre(MembreModel membre) async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _membreBox.put(membre.id, membre);
|
||||
notifyListeners();
|
||||
return membre;
|
||||
}
|
||||
|
||||
// Supprimer un membre
|
||||
@@ -110,72 +106,35 @@ class MembreRepository extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Récupérer les membres depuis l'API (uniquement pour l'interface admin)
|
||||
Future<List<MembreModel>> fetchMembresFromApi() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final hasConnection = await _apiService.hasInternetConnection();
|
||||
if (!hasConnection) {
|
||||
debugPrint('Pas de connexion Internet, utilisation des données locales');
|
||||
return getAllMembres();
|
||||
}
|
||||
|
||||
// Endpoint à adapter selon votre API
|
||||
final response = await _apiService.get('/membres');
|
||||
final List<dynamic> membresData = response.data['membres'];
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouveaux membres
|
||||
await _ensureBoxIsOpen();
|
||||
await _membreBox.clear();
|
||||
|
||||
final List<MembreModel> membres = [];
|
||||
for (var membreData in membresData) {
|
||||
try {
|
||||
final membre = MembreModel.fromJson(membreData);
|
||||
await _membreBox.put(membre.id, membre);
|
||||
membres.add(membre);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'un membre: $e');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
return membres;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des membres depuis l\'API: $e');
|
||||
return getAllMembres();
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Créer un membre via l'API
|
||||
Future<MembreModel?> createMembreViaApi(MembreModel membre) async {
|
||||
Future<bool> createMembre(MembreModel membre) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final hasConnection = await _apiService.hasInternetConnection();
|
||||
if (!hasConnection) {
|
||||
debugPrint('Pas de connexion Internet, impossible de créer le membre');
|
||||
return null;
|
||||
// Préparer les données pour l'API
|
||||
final data = membre.toJson();
|
||||
|
||||
// Appeler l'API pour créer le membre
|
||||
final response = await ApiService.instance.post('/membres', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Récupérer l'ID du nouveau membre
|
||||
final membreId = response.data['id'] is String ? int.parse(response.data['id']) : response.data['id'] as int;
|
||||
|
||||
// Créer le membre localement avec l'ID retourné par l'API
|
||||
final newMembre = membre.copyWith(
|
||||
id: membreId,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
await saveMembre(newMembre);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Endpoint à adapter selon votre API
|
||||
final response = await _apiService.post('/membres', data: membre.toJson());
|
||||
final membreData = response.data['membre'];
|
||||
|
||||
final newMembre = MembreModel.fromJson(membreData);
|
||||
await saveMembre(newMembre);
|
||||
|
||||
return newMembre;
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création du membre via l\'API: $e');
|
||||
return null;
|
||||
debugPrint('Erreur lors de la création du membre: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
@@ -183,28 +142,32 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// Mettre à jour un membre via l'API
|
||||
Future<MembreModel?> updateMembreViaApi(MembreModel membre) async {
|
||||
Future<bool> updateMembre(MembreModel membre) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final hasConnection = await _apiService.hasInternetConnection();
|
||||
if (!hasConnection) {
|
||||
debugPrint('Pas de connexion Internet, impossible de mettre à jour le membre');
|
||||
return null;
|
||||
// Préparer les données pour l'API
|
||||
final data = membre.toJson();
|
||||
|
||||
// Appeler l'API pour mettre à jour le membre
|
||||
final response = await ApiService.instance.put('/membres/${membre.id}', data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Mettre à jour le membre localement
|
||||
final updatedMembre = membre.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await saveMembre(updatedMembre);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Endpoint à adapter selon votre API
|
||||
final response = await _apiService.put('/membres/${membre.id}', data: membre.toJson());
|
||||
final membreData = response.data['membre'];
|
||||
|
||||
final updatedMembre = MembreModel.fromJson(membreData);
|
||||
await saveMembre(updatedMembre);
|
||||
|
||||
return updatedMembre;
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour du membre via l\'API: $e');
|
||||
return null;
|
||||
debugPrint('Erreur lors de la mise à jour du membre: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
@@ -217,25 +180,66 @@ class MembreRepository extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final hasConnection = await _apiService.hasInternetConnection();
|
||||
if (!hasConnection) {
|
||||
debugPrint('Pas de connexion Internet, impossible de supprimer le membre');
|
||||
return false;
|
||||
// Appeler l'API pour supprimer le membre
|
||||
final response = await ApiService.instance.delete('/membres/$id');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer le membre localement
|
||||
await deleteMembre(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Endpoint à adapter selon votre API
|
||||
await _apiService.delete('/membres/$id');
|
||||
|
||||
// Supprimer localement
|
||||
await deleteMembre(id);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la suppression du membre via l\'API: $e');
|
||||
debugPrint('Erreur lors de la suppression du membre: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Traitement des données de membres depuis l'API
|
||||
Future<void> processMembresData(dynamic membresData) async {
|
||||
try {
|
||||
debugPrint('Traitement des données des membres...');
|
||||
|
||||
// Vérifier que les données sont au bon format
|
||||
if (membresData == null) {
|
||||
debugPrint('Aucune donnée de membre à traiter');
|
||||
return;
|
||||
}
|
||||
|
||||
List<dynamic> membresList;
|
||||
if (membresData is List) {
|
||||
membresList = membresData;
|
||||
} else if (membresData is Map && membresData.containsKey('data')) {
|
||||
membresList = membresData['data'] as List<dynamic>;
|
||||
} else {
|
||||
debugPrint('Format de données de membres non reconnu');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouvelles données
|
||||
await _ensureBoxIsOpen();
|
||||
await _membreBox.clear();
|
||||
|
||||
// Traiter chaque membre
|
||||
int count = 0;
|
||||
for (final membreData in membresList) {
|
||||
try {
|
||||
final membre = MembreModel.fromJson(membreData);
|
||||
await _membreBox.put(membre.id, membre);
|
||||
count++;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'un membre: $e');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$count membres traités et stockés');
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des membres: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,20 @@ class OperationRepository extends ChangeNotifier {
|
||||
_ensureBoxIsOpen();
|
||||
return Hive.box<OperationModel>(AppKeys.operationsBoxName);
|
||||
}
|
||||
|
||||
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
final boxName = AppKeys.operationsBoxName;
|
||||
const boxName = AppKeys.operationsBoxName;
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName dans OperationRepository...');
|
||||
await Hive.openBox<OperationModel>(boxName);
|
||||
}
|
||||
}
|
||||
final ApiService _apiService;
|
||||
|
||||
bool _isLoading = false;
|
||||
|
||||
OperationRepository(this._apiService);
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
OperationRepository();
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
@@ -61,13 +61,11 @@ class OperationRepository extends ChangeNotifier {
|
||||
try {
|
||||
for (var operationData in operationsData) {
|
||||
final operationJson = operationData as Map<String, dynamic>;
|
||||
final operationId = operationJson['id'] is String
|
||||
? int.parse(operationJson['id'])
|
||||
: operationJson['id'] as int;
|
||||
|
||||
final operationId = operationJson['id'] is String ? int.parse(operationJson['id']) : operationJson['id'] as int;
|
||||
|
||||
// Vérifier si l'opération existe déjà
|
||||
OperationModel? existingOperation = getOperationById(operationId);
|
||||
|
||||
|
||||
if (existingOperation == null) {
|
||||
// Créer une nouvelle opération
|
||||
final newOperation = OperationModel.fromJson(operationJson);
|
||||
@@ -106,14 +104,12 @@ class OperationRepository extends ChangeNotifier {
|
||||
};
|
||||
|
||||
// Appeler l'API pour créer l'opération
|
||||
final response = await _apiService.post('/operations', data: data);
|
||||
|
||||
final response = await ApiService.instance.post('/operations', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Récupérer l'ID de la nouvelle opération
|
||||
final operationId = response.data['id'] is String
|
||||
? int.parse(response.data['id'])
|
||||
: response.data['id'] as int;
|
||||
|
||||
final operationId = response.data['id'] is String ? int.parse(response.data['id']) : response.data['id'] as int;
|
||||
|
||||
// Créer l'opération localement
|
||||
final newOperation = OperationModel(
|
||||
id: operationId,
|
||||
@@ -124,11 +120,11 @@ class OperationRepository extends ChangeNotifier {
|
||||
isActive: true,
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
|
||||
await saveOperation(newOperation);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création de l\'opération: $e');
|
||||
@@ -161,8 +157,8 @@ class OperationRepository extends ChangeNotifier {
|
||||
};
|
||||
|
||||
// Appeler l'API pour mettre à jour l'opération
|
||||
final response = await _apiService.put('/operations/$id', data: data);
|
||||
|
||||
final response = await ApiService.instance.put('/operations/$id', data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Mettre à jour l'opération localement
|
||||
final updatedOperation = existingOperation.copyWith(
|
||||
@@ -173,11 +169,11 @@ class OperationRepository extends ChangeNotifier {
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
|
||||
await saveOperation(updatedOperation);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'opération: $e');
|
||||
@@ -195,14 +191,14 @@ class OperationRepository extends ChangeNotifier {
|
||||
|
||||
try {
|
||||
// Appeler l'API pour supprimer l'opération
|
||||
final response = await _apiService.delete('/operations/$id');
|
||||
|
||||
final response = await ApiService.instance.delete('/operations/$id');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer l'opération localement
|
||||
await deleteOperation(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la suppression de l\'opération: $e');
|
||||
|
||||
@@ -1,344 +1,141 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:geosector_app/core/data/models/passage_model.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
|
||||
class PassageRepository extends ChangeNotifier {
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
PassageRepository();
|
||||
|
||||
// Utiliser un getter lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
// et vérifier qu'elle est ouverte avant accès
|
||||
Box<PassageModel>? _box;
|
||||
|
||||
Box<PassageModel> get _passageBox {
|
||||
if (_box != null && _box!.isOpen) {
|
||||
return _box!;
|
||||
}
|
||||
|
||||
if (!Hive.isBoxOpen(AppKeys.passagesBoxName)) {
|
||||
throw StateError(
|
||||
'La boîte ${AppKeys.passagesBoxName} n\'est pas ouverte. Appelez _ensureBoxIsOpen() avant d\'accéder à la boîte.');
|
||||
}
|
||||
|
||||
_box = Hive.box<PassageModel>(AppKeys.passagesBoxName);
|
||||
return _box!;
|
||||
_ensureBoxIsOpen();
|
||||
return Hive.box<PassageModel>(AppKeys.passagesBoxName);
|
||||
}
|
||||
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
final boxName = AppKeys.passagesBoxName;
|
||||
// Stream pour notifier des changements de passages
|
||||
StreamController<List<PassageModel>>? _passageStreamController;
|
||||
|
||||
// Si nous avons déjà une référence à la boîte et qu'elle est ouverte, retourner
|
||||
if (_box != null && _box!.isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Si la boîte est déjà ouverte, récupérer la référence
|
||||
if (Hive.isBoxOpen(boxName)) {
|
||||
_box = Hive.box<PassageModel>(boxName);
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte $boxName déjà ouverte, référence récupérée');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sinon, ouvrir la boîte
|
||||
try {
|
||||
debugPrint('PassageRepository: Ouverture de la boîte $boxName...');
|
||||
_box = await Hive.openBox<PassageModel>(boxName);
|
||||
debugPrint('PassageRepository: Boîte $boxName ouverte avec succès');
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'PassageRepository: ERREUR lors de l\'ouverture de la boîte $boxName: $e');
|
||||
rethrow; // Propager l'erreur pour permettre une gestion appropriée
|
||||
}
|
||||
// Getter pour le stream des passages
|
||||
Stream<List<PassageModel>> get passageStream {
|
||||
_passageStreamController ??= StreamController<List<PassageModel>>.broadcast();
|
||||
return _passageStreamController!.stream;
|
||||
}
|
||||
|
||||
final ApiService _apiService;
|
||||
@override
|
||||
void dispose() {
|
||||
_passageStreamController?.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// ID du secteur par défaut
|
||||
int defaultSectorId = 1;
|
||||
bool _isLoading = false;
|
||||
|
||||
PassageRepository(this._apiService);
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
List<PassageModel> get passages => getAllPassages();
|
||||
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
const boxName = AppKeys.passagesBoxName;
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName dans PassageRepository...');
|
||||
await Hive.openBox<PassageModel>(boxName);
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer tous les passages
|
||||
List<PassageModel> getAllPassages() {
|
||||
try {
|
||||
// S'assurer que la boîte est ouverte avant d'y accéder
|
||||
_ensureBoxIsOpen().then((_) {
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte ouverte avec succès pour getAllPassages');
|
||||
}).catchError((e) {
|
||||
debugPrint(
|
||||
'PassageRepository: Erreur lors de l\'ouverture de la boîte pour getAllPassages: $e');
|
||||
});
|
||||
|
||||
return _passageBox.values.toList();
|
||||
} catch (e) {
|
||||
debugPrint('PassageRepository: Erreur dans getAllPassages: $e');
|
||||
return []; // Retourner une liste vide en cas d'erreur
|
||||
}
|
||||
return _passageBox.values.toList();
|
||||
}
|
||||
|
||||
// Récupérer un passage par son ID
|
||||
PassageModel? getPassageById(int id) {
|
||||
try {
|
||||
// S'assurer que la boîte est ouverte avant d'y accéder
|
||||
_ensureBoxIsOpen().then((_) {
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte ouverte avec succès pour getPassageById');
|
||||
}).catchError((e) {
|
||||
debugPrint(
|
||||
'PassageRepository: Erreur lors de l\'ouverture de la boîte pour getPassageById: $e');
|
||||
});
|
||||
|
||||
return _passageBox.get(id);
|
||||
} catch (e) {
|
||||
debugPrint('PassageRepository: Erreur dans getPassageById: $e');
|
||||
return null;
|
||||
}
|
||||
return _passageBox.get(id);
|
||||
}
|
||||
|
||||
// Récupérer les passages par secteur
|
||||
List<PassageModel> getPassagesBySector(int sectorId) {
|
||||
try {
|
||||
// S'assurer que la boîte est ouverte avant d'y accéder
|
||||
_ensureBoxIsOpen().then((_) {
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte ouverte avec succès pour getPassagesBySector');
|
||||
}).catchError((e) {
|
||||
debugPrint(
|
||||
'PassageRepository: Erreur lors de l\'ouverture de la boîte pour getPassagesBySector: $e');
|
||||
});
|
||||
|
||||
return _passageBox.values
|
||||
.where((passage) => passage.fkSector == sectorId)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
debugPrint('PassageRepository: Erreur dans getPassagesBySector: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les passages par opération
|
||||
List<PassageModel> getPassagesByOperation(int operationId) {
|
||||
try {
|
||||
// S'assurer que la boîte est ouverte avant d'y accéder
|
||||
_ensureBoxIsOpen().then((_) {
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte ouverte avec succès pour getPassagesByOperation');
|
||||
}).catchError((e) {
|
||||
debugPrint(
|
||||
'PassageRepository: Erreur lors de l\'ouverture de la boîte pour getPassagesByOperation: $e');
|
||||
});
|
||||
|
||||
return _passageBox.values
|
||||
.where((passage) => passage.fkOperation == operationId)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
debugPrint('PassageRepository: Erreur dans getPassagesByOperation: $e');
|
||||
return [];
|
||||
}
|
||||
List<PassageModel> getPassagesBySectorId(int sectorId) {
|
||||
return _passageBox.values.where((passage) => passage.fkSector == sectorId).toList();
|
||||
}
|
||||
|
||||
// Récupérer les passages par type
|
||||
List<PassageModel> getPassagesByType(int typeId) {
|
||||
try {
|
||||
// S'assurer que la boîte est ouverte avant d'y accéder
|
||||
_ensureBoxIsOpen().then((_) {
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte ouverte avec succès pour getPassagesByType');
|
||||
}).catchError((e) {
|
||||
debugPrint(
|
||||
'PassageRepository: Erreur lors de l\'ouverture de la boîte pour getPassagesByType: $e');
|
||||
});
|
||||
|
||||
return _passageBox.values
|
||||
.where((passage) => passage.fkType == typeId)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
debugPrint('PassageRepository: Erreur dans getPassagesByType: $e');
|
||||
return [];
|
||||
}
|
||||
List<PassageModel> getPassagesByType(int type) {
|
||||
return _passageBox.values.where((passage) => passage.fkType == type).toList();
|
||||
}
|
||||
|
||||
// Récupérer les passages par type de règlement
|
||||
List<PassageModel> getPassagesByPaymentType(int paymentTypeId) {
|
||||
try {
|
||||
// S'assurer que la boîte est ouverte avant d'y accéder
|
||||
_ensureBoxIsOpen().then((_) {
|
||||
debugPrint(
|
||||
'PassageRepository: Boîte ouverte avec succès pour getPassagesByPaymentType');
|
||||
}).catchError((e) {
|
||||
debugPrint(
|
||||
'PassageRepository: Erreur lors de l\'ouverture de la boîte pour getPassagesByPaymentType: $e');
|
||||
});
|
||||
|
||||
return _passageBox.values
|
||||
.where((passage) => passage.fkTypeReglement == paymentTypeId)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
debugPrint('PassageRepository: Erreur dans getPassagesByPaymentType: $e');
|
||||
return [];
|
||||
}
|
||||
// Récupérer les passages par date
|
||||
List<PassageModel> getPassagesByDate(DateTime date) {
|
||||
return _passageBox.values.where((passage) {
|
||||
final passageDate = DateTime(
|
||||
passage.passedAt.year,
|
||||
passage.passedAt.month,
|
||||
passage.passedAt.day,
|
||||
);
|
||||
final searchDate = DateTime(date.year, date.month, date.day);
|
||||
return passageDate.isAtSameMomentAs(searchDate);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Sauvegarder un passage
|
||||
Future<void> savePassage(PassageModel passage) async {
|
||||
await _passageBox.put(passage.id, passage);
|
||||
notifyListeners();
|
||||
_notifyPassageStream();
|
||||
}
|
||||
|
||||
// Sauvegarder plusieurs passages
|
||||
Future<void> savePassages(List<PassageModel> passages) async {
|
||||
for (final passage in passages) {
|
||||
await _passageBox.put(passage.id, passage);
|
||||
}
|
||||
notifyListeners();
|
||||
_notifyPassageStream();
|
||||
}
|
||||
|
||||
// Supprimer un passage
|
||||
Future<void> deletePassage(int id) async {
|
||||
await _passageBox.delete(id);
|
||||
notifyListeners();
|
||||
_notifyPassageStream();
|
||||
}
|
||||
|
||||
// Traiter les passages reçus de l'API
|
||||
Future<void> processPassagesFromApi(List<dynamic> passagesData) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
for (var passageData in passagesData) {
|
||||
final passageJson = passageData as Map<String, dynamic>;
|
||||
final passageId = passageJson['id'] is String
|
||||
? int.parse(passageJson['id'])
|
||||
: passageJson['id'] as int;
|
||||
|
||||
// Vérifier si le passage existe déjà
|
||||
PassageModel? existingPassage = getPassageById(passageId);
|
||||
|
||||
if (existingPassage == null) {
|
||||
// Créer un nouveau passage
|
||||
final newPassage = PassageModel.fromJson(passageJson);
|
||||
await savePassage(newPassage);
|
||||
} else {
|
||||
// Mettre à jour le passage existant avec les nouvelles données
|
||||
final updatedPassage = PassageModel.fromJson(passageJson).copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isActive: existingPassage.isActive,
|
||||
isSynced: true,
|
||||
);
|
||||
await savePassage(updatedPassage);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des passages: $e');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
// Notifier le stream des changements
|
||||
void _notifyPassageStream() {
|
||||
_passageStreamController?.add(getAllPassages());
|
||||
}
|
||||
|
||||
// Créer un nouveau passage
|
||||
Future<bool> createPassage({
|
||||
required int fkOperation,
|
||||
required int fkSector,
|
||||
required int fkUser,
|
||||
required int fkType,
|
||||
required String fkAdresse,
|
||||
required DateTime passedAt,
|
||||
required String numero,
|
||||
required String rue,
|
||||
String rueBis = '',
|
||||
required String ville,
|
||||
String residence = '',
|
||||
required int fkHabitat,
|
||||
String appt = '',
|
||||
String niveau = '',
|
||||
required String gpsLat,
|
||||
required String gpsLng,
|
||||
String nomRecu = '',
|
||||
String remarque = '',
|
||||
required String montant,
|
||||
required int fkTypeReglement,
|
||||
String name = '',
|
||||
String email = '',
|
||||
String phone = '',
|
||||
}) async {
|
||||
// Créer un passage via l'API
|
||||
Future<bool> createPassage(PassageModel passage) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
final Map<String, dynamic> data = {
|
||||
'fk_operation': fkOperation,
|
||||
'fk_sector': fkSector,
|
||||
'fk_user': fkUser,
|
||||
'fk_type': fkType,
|
||||
'fk_adresse': fkAdresse,
|
||||
'passed_at': passedAt.toIso8601String(),
|
||||
'numero': numero,
|
||||
'rue': rue,
|
||||
'rue_bis': rueBis,
|
||||
'ville': ville,
|
||||
'residence': residence,
|
||||
'fk_habitat': fkHabitat,
|
||||
'appt': appt,
|
||||
'niveau': niveau,
|
||||
'gps_lat': gpsLat,
|
||||
'gps_lng': gpsLng,
|
||||
'nom_recu': nomRecu,
|
||||
'remarque': remarque,
|
||||
'montant': montant,
|
||||
'fk_type_reglement': fkTypeReglement,
|
||||
'name': name,
|
||||
'email': email,
|
||||
'phone': phone,
|
||||
};
|
||||
final data = passage.toJson();
|
||||
|
||||
// Appeler l'API pour créer le passage
|
||||
final response = await _apiService.post('/passages', data: data);
|
||||
final response = await ApiService.instance.post('/passages', data: data);
|
||||
|
||||
if (response.statusCode == 201 || response.statusCode == 200) {
|
||||
// Récupérer l'ID du nouveau passage
|
||||
final passageId = response.data['id'] is String
|
||||
? int.parse(response.data['id'])
|
||||
: response.data['id'] as int;
|
||||
final passageId = response.data['id'] is String ? int.parse(response.data['id']) : response.data['id'] as int;
|
||||
|
||||
// Créer le modèle local
|
||||
final newPassage = PassageModel(
|
||||
// Créer le passage localement avec l'ID retourné par l'API
|
||||
final newPassage = passage.copyWith(
|
||||
id: passageId,
|
||||
fkOperation: fkOperation,
|
||||
fkSector: fkSector,
|
||||
fkUser: fkUser,
|
||||
fkType: fkType,
|
||||
fkAdresse: fkAdresse,
|
||||
passedAt: passedAt,
|
||||
numero: numero,
|
||||
rue: rue,
|
||||
rueBis: rueBis,
|
||||
ville: ville,
|
||||
residence: residence,
|
||||
fkHabitat: fkHabitat,
|
||||
appt: appt,
|
||||
niveau: niveau,
|
||||
gpsLat: gpsLat,
|
||||
gpsLng: gpsLng,
|
||||
nomRecu: nomRecu,
|
||||
remarque: remarque,
|
||||
montant: montant,
|
||||
fkTypeReglement: fkTypeReglement,
|
||||
nbPassages: 1, // Par défaut pour un nouveau passage
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isActive: true,
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await savePassage(newPassage);
|
||||
return true;
|
||||
} else {
|
||||
debugPrint(
|
||||
'Erreur lors de la création du passage: ${response.statusMessage}');
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la création du passage: $e');
|
||||
return false;
|
||||
@@ -348,51 +145,30 @@ class PassageRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour un passage existant
|
||||
// Mettre à jour un passage via l'API
|
||||
Future<bool> updatePassage(PassageModel passage) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
final Map<String, dynamic> data = passage.toJson();
|
||||
final data = passage.toJson();
|
||||
|
||||
// Appeler l'API pour mettre à jour le passage
|
||||
final response =
|
||||
await _apiService.put('/passages/${passage.id}', data: data);
|
||||
|
||||
final response = await ApiService.instance.put('/passages/${passage.id}', data: data);
|
||||
if (response.statusCode == 200) {
|
||||
// Mettre à jour le modèle local
|
||||
// Mettre à jour le passage localement
|
||||
final updatedPassage = passage.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
);
|
||||
|
||||
await savePassage(updatedPassage);
|
||||
return true;
|
||||
} else {
|
||||
debugPrint(
|
||||
'Erreur lors de la mise à jour du passage: ${response.statusMessage}');
|
||||
|
||||
// Marquer comme non synchronisé mais sauvegarder localement
|
||||
final updatedPassage = passage.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: false,
|
||||
);
|
||||
|
||||
await savePassage(updatedPassage);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour du passage: $e');
|
||||
|
||||
// Marquer comme non synchronisé mais sauvegarder localement
|
||||
final updatedPassage = passage.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: false,
|
||||
);
|
||||
|
||||
await savePassage(updatedPassage);
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
@@ -400,103 +176,107 @@ class PassageRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Synchroniser tous les passages non synchronisés
|
||||
Future<void> syncUnsyncedPassages() async {
|
||||
// Supprimer un passage via l'API
|
||||
Future<bool> deletePassageViaApi(int id) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final hasConnection = await _apiService.hasInternetConnection();
|
||||
// Appeler l'API pour supprimer le passage
|
||||
final response = await ApiService.instance.delete('/passages/$id');
|
||||
|
||||
if (!hasConnection) {
|
||||
return;
|
||||
if (response.statusCode == 200 || response.statusCode == 204) {
|
||||
// Supprimer le passage localement
|
||||
await deletePassage(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
final unsyncedPassages =
|
||||
_passageBox.values.where((passage) => !passage.isSynced).toList();
|
||||
|
||||
if (unsyncedPassages.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
for (final passage in unsyncedPassages) {
|
||||
try {
|
||||
if (passage.id < 0) {
|
||||
// Nouveau passage créé localement, à envoyer à l'API
|
||||
await createPassage(
|
||||
fkOperation: passage.fkOperation,
|
||||
fkSector: passage.fkSector,
|
||||
fkUser: passage.fkUser,
|
||||
fkType: passage.fkType,
|
||||
fkAdresse: passage.fkAdresse,
|
||||
passedAt: passage.passedAt,
|
||||
numero: passage.numero,
|
||||
rue: passage.rue,
|
||||
rueBis: passage.rueBis,
|
||||
ville: passage.ville,
|
||||
residence: passage.residence,
|
||||
fkHabitat: passage.fkHabitat,
|
||||
appt: passage.appt,
|
||||
niveau: passage.niveau,
|
||||
gpsLat: passage.gpsLat,
|
||||
gpsLng: passage.gpsLng,
|
||||
nomRecu: passage.nomRecu,
|
||||
remarque: passage.remarque,
|
||||
montant: passage.montant,
|
||||
fkTypeReglement: passage.fkTypeReglement,
|
||||
name: passage.name,
|
||||
email: passage.email,
|
||||
phone: passage.phone,
|
||||
);
|
||||
|
||||
// Supprimer l'ancien passage avec ID temporaire
|
||||
await deletePassage(passage.id);
|
||||
} else {
|
||||
// Passage existant à mettre à jour
|
||||
await updatePassage(passage);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'Erreur lors de la synchronisation du passage ${passage.id}: $e');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la synchronisation des passages: $e');
|
||||
debugPrint('Erreur lors de la suppression du passage: $e');
|
||||
return false;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les passages depuis l'API
|
||||
Future<void> fetchPassages() async {
|
||||
// Traitement des données de passages depuis l'API
|
||||
Future<void> processPassagesData(dynamic passagesData) async {
|
||||
try {
|
||||
final hasConnection = await _apiService.hasInternetConnection();
|
||||
debugPrint('Traitement des données des passages...');
|
||||
|
||||
if (!hasConnection) {
|
||||
// Vérifier que les données sont au bon format
|
||||
if (passagesData == null) {
|
||||
debugPrint('Aucune donnée de passage à traiter');
|
||||
return;
|
||||
}
|
||||
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
final response = await _apiService.get('/passages');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> passagesData = response.data;
|
||||
await processPassagesFromApi(passagesData);
|
||||
List<dynamic> passagesList;
|
||||
if (passagesData is List) {
|
||||
passagesList = passagesData;
|
||||
} else if (passagesData is Map && passagesData.containsKey('data')) {
|
||||
passagesList = passagesData['data'] as List<dynamic>;
|
||||
} else {
|
||||
debugPrint('Format de données de passages non reconnu');
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des passages: $e');
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouvelles données
|
||||
await _passageBox.clear();
|
||||
|
||||
// Traiter chaque passage
|
||||
int count = 0;
|
||||
for (final passageData in passagesList) {
|
||||
try {
|
||||
final passage = PassageModel.fromJson(passageData);
|
||||
await _passageBox.put(passage.id, passage);
|
||||
count++;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'un passage: $e');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$count passages traités et stockés');
|
||||
notifyListeners();
|
||||
_notifyPassageStream();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des passages: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Synchroniser tous les passages non synchronisés
|
||||
Future<void> syncUnsyncedPassages() async {
|
||||
final unsyncedPassages = _passageBox.values.where((passage) => !passage.isSynced).toList();
|
||||
|
||||
for (final passage in unsyncedPassages) {
|
||||
try {
|
||||
await updatePassage(passage);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la synchronisation du passage ${passage.id}: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Statistiques
|
||||
Map<String, int> getPassageStatistics() {
|
||||
final allPassages = getAllPassages();
|
||||
|
||||
return {
|
||||
'total': allPassages.length,
|
||||
'effectues': allPassages.where((p) => p.fkType == 1).length,
|
||||
'a_finaliser': allPassages.where((p) => p.fkType == 2).length,
|
||||
'refuses': allPassages.where((p) => p.fkType == 3).length,
|
||||
'dons': allPassages.where((p) => p.fkType == 4).length,
|
||||
'lots': allPassages.where((p) => p.fkType == 5).length,
|
||||
'maisons_vides': allPassages.where((p) => p.fkType == 6).length,
|
||||
};
|
||||
}
|
||||
|
||||
// Vider tous les passages
|
||||
Future<void> clearAllPassages() async {
|
||||
await _passageBox.clear();
|
||||
notifyListeners();
|
||||
_notifyPassageStream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +1,186 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:geosector_app/core/data/models/sector_model.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
|
||||
class SectorRepository {
|
||||
final ApiService _apiService;
|
||||
|
||||
SectorRepository(this._apiService);
|
||||
|
||||
class SectorRepository extends ChangeNotifier {
|
||||
// Constructeur sans paramètres - utilise ApiService.instance
|
||||
SectorRepository();
|
||||
// Utiliser un getter lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
// et vérifier qu'elle est ouverte avant accès
|
||||
Box<SectorModel> get _sectorsBox {
|
||||
Box<SectorModel> get _sectorBox {
|
||||
_ensureBoxIsOpen();
|
||||
return Hive.box<SectorModel>(AppKeys.sectorsBoxName);
|
||||
}
|
||||
|
||||
|
||||
// Constante pour l'ID par défaut
|
||||
static const int defaultSectorId = 1;
|
||||
|
||||
// Méthode pour vérifier si la boîte est ouverte et l'ouvrir si nécessaire
|
||||
Future<void> _ensureBoxIsOpen() async {
|
||||
final boxName = AppKeys.sectorsBoxName;
|
||||
debugPrint('SectorRepository: Vérification de l\'ouverture de la boîte ${AppKeys.sectorsBoxName}...');
|
||||
const boxName = AppKeys.sectorsBoxName;
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
print('Ouverture de la boîte $boxName dans SectorRepository...');
|
||||
debugPrint('Ouverture de la boîte $boxName dans SectorRepository...');
|
||||
await Hive.openBox<SectorModel>(boxName);
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer tous les secteurs depuis la base de données locale
|
||||
|
||||
// Récupérer tous les secteurs
|
||||
List<SectorModel> getAllSectors() {
|
||||
return _sectorsBox.values.toList();
|
||||
return _sectorBox.values.toList();
|
||||
}
|
||||
|
||||
|
||||
// Récupérer un secteur par son ID
|
||||
SectorModel? getSectorById(int id) {
|
||||
try {
|
||||
return _sectorsBox.values.firstWhere(
|
||||
(sector) => sector.id == id,
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
return _sectorBox.get(id);
|
||||
}
|
||||
|
||||
|
||||
// Sauvegarder un secteur
|
||||
Future<void> saveSector(SectorModel sector) async {
|
||||
await _sectorBox.put(sector.id, sector);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Supprimer un secteur
|
||||
Future<void> deleteSector(int id) async {
|
||||
await _sectorBox.delete(id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Sauvegarder les secteurs dans la base de données locale
|
||||
Future<void> saveSectors(List<SectorModel> sectors) async {
|
||||
// Vider la box avant d'ajouter les nouveaux secteurs
|
||||
await _sectorsBox.clear();
|
||||
|
||||
await _sectorBox.clear();
|
||||
|
||||
// Ajouter les nouveaux secteurs
|
||||
for (final sector in sectors) {
|
||||
await _sectorsBox.put(sector.id, sector);
|
||||
await _sectorBox.put(sector.id, sector);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Traitement des données de secteurs depuis l'API
|
||||
Future<void> processSectorsData(dynamic sectorsData) async {
|
||||
try {
|
||||
debugPrint('Traitement des données des secteurs...');
|
||||
|
||||
// Vérifier que les données sont au bon format
|
||||
if (sectorsData == null) {
|
||||
debugPrint('Aucune donnée de secteur à traiter');
|
||||
return;
|
||||
}
|
||||
|
||||
List<dynamic> sectorsList;
|
||||
if (sectorsData is List) {
|
||||
sectorsList = sectorsData;
|
||||
} else if (sectorsData is Map && sectorsData.containsKey('data')) {
|
||||
sectorsList = sectorsData['data'] as List<dynamic>;
|
||||
} else {
|
||||
debugPrint('Format de données de secteurs non reconnu');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouvelles données
|
||||
await _sectorBox.clear();
|
||||
|
||||
// Traiter chaque secteur
|
||||
int count = 0;
|
||||
for (final sectorData in sectorsList) {
|
||||
try {
|
||||
final sector = SectorModel.fromJson(sectorData);
|
||||
await _sectorBox.put(sector.id, sector);
|
||||
count++;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'un secteur: $e');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$count secteurs traités et stockés');
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des secteurs: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter ou mettre à jour un secteur
|
||||
Future<void> saveSector(SectorModel sector) async {
|
||||
await _sectorsBox.put(sector.id, sector);
|
||||
}
|
||||
|
||||
// Supprimer un secteur
|
||||
Future<void> deleteSector(int id) async {
|
||||
await _sectorsBox.delete(id);
|
||||
}
|
||||
|
||||
|
||||
// Récupérer les secteurs depuis l'API
|
||||
Future<List<SectorModel>> fetchSectorsFromApi() async {
|
||||
try {
|
||||
final response = await _apiService.get(AppKeys.sectorsEndpoint);
|
||||
final response = await ApiService.instance.get(AppKeys.sectorsEndpoint);
|
||||
final Map<String, dynamic> responseData = response as Map<String, dynamic>;
|
||||
|
||||
|
||||
if (responseData['status'] == 'success' && responseData['sectors'] != null) {
|
||||
final List<dynamic> sectorsJson = responseData['sectors'];
|
||||
final List<SectorModel> sectors = sectorsJson
|
||||
.map((json) => SectorModel.fromJson(json))
|
||||
.toList();
|
||||
|
||||
final List<SectorModel> sectors = sectorsJson.map((json) => SectorModel.fromJson(json)).toList();
|
||||
|
||||
// Sauvegarder les secteurs localement
|
||||
await saveSectors(sectors);
|
||||
|
||||
return sectors;
|
||||
}
|
||||
|
||||
|
||||
return [];
|
||||
} catch (e) {
|
||||
// En cas d'erreur, retourner les secteurs locaux
|
||||
return getAllSectors();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Créer un nouveau secteur via l'API
|
||||
Future<SectorModel?> createSector(SectorModel sector) async {
|
||||
try {
|
||||
final response = await _apiService.post(
|
||||
final response = await ApiService.instance.post(
|
||||
AppKeys.sectorsEndpoint,
|
||||
data: sector.toJson(),
|
||||
);
|
||||
final Map<String, dynamic> responseData = response as Map<String, dynamic>;
|
||||
|
||||
|
||||
if (responseData['status'] == 'success' && responseData['sector'] != null) {
|
||||
final SectorModel newSector = SectorModel.fromJson(responseData['sector']);
|
||||
await saveSector(newSector);
|
||||
return newSector;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mettre à jour un secteur via l'API
|
||||
Future<SectorModel?> updateSector(SectorModel sector) async {
|
||||
try {
|
||||
final response = await _apiService.put(
|
||||
final response = await ApiService.instance.put(
|
||||
'${AppKeys.sectorsEndpoint}/${sector.id}',
|
||||
data: sector.toJson(),
|
||||
);
|
||||
final Map<String, dynamic> responseData = response as Map<String, dynamic>;
|
||||
|
||||
|
||||
if (responseData['status'] == 'success' && responseData['sector'] != null) {
|
||||
final SectorModel updatedSector = SectorModel.fromJson(responseData['sector']);
|
||||
await saveSector(updatedSector);
|
||||
return updatedSector;
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Supprimer un secteur via l'API
|
||||
Future<bool> deleteSectorFromApi(int id) async {
|
||||
try {
|
||||
final response = await _apiService.delete(
|
||||
final response = await ApiService.instance.delete(
|
||||
'${AppKeys.sectorsEndpoint}/$id',
|
||||
);
|
||||
final Map<String, dynamic> responseData = response as Map<String, dynamic>;
|
||||
|
||||
|
||||
if (responseData['status'] == 'success') {
|
||||
await deleteSector(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user