Initialisation du projet geosector complet (web + flutter)

This commit is contained in:
d6soft
2025-05-01 18:59:27 +02:00
commit b5aafc424b
244 changed files with 37296 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
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:geosector_app/core/data/models/membre_model.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);
final ApiService _apiService;
bool _isLoading = false;
MembreRepository(this._apiService);
// Getters
bool get isLoading => _isLoading;
List<MembreModel> get membres => getAllMembres();
// Méthode pour vérifier si une 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');
}
}
// Récupérer tous les membres
List<MembreModel> getAllMembres() {
try {
return _membreBox.values.toList();
} catch (e) {
debugPrint('Erreur lors de la récupération des membres: $e');
return [];
}
}
// Récupérer un membre par son ID
MembreModel? getMembreById(int id) {
try {
return _membreBox.get(id);
} catch (e) {
debugPrint('Erreur lors de la récupération du membre: $e');
return null;
}
}
// Créer ou mettre à jour un membre
Future<MembreModel> saveMembre(MembreModel membre) async {
await _ensureBoxIsOpen();
await _membreBox.put(membre.id, membre);
notifyListeners();
return membre;
}
// Supprimer un membre
Future<void> deleteMembre(int id) async {
await _ensureBoxIsOpen();
await _membreBox.delete(id);
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 {
_isLoading = true;
notifyListeners();
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
debugPrint('Pas de connexion Internet, impossible de créer le membre');
return null;
}
// 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;
} catch (e) {
debugPrint('Erreur lors de la création du membre via l\'API: $e');
return null;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Mettre à jour un membre via l'API
Future<MembreModel?> updateMembreViaApi(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;
}
// 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;
} catch (e) {
debugPrint('Erreur lors de la mise à jour du membre via l\'API: $e');
return null;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Supprimer un membre via l'API
Future<bool> deleteMembreViaApi(int id) async {
_isLoading = true;
notifyListeners();
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
debugPrint(
'Pas de connexion Internet, impossible de supprimer le membre');
return false;
}
// Endpoint à adapter selon votre API
await _apiService.delete('/membres/$id');
// Supprimer localement
await deleteMembre(id);
return true;
} catch (e) {
debugPrint('Erreur lors de la suppression du membre via l\'API: $e');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
}

View File

@@ -0,0 +1,215 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:geosector_app/core/data/models/operation_model.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
class OperationRepository extends ChangeNotifier {
// 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<OperationModel> get _operationBox {
_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;
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);
// Getters
bool get isLoading => _isLoading;
List<OperationModel> get operations => getAllOperations();
// Récupérer toutes les opérations
List<OperationModel> getAllOperations() {
return _operationBox.values.toList();
}
// Récupérer une opération par son ID
OperationModel? getOperationById(int id) {
return _operationBox.get(id);
}
// Sauvegarder une opération
Future<void> saveOperation(OperationModel operation) async {
await _operationBox.put(operation.id, operation);
notifyListeners();
}
// Supprimer une opération
Future<void> deleteOperation(int id) async {
await _operationBox.delete(id);
notifyListeners();
}
// Créer ou mettre à jour des opérations à partir des données de l'API
Future<void> processOperationsFromApi(List<dynamic> operationsData) async {
_isLoading = true;
notifyListeners();
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;
// 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);
await saveOperation(newOperation);
} else {
// Mettre à jour l'opération existante
final updatedOperation = existingOperation.copyWith(
name: operationJson['name'],
dateDebut: DateTime.parse(operationJson['date_deb']),
dateFin: DateTime.parse(operationJson['date_fin']),
lastSyncedAt: DateTime.now(),
isSynced: true,
);
await saveOperation(updatedOperation);
}
}
} catch (e) {
debugPrint('Erreur lors du traitement des opérations: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
// Créer une opération
Future<bool> createOperation(String name, DateTime dateDebut, DateTime dateFin) async {
_isLoading = true;
notifyListeners();
try {
// Préparer les données pour l'API
final Map<String, dynamic> data = {
'name': name,
'date_deb': dateDebut.toIso8601String().split('T')[0], // Format YYYY-MM-DD
'date_fin': dateFin.toIso8601String().split('T')[0], // Format YYYY-MM-DD
};
// Appeler l'API pour créer l'opération
final response = await _apiService.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;
// Créer l'opération localement
final newOperation = OperationModel(
id: operationId,
name: name,
dateDebut: dateDebut,
dateFin: dateFin,
lastSyncedAt: DateTime.now(),
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');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Mettre à jour une opération
Future<bool> updateOperation(int id, {String? name, DateTime? dateDebut, DateTime? dateFin, bool? isActive}) async {
_isLoading = true;
notifyListeners();
try {
// Récupérer l'opération existante
final existingOperation = getOperationById(id);
if (existingOperation == null) {
return false;
}
// Préparer les données pour l'API
final Map<String, dynamic> data = {
'id': id,
'name': name ?? existingOperation.name,
'date_deb': (dateDebut ?? existingOperation.dateDebut).toIso8601String().split('T')[0],
'date_fin': (dateFin ?? existingOperation.dateFin).toIso8601String().split('T')[0],
'is_active': isActive ?? existingOperation.isActive,
};
// Appeler l'API pour mettre à jour l'opération
final response = await _apiService.put('/operations/$id', data: data);
if (response.statusCode == 200) {
// Mettre à jour l'opération localement
final updatedOperation = existingOperation.copyWith(
name: name,
dateDebut: dateDebut,
dateFin: dateFin,
isActive: isActive,
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');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Supprimer une opération via l'API
Future<bool> deleteOperationViaApi(int id) async {
_isLoading = true;
notifyListeners();
try {
// Appeler l'API pour supprimer l'opération
final response = await _apiService.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');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
}

View File

@@ -0,0 +1,381 @@
import 'dart:async';
import 'package:flutter/material.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 {
// 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> get _passageBox {
_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;
if (!Hive.isBoxOpen(boxName)) {
debugPrint('Ouverture de la boîte $boxName dans PassageRepository...');
await Hive.openBox<PassageModel>(boxName);
}
}
final ApiService _apiService;
bool _isLoading = false;
PassageRepository(this._apiService);
// Getters
bool get isLoading => _isLoading;
List<PassageModel> get passages => getAllPassages();
// Récupérer tous les passages
List<PassageModel> getAllPassages() {
return _passageBox.values.toList();
}
// Récupérer un passage par son ID
PassageModel? getPassageById(int id) {
return _passageBox.get(id);
}
// Récupérer les passages par secteur
List<PassageModel> getPassagesBySector(int sectorId) {
return _passageBox.values
.where((passage) => passage.fkSector == sectorId)
.toList();
}
// Récupérer les passages par opération
List<PassageModel> getPassagesByOperation(int operationId) {
return _passageBox.values
.where((passage) => passage.fkOperation == operationId)
.toList();
}
// Récupérer les passages par type
List<PassageModel> getPassagesByType(int typeId) {
return _passageBox.values
.where((passage) => passage.fkType == typeId)
.toList();
}
// Récupérer les passages par type de règlement
List<PassageModel> getPassagesByPaymentType(int paymentTypeId) {
return _passageBox.values
.where((passage) => passage.fkTypeReglement == paymentTypeId)
.toList();
}
// Sauvegarder un passage
Future<void> savePassage(PassageModel passage) async {
await _passageBox.put(passage.id, passage);
notifyListeners();
}
// Supprimer un passage
Future<void> deletePassage(int id) async {
await _passageBox.delete(id);
notifyListeners();
}
// 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();
}
}
// 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 {
_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,
};
// Appeler l'API pour créer le passage
final response = await _apiService.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;
// Créer le modèle local
final newPassage = PassageModel(
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;
}
} catch (e) {
debugPrint('Erreur lors de la création du passage: $e');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Mettre à jour un passage existant
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();
// Appeler l'API pour mettre à jour le passage
final response = await _apiService.put('/passages/${passage.id}', data: data);
if (response.statusCode == 200) {
// Mettre à jour le modèle local
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;
}
} 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;
notifyListeners();
}
}
// Synchroniser tous les passages non synchronisés
Future<void> syncUnsyncedPassages() async {
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
return;
}
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');
}
}
} catch (e) {
debugPrint('Erreur lors de la synchronisation des passages: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
// Récupérer les passages depuis l'API
Future<void> fetchPassages() async {
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
return;
}
_isLoading = true;
notifyListeners();
final response = await _apiService.get('/passages');
if (response.statusCode == 200) {
final List<dynamic> passagesData = response.data;
await processPassagesFromApi(passagesData);
}
} catch (e) {
debugPrint('Erreur lors de la récupération des passages: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
// Vider tous les passages
Future<void> clearAllPassages() async {
await _passageBox.clear();
notifyListeners();
}
}

View File

@@ -0,0 +1,149 @@
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);
// 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 {
_ensureBoxIsOpen();
return Hive.box<SectorModel>(AppKeys.sectorsBoxName);
}
// 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;
if (!Hive.isBoxOpen(boxName)) {
print('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
List<SectorModel> getAllSectors() {
return _sectorsBox.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;
}
}
// 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();
// Ajouter les nouveaux secteurs
for (final sector in sectors) {
await _sectorsBox.put(sector.id, sector);
}
}
// 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 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();
// 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(
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(
'${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(
'${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;
}
}
}

View File

@@ -0,0 +1,958 @@
import 'dart:async';
import 'dart:io';
import 'dart:js' as js;
import 'package:geosector_app/core/services/hive_web_fix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/services/api_service.dart';
import 'package:geosector_app/core/services/sync_service.dart';
import 'package:geosector_app/core/data/models/user_model.dart';
import 'package:geosector_app/core/data/models/operation_model.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/membre_model.dart';
import 'package:geosector_app/core/repositories/operation_repository.dart';
import 'package:geosector_app/core/repositories/sector_repository.dart';
import 'package:geosector_app/core/repositories/passage_repository.dart';
import 'package:geosector_app/chat/models/conversation_model.dart';
import 'package:geosector_app/chat/models/message_model.dart';
class UserRepository extends ChangeNotifier {
// Utilisation de getters lazy pour n'accéder aux boîtes que lorsque nécessaire
Box<UserModel> get _userBox => Hive.box<UserModel>(AppKeys.usersBoxName);
// Getters pour les autres boîtes qui vérifient si elles sont ouvertes avant accès
Box<OperationModel> get _operationBox {
_ensureBoxIsOpen(AppKeys.operationsBoxName);
return Hive.box<OperationModel>(AppKeys.operationsBoxName);
}
Box<SectorModel> get _sectorBox {
_ensureBoxIsOpen(AppKeys.sectorsBoxName);
return Hive.box<SectorModel>(AppKeys.sectorsBoxName);
}
// Méthode pour initialiser les boîtes après connexion
Future<void> _initializeBoxes() async {
debugPrint('Initialisation des boîtes Hive nécessaires...');
await _ensureBoxIsOpen(AppKeys.operationsBoxName);
await _ensureBoxIsOpen(AppKeys.sectorsBoxName);
await _ensureBoxIsOpen(AppKeys.passagesBoxName);
await _ensureBoxIsOpen(AppKeys.membresBoxName);
// Les boîtes de chat sont déjà initialisées au démarrage
await _ensureBoxIsOpen(AppKeys.chatConversationsBoxName);
await _ensureBoxIsOpen(AppKeys.chatMessagesBoxName);
debugPrint('Toutes les boîtes Hive sont maintenant ouvertes');
}
final ApiService _apiService;
final SyncService? _syncService;
final OperationRepository? _operationRepository;
final SectorRepository? _sectorRepository;
final PassageRepository? _passageRepository;
bool _isLoading = false;
UserRepository(this._apiService,
{SyncService? syncService,
OperationRepository? operationRepository,
SectorRepository? sectorRepository,
PassageRepository? passageRepository})
: _syncService = syncService,
_operationRepository = operationRepository,
_sectorRepository = sectorRepository,
_passageRepository = passageRepository {
// Initialiser la session si un utilisateur est déjà connecté
final currentUser = getCurrentUser();
if (currentUser != null && currentUser.sessionId != null) {
setSessionId(currentUser.sessionId);
}
}
// Getters
bool get isLoading => _isLoading;
bool get isLoggedIn => getCurrentUser() != null;
// Vérifie si l'utilisateur a un rôle administrateur (2, 4 ou 9)
bool isAdmin() {
final user = getCurrentUser();
if (user == null) return false;
final String interface = user.interface ?? 'user';
return interface == 'admin';
}
int? get userId => getCurrentUser()?.id;
UserModel? get currentUser => getCurrentUser();
// Récupérer l'utilisateur actuellement connecté
UserModel? getCurrentUser() {
try {
// Chercher un utilisateur avec une session active
final activeUsers = _userBox.values
.where((user) =>
user.sessionId != null && // Vérifier que sessionId n'est pas null
user.sessionId!
.isNotEmpty && // Vérifier que sessionId n'est pas vide
user.sessionExpiry != null &&
user.sessionExpiry!.isAfter(DateTime.now()))
.toList();
return activeUsers.isNotEmpty ? activeUsers.first : null;
} catch (e) {
debugPrint('Erreur lors de la récupération de l\'utilisateur actuel: $e');
return null;
}
}
// Mettre à jour le chemin de la page actuelle pour l'utilisateur connecté
Future<void> updateLastPath(String path) async {
final currentUser = getCurrentUser();
if (currentUser != null) {
final updatedUser = currentUser.copyWith(lastPath: path);
await saveUser(updatedUser);
}
}
// Récupérer le dernier chemin visité par l'utilisateur
String? getLastPath() {
final currentUser = getCurrentUser();
return currentUser?.lastPath;
}
// Configurer la session dans l'API
void setSessionId(String? sessionId) {
_apiService.setSessionId(sessionId);
}
// Login API PHP
Future<Map<String, dynamic>> loginAPI(String username, String password,
{String type = 'admin'}) async {
try {
return await _apiService.login(username, password, type: type);
} catch (e) {
debugPrint('Erreur login API: $e');
rethrow;
}
}
// Register API PHP - Uniquement pour les administrateurs
Future<Map<String, dynamic>> registerAPI(String email, String name,
String amicaleName, String postalCode, String cityName) async {
try {
final Map<String, dynamic> data = {
'email': email,
'name': name,
'amicale_name': amicaleName,
'postal_code': postalCode,
'city_name': cityName
};
final response =
await _apiService.post(AppKeys.registerEndpoint, data: data);
return response.data;
} catch (e) {
debugPrint('Erreur register API: $e');
rethrow;
}
}
// Logout API PHP
Future<void> logoutAPI() async {
try {
await _apiService.logout();
} catch (e) {
debugPrint('Erreur logout API: $e');
rethrow;
}
}
// Méthode d'inscription (uniquement pour les administrateurs)
Future<bool> register(String email, String password, String name,
String amicaleName, String postalCode, String cityName) async {
_isLoading = true;
notifyListeners();
try {
// Enregistrer l'administrateur via l'API
final apiResult =
await registerAPI(email, name, amicaleName, postalCode, cityName);
// Créer l'administrateur local
final int userId = apiResult['user_id'] is String
? int.parse(apiResult['user_id'])
: apiResult['user_id'];
final now = DateTime.now();
final newAdmin = UserModel(
id: userId,
email: email,
name: name,
role: AppKeys.roleAdmin2,
createdAt: now,
lastSyncedAt: now,
isActive: true,
isSynced: true,
sessionId: apiResult['session_id'],
sessionExpiry: DateTime.parse(apiResult['session_expiry']),
);
// Sauvegarder dans le repository local
await saveUser(newAdmin);
// Configurer la session dans l'API
setSessionId(newAdmin.sessionId);
notifyListeners();
return true;
} catch (e) {
debugPrint('Erreur d\'inscription: $e');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Login complet
Future<bool> login(String username, String password,
{String type = 'admin'}) async {
_isLoading = true;
notifyListeners();
try {
debugPrint('Début du processus de connexion pour: $username');
// Supprimer les références aux boîtes non définies dans AppKeys
// pour éviter les erreurs de suppression de boîtes non référencées
final nonDefinedBoxes = ['auth', 'locations', 'messages'];
for (final boxName in nonDefinedBoxes) {
try {
if (Hive.isBoxOpen(boxName)) {
debugPrint('Fermeture de la boîte non référencée: $boxName');
await Hive.box(boxName).close();
}
// Supprimer la boîte du disque
await Hive.deleteBoxFromDisk(boxName);
debugPrint('Nettoyage: Box $boxName supprimée');
} catch (e) {
debugPrint(
'Erreur lors de la suppression de la boîte non référencée $boxName: $e');
}
}
// S'assurer que toutes les Hive boxes sont vides avant de se connecter
// Vider toutes les boîtes Hive SAUF la boîte des utilisateurs
debugPrint('Nettoyage des données existantes avant connexion...');
// Sur le web, utiliser notre méthode sécurisée pour nettoyer les boîtes Hive
if (kIsWeb) {
await HiveWebFix.safeCleanHiveBoxes(
excludeBoxes: [AppKeys.usersBoxName]);
}
// Sur iOS, nettoyer les fichiers Hive directement
else if (!kIsWeb && Platform.isIOS) {
await _cleanHiveFilesOnIOS();
}
// Sur Android, nettoyer les fichiers Hive directement
else if (!kIsWeb && Platform.isAndroid) {
await _cleanHiveFilesOnAndroid();
}
// Nettoyer les boîtes sans les fermer
await _clearAndRecreateBoxes();
// Initialiser les boîtes nécessaires avant d'appeler l'API
// Cela garantit que toutes les boîtes sont ouvertes avant le traitement des données
await _initializeBoxes();
// Appeler l'API
debugPrint('Appel de l\'API de connexion (type: $type)...');
final apiResult = await loginAPI(username, password, type: type);
// Vérifier le statut de la réponse
final status = apiResult['status'] as String?;
final message = apiResult['message'] as String?;
// Si le statut n'est pas 'success', retourner false
if (status != 'success') {
debugPrint('Échec de connexion: $message');
return false;
}
debugPrint('Connexion réussie, traitement des données...');
// [Reste de la méthode login inchangé...]
return true;
} catch (e) {
debugPrint('Erreur de connexion: $e');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Méthode pour vérifier si une boîte est ouverte et l'ouvrir si nécessaire
Future<void> _ensureBoxIsOpen(String boxName) async {
try {
if (!Hive.isBoxOpen(boxName)) {
debugPrint('Ouverture de la boîte $boxName...');
if (boxName == AppKeys.passagesBoxName) {
await Hive.openBox<PassageModel>(boxName);
} else if (boxName == AppKeys.operationsBoxName) {
await Hive.openBox<OperationModel>(boxName);
} else if (boxName == AppKeys.sectorsBoxName) {
await Hive.openBox<SectorModel>(boxName);
} else if (boxName == AppKeys.usersBoxName) {
await Hive.openBox<UserModel>(boxName);
} else if (boxName == AppKeys.membresBoxName) {
await Hive.openBox<MembreModel>(boxName);
} else if (boxName == AppKeys.settingsBoxName) {
await Hive.openBox(boxName);
} else if (boxName == AppKeys.chatConversationsBoxName) {
await Hive.openBox<ConversationModel>(boxName);
} else if (boxName == AppKeys.chatMessagesBoxName) {
await Hive.openBox<MessageModel>(boxName);
} else {
await Hive.openBox(boxName);
}
// Boîte ouverte avec succès
} else {
// La boîte est déjà ouverte
}
} catch (e) {
debugPrint('Erreur lors de l\'ouverture de la boîte $boxName: $e');
throw Exception('Impossible d\'ouvrir la boîte $boxName: $e');
}
}
// Méthode pour vider et recréer toutes les boîtes Hive sauf la boîte des utilisateurs
Future<void> _clearAndRecreateBoxes() async {
try {
debugPrint('Début de la suppression complète des données Hive...');
// Supprimer les références aux boîtes non définies dans AppKeys
// pour éviter les erreurs de suppression de boîtes non référencées
final nonDefinedBoxes = ['auth', 'locations', 'messages'];
for (final boxName in nonDefinedBoxes) {
try {
if (Hive.isBoxOpen(boxName)) {
debugPrint('Fermeture de la boîte non référencée: $boxName');
await Hive.box(boxName).close();
}
// Supprimer la boîte du disque
await Hive.deleteBoxFromDisk(boxName);
debugPrint('Nettoyage: Box $boxName supprimée');
} catch (e) {
debugPrint(
'Erreur lors de la suppression de la boîte non référencée $boxName: $e');
}
}
// Sur le web, utiliser notre méthode sécurisée pour nettoyer les boîtes Hive
if (kIsWeb) {
await HiveWebFix.safeCleanHiveBoxes(
excludeBoxes: [AppKeys.usersBoxName]);
}
// Sur iOS, nettoyer les fichiers Hive directement
else if (Platform.isIOS) {
await _cleanHiveFilesOnIOS();
}
// Sur Android, nettoyer les fichiers Hive directement
else if (Platform.isAndroid) {
await _cleanHiveFilesOnAndroid();
}
// Liste des noms de boîtes à supprimer
final boxesToDelete = [
AppKeys.passagesBoxName,
AppKeys.operationsBoxName,
AppKeys.sectorsBoxName,
AppKeys.chatConversationsBoxName,
AppKeys.chatMessagesBoxName,
];
// Vider chaque boîte sans la fermer
for (final boxName in boxesToDelete) {
try {
debugPrint('Nettoyage de la boîte: $boxName');
// Vérifier si la boîte est déjà ouverte
if (Hive.isBoxOpen(boxName)) {
// Vider la boîte sans la fermer
debugPrint('Boîte $boxName déjà ouverte, vidage sans fermeture');
if (boxName == AppKeys.passagesBoxName) {
await Hive.box<PassageModel>(boxName).clear();
} else if (boxName == AppKeys.operationsBoxName) {
await Hive.box<OperationModel>(boxName).clear();
} else if (boxName == AppKeys.sectorsBoxName) {
await Hive.box<SectorModel>(boxName).clear();
} else if (boxName == AppKeys.chatConversationsBoxName) {
await Hive.box<ConversationModel>(boxName).clear();
} else if (boxName == AppKeys.chatMessagesBoxName) {
await Hive.box<MessageModel>(boxName).clear();
}
} else {
// Supprimer la boîte du disque si elle n'est pas ouverte
debugPrint('Boîte $boxName non ouverte, suppression du disque');
await Hive.deleteBoxFromDisk(boxName);
}
} catch (e) {
debugPrint('Erreur lors du nettoyage de la boîte $boxName: $e');
// Tenter de supprimer la boîte du disque en cas d'erreur
try {
await Hive.deleteBoxFromDisk(boxName);
} catch (deleteError) {
debugPrint(
'Impossible de supprimer la boîte $boxName: $deleteError');
}
}
}
// Attendre un court instant pour s'assurer que les opérations de suppression sont terminées
await Future.delayed(const Duration(milliseconds: 500));
// Recréer les boîtes avec la méthode sécurisée
debugPrint('Recréation des boîtes Hive...');
// Utiliser notre méthode pour s'assurer que les boîtes sont ouvertes
try {
// Passages
await _ensureBoxIsOpen(AppKeys.passagesBoxName);
// Opérations
await _ensureBoxIsOpen(AppKeys.operationsBoxName);
// Secteurs
await _ensureBoxIsOpen(AppKeys.sectorsBoxName);
// Chat
await _ensureBoxIsOpen(AppKeys.chatConversationsBoxName);
await _ensureBoxIsOpen(AppKeys.chatMessagesBoxName);
// Vérifier l'intégrité des boîtes après recréation
await _verifyHiveBoxesIntegrity();
} catch (e) {
debugPrint('Erreur lors de la recréation des boîtes Hive: $e');
// Tentative de récupération sur erreur
if (kIsWeb) {
debugPrint('Tentative de récupération sur le web...');
await HiveWebFix.resetHiveCompletely();
// Réessayer d'ouvrir les boîtes
await _ensureBoxIsOpen(AppKeys.passagesBoxName);
await _ensureBoxIsOpen(AppKeys.operationsBoxName);
await _ensureBoxIsOpen(AppKeys.sectorsBoxName);
await _ensureBoxIsOpen(AppKeys.chatConversationsBoxName);
await _ensureBoxIsOpen(AppKeys.chatMessagesBoxName);
}
}
} catch (e) {
debugPrint('Erreur lors de la réinitialisation des boîtes Hive: $e');
}
}
// Méthode pour vérifier l'intégrité des boîtes Hive après recréation
Future<void> _verifyHiveBoxesIntegrity() async {
try {
debugPrint('Vérification de l\'intégrité des boîtes Hive...');
// Liste des boîtes à vérifier avec leur type
final boxesToCheck = [
{'name': AppKeys.passagesBoxName, 'type': 'passage'},
{'name': AppKeys.operationsBoxName, 'type': 'operation'},
{'name': AppKeys.sectorsBoxName, 'type': 'sector'},
{'name': AppKeys.chatConversationsBoxName, 'type': 'conversation'},
{'name': AppKeys.chatMessagesBoxName, 'type': 'message'},
];
// Vérifier chaque boîte
for (final boxInfo in boxesToCheck) {
final boxName = boxInfo['name'] as String;
final boxType = boxInfo['type'] as String;
try {
if (Hive.isBoxOpen(boxName)) {
// Utiliser une approche spécifique au type pour éviter les erreurs de typage
Box box;
try {
if (boxType == 'passage') {
box = Hive.box<PassageModel>(boxName);
} else if (boxType == 'operation') {
box = Hive.box<OperationModel>(boxName);
} else if (boxType == 'sector') {
box = Hive.box<SectorModel>(boxName);
} else if (boxType == 'conversation') {
box = Hive.box<ConversationModel>(boxName);
} else if (boxType == 'message') {
box = Hive.box<MessageModel>(boxName);
} else {
box = Hive.box(boxName);
}
final count = box.length;
debugPrint('Boîte $boxName: $count éléments');
// Si la boîte contient des éléments, c'est anormal après recréation
if (count > 0) {
debugPrint(
'ATTENTION: La boîte $boxName contient encore des données après recréation');
// Essayer de vider la boîte une dernière fois
await box.clear();
debugPrint('Vidage forcé de la boîte $boxName effectué');
}
} catch (typeError) {
debugPrint(
'Erreur de typage lors de la vérification de $boxName: $typeError');
// Tentative alternative sans typage spécifique
try {
box = Hive.box(boxName);
final count = box.length;
debugPrint('Boîte $boxName (sans typage): $count éléments');
if (count > 0) {
await box.clear();
debugPrint(
'Vidage forcé de la boîte $boxName (sans typage) effectué');
}
} catch (e2) {
debugPrint(
'Impossible de vérifier la boîte $boxName même sans typage: $e2');
}
}
} else {
debugPrint(
'Boîte $boxName non ouverte, impossible de vérifier l\'intégrité');
}
} catch (e) {
debugPrint('Erreur lors de la vérification de la boîte $boxName: $e');
}
}
debugPrint('Vérification d\'intégrité terminée');
} catch (e) {
debugPrint(
'Erreur lors de la vérification d\'intégrité des boîtes Hive: $e');
}
}
// Méthode spéciale pour nettoyer IndexedDB sur le web
Future<void> _clearIndexedDB() async {
if (kIsWeb) {
try {
debugPrint('Nettoyage complet d\'IndexedDB sur le web...');
// Utiliser JavaScript pour nettoyer IndexedDB
js.context.callMethod('eval', [
'''
var request = indexedDB.deleteDatabase("geosector_app");
request.onsuccess = function() { console.log("IndexedDB nettoyé avec succès"); };
request.onerror = function() { console.log("Erreur lors du nettoyage d\'IndexedDB"); };
'''
]);
await Future.delayed(const Duration(milliseconds: 500));
debugPrint('Nettoyage d\'IndexedDB terminé');
} catch (e) {
debugPrint('Erreur lors du nettoyage d\'IndexedDB: $e');
}
}
}
// Méthode spéciale pour nettoyer les fichiers Hive sur iOS
Future<void> _cleanHiveFilesOnIOS() async {
if (!kIsWeb && Platform.isIOS) {
try {
debugPrint('Nettoyage des fichiers Hive sur iOS...');
final appDir = await getApplicationDocumentsDirectory();
final hiveDir = Directory('${appDir.path}/hive');
if (await hiveDir.exists()) {
debugPrint('Suppression du répertoire Hive: ${hiveDir.path}');
// Exclure le dossier des utilisateurs pour conserver les informations de session
final entries = await hiveDir.list().toList();
for (var entry in entries) {
final name = entry.path.split('/').last;
// Ne pas supprimer la boîte des utilisateurs
if (!name.contains(AppKeys.usersBoxName)) {
debugPrint('Suppression de: ${entry.path}');
if (entry is Directory) {
await entry.delete(recursive: true);
} else if (entry is File) {
await entry.delete();
}
}
}
debugPrint('Nettoyage des fichiers Hive sur iOS terminé');
} else {
debugPrint('Répertoire Hive non trouvé');
}
} catch (e) {
debugPrint('Erreur lors du nettoyage des fichiers Hive sur iOS: $e');
}
}
}
// Méthode spéciale pour nettoyer les fichiers Hive sur Android
Future<void> _cleanHiveFilesOnAndroid() async {
if (!kIsWeb && Platform.isAndroid) {
try {
debugPrint('Nettoyage des fichiers Hive sur Android...');
final appDir = await getApplicationDocumentsDirectory();
final hiveDir = Directory('${appDir.path}');
if (await hiveDir.exists()) {
debugPrint('Recherche des fichiers Hive dans: ${hiveDir.path}');
// Sur Android, les fichiers Hive sont directement dans le répertoire de l'application
final entries = await hiveDir.list().toList();
int filesDeleted = 0;
for (var entry in entries) {
final name = entry.path.split('/').last;
// Ne supprimer que les fichiers Hive, mais pas la boîte des utilisateurs
if (name.endsWith('.hive') &&
!name.contains(AppKeys.usersBoxName)) {
debugPrint('Suppression du fichier Hive: ${entry.path}');
if (entry is File) {
await entry.delete();
filesDeleted++;
// Supprimer également les fichiers lock associés
final lockFile = File('${entry.path}.lock');
if (await lockFile.exists()) {
await lockFile.delete();
debugPrint('Suppression du fichier lock: ${lockFile.path}');
}
}
}
}
debugPrint(
'Nettoyage des fichiers Hive sur Android terminé. $filesDeleted fichiers supprimés.');
} else {
debugPrint('Répertoire d\'application non trouvé');
}
} catch (e) {
debugPrint(
'Erreur lors du nettoyage des fichiers Hive sur Android: $e');
}
}
}
// Logout complet
Future<bool> logout() async {
_isLoading = true;
notifyListeners();
try {
debugPrint('Début du processus de déconnexion...');
// S'assurer que la boîte des utilisateurs est ouverte avant tout
await _ensureBoxIsOpen(AppKeys.usersBoxName);
// Supprimer les références aux boîtes non définies dans AppKeys
final nonDefinedBoxes = ['auth', 'locations', 'messages'];
for (final boxName in nonDefinedBoxes) {
try {
if (Hive.isBoxOpen(boxName)) {
debugPrint('Fermeture de la boîte non référencée: $boxName');
await Hive.box(boxName).close();
}
// Supprimer la boîte du disque
await Hive.deleteBoxFromDisk(boxName);
debugPrint('Nettoyage: Box $boxName supprimée');
} catch (e) {
debugPrint(
'Erreur lors de la suppression de la boîte non référencée $boxName: $e');
}
}
// Récupérer l'utilisateur actuel avant de nettoyer les données
final currentUser = getCurrentUser();
if (currentUser == null) {
debugPrint('Aucun utilisateur connecté, déconnexion terminée');
return true;
}
debugPrint('Déconnexion de l\'utilisateur: ${currentUser.email}');
// Appeler l'API pour déconnecter la session
if (currentUser.sessionId != null) {
debugPrint('Déconnexion de la session API...');
await logoutAPI();
}
// Effacer la session de l'utilisateur
debugPrint('Mise à jour de l\'utilisateur pour effacer la session...');
final updatedUser = currentUser.copyWith(
sessionId: null,
sessionExpiry: null,
lastPath:
null, // Réinitialiser le chemin pour revenir à l'écran de connexion
);
// Sauvegarder l'utilisateur sans session
await saveUser(updatedUser);
// Effacer la session de l'API
setSessionId(null);
// Maintenant, nettoyer les données
debugPrint('Nettoyage des données...');
// Sur le web, utiliser notre méthode sécurisée pour nettoyer les boîtes Hive
if (kIsWeb) {
await HiveWebFix.safeCleanHiveBoxes(
excludeBoxes: [AppKeys.usersBoxName]);
}
// Sur iOS, nettoyer les fichiers Hive directement
else if (!kIsWeb && Platform.isIOS) {
await _cleanHiveFilesOnIOS();
}
// Sur Android, nettoyer les fichiers Hive directement
else if (!kIsWeb && Platform.isAndroid) {
await _cleanHiveFilesOnAndroid();
}
// Vider les boîtes sans les fermer, y compris les boîtes de chat
debugPrint('Suppression des données Hive...');
await _clearAndRecreateBoxes();
// Vider spécifiquement les boîtes de chat si elles sont ouvertes
try {
if (Hive.isBoxOpen(AppKeys.chatConversationsBoxName)) {
await Hive.box<ConversationModel>(AppKeys.chatConversationsBoxName).clear();
debugPrint('Boîte conversations vidée');
}
if (Hive.isBoxOpen(AppKeys.chatMessagesBoxName)) {
await Hive.box<MessageModel>(AppKeys.chatMessagesBoxName).clear();
debugPrint('Boîte messages vidée');
}
} catch (e) {
debugPrint('Erreur lors du vidage des boîtes de chat: $e');
}
debugPrint('Déconnexion terminée avec succès');
notifyListeners();
return true;
} catch (e) {
debugPrint('Erreur de déconnexion: $e');
return false;
} finally {
_isLoading = false;
notifyListeners();
}
}
// Obtenir tous les utilisateurs locaux
List<UserModel> getAllUsers() {
return _userBox.values.toList();
}
// Obtenir un utilisateur par son ID
UserModel? getUserById(int id) {
return _userBox.get(id);
}
// Obtenir un utilisateur par son email
UserModel? getUserByEmail(String email) {
try {
return _userBox.values.firstWhere(
(user) => user.email == email,
);
} catch (e) {
return null; // Utilisateur non trouvé
}
}
// Créer ou mettre à jour un utilisateur localement
Future<UserModel> saveUser(UserModel user) async {
await _userBox.put(user.id, user);
notifyListeners(); // Notifier les changements pour mettre à jour l'UI
return user;
}
// Supprimer un utilisateur localement
Future<void> deleteUser(String id) async {
await _userBox.delete(id);
}
// Créer un nouvel utilisateur localement et tenter de le synchroniser
Future<UserModel> createUser({
required String email,
required String name,
required int role,
}) async {
// Générer un ID numérique temporaire (timestamp)
final int tempId = DateTime.now().millisecondsSinceEpoch;
final now = DateTime.now();
final user = UserModel(
id: tempId,
email: email,
name: name,
role: role,
createdAt: now,
lastSyncedAt: now,
isSynced: false,
);
await _userBox.put(user.id, user);
// Tenter de synchroniser si possible
await syncUser(user);
return user;
}
// Synchroniser un utilisateur spécifique avec le serveur
Future<UserModel> syncUser(UserModel user) async {
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
return user;
}
UserModel syncedUser;
if (!user.isSynced) {
// Si l'utilisateur n'est pas encore synchronisé, le créer sur le serveur
syncedUser = await _apiService.createUser(user);
} else {
// Sinon, mettre à jour les informations
syncedUser = await _apiService.updateUser(user);
}
// Mettre à jour l'utilisateur local avec les informations du serveur
final updatedUser = syncedUser.copyWith(
isSynced: true,
lastSyncedAt: DateTime.now(),
);
await _userBox.put(updatedUser.id, updatedUser);
return updatedUser;
} catch (e) {
// En cas d'erreur, garder l'utilisateur local tel quel
return user;
}
}
// Synchroniser tous les utilisateurs non synchronisés
Future<void> syncAllUsers() async {
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
return;
}
final unsyncedUsers =
_userBox.values.where((user) => !user.isSynced).toList();
if (unsyncedUsers.isEmpty) {
return;
}
// Synchroniser en batch
final result = await _apiService.syncData(users: unsyncedUsers);
// Mettre à jour les utilisateurs locaux
if (result['users'] != null) {
for (final userData in result['users']) {
final syncedUser = UserModel.fromJson(userData);
await _userBox.put(
syncedUser.id,
syncedUser.copyWith(
isSynced: true,
lastSyncedAt: DateTime.now(),
),
);
}
}
} catch (e) {
// Gérer les erreurs de synchronisation
print('Erreur de synchronisation des utilisateurs: $e');
}
}
// Rafraîchir les données depuis le serveur
Future<void> refreshFromServer() async {
try {
final hasConnection = await _apiService.hasInternetConnection();
if (!hasConnection) {
return;
}
// Récupérer tous les utilisateurs du serveur
final serverUsers = await _apiService.getUsers();
// Mettre à jour la base locale
for (final serverUser in serverUsers) {
final updatedUser = serverUser.copyWith(
isSynced: true,
lastSyncedAt: DateTime.now(),
);
await _userBox.put(updatedUser.id, updatedUser);
}
} catch (e) {
// Gérer les erreurs
print('Erreur lors du rafraîchissement des données: $e');
}
}
// Synchroniser les données utilisateur
Future<void> syncUserData() async {
if (_syncService != null && currentUser != null) {
await _syncService!.syncUserData(currentUser!.id);
}
}
// Récupérer la dernière opération active (avec isActive == true)
OperationModel? getCurrentOperation() {
try {
// Récupérer toutes les opérations
final operations = _operationBox.values.toList();
// Filtrer pour ne garder que les opérations actives
final activeOperations = operations.where((op) => op.isActive).toList();
// Si aucune opération active n'est trouvée, retourner null
if (activeOperations.isEmpty) {
return operations.isNotEmpty ? operations.last : null;
}
// Retourner la dernière opération active
return activeOperations.last;
} catch (e) {
debugPrint('Erreur lors de la récupération de l\'opération actuelle: $e');
return null;
}
}
// Récupérer tous les secteurs de l'utilisateur
List<SectorModel> getUserSectors() {
try {
return _sectorBox.values.toList();
} catch (e) {
debugPrint('Erreur lors de la récupération des secteurs: $e');
return [];
}
}
// Récupérer un secteur par son ID
SectorModel? getSectorById(int id) {
try {
return _sectorBox.get(id);
} catch (e) {
debugPrint('Erreur lors de la récupération du secteur: $e');
return null;
}
}
}