Fix: Hive sync et update entité via API REST
- Correction mapping JSON membres (fk_role, chk_active) - Ajout traitement amicale au login - Fix callback onSubmit pour sync Hive après update API
This commit is contained in:
@@ -8,7 +8,7 @@ part 'anonymous_user_model.g.dart';
|
||||
/// Ce modèle représente un utilisateur anonyme (pour le cas Resalice)
|
||||
/// et permet de tracker sa conversion éventuelle en utilisateur authentifié
|
||||
|
||||
@HiveType(typeId: 24)
|
||||
@HiveType(typeId: 23)
|
||||
class AnonymousUserModel extends HiveObject with EquatableMixin {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@@ -8,7 +8,7 @@ part of 'anonymous_user_model.dart';
|
||||
|
||||
class AnonymousUserModelAdapter extends TypeAdapter<AnonymousUserModel> {
|
||||
@override
|
||||
final int typeId = 24;
|
||||
final int typeId = 23;
|
||||
|
||||
@override
|
||||
AnonymousUserModel read(BinaryReader reader) {
|
||||
|
||||
@@ -8,7 +8,7 @@ part 'audience_target_model.g.dart';
|
||||
/// Ce modèle représente une cible d'audience pour les annonces et broadcasts
|
||||
/// Il supporte maintenant le ciblage combiné avec les filtres de rôle et d'entité
|
||||
|
||||
@HiveType(typeId: 23)
|
||||
@HiveType(typeId: 24)
|
||||
class AudienceTargetModel extends HiveObject with EquatableMixin {
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
@@ -8,7 +8,7 @@ part of 'audience_target_model.dart';
|
||||
|
||||
class AudienceTargetModelAdapter extends TypeAdapter<AudienceTargetModel> {
|
||||
@override
|
||||
final int typeId = 23;
|
||||
final int typeId = 24;
|
||||
|
||||
@override
|
||||
AudienceTargetModel read(BinaryReader reader) {
|
||||
|
||||
@@ -61,6 +61,15 @@ class ClientModel extends HiveObject {
|
||||
@HiveField(18)
|
||||
final bool? chkActive;
|
||||
|
||||
@HiveField(19)
|
||||
final bool? chkStripe;
|
||||
|
||||
@HiveField(20)
|
||||
final DateTime? createdAt;
|
||||
|
||||
@HiveField(21)
|
||||
final DateTime? updatedAt;
|
||||
|
||||
ClientModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
@@ -81,6 +90,9 @@ class ClientModel extends HiveObject {
|
||||
this.chkCopieMailRecu,
|
||||
this.chkAcceptSms,
|
||||
this.chkActive,
|
||||
this.chkStripe,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
// Factory pour convertir depuis JSON (API)
|
||||
@@ -93,8 +105,7 @@ class ClientModel extends HiveObject {
|
||||
int? fkRegion;
|
||||
if (json['fk_region'] != null) {
|
||||
final dynamic rawFkRegion = json['fk_region'];
|
||||
fkRegion =
|
||||
rawFkRegion is String ? int.parse(rawFkRegion) : rawFkRegion as int;
|
||||
fkRegion = rawFkRegion is String ? int.parse(rawFkRegion) : rawFkRegion as int;
|
||||
}
|
||||
|
||||
// Convertir fk_type en int si présent
|
||||
@@ -121,11 +132,12 @@ class ClientModel extends HiveObject {
|
||||
gpsLng: json['gps_lng'],
|
||||
stripeId: json['stripe_id'],
|
||||
chkDemo: json['chk_demo'] == 1 || json['chk_demo'] == true,
|
||||
chkCopieMailRecu: json['chk_copie_mail_recu'] == 1 ||
|
||||
json['chk_copie_mail_recu'] == true,
|
||||
chkAcceptSms:
|
||||
json['chk_accept_sms'] == 1 || json['chk_accept_sms'] == true,
|
||||
chkCopieMailRecu: json['chk_copie_mail_recu'] == 1 || json['chk_copie_mail_recu'] == true,
|
||||
chkAcceptSms: json['chk_accept_sms'] == 1 || json['chk_accept_sms'] == true,
|
||||
chkActive: json['chk_active'] == 1 || json['chk_active'] == true,
|
||||
chkStripe: json['chk_stripe'] == 1 || json['chk_stripe'] == true,
|
||||
createdAt: json['created_at'] != null ? DateTime.parse(json['created_at']) : null,
|
||||
updatedAt: json['updated_at'] != null ? DateTime.parse(json['updated_at']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,6 +163,9 @@ class ClientModel extends HiveObject {
|
||||
'chk_copie_mail_recu': chkCopieMailRecu,
|
||||
'chk_accept_sms': chkAcceptSms,
|
||||
'chk_active': chkActive,
|
||||
'chk_stripe': chkStripe,
|
||||
'created_at': createdAt?.toIso8601String(),
|
||||
'updated_at': updatedAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -174,9 +189,12 @@ class ClientModel extends HiveObject {
|
||||
bool? chkCopieMailRecu,
|
||||
bool? chkAcceptSms,
|
||||
bool? chkActive,
|
||||
bool? chkStripe,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return ClientModel(
|
||||
id: this.id,
|
||||
id: id,
|
||||
name: name ?? this.name,
|
||||
adresse1: adresse1 ?? this.adresse1,
|
||||
adresse2: adresse2 ?? this.adresse2,
|
||||
@@ -195,6 +213,9 @@ class ClientModel extends HiveObject {
|
||||
chkCopieMailRecu: chkCopieMailRecu ?? this.chkCopieMailRecu,
|
||||
chkAcceptSms: chkAcceptSms ?? this.chkAcceptSms,
|
||||
chkActive: chkActive ?? this.chkActive,
|
||||
chkStripe: chkStripe ?? this.chkStripe,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,13 +36,16 @@ class ClientModelAdapter extends TypeAdapter<ClientModel> {
|
||||
chkCopieMailRecu: fields[16] as bool?,
|
||||
chkAcceptSms: fields[17] as bool?,
|
||||
chkActive: fields[18] as bool?,
|
||||
chkStripe: fields[19] as bool?,
|
||||
createdAt: fields[20] as DateTime?,
|
||||
updatedAt: fields[21] as DateTime?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, ClientModel obj) {
|
||||
writer
|
||||
..writeByte(19)
|
||||
..writeByte(22)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
@@ -80,7 +83,13 @@ class ClientModelAdapter extends TypeAdapter<ClientModel> {
|
||||
..writeByte(17)
|
||||
..write(obj.chkAcceptSms)
|
||||
..writeByte(18)
|
||||
..write(obj.chkActive);
|
||||
..write(obj.chkActive)
|
||||
..writeByte(19)
|
||||
..write(obj.chkStripe)
|
||||
..writeByte(20)
|
||||
..write(obj.createdAt)
|
||||
..writeByte(21)
|
||||
..write(obj.updatedAt);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -8,51 +8,53 @@ class MembreModel extends HiveObject {
|
||||
final int id;
|
||||
|
||||
@HiveField(1)
|
||||
final int fkRole;
|
||||
|
||||
int? fkEntite;
|
||||
@HiveField(2)
|
||||
final int fkTitre;
|
||||
final int role;
|
||||
|
||||
@HiveField(3)
|
||||
final String firstName;
|
||||
|
||||
int? fkTitre;
|
||||
@HiveField(4)
|
||||
final String? sectName;
|
||||
String? name;
|
||||
|
||||
@HiveField(5)
|
||||
final DateTime? dateNaissance;
|
||||
|
||||
String? firstName;
|
||||
@HiveField(6)
|
||||
final DateTime? dateEmbauche;
|
||||
|
||||
String? username;
|
||||
@HiveField(7)
|
||||
final int chkActive;
|
||||
|
||||
String? sectName;
|
||||
@HiveField(8)
|
||||
final String name;
|
||||
|
||||
@HiveField(9)
|
||||
final String username;
|
||||
|
||||
@HiveField(10)
|
||||
final String email;
|
||||
|
||||
@HiveField(9)
|
||||
String? phone;
|
||||
@HiveField(10)
|
||||
String? mobile;
|
||||
@HiveField(11)
|
||||
final int fkEntite;
|
||||
DateTime? dateNaissance;
|
||||
@HiveField(12)
|
||||
DateTime? dateEmbauche;
|
||||
@HiveField(13)
|
||||
final DateTime createdAt;
|
||||
@HiveField(14)
|
||||
bool isActive;
|
||||
|
||||
MembreModel({
|
||||
required this.id,
|
||||
required this.fkRole,
|
||||
required this.fkTitre,
|
||||
required this.firstName,
|
||||
this.fkEntite,
|
||||
required this.role,
|
||||
this.fkTitre,
|
||||
this.name,
|
||||
this.firstName,
|
||||
this.username,
|
||||
this.sectName,
|
||||
required this.email,
|
||||
this.phone,
|
||||
this.mobile,
|
||||
this.dateNaissance,
|
||||
this.dateEmbauche,
|
||||
required this.chkActive,
|
||||
required this.name,
|
||||
required this.username,
|
||||
required this.email,
|
||||
required this.fkEntite,
|
||||
required this.createdAt,
|
||||
required this.isActive,
|
||||
});
|
||||
|
||||
// Factory pour convertir depuis JSON (API)
|
||||
@@ -61,35 +63,53 @@ class MembreModel extends HiveObject {
|
||||
final dynamic rawId = json['id'];
|
||||
final int id = rawId is String ? int.parse(rawId) : rawId as int;
|
||||
|
||||
// Convertir le rôle en int, qu'il soit déjà int ou string
|
||||
final dynamic rawRole = json['fk_role'];
|
||||
final int fkRole = rawRole is String ? int.parse(rawRole) : rawRole as int;
|
||||
// Convertir le rôle en int (ATTENTION: le champ JSON est 'fk_role' pas 'role')
|
||||
final dynamic rawRole = json['fk_role']; // Correction ici !
|
||||
final int role = rawRole is String ? int.parse(rawRole) : rawRole as int;
|
||||
|
||||
// Convertir le titre en int, qu'il soit déjà int ou string
|
||||
final dynamic rawTitre = json['fk_titre'];
|
||||
final int fkTitre = rawTitre is String ? int.parse(rawTitre) : rawTitre as int;
|
||||
// Convertir fkEntite en int si présent
|
||||
int? fkEntite;
|
||||
if (json['fk_entite'] != null) {
|
||||
final dynamic rawEntite = json['fk_entite'];
|
||||
fkEntite = rawEntite is String ? int.parse(rawEntite) : rawEntite as int;
|
||||
}
|
||||
|
||||
// Convertir le chkActive en int, qu'il soit déjà int ou string
|
||||
final dynamic rawActive = json['chk_active'];
|
||||
final int chkActive = rawActive is String ? int.parse(rawActive) : rawActive as int;
|
||||
// Convertir fkTitre en int si présent
|
||||
int? fkTitre;
|
||||
if (json['fk_titre'] != null) {
|
||||
final dynamic rawTitre = json['fk_titre'];
|
||||
fkTitre = rawTitre is String ? int.parse(rawTitre) : rawTitre as int;
|
||||
}
|
||||
|
||||
// Convertir le fkEntite en int, qu'il soit déjà int ou string
|
||||
final dynamic rawEntite = json['fk_entite'];
|
||||
final int fkEntite = rawEntite is String ? int.parse(rawEntite) : rawEntite as int;
|
||||
// Gérer les dates nulles ou avec des valeurs invalides comme "0000-00-00"
|
||||
DateTime? parseDate(String? dateStr) {
|
||||
if (dateStr == null || dateStr.isEmpty || dateStr == "0000-00-00") {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DateTime.parse(dateStr);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return MembreModel(
|
||||
id: id,
|
||||
fkRole: fkRole,
|
||||
fkTitre: fkTitre,
|
||||
firstName: json['first_name'] ?? '',
|
||||
sectName: json['sect_name'],
|
||||
dateNaissance: json['date_naissance'] != null ? DateTime.parse(json['date_naissance']) : null,
|
||||
dateEmbauche: json['date_embauche'] != null ? DateTime.parse(json['date_embauche']) : null,
|
||||
chkActive: chkActive,
|
||||
name: json['name'] ?? '',
|
||||
username: json['username'] ?? '',
|
||||
email: json['email'] ?? '',
|
||||
fkEntite: fkEntite,
|
||||
role: role,
|
||||
fkTitre: fkTitre,
|
||||
name: json['name'],
|
||||
firstName: json['first_name'],
|
||||
username: json['username'],
|
||||
sectName: json['sect_name'],
|
||||
email: json['email'] ?? '',
|
||||
phone: json['phone'],
|
||||
mobile: json['mobile'],
|
||||
dateNaissance: parseDate(json['date_naissance']),
|
||||
dateEmbauche: parseDate(json['date_embauche']),
|
||||
createdAt: json['created_at'] != null ? DateTime.parse(json['created_at']) : DateTime.now(),
|
||||
// Le champ JSON est 'chk_active' pas 'is_active'
|
||||
isActive: json['chk_active'] == 1 || json['chk_active'] == true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -97,47 +117,56 @@ class MembreModel extends HiveObject {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'fk_role': fkRole,
|
||||
'fk_entite': fkEntite,
|
||||
'fk_role': role, // Changé pour correspondre à l'API
|
||||
'fk_titre': fkTitre,
|
||||
'name': name,
|
||||
'first_name': firstName,
|
||||
'username': username,
|
||||
'sect_name': sectName,
|
||||
'email': email,
|
||||
'phone': phone,
|
||||
'mobile': mobile,
|
||||
'date_naissance': dateNaissance?.toIso8601String(),
|
||||
'date_embauche': dateEmbauche?.toIso8601String(),
|
||||
'chk_active': chkActive,
|
||||
'name': name,
|
||||
'username': username,
|
||||
'email': email,
|
||||
'fk_entite': fkEntite,
|
||||
'created_at': createdAt.toIso8601String(),
|
||||
'chk_active': isActive ? 1 : 0, // Changé pour correspondre à l'API
|
||||
};
|
||||
}
|
||||
|
||||
// Copier avec de nouvelles valeurs
|
||||
MembreModel copyWith({
|
||||
int? fkRole,
|
||||
int? fkEntite,
|
||||
int? role,
|
||||
int? fkTitre,
|
||||
String? name,
|
||||
String? firstName,
|
||||
String? username,
|
||||
String? sectName,
|
||||
String? email,
|
||||
String? phone,
|
||||
String? mobile,
|
||||
DateTime? dateNaissance,
|
||||
DateTime? dateEmbauche,
|
||||
int? chkActive,
|
||||
String? name,
|
||||
String? username,
|
||||
String? email,
|
||||
int? fkEntite,
|
||||
DateTime? createdAt,
|
||||
bool? isActive,
|
||||
}) {
|
||||
return MembreModel(
|
||||
id: id,
|
||||
fkRole: fkRole ?? this.fkRole,
|
||||
fkEntite: fkEntite ?? this.fkEntite,
|
||||
role: role ?? this.role,
|
||||
fkTitre: fkTitre ?? this.fkTitre,
|
||||
name: name ?? this.name,
|
||||
firstName: firstName ?? this.firstName,
|
||||
username: username ?? this.username,
|
||||
sectName: sectName ?? this.sectName,
|
||||
email: email ?? this.email,
|
||||
phone: phone ?? this.phone,
|
||||
mobile: mobile ?? this.mobile,
|
||||
dateNaissance: dateNaissance ?? this.dateNaissance,
|
||||
dateEmbauche: dateEmbauche ?? this.dateEmbauche,
|
||||
chkActive: chkActive ?? this.chkActive,
|
||||
name: name ?? this.name,
|
||||
username: username ?? this.username,
|
||||
email: email ?? this.email,
|
||||
fkEntite: fkEntite ?? this.fkEntite,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
isActive: isActive ?? this.isActive,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,48 +18,57 @@ class MembreModelAdapter extends TypeAdapter<MembreModel> {
|
||||
};
|
||||
return MembreModel(
|
||||
id: fields[0] as int,
|
||||
fkRole: fields[1] as int,
|
||||
fkTitre: fields[2] as int,
|
||||
firstName: fields[3] as String,
|
||||
sectName: fields[4] as String?,
|
||||
dateNaissance: fields[5] as DateTime?,
|
||||
dateEmbauche: fields[6] as DateTime?,
|
||||
chkActive: fields[7] as int,
|
||||
name: fields[8] as String,
|
||||
username: fields[9] as String,
|
||||
email: fields[10] as String,
|
||||
fkEntite: fields[11] as int,
|
||||
fkEntite: fields[1] as int?,
|
||||
role: fields[2] as int,
|
||||
fkTitre: fields[3] as int?,
|
||||
name: fields[4] as String?,
|
||||
firstName: fields[5] as String?,
|
||||
username: fields[6] as String?,
|
||||
sectName: fields[7] as String?,
|
||||
email: fields[8] as String,
|
||||
phone: fields[9] as String?,
|
||||
mobile: fields[10] as String?,
|
||||
dateNaissance: fields[11] as DateTime?,
|
||||
dateEmbauche: fields[12] as DateTime?,
|
||||
createdAt: fields[13] as DateTime,
|
||||
isActive: fields[14] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, MembreModel obj) {
|
||||
writer
|
||||
..writeByte(12)
|
||||
..writeByte(15)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.fkRole)
|
||||
..write(obj.fkEntite)
|
||||
..writeByte(2)
|
||||
..write(obj.fkTitre)
|
||||
..write(obj.role)
|
||||
..writeByte(3)
|
||||
..write(obj.firstName)
|
||||
..write(obj.fkTitre)
|
||||
..writeByte(4)
|
||||
..write(obj.sectName)
|
||||
..writeByte(5)
|
||||
..write(obj.dateNaissance)
|
||||
..writeByte(6)
|
||||
..write(obj.dateEmbauche)
|
||||
..writeByte(7)
|
||||
..write(obj.chkActive)
|
||||
..writeByte(8)
|
||||
..write(obj.name)
|
||||
..writeByte(9)
|
||||
..writeByte(5)
|
||||
..write(obj.firstName)
|
||||
..writeByte(6)
|
||||
..write(obj.username)
|
||||
..writeByte(10)
|
||||
..writeByte(7)
|
||||
..write(obj.sectName)
|
||||
..writeByte(8)
|
||||
..write(obj.email)
|
||||
..writeByte(9)
|
||||
..write(obj.phone)
|
||||
..writeByte(10)
|
||||
..write(obj.mobile)
|
||||
..writeByte(11)
|
||||
..write(obj.fkEntite);
|
||||
..write(obj.dateNaissance)
|
||||
..writeByte(12)
|
||||
..write(obj.dateEmbauche)
|
||||
..writeByte(13)
|
||||
..write(obj.createdAt)
|
||||
..writeByte(14)
|
||||
..write(obj.isActive);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:hive/hive.dart';
|
||||
|
||||
part 'region_model.g.dart';
|
||||
|
||||
@HiveType(typeId: 7) // Assurez-vous que cet ID est unique
|
||||
@HiveType(typeId: 7)
|
||||
class RegionModel extends HiveObject {
|
||||
@HiveField(0)
|
||||
final int id;
|
||||
|
||||
@@ -6,8 +6,7 @@ part 'user_sector_model.g.dart';
|
||||
///
|
||||
/// Cette classe représente l'association entre un utilisateur et un secteur,
|
||||
/// telle que reçue de l'API dans la réponse users_sectors.
|
||||
@HiveType(
|
||||
typeId: 7) // Assurez-vous que cet ID est unique parmi vos modèles Hive
|
||||
@HiveType(typeId: 6)
|
||||
class UserSectorModel extends HiveObject {
|
||||
@HiveField(0)
|
||||
final int id; // ID de l'utilisateur
|
||||
@@ -38,9 +37,7 @@ class UserSectorModel extends HiveObject {
|
||||
id: json['id'] is String ? int.parse(json['id']) : json['id'],
|
||||
firstName: json['first_name'],
|
||||
sectName: json['sect_name'],
|
||||
fkSector: json['fk_sector'] is String
|
||||
? int.parse(json['fk_sector'])
|
||||
: json['fk_sector'],
|
||||
fkSector: json['fk_sector'] is String ? int.parse(json['fk_sector']) : json['fk_sector'],
|
||||
name: json['name'],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ part of 'user_sector_model.dart';
|
||||
|
||||
class UserSectorModelAdapter extends TypeAdapter<UserSectorModel> {
|
||||
@override
|
||||
final int typeId = 7;
|
||||
final int typeId = 6;
|
||||
|
||||
@override
|
||||
UserSectorModel read(BinaryReader reader) {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
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';
|
||||
@@ -34,6 +31,19 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour exposer la Box Hive (nécessaire pour ValueListenableBuilder)
|
||||
Box<AmicaleModel> getAmicalesBox() {
|
||||
try {
|
||||
if (!Hive.isBoxOpen(AppKeys.amicaleBoxName)) {
|
||||
throw Exception('La boîte amicales n\'est pas ouverte');
|
||||
}
|
||||
return _amicaleBox;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'accès à la boîte amicales: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer toutes les amicales
|
||||
List<AmicaleModel> getAllAmicales() {
|
||||
return _amicaleBox.values.toList();
|
||||
@@ -54,6 +64,16 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
return getAmicalesByType(1);
|
||||
}
|
||||
|
||||
// Récupérer l'amicale de l'utilisateur connecté (basé sur fkEntite)
|
||||
AmicaleModel? getAmicaleByUserId(int userId, int fkEntite) {
|
||||
try {
|
||||
return _amicaleBox.get(fkEntite);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération de l\'amicale de l\'utilisateur: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Sauvegarder une amicale
|
||||
Future<void> saveAmicale(AmicaleModel amicale) async {
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
@@ -66,6 +86,12 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Vider la boîte des amicales
|
||||
Future<void> clearAmicales() async {
|
||||
await _amicaleBox.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Créer une amicale via l'API
|
||||
Future<bool> createAmicale(AmicaleModel amicale) async {
|
||||
_isLoading = true;
|
||||
@@ -82,14 +108,33 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
// 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(
|
||||
// Créer l'amicale localement avec l'ID retourné par l'API et updatedAt
|
||||
final amicaleWithNewId = AmicaleModel(
|
||||
id: amicaleId,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
name: amicale.name,
|
||||
adresse1: amicale.adresse1,
|
||||
adresse2: amicale.adresse2,
|
||||
codePostal: amicale.codePostal,
|
||||
ville: amicale.ville,
|
||||
fkRegion: amicale.fkRegion,
|
||||
libRegion: amicale.libRegion,
|
||||
fkType: amicale.fkType,
|
||||
phone: amicale.phone,
|
||||
mobile: amicale.mobile,
|
||||
email: amicale.email,
|
||||
gpsLat: amicale.gpsLat,
|
||||
gpsLng: amicale.gpsLng,
|
||||
stripeId: amicale.stripeId,
|
||||
chkDemo: amicale.chkDemo,
|
||||
chkCopieMailRecu: amicale.chkCopieMailRecu,
|
||||
chkAcceptSms: amicale.chkAcceptSms,
|
||||
chkActive: amicale.chkActive,
|
||||
chkStripe: amicale.chkStripe,
|
||||
createdAt: amicale.createdAt ?? DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
await saveAmicale(newAmicale);
|
||||
await saveAmicale(amicaleWithNewId);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -116,10 +161,9 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
final response = await ApiService.instance.put('/amicales/${amicale.id}', data: data);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
// Mettre à jour l'amicale localement
|
||||
// Mettre à jour l'amicale localement avec updatedAt
|
||||
final updatedAmicale = amicale.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
await saveAmicale(updatedAmicale);
|
||||
@@ -136,6 +180,35 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour une amicale via l'API (version alternative avec retour d'objet)
|
||||
Future<AmicaleModel?> updateAmicaleViaApi(AmicaleModel amicale) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await ApiService.instance.put(
|
||||
'/amicales/${amicale.id}',
|
||||
data: amicale.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final updatedAmicaleData = response.data;
|
||||
final updatedAmicale = AmicaleModel.fromJson(updatedAmicaleData);
|
||||
await saveAmicale(updatedAmicale);
|
||||
return updatedAmicale;
|
||||
} else {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'amicale: ${response.statusCode}');
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'amicale: $e');
|
||||
return null;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer une amicale via l'API
|
||||
Future<bool> deleteAmicaleViaApi(int id) async {
|
||||
_isLoading = true;
|
||||
@@ -161,228 +234,55 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Traitement des données d'amicales depuis l'API
|
||||
Future<void> processAmicalesData(dynamic amicalesData) async {
|
||||
// Traitement des données d'amicale depuis l'API (amicale unique)
|
||||
Future<void> processAmicalesData(dynamic amicaleData) async {
|
||||
try {
|
||||
debugPrint('Traitement des données des amicales...');
|
||||
debugPrint('Traitement de l\'amicale utilisateur...');
|
||||
|
||||
// Vérifier que les données sont au bon format
|
||||
if (amicalesData == null) {
|
||||
if (amicaleData == 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
|
||||
// Vider la boîte avant d'ajouter la nouvelle amicale
|
||||
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';
|
||||
|
||||
class AmicaleRepository extends ChangeNotifier {
|
||||
// Utilisation de getters lazy pour n'accéder à la boîte que lorsque nécessaire
|
||||
Box<AmicaleModel> get _amicaleBox => Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
|
||||
|
||||
final ApiService _apiService;
|
||||
bool _isLoading = false;
|
||||
|
||||
AmicaleRepository(this._apiService);
|
||||
|
||||
// Getters
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
// Méthode pour exposer la Box Hive (nécessaire pour ValueListenableBuilder)
|
||||
Box<AmicaleModel> getAmicalesBox() {
|
||||
try {
|
||||
if (!Hive.isBoxOpen(AppKeys.amicaleBoxName)) {
|
||||
throw Exception('La boîte amicales n\'est pas ouverte');
|
||||
}
|
||||
return _amicaleBox;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'accès à la boîte amicales: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.amicaleBoxName)) {
|
||||
debugPrint('Ouverture de la boîte amicale...');
|
||||
await Hive.openBox<AmicaleModel>(AppKeys.amicaleBoxName);
|
||||
try {
|
||||
// Les données sont un objet amicale unique
|
||||
final Map<String, dynamic> amicaleMap = Map<String, dynamic>.from(amicaleData as Map);
|
||||
final amicale = AmicaleModel.fromJson(amicaleMap);
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
debugPrint('✅ Amicale utilisateur traitée: ${amicale.name} (ID: ${amicale.id})');
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du traitement de l\'amicale: $e');
|
||||
debugPrint('Données reçues: $amicaleData');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'ouverture de la boîte amicale: $e');
|
||||
throw Exception('Impossible d\'ouvrir la boîte amicale: $e');
|
||||
debugPrint('❌ Erreur lors du traitement de l\'amicale: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer toutes les amicales
|
||||
List<AmicaleModel> getAllAmicales() {
|
||||
// Méthode spécifique pour récupérer l'amicale de l'utilisateur connecté
|
||||
AmicaleModel? getUserAmicale(int fkEntite) {
|
||||
try {
|
||||
_ensureBoxIsOpen();
|
||||
return _amicaleBox.values.toList();
|
||||
final amicale = _amicaleBox.get(fkEntite);
|
||||
debugPrint('🔍 Recherche amicale ID $fkEntite: ${amicale?.name ?? 'non trouvée'}');
|
||||
return amicale;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des amicales: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer une amicale par son ID
|
||||
AmicaleModel? getAmicaleById(int id) {
|
||||
try {
|
||||
_ensureBoxIsOpen();
|
||||
return _amicaleBox.get(id);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération de l\'amicale: $e');
|
||||
debugPrint('❌ Erreur lors de la récupération de l\'amicale utilisateur: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer l'amicale de l'utilisateur connecté (basé sur fkEntite)
|
||||
AmicaleModel? getAmicaleByUserId(int userId, int fkEntite) {
|
||||
try {
|
||||
_ensureBoxIsOpen();
|
||||
return _amicaleBox.get(fkEntite);
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération de l\'amicale de l\'utilisateur: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Créer ou mettre à jour une amicale localement
|
||||
Future<AmicaleModel> saveAmicale(AmicaleModel amicale) async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
notifyListeners(); // Notifier les changements pour mettre à jour l'UI
|
||||
return amicale;
|
||||
}
|
||||
|
||||
// Supprimer une amicale localement
|
||||
Future<void> deleteAmicale(int id) async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _amicaleBox.delete(id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Vider la boîte des amicales
|
||||
Future<void> clearAmicales() async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _amicaleBox.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Traiter les données des amicales reçues de l'API
|
||||
Future<void> processAmicalesData(dynamic amicalesData) async {
|
||||
try {
|
||||
debugPrint('Traitement des données des amicales...');
|
||||
debugPrint('Détails amicale: $amicalesData');
|
||||
|
||||
// Vérifier que les données sont au bon format
|
||||
if (amicalesData == null) {
|
||||
debugPrint('Aucune donnée d\'amicale à traiter');
|
||||
return;
|
||||
}
|
||||
|
||||
// Vider la boîte avant d'ajouter les nouvelles données
|
||||
await _ensureBoxIsOpen();
|
||||
await _amicaleBox.clear();
|
||||
|
||||
int count = 0;
|
||||
|
||||
// Cas 1: Les données sont une liste d'amicales
|
||||
if (amicalesData is List) {
|
||||
for (final amicaleData in amicalesData) {
|
||||
try {
|
||||
final amicale = AmicaleModel.fromJson(amicaleData);
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
count++;
|
||||
debugPrint('Amicale traitée: ${amicale.name} (ID: ${amicale.id})');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'une amicale: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cas 2: Les données sont un objet avec une clé 'data' contenant une liste
|
||||
else if (amicalesData is Map && amicalesData.containsKey('data')) {
|
||||
final amicalesList = amicalesData['data'] as List<dynamic>;
|
||||
for (final amicaleData in amicalesList) {
|
||||
try {
|
||||
final amicale = AmicaleModel.fromJson(amicaleData);
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
count++;
|
||||
debugPrint('Amicale traitée: ${amicale.name} (ID: ${amicale.id})');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement d\'une amicale: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
// Cas 3: Les données sont un objet amicale unique (pas une liste)
|
||||
else if (amicalesData is Map) {
|
||||
try {
|
||||
// Convertir Map<dynamic, dynamic> en Map<String, dynamic>
|
||||
final Map<String, dynamic> amicaleMap = {};
|
||||
amicalesData.forEach((key, value) {
|
||||
if (key is String) {
|
||||
amicaleMap[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
final amicale = AmicaleModel.fromJson(amicaleMap);
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
count++;
|
||||
debugPrint('Amicale unique traitée: ${amicale.name} (ID: ${amicale.id})');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement de l\'amicale unique: $e');
|
||||
debugPrint('Exception détaillée: $e');
|
||||
}
|
||||
} else {
|
||||
debugPrint('Format de données d\'amicale non reconnu');
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint('$count amicales traitées et stockées');
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors du traitement des amicales: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les amicales depuis l'API
|
||||
Future<List<AmicaleModel>> fetchAmicalesFromApi() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _apiService.get('/amicales');
|
||||
final response = await ApiService.instance.get('/amicales');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final amicalesData = response.data;
|
||||
@@ -407,7 +307,7 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _apiService.get('/amicales/$id');
|
||||
final response = await ApiService.instance.get('/amicales/$id');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final amicaleData = response.data;
|
||||
@@ -427,34 +327,7 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Mettre à jour une amicale via l'API
|
||||
Future<AmicaleModel?> updateAmicaleViaApi(AmicaleModel amicale) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await _apiService.put(
|
||||
'/amicales/${amicale.id}',
|
||||
data: amicale.toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final updatedAmicaleData = response.data;
|
||||
final updatedAmicale = AmicaleModel.fromJson(updatedAmicaleData);
|
||||
await saveAmicale(updatedAmicale);
|
||||
return updatedAmicale;
|
||||
} else {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'amicale: ${response.statusCode}');
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la mise à jour de l\'amicale: $e');
|
||||
return null;
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
// === MÉTHODES DE FILTRAGE ET RECHERCHE ===
|
||||
|
||||
// Filtrer les amicales par nom
|
||||
List<AmicaleModel> searchAmicalesByName(String query) {
|
||||
@@ -466,11 +339,6 @@ class AmicaleRepository extends ChangeNotifier {
|
||||
return _amicaleBox.values.where((amicale) => amicale.name.toLowerCase().contains(lowercaseQuery)).toList();
|
||||
}
|
||||
|
||||
// Filtrer les amicales par type
|
||||
List<AmicaleModel> getAmicalesByType(int type) {
|
||||
return _amicaleBox.values.where((amicale) => amicale.fkType == type).toList();
|
||||
}
|
||||
|
||||
// Filtrer les amicales par région
|
||||
List<AmicaleModel> getAmicalesByRegion(int regionId) {
|
||||
return _amicaleBox.values.where((amicale) => amicale.fkRegion == regionId).toList();
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
class ClientRepository extends ChangeNotifier {
|
||||
// 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 {
|
||||
@@ -58,8 +59,11 @@ class ClientRepository extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
// Préparer les données pour l'API - exclure l'id pour la création
|
||||
final data = client.toJson();
|
||||
data.remove('id'); // L'API génère l'ID
|
||||
data.remove('created_at'); // L'API génère created_at
|
||||
data.remove('updated_at'); // L'API génère updated_at
|
||||
|
||||
// Appeler l'API pour créer le client
|
||||
final response = await ApiService.instance.post('/clients', data: data);
|
||||
@@ -70,11 +74,13 @@ class ClientRepository extends ChangeNotifier {
|
||||
|
||||
// Créer le client localement avec l'ID retourné par l'API
|
||||
final newClient = client.copyWith(
|
||||
id: clientId,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
await saveClient(newClient);
|
||||
|
||||
// Sauvegarder avec le nouvel ID
|
||||
await _clientBox.put(clientId, newClient);
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -95,17 +101,14 @@ class ClientRepository extends ChangeNotifier {
|
||||
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
|
||||
// Mettre à jour le client localement avec updatedAt
|
||||
final updatedClient = client.copyWith(
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
|
||||
await saveClient(updatedClient);
|
||||
return true;
|
||||
}
|
||||
@@ -190,7 +193,6 @@ class ClientRepository extends ChangeNotifier {
|
||||
|
||||
// Vider la boîte des clients
|
||||
Future<void> clearClients() async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _clientBox.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -220,6 +222,7 @@ class ClientRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES DE FILTRAGE ET RECHERCHE ===
|
||||
// Filtrer les clients par nom
|
||||
List<ClientModel> searchClientsByName(String query) {
|
||||
if (query.isEmpty) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
class MembreRepository extends ChangeNotifier {
|
||||
// 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 {
|
||||
@@ -43,6 +44,9 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES SPÉCIFIQUES AUX MEMBRES ===
|
||||
|
||||
// Récupérer les membres par amicale
|
||||
List<MembreModel> getMembresByAmicale(int fkEntite) {
|
||||
try {
|
||||
return _membreBox.values.where((membre) => membre.fkEntite == fkEntite).toList();
|
||||
@@ -55,7 +59,7 @@ class MembreRepository extends ChangeNotifier {
|
||||
// Récupérer les membres actifs par amicale
|
||||
List<MembreModel> getActiveMembresByAmicale(int fkEntite) {
|
||||
try {
|
||||
return _membreBox.values.where((membre) => membre.fkEntite == fkEntite && membre.chkActive == 1).toList();
|
||||
return _membreBox.values.where((membre) => membre.fkEntite == fkEntite && membre.isActive == true).toList();
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des membres actifs par amicale: $e');
|
||||
return [];
|
||||
@@ -72,6 +76,8 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES CRUD DE BASE ===
|
||||
|
||||
// Récupérer tous les membres
|
||||
List<MembreModel> getAllMembres() {
|
||||
try {
|
||||
@@ -94,27 +100,28 @@ class MembreRepository extends ChangeNotifier {
|
||||
|
||||
// Sauvegarder un membre
|
||||
Future<void> saveMembre(MembreModel membre) async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _membreBox.put(membre.id, membre);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Supprimer un membre
|
||||
Future<void> deleteMembre(int id) async {
|
||||
await _ensureBoxIsOpen();
|
||||
await _membreBox.delete(id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// === MÉTHODES API ===
|
||||
|
||||
// Créer un membre via l'API
|
||||
Future<bool> createMembre(MembreModel membre) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
// Préparer les données pour l'API
|
||||
// Préparer les données pour l'API - exclure l'id pour la création
|
||||
final data = membre.toJson();
|
||||
|
||||
data.remove('id'); // L'API génère l'ID
|
||||
data.remove('created_at'); // L'API génère created_at
|
||||
// Appeler l'API pour créer le membre
|
||||
final response = await ApiService.instance.post('/membres', data: data);
|
||||
|
||||
@@ -123,11 +130,24 @@ class MembreRepository extends ChangeNotifier {
|
||||
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(
|
||||
final newMembre = MembreModel(
|
||||
id: membreId,
|
||||
lastSyncedAt: DateTime.now(),
|
||||
isSynced: true,
|
||||
fkEntite: membre.fkEntite,
|
||||
role: membre.role,
|
||||
fkTitre: membre.fkTitre,
|
||||
name: membre.name,
|
||||
firstName: membre.firstName,
|
||||
username: membre.username,
|
||||
sectName: membre.sectName,
|
||||
email: membre.email,
|
||||
phone: membre.phone,
|
||||
mobile: membre.mobile,
|
||||
dateNaissance: membre.dateNaissance,
|
||||
dateEmbauche: membre.dateEmbauche,
|
||||
createdAt: DateTime.now(),
|
||||
isActive: membre.isActive,
|
||||
);
|
||||
|
||||
await saveMembre(newMembre);
|
||||
return true;
|
||||
}
|
||||
@@ -154,13 +174,8 @@ class MembreRepository extends ChangeNotifier {
|
||||
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);
|
||||
// Sauvegarder le membre mis à jour localement
|
||||
await saveMembre(membre);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -199,6 +214,8 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// === TRAITEMENT DES DONNÉES ===
|
||||
|
||||
// Traitement des données de membres depuis l'API
|
||||
Future<void> processMembresData(dynamic membresData) async {
|
||||
try {
|
||||
@@ -221,9 +238,7 @@ class MembreRepository extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -242,4 +257,55 @@ class MembreRepository extends ChangeNotifier {
|
||||
debugPrint('Erreur lors du traitement des membres: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer les membres depuis l'API
|
||||
Future<List<MembreModel>> fetchMembresFromApi() async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final response = await ApiService.instance.get('/membres');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final membresData = response.data;
|
||||
await processMembresData(membresData);
|
||||
return getAllMembres();
|
||||
} else {
|
||||
debugPrint('Erreur lors de la récupération des membres: ${response.statusCode}');
|
||||
return [];
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération des membres: $e');
|
||||
return [];
|
||||
} finally {
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES DE FILTRAGE ET RECHERCHE ===
|
||||
|
||||
// Filtrer les membres par nom
|
||||
List<MembreModel> searchMembresByName(String query) {
|
||||
if (query.isEmpty) {
|
||||
return getAllMembres();
|
||||
}
|
||||
|
||||
final lowercaseQuery = query.toLowerCase();
|
||||
return _membreBox.values
|
||||
.where(
|
||||
(membre) => (membre.name?.toLowerCase().contains(lowercaseQuery) ?? false) || (membre.firstName?.toLowerCase().contains(lowercaseQuery) ?? false))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Filtrer les membres actifs
|
||||
List<MembreModel> getActiveMembres() {
|
||||
return _membreBox.values.where((membre) => membre.isActive == true).toList();
|
||||
}
|
||||
|
||||
// Vider la boîte des membres
|
||||
Future<void> clearMembres() async {
|
||||
await _membreBox.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,11 @@ class PassageRepository extends ChangeNotifier {
|
||||
return _passageBox.values.where((passage) => passage.fkType == type).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 date
|
||||
List<PassageModel> getPassagesByDate(DateTime date) {
|
||||
return _passageBox.values.where((passage) {
|
||||
|
||||
@@ -226,9 +226,6 @@ class UserRepository extends ChangeNotifier {
|
||||
try {
|
||||
debugPrint('🔐 Tentative de connexion: $username');
|
||||
|
||||
// Étape 1: Nettoyage des données via DataLoadingService
|
||||
await DataLoadingService.instance.cleanDataBeforeLogin();
|
||||
|
||||
// Étape 2: Connexion à l'API (25%)
|
||||
final apiResult = await loginAPI(username, password, type: type);
|
||||
|
||||
@@ -264,7 +261,13 @@ class UserRepository extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// Étape 5: Traitement de toutes les autres données via DataLoadingService
|
||||
await DataLoadingService.instance.processLoginData(apiResult);
|
||||
try {
|
||||
await DataLoadingService.instance.processLoginData(apiResult);
|
||||
} catch (processingError) {
|
||||
debugPrint('❌ Erreur lors du traitement des données: $processingError');
|
||||
// On continue quand même car l'utilisateur est connecté
|
||||
debugPrint('⚠️ Connexion réussie mais avec des données partielles');
|
||||
}
|
||||
|
||||
debugPrint('✅ Connexion réussie');
|
||||
return true;
|
||||
|
||||
@@ -72,8 +72,6 @@ class ApiService {
|
||||
return computation();
|
||||
}
|
||||
|
||||
// === TOUTES LES MÉTHODES EXISTANTES RESTENT IDENTIQUES ===
|
||||
|
||||
// Détermine l'environnement actuel (DEV, REC, PROD) en fonction de l'URL
|
||||
String _determineEnvironment() {
|
||||
if (!kIsWeb) {
|
||||
@@ -136,7 +134,7 @@ class ApiService {
|
||||
// Vérifier la connectivité réseau
|
||||
Future<bool> hasInternetConnection() async {
|
||||
final connectivityResult = await (Connectivity().checkConnectivity());
|
||||
return connectivityResult != ConnectivityResult.none;
|
||||
return connectivityResult.contains(ConnectivityResult.none) == false;
|
||||
}
|
||||
|
||||
// Méthode POST générique
|
||||
@@ -273,10 +271,6 @@ class ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
// Espace réservé pour les futures méthodes de gestion des profils
|
||||
|
||||
// Espace réservé pour les futures méthodes de gestion des données
|
||||
|
||||
// Synchronisation en batch
|
||||
Future<Map<String, dynamic>> syncData({
|
||||
List<UserModel>? users,
|
||||
@@ -297,228 +291,4 @@ class ApiService {
|
||||
static void reset() {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
final Dio _dio = Dio();
|
||||
late final String _baseUrl;
|
||||
late final String _appIdentifier;
|
||||
String? _sessionId;
|
||||
|
||||
// Détermine l'environnement actuel (DEV, REC, PROD) en fonction de l'URL
|
||||
String _determineEnvironment() {
|
||||
if (!kIsWeb) {
|
||||
// En mode non-web, utiliser l'environnement de développement par défaut
|
||||
return 'DEV';
|
||||
}
|
||||
|
||||
final currentUrl = html.window.location.href.toLowerCase();
|
||||
|
||||
if (currentUrl.contains('dapp.geosector.fr')) {
|
||||
return 'DEV';
|
||||
} else if (currentUrl.contains('rapp.geosector.fr')) {
|
||||
return 'REC';
|
||||
} else {
|
||||
return 'PROD';
|
||||
}
|
||||
}
|
||||
|
||||
// Configure l'URL de base API et l'identifiant d'application selon l'environnement
|
||||
void _configureEnvironment() {
|
||||
final env = _determineEnvironment();
|
||||
|
||||
switch (env) {
|
||||
case 'DEV':
|
||||
_baseUrl = AppKeys.baseApiUrlDev;
|
||||
_appIdentifier = AppKeys.appIdentifierDev;
|
||||
break;
|
||||
case 'REC':
|
||||
_baseUrl = AppKeys.baseApiUrlRec;
|
||||
_appIdentifier = AppKeys.appIdentifierRec;
|
||||
break;
|
||||
default: // PROD
|
||||
_baseUrl = AppKeys.baseApiUrlProd;
|
||||
_appIdentifier = AppKeys.appIdentifierProd;
|
||||
}
|
||||
|
||||
debugPrint('GEOSECTOR 🔗 Environnement: $env, API: $_baseUrl');
|
||||
}
|
||||
|
||||
// Définir l'ID de session
|
||||
void setSessionId(String? sessionId) {
|
||||
_sessionId = sessionId;
|
||||
}
|
||||
|
||||
// Obtenir l'environnement actuel (utile pour le débogage)
|
||||
String getCurrentEnvironment() {
|
||||
return _determineEnvironment();
|
||||
}
|
||||
|
||||
// Obtenir l'URL API actuelle (utile pour le débogage)
|
||||
String getCurrentApiUrl() {
|
||||
return _baseUrl;
|
||||
}
|
||||
|
||||
// Obtenir l'identifiant d'application actuel (utile pour le débogage)
|
||||
String getCurrentAppIdentifier() {
|
||||
return _appIdentifier;
|
||||
}
|
||||
|
||||
// Vérifier la connectivité réseau
|
||||
Future<bool> hasInternetConnection() async {
|
||||
final connectivityResult = await (Connectivity().checkConnectivity());
|
||||
return connectivityResult.contains(ConnectivityResult.none) == false;
|
||||
}
|
||||
|
||||
// Méthode POST générique
|
||||
Future<Response> post(String path, {dynamic data}) async {
|
||||
try {
|
||||
return await _dio.post(path, data: data);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode GET générique
|
||||
Future<Response> get(String path, {Map<String, dynamic>? queryParameters}) async {
|
||||
try {
|
||||
return await _dio.get(path, queryParameters: queryParameters);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode PUT générique
|
||||
Future<Response> put(String path, {dynamic data}) async {
|
||||
try {
|
||||
return await _dio.put(path, data: data);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode DELETE générique
|
||||
Future<Response> delete(String path) async {
|
||||
try {
|
||||
return await _dio.delete(path);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Authentification avec PHP session
|
||||
Future<Map<String, dynamic>> login(String username, String password, {required String type}) async {
|
||||
try {
|
||||
final response = await _dio.post(AppKeys.loginEndpoint, data: {
|
||||
'username': username,
|
||||
'password': password,
|
||||
'type': type, // Ajouter le type de connexion (user ou admin)
|
||||
});
|
||||
|
||||
// Vérifier la structure de la réponse
|
||||
final data = response.data as Map<String, dynamic>;
|
||||
final status = data['status'] as String?;
|
||||
|
||||
// Afficher le message en cas d'erreur
|
||||
if (status != 'success') {
|
||||
final message = data['message'] as String?;
|
||||
debugPrint('Erreur d\'authentification: $message');
|
||||
}
|
||||
|
||||
// Si le statut est 'success', récupérer le session_id
|
||||
if (status == 'success' && data.containsKey('session_id')) {
|
||||
final sessionId = data['session_id'];
|
||||
// Définir la session pour les futures requêtes
|
||||
if (sessionId != null) {
|
||||
setSessionId(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Déconnexion
|
||||
Future<void> logout() async {
|
||||
try {
|
||||
if (_sessionId != null) {
|
||||
await _dio.post(AppKeys.logoutEndpoint);
|
||||
_sessionId = null;
|
||||
}
|
||||
} catch (e) {
|
||||
// Même en cas d'erreur, on réinitialise la session
|
||||
_sessionId = null;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Utilisateurs
|
||||
Future<List<UserModel>> getUsers() async {
|
||||
try {
|
||||
final response = await retry(
|
||||
() => _dio.get('/users'),
|
||||
retryIf: (e) => e is SocketException || e is TimeoutException,
|
||||
);
|
||||
|
||||
return (response.data as List).map((json) => UserModel.fromJson(json)).toList();
|
||||
} catch (e) {
|
||||
// Gérer les erreurs
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<UserModel> getUserById(int id) async {
|
||||
try {
|
||||
final response = await _dio.get('/users/$id');
|
||||
return UserModel.fromJson(response.data);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<UserModel> createUser(UserModel user) async {
|
||||
try {
|
||||
final response = await _dio.post('/users', data: user.toJson());
|
||||
return UserModel.fromJson(response.data);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<UserModel> updateUser(UserModel user) async {
|
||||
try {
|
||||
final response = await _dio.put('/users/${user.id}', data: user.toJson());
|
||||
return UserModel.fromJson(response.data);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteUser(String id) async {
|
||||
try {
|
||||
await _dio.delete('/users/$id');
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// Espace réservé pour les futures méthodes de gestion des profils
|
||||
|
||||
// Espace réservé pour les futures méthodes de gestion des données
|
||||
|
||||
// Synchronisation en batch
|
||||
Future<Map<String, dynamic>> syncData({
|
||||
List<UserModel>? users,
|
||||
}) async {
|
||||
try {
|
||||
final Map<String, dynamic> payload = {
|
||||
if (users != null) 'users': users.map((u) => u.toJson()).toList(),
|
||||
};
|
||||
|
||||
final response = await _dio.post('/sync', data: payload);
|
||||
return response.data;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,135 +53,67 @@ class DataLoadingService extends ChangeNotifier {
|
||||
Box<MessageModel> get _chatMessageBox => Hive.box<MessageModel>(AppKeys.chatMessagesBoxName);
|
||||
Box get _settingsBox => Hive.box(AppKeys.settingsBoxName);
|
||||
|
||||
// === NETTOYAGE ET PRÉPARATION DES DONNÉES ===
|
||||
|
||||
/// Nettoie toutes les données avant le login
|
||||
Future<void> cleanDataBeforeLogin() async {
|
||||
try {
|
||||
_updateLoadingState(LoadingState.initial.copyWith(
|
||||
progress: 0.05,
|
||||
message: 'Nettoyage des données...',
|
||||
stepDescription: 'Suppression des données obsolètes',
|
||||
));
|
||||
|
||||
debugPrint('🧹 Début du nettoyage des données avant login...');
|
||||
|
||||
// Étape 1: Nettoyage des boîtes non référencées (5%)
|
||||
await _cleanNonDefinedBoxes();
|
||||
|
||||
// Étape 2: Nettoyage sécurisé des données (10%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.10,
|
||||
stepDescription: 'Préparation du stockage local',
|
||||
));
|
||||
|
||||
if (kIsWeb) {
|
||||
await HiveWebFix.safeCleanHiveBoxes(excludeBoxes: [AppKeys.userBoxName]);
|
||||
} else if (Platform.isIOS) {
|
||||
await _cleanHiveFilesOnIOS();
|
||||
} else if (Platform.isAndroid) {
|
||||
await _cleanHiveFilesOnAndroid();
|
||||
}
|
||||
|
||||
// Étape 3: Recréation des boîtes (15%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.15,
|
||||
stepDescription: 'Initialisation des bases de données',
|
||||
));
|
||||
|
||||
await _clearAndRecreateBoxes();
|
||||
await _initializeBoxes();
|
||||
|
||||
debugPrint('✅ Nettoyage des données terminé');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du nettoyage des données: $e');
|
||||
_updateLoadingState(LoadingState.error('Erreur lors du nettoyage: $e'));
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Traite toutes les données reçues de l'API lors du login
|
||||
/// Les boxes sont déjà propres, on charge juste les données
|
||||
Future<void> processLoginData(Map<String, dynamic> apiResult) async {
|
||||
try {
|
||||
debugPrint('📊 Début du traitement des données de login...');
|
||||
debugPrint('📊 Début du chargement des données (boxes déjà propres)...');
|
||||
|
||||
// Étape 4: Traitement des clients (35%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.35,
|
||||
stepDescription: 'Chargement des clients',
|
||||
));
|
||||
// Vérifier que les boxes sont ouvertes
|
||||
_verifyBoxesAreOpen();
|
||||
|
||||
// Charger les données directement (les boxes sont vides et propres)
|
||||
if (apiResult['clients'] != null) {
|
||||
await _processClients(apiResult['clients']);
|
||||
}
|
||||
|
||||
// Étape 5: Traitement des opérations (50%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.50,
|
||||
stepDescription: 'Chargement des opérations',
|
||||
));
|
||||
|
||||
if (apiResult['operations'] != null) {
|
||||
await _processOperations(apiResult['operations']);
|
||||
}
|
||||
|
||||
// Étape 6: Traitement des secteurs (65%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.65,
|
||||
stepDescription: 'Chargement des secteurs',
|
||||
));
|
||||
|
||||
if (apiResult['sectors'] != null) {
|
||||
await _processSectors(apiResult['sectors']);
|
||||
if (apiResult['secteurs'] != null) {
|
||||
await _processSectors(apiResult['secteurs']);
|
||||
}
|
||||
|
||||
// Étape 7: Traitement des passages (75%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.75,
|
||||
stepDescription: 'Chargement des passages',
|
||||
));
|
||||
|
||||
if (apiResult['passages'] != null) {
|
||||
await _processPassages(apiResult['passages']);
|
||||
}
|
||||
|
||||
// Étape 8: Traitement des membres (85%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.85,
|
||||
stepDescription: 'Chargement des membres',
|
||||
));
|
||||
|
||||
final membresData = apiResult['membres'] ?? apiResult['members'];
|
||||
if (membresData != null) {
|
||||
await _processMembres(membresData);
|
||||
if (apiResult['amicale'] != null) {
|
||||
await _processAmicale(apiResult['amicale']);
|
||||
}
|
||||
if (apiResult['membres'] != null) {
|
||||
await _processMembres(apiResult['membres']);
|
||||
}
|
||||
|
||||
// Étape 9: Traitement des associations utilisateurs-secteurs (95%)
|
||||
_updateLoadingState(_loadingState.copyWith(
|
||||
progress: 0.95,
|
||||
stepDescription: 'Finalisation du chargement',
|
||||
));
|
||||
|
||||
if (apiResult['users_sectors'] != null) {
|
||||
await _processUserSectors(apiResult['users_sectors']);
|
||||
if (apiResult['userSecteurs'] != null) {
|
||||
await _processUserSectors(apiResult['userSecteurs']);
|
||||
}
|
||||
|
||||
// Étape finale: Chargement terminé (100%)
|
||||
_updateLoadingState(LoadingState.completed.copyWith(
|
||||
progress: 1.0,
|
||||
message: 'Chargement terminé avec succès',
|
||||
stepDescription: 'Données synchronisées',
|
||||
));
|
||||
|
||||
debugPrint('✅ Traitement des données de login terminé');
|
||||
_logDataSummary();
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du traitement des données de login: $e');
|
||||
_updateLoadingState(LoadingState.error('Erreur lors du chargement: $e'));
|
||||
debugPrint('❌ Erreur lors du chargement: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void _verifyBoxesAreOpen() {
|
||||
final requiredBoxes = [
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.membresBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.amicaleBoxName,
|
||||
];
|
||||
|
||||
for (final boxName in requiredBoxes) {
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
throw Exception('La boîte $boxName n\'est pas ouverte. Redémarrez l\'application.');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('✅ Toutes les boîtes requises sont ouvertes');
|
||||
}
|
||||
|
||||
/// Nettoie complètement toutes les données lors du logout
|
||||
Future<void> cleanDataAfterLogout() async {
|
||||
try {
|
||||
@@ -194,123 +126,12 @@ class DataLoadingService extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES PRIVÉES DE NETTOYAGE ===
|
||||
|
||||
Future<void> _cleanNonDefinedBoxes() async {
|
||||
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();
|
||||
}
|
||||
await Hive.deleteBoxFromDisk(boxName);
|
||||
debugPrint('✅ Box $boxName supprimée');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur suppression box $boxName: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _clearAndRecreateBoxes() async {
|
||||
try {
|
||||
debugPrint('🔄 Recréation des boîtes Hive...');
|
||||
|
||||
final boxesToDelete = [
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.chatConversationsBoxName,
|
||||
AppKeys.chatMessagesBoxName,
|
||||
];
|
||||
|
||||
// Vider chaque boîte sans la fermer
|
||||
for (final boxName in boxesToDelete) {
|
||||
try {
|
||||
if (Hive.isBoxOpen(boxName)) {
|
||||
final box = Hive.box(boxName);
|
||||
await box.clear();
|
||||
debugPrint('✅ Box $boxName vidée');
|
||||
} else {
|
||||
await Hive.deleteBoxFromDisk(boxName);
|
||||
debugPrint('✅ Box $boxName supprimée du disque');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur nettoyage box $boxName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur recréation des boîtes: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeBoxes() async {
|
||||
debugPrint('📦 Initialisation des boîtes Hive...');
|
||||
|
||||
final boxesToInit = [
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.membresBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.chatConversationsBoxName,
|
||||
AppKeys.chatMessagesBoxName,
|
||||
];
|
||||
|
||||
for (final boxName in boxesToInit) {
|
||||
await _ensureBoxIsOpen(boxName);
|
||||
}
|
||||
|
||||
debugPrint('✅ Toutes les boîtes Hive sont ouvertes');
|
||||
}
|
||||
|
||||
Future<void> _ensureBoxIsOpen(String boxName) async {
|
||||
try {
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName...');
|
||||
|
||||
switch (boxName) {
|
||||
case AppKeys.passagesBoxName:
|
||||
await Hive.openBox<PassageModel>(boxName);
|
||||
break;
|
||||
case AppKeys.operationsBoxName:
|
||||
await Hive.openBox<OperationModel>(boxName);
|
||||
break;
|
||||
case AppKeys.sectorsBoxName:
|
||||
await Hive.openBox<SectorModel>(boxName);
|
||||
break;
|
||||
case AppKeys.membresBoxName:
|
||||
await Hive.openBox<MembreModel>(boxName);
|
||||
break;
|
||||
case AppKeys.userSectorBoxName:
|
||||
await Hive.openBox<UserSectorModel>(boxName);
|
||||
break;
|
||||
case AppKeys.chatConversationsBoxName:
|
||||
await Hive.openBox<ConversationModel>(boxName);
|
||||
break;
|
||||
case AppKeys.chatMessagesBoxName:
|
||||
await Hive.openBox<MessageModel>(boxName);
|
||||
break;
|
||||
default:
|
||||
await Hive.openBox(boxName);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur ouverture box $boxName: $e');
|
||||
throw Exception('Impossible d\'ouvrir la boîte $boxName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES DE TRAITEMENT DES DONNÉES ===
|
||||
|
||||
Future<void> _processClients(dynamic clientsData) async {
|
||||
try {
|
||||
debugPrint('👥 Traitement des clients...');
|
||||
|
||||
|
||||
List<dynamic> clientsList;
|
||||
if (clientsData is List) {
|
||||
clientsList = clientsData;
|
||||
@@ -335,7 +156,7 @@ class DataLoadingService extends ChangeNotifier {
|
||||
Future<void> _processOperations(dynamic operationsData) async {
|
||||
try {
|
||||
debugPrint('⚙️ Traitement des opérations...');
|
||||
|
||||
|
||||
if (operationsData == null) {
|
||||
debugPrint('ℹ️ Aucune donnée d\'opération à traiter');
|
||||
return;
|
||||
@@ -373,7 +194,7 @@ class DataLoadingService extends ChangeNotifier {
|
||||
Future<void> _processSectors(dynamic sectorsData) async {
|
||||
try {
|
||||
debugPrint('📍 Traitement des secteurs...');
|
||||
|
||||
|
||||
if (sectorsData == null) {
|
||||
debugPrint('ℹ️ Aucune donnée de secteur à traiter');
|
||||
return;
|
||||
@@ -411,7 +232,7 @@ class DataLoadingService extends ChangeNotifier {
|
||||
Future<void> _processPassages(dynamic passagesData) async {
|
||||
try {
|
||||
debugPrint('🚶 Traitement des passages...');
|
||||
|
||||
|
||||
if (passagesData == null) {
|
||||
debugPrint('ℹ️ Aucune donnée de passage à traiter');
|
||||
return;
|
||||
@@ -446,10 +267,37 @@ class DataLoadingService extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter cette nouvelle méthode pour traiter l'amicale unique
|
||||
Future<void> _processAmicale(dynamic amicaleData) async {
|
||||
try {
|
||||
debugPrint('🏢 Traitement de l\'amicale unique...');
|
||||
|
||||
if (amicaleData == null) {
|
||||
debugPrint('ℹ️ Aucune donnée d\'amicale à traiter');
|
||||
return;
|
||||
}
|
||||
|
||||
await _amicaleBox.clear();
|
||||
|
||||
try {
|
||||
// Les données d'amicale sont un objet unique
|
||||
final Map<String, dynamic> amicaleMap = Map<String, dynamic>.from(amicaleData as Map);
|
||||
final amicale = AmicaleModel.fromJson(amicaleMap);
|
||||
await _amicaleBox.put(amicale.id, amicale);
|
||||
debugPrint('✅ Amicale stockée: ${amicale.name} (ID: ${amicale.id})');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur traitement amicale: $e');
|
||||
debugPrint('⚠️ Données reçues: $amicaleData');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur traitement amicale: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _processMembres(dynamic membresData) async {
|
||||
try {
|
||||
debugPrint('👤 Traitement des membres...');
|
||||
|
||||
|
||||
if (membresData == null) {
|
||||
debugPrint('ℹ️ Aucune donnée de membre à traiter');
|
||||
return;
|
||||
@@ -487,7 +335,7 @@ class DataLoadingService extends ChangeNotifier {
|
||||
Future<void> _processUserSectors(dynamic userSectorsData) async {
|
||||
try {
|
||||
debugPrint('🔗 Traitement des associations utilisateurs-secteurs...');
|
||||
|
||||
|
||||
if (userSectorsData == null) {
|
||||
debugPrint('ℹ️ Aucune association utilisateur-secteur à traiter');
|
||||
return;
|
||||
@@ -625,43 +473,8 @@ class DataLoadingService extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// === MÉTHODES UTILITAIRES ===
|
||||
|
||||
/// Affiche un résumé des données chargées
|
||||
void _logDataSummary() {
|
||||
try {
|
||||
debugPrint('📊 === RÉSUMÉ DES DONNÉES CHARGÉES ===');
|
||||
debugPrint('Opérations: ${_operationBox.length}');
|
||||
debugPrint('Secteurs: ${_sectorBox.length}');
|
||||
debugPrint('Passages: ${_passageBox.length}');
|
||||
debugPrint('Membres: ${_membreBox.length}');
|
||||
debugPrint('Associations User-Sector: ${_userSectorBox.length}');
|
||||
debugPrint('Amicales: ${_amicaleBox.length}');
|
||||
debugPrint('=================================');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur lors du résumé: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Retourne un résumé des données pour l'UI
|
||||
Map<String, int> getDataSummary() {
|
||||
try {
|
||||
return {
|
||||
'operations': _operationBox.length,
|
||||
'sectors': _sectorBox.length,
|
||||
'passages': _passageBox.length,
|
||||
'membres': _membreBox.length,
|
||||
'userSectors': _userSectorBox.length,
|
||||
'amicales': _amicaleBox.length,
|
||||
};
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur génération résumé: $e');
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// === RESET POUR TESTS ===
|
||||
static void reset() {
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
65
app/lib/core/services/hive_adapters.dart
Normal file
65
app/lib/core/services/hive_adapters.dart
Normal file
@@ -0,0 +1,65 @@
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:geosector_app/core/data/models/user_model.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
import 'package:geosector_app/core/data/models/client_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/data/models/user_sector_model.dart';
|
||||
import 'package:geosector_app/core/data/models/region_model.dart';
|
||||
import 'package:geosector_app/chat/models/chat_adapters.dart';
|
||||
|
||||
class HiveAdapters {
|
||||
/// Enregistre tous les TypeAdapters nécessaires
|
||||
static void registerAll() {
|
||||
// Modèles principaux
|
||||
if (!Hive.isAdapterRegistered(0)) {
|
||||
Hive.registerAdapter(UserModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(1)) {
|
||||
Hive.registerAdapter(OperationModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(3)) {
|
||||
Hive.registerAdapter(SectorModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(4)) {
|
||||
Hive.registerAdapter(PassageModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(5)) {
|
||||
Hive.registerAdapter(MembreModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(6)) {
|
||||
Hive.registerAdapter(UserSectorModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(7)) {
|
||||
Hive.registerAdapter(RegionModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(10)) {
|
||||
Hive.registerAdapter(ClientModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(11)) {
|
||||
Hive.registerAdapter(AmicaleModelAdapter());
|
||||
}
|
||||
|
||||
// Chat adapters
|
||||
if (!Hive.isAdapterRegistered(20)) {
|
||||
Hive.registerAdapter(ConversationModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(21)) {
|
||||
Hive.registerAdapter(MessageModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(22)) {
|
||||
Hive.registerAdapter(ParticipantModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(23)) {
|
||||
Hive.registerAdapter(AnonymousUserModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(24)) {
|
||||
Hive.registerAdapter(AudienceTargetModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(25)) {
|
||||
Hive.registerAdapter(NotificationSettingsAdapter());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,9 @@ import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
||||
import 'package:geosector_app/core/services/app_info_service.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'package:geosector_app/core/services/current_user_service.dart';
|
||||
import 'package:geosector_app/core/services/current_amicale_service.dart';
|
||||
import 'package:geosector_app/app.dart';
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:geosector_app/core/data/models/user_model.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
import 'package:geosector_app/core/data/models/client_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/data/models/user_sector_model.dart';
|
||||
import 'package:geosector_app/core/data/models/region_model.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
import 'package:geosector_app/core/services/hive_reset_state_service.dart';
|
||||
import 'package:geosector_app/chat/models/chat_adapters.dart';
|
||||
import 'package:geosector_app/core/services/hive_adapters.dart';
|
||||
|
||||
void main() async {
|
||||
// IMPORTANT: Configurer l'URL strategy pour éviter les # dans les URLs
|
||||
@@ -31,13 +18,7 @@ void main() async {
|
||||
await _initializeServices();
|
||||
|
||||
// Initialiser Hive avec gestion des erreurs
|
||||
final hiveInitialized = await _initializeHive();
|
||||
|
||||
// TEMPORAIREMENT: Ne pas marquer l'erreur pour éviter la redirection
|
||||
// if (!hiveInitialized) {
|
||||
// debugPrint('Incompatibilité détectée dans les données Hive. Marquage pour affichage du dialogue...');
|
||||
// hiveResetStateService.markAsReset();
|
||||
// }
|
||||
await _initializeHive();
|
||||
|
||||
// Configurer l'orientation de l'application (mobile uniquement)
|
||||
if (!kIsWeb) {
|
||||
@@ -71,185 +52,16 @@ Future<void> _initializeServices() async {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise Hive et les adaptateurs
|
||||
Future<bool> _initializeHive() async {
|
||||
Future<void> _initializeHive() async {
|
||||
try {
|
||||
// Initialiser Hive
|
||||
await Hive.initFlutter();
|
||||
|
||||
// Enregistrer les adaptateurs Hive pour les modèles principaux
|
||||
_registerHiveAdapters();
|
||||
// Enregistrer tous les adapters
|
||||
HiveAdapters.registerAll();
|
||||
|
||||
// Ouvrir uniquement les boîtes essentielles au démarrage
|
||||
await _openEssentialHiveBoxes();
|
||||
|
||||
// Charger les données depuis Hive au démarrage
|
||||
await CurrentUserService.instance.loadFromHive();
|
||||
await CurrentAmicaleService.instance.loadFromHive();
|
||||
debugPrint('✅ Données utilisateur/amicale chargées depuis Hive');
|
||||
|
||||
debugPrint('Hive initialisé avec succès');
|
||||
return true;
|
||||
debugPrint('✅ Hive et TypeAdapters initialisés');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'initialisation de Hive: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Enregistre tous les adaptateurs Hive
|
||||
void _registerHiveAdapters() {
|
||||
// Vérifier si les adaptateurs sont déjà enregistrés pour éviter les doublons
|
||||
if (!Hive.isAdapterRegistered(0)) {
|
||||
Hive.registerAdapter(UserModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(1)) {
|
||||
Hive.registerAdapter(AmicaleModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(2)) {
|
||||
Hive.registerAdapter(ClientModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(3)) {
|
||||
Hive.registerAdapter(OperationModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(4)) {
|
||||
Hive.registerAdapter(SectorModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(5)) {
|
||||
Hive.registerAdapter(PassageModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(6)) {
|
||||
Hive.registerAdapter(MembreModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(7)) {
|
||||
Hive.registerAdapter(UserSectorModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(8)) {
|
||||
Hive.registerAdapter(RegionModelAdapter());
|
||||
}
|
||||
|
||||
// Modèles de chat
|
||||
if (!Hive.isAdapterRegistered(9)) {
|
||||
Hive.registerAdapter(ConversationModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(10)) {
|
||||
Hive.registerAdapter(MessageModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(11)) {
|
||||
Hive.registerAdapter(ParticipantModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(12)) {
|
||||
Hive.registerAdapter(AnonymousUserModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(13)) {
|
||||
Hive.registerAdapter(AudienceTargetModelAdapter());
|
||||
}
|
||||
if (!Hive.isAdapterRegistered(14)) {
|
||||
Hive.registerAdapter(NotificationSettingsAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
/// Ouvre les boîtes Hive essentielles avec migration users -> user
|
||||
Future<void> _openEssentialHiveBoxes() async {
|
||||
final boxesToOpen = [
|
||||
{'name': AppKeys.userBoxName, 'type': 'UserModel'},
|
||||
{'name': AppKeys.amicaleBoxName, 'type': 'AmicaleModel'},
|
||||
{'name': AppKeys.clientsBoxName, 'type': 'ClientModel'},
|
||||
{'name': AppKeys.settingsBoxName, 'type': 'dynamic'},
|
||||
{'name': AppKeys.chatConversationsBoxName, 'type': 'ConversationModel'},
|
||||
{'name': AppKeys.chatMessagesBoxName, 'type': 'MessageModel'},
|
||||
];
|
||||
|
||||
// Logique de migration de l'ancienne box users vers user
|
||||
try {
|
||||
// Vérifier si l'ancienne box users existe
|
||||
if (await _doesBoxExist(AppKeys.usersBoxNameOld)) {
|
||||
debugPrint('🔄 Migration détectée: box users -> user');
|
||||
|
||||
// Ouvrir l'ancienne box
|
||||
final oldBox = await Hive.openBox<UserModel>(AppKeys.usersBoxNameOld);
|
||||
|
||||
// Ouvrir la nouvelle box
|
||||
final newBox = await Hive.openBox<UserModel>(AppKeys.userBoxName);
|
||||
|
||||
// Migrer les données si la nouvelle box est vide
|
||||
if (oldBox.isNotEmpty && newBox.isEmpty) {
|
||||
debugPrint('📦 Migration des données users -> user...');
|
||||
|
||||
// Chercher l'utilisateur actuel dans l'ancienne box
|
||||
final userData = oldBox.get('current_user') ?? oldBox.values.firstOrNull;
|
||||
if (userData != null) {
|
||||
await newBox.put('current_user', userData);
|
||||
debugPrint('✅ Migration de users -> user réussie pour ${userData.email}');
|
||||
}
|
||||
}
|
||||
|
||||
// Fermer et supprimer l'ancienne box
|
||||
await oldBox.close();
|
||||
await Hive.deleteBoxFromDisk(AppKeys.usersBoxNameOld);
|
||||
debugPrint('🗑️ Ancienne box users supprimée');
|
||||
} else {
|
||||
// Ouvrir normalement la nouvelle box
|
||||
await Hive.openBox<UserModel>(AppKeys.userBoxName);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur migration box users: $e');
|
||||
// En cas d'erreur, ouvrir quand même la nouvelle box
|
||||
try {
|
||||
await Hive.openBox<UserModel>(AppKeys.userBoxName);
|
||||
} catch (e2) {
|
||||
debugPrint('❌ Impossible d\'ouvrir la box user: $e2');
|
||||
}
|
||||
}
|
||||
|
||||
// Ouvrir les autres boîtes
|
||||
for (final box in boxesToOpen) {
|
||||
try {
|
||||
final boxName = box['name'] as String;
|
||||
final boxType = box['type'] as String;
|
||||
|
||||
// Skip userBoxName car déjà traité dans la migration
|
||||
if (boxName == AppKeys.userBoxName) continue;
|
||||
|
||||
// Vérifier si la boîte est déjà ouverte
|
||||
if (Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('📦 Boîte $boxName déjà ouverte');
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (boxType) {
|
||||
case 'AmicaleModel':
|
||||
await Hive.openBox<AmicaleModel>(boxName);
|
||||
break;
|
||||
case 'ClientModel':
|
||||
await Hive.openBox<ClientModel>(boxName);
|
||||
break;
|
||||
case 'ConversationModel':
|
||||
await Hive.openBox<ConversationModel>(boxName);
|
||||
break;
|
||||
case 'MessageModel':
|
||||
await Hive.openBox<MessageModel>(boxName);
|
||||
break;
|
||||
default:
|
||||
await Hive.openBox(boxName);
|
||||
}
|
||||
|
||||
debugPrint('✅ Boîte $boxName ouverte avec succès');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors de l\'ouverture de la boîte ${box['name']}: $e');
|
||||
// Ne pas lancer d'erreur, continuer avec les autres boîtes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Vérifie si une box Hive existe sur le disque
|
||||
Future<bool> _doesBoxExist(String boxName) async {
|
||||
try {
|
||||
// Tentative d'ouverture pour vérifier l'existence
|
||||
final box = await Hive.openBox<UserModel>(boxName);
|
||||
final exists = box.isNotEmpty;
|
||||
await box.close();
|
||||
return exists;
|
||||
} catch (e) {
|
||||
return false;
|
||||
debugPrint('❌ Erreur Hive: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geosector_app/core/services/api_service.dart';
|
||||
import 'dart:math' as math;
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:geosector_app/core/data/models/amicale_model.dart';
|
||||
@@ -64,6 +65,9 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
void _loadCurrentUser() {
|
||||
final currentUser = widget.userRepository.getCurrentUser();
|
||||
|
||||
debugPrint('🔍 _loadCurrentUser - Utilisateur: ${currentUser?.username} (ID: ${currentUser?.id})');
|
||||
debugPrint('🔍 _loadCurrentUser - fkEntite: ${currentUser?.fkEntite}');
|
||||
|
||||
if (currentUser == null) {
|
||||
setState(() {
|
||||
_errorMessage = 'Utilisateur non connecté';
|
||||
@@ -78,43 +82,16 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérifier immédiatement si l'amicale existe
|
||||
final amicale = widget.amicaleRepository.getUserAmicale(currentUser.fkEntite!);
|
||||
debugPrint('🔍 Amicale trouvée dans le repository: ${amicale?.name ?? 'null'}');
|
||||
|
||||
setState(() {
|
||||
_currentUser = currentUser;
|
||||
_errorMessage = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleEditAmicale(AmicaleModel amicale) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Modifier l\'amicale'),
|
||||
content: Text('Voulez-vous modifier l\'amicale ${amicale.name} ?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
// TODO: Naviguer vers la page de modification
|
||||
// Navigator.of(context).push(
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) => EditAmicalePage(
|
||||
// amicale: amicale,
|
||||
// amicaleRepository: widget.amicaleRepository,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
child: const Text('Modifier'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleEditMembre(MembreModel membre) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -226,9 +203,19 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
child: ValueListenableBuilder<Box<AmicaleModel>>(
|
||||
valueListenable: widget.amicaleRepository.getAmicalesBox().listenable(),
|
||||
builder: (context, amicalesBox, child) {
|
||||
debugPrint('🔍 AmicalesBox - Nombre d\'amicales: ${amicalesBox.length}');
|
||||
debugPrint('🔍 AmicalesBox - Clés disponibles: ${amicalesBox.keys.toList()}');
|
||||
debugPrint('🔍 Recherche amicale avec fkEntite: ${_currentUser!.fkEntite}');
|
||||
|
||||
final amicale = amicalesBox.get(_currentUser!.fkEntite!);
|
||||
debugPrint('🔍 Amicale récupérée: ${amicale?.name ?? 'AUCUNE'}');
|
||||
|
||||
if (amicale == null) {
|
||||
// Ajouter plus d'informations de debug
|
||||
debugPrint('❌ PROBLÈME: Amicale non trouvée');
|
||||
debugPrint('❌ fkEntite recherché: ${_currentUser!.fkEntite}');
|
||||
debugPrint('❌ Contenu de la box: ${amicalesBox.values.map((a) => '${a.id}: ${a.name}').join(', ')}');
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -245,7 +232,7 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'L\'amicale associée à votre compte n\'existe plus.',
|
||||
'L\'amicale associée à votre compte n\'existe plus.\nfkEntite: ${_currentUser!.fkEntite}',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.bodyLarge,
|
||||
),
|
||||
@@ -293,7 +280,7 @@ class _AdminAmicalePageState extends State<AdminAmicalePage> {
|
||||
onDelete: null,
|
||||
amicaleRepository: widget.amicaleRepository,
|
||||
userRepository: widget.userRepository,
|
||||
apiService: null, // Ou passez l'ApiService si vous l'avez disponible
|
||||
apiService: ApiService.instance,
|
||||
showActionsColumn: false,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -68,10 +68,9 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de la récupération de la version: $e');
|
||||
// Fallback sur la version du AppInfoService si elle existe
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_appVersion = AppInfoService.fullVersion.split(' ').last; // Extraire juste le numéro
|
||||
_appVersion = AppInfoService.fullVersion.split(' ').last;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -81,29 +80,24 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Animation controller sur 5 secondes (augmenté de 3 à 5 secondes)
|
||||
// Animation controller
|
||||
_animationController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
// Animation de 4x la taille à 1x la taille (augmenté de 3x à 4x)
|
||||
_scaleAnimation = Tween<double>(
|
||||
begin: 4.0, // Commencer à 4x la taille
|
||||
end: 1.0, // Terminer à la taille normale
|
||||
begin: 4.0,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.easeOutBack, // Curve pour un effet de rebond
|
||||
curve: Curves.easeOutBack,
|
||||
),
|
||||
);
|
||||
|
||||
// Démarrer l'animation immédiatement
|
||||
_animationController.forward();
|
||||
|
||||
_getAppVersion();
|
||||
|
||||
// Simuler le processus d'initialisation
|
||||
_startInitialization();
|
||||
}
|
||||
|
||||
@@ -114,151 +108,337 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
}
|
||||
|
||||
void _startInitialization() async {
|
||||
// Étape 1: Initialisation des boîtes Hive (0% à 75%)
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Initialisation des données...";
|
||||
_progress = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
// Initialiser toutes les boîtes Hive
|
||||
await _initializeAllHiveBoxes();
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// Étape 2: Initialisation des services (75% à 100%)
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Préparation de l'application...";
|
||||
_progress = 0.75;
|
||||
});
|
||||
}
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// Table rase complète et recréation propre
|
||||
await _completeReset();
|
||||
|
||||
// Finalisation
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Application prête !";
|
||||
_progress = 1.0;
|
||||
_isInitializing = false;
|
||||
_showButtons = true;
|
||||
_progress = 1.0; // S'assurer que la barre est à 100%
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Méthode pour initialiser toutes les boîtes Hive
|
||||
Future<void> _initializeAllHiveBoxes() async {
|
||||
/// RESET COMPLET : Destruction totale et recréation propre
|
||||
Future<void> _completeReset() async {
|
||||
try {
|
||||
debugPrint('Initialisation de toutes les boîtes Hive...');
|
||||
debugPrint('🧹 RESET COMPLET : Destruction totale des données Hive...');
|
||||
|
||||
// Structure pour les boîtes à ouvrir avec leurs noms d'affichage
|
||||
final boxesToOpen = [
|
||||
{'name': AppKeys.userBoxName, 'display': 'Préparation utilisateurs'},
|
||||
{'name': AppKeys.amicaleBoxName, 'display': 'Préparation amicale'},
|
||||
{'name': AppKeys.clientsBoxName, 'display': 'Préparation clients'},
|
||||
{'name': AppKeys.regionsBoxName, 'display': 'Préparation régions'},
|
||||
{'name': AppKeys.operationsBoxName, 'display': 'Préparation opérations'},
|
||||
{'name': AppKeys.sectorsBoxName, 'display': 'Préparation secteurs'},
|
||||
{'name': AppKeys.passagesBoxName, 'display': 'Préparation passages'},
|
||||
{'name': AppKeys.membresBoxName, 'display': 'Préparation membres'},
|
||||
{'name': AppKeys.userSectorBoxName, 'display': 'Préparation secteurs utilisateurs'},
|
||||
{'name': AppKeys.settingsBoxName, 'display': 'Préparation paramètres'},
|
||||
{'name': AppKeys.chatConversationsBoxName, 'display': 'Préparation conversations'},
|
||||
{'name': AppKeys.chatMessagesBoxName, 'display': 'Préparation messages'},
|
||||
];
|
||||
// Étape 1: Sauvegarder les utilisateurs existants (optionnel)
|
||||
Map<dynamic, UserModel>? existingUsers;
|
||||
|
||||
// Calculer l'incrément de progression pour chaque boîte (0.75 / nombre de boîtes)
|
||||
final progressIncrement = 0.75 / boxesToOpen.length;
|
||||
double currentProgress = 0.0;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Sauvegarde des utilisateurs...";
|
||||
_progress = 0.05;
|
||||
});
|
||||
}
|
||||
|
||||
// Ouvrir chaque boîte si elle n'est pas déjà ouverte
|
||||
for (int i = 0; i < boxesToOpen.length; i++) {
|
||||
final boxName = boxesToOpen[i]['name'] as String;
|
||||
final displayName = boxesToOpen[i]['display'] as String;
|
||||
try {
|
||||
if (Hive.isBoxOpen(AppKeys.userBoxName)) {
|
||||
final userBox = Hive.box<UserModel>(AppKeys.userBoxName);
|
||||
existingUsers = Map.from(userBox.toMap());
|
||||
debugPrint('📦 ${existingUsers.length} utilisateurs sauvegardés');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur sauvegarde utilisateurs: $e');
|
||||
existingUsers = null;
|
||||
}
|
||||
|
||||
// Mettre à jour la barre de progression et le message
|
||||
currentProgress = progressIncrement * (i + 1);
|
||||
// Étape 2: DESTRUCTION RADICALE - Fermer tout ce qui peut être ouvert
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Fermeture de toutes les bases de données...";
|
||||
_progress = 0.15;
|
||||
});
|
||||
}
|
||||
|
||||
await _closeAllKnownBoxes();
|
||||
|
||||
// Étape 3: DESTRUCTION RADICALE - Supprimer tout Hive du disque
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Suppression complète des anciennes données...";
|
||||
_progress = 0.25;
|
||||
});
|
||||
}
|
||||
|
||||
await _nukeHiveCompletely();
|
||||
|
||||
// Étape 4: RECRÉATION PROPRE
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Création des nouvelles bases de données...";
|
||||
_progress = 0.40;
|
||||
});
|
||||
}
|
||||
|
||||
await _createAllBoxesFresh();
|
||||
|
||||
// Étape 5: Restaurer les utilisateurs (optionnel)
|
||||
if (existingUsers != null && existingUsers.isNotEmpty) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = displayName;
|
||||
_progress = currentProgress;
|
||||
_statusMessage = "Restauration des utilisateurs...";
|
||||
_progress = 0.80;
|
||||
});
|
||||
}
|
||||
|
||||
if (!Hive.isBoxOpen(boxName)) {
|
||||
debugPrint('Ouverture de la boîte $boxName ($displayName)...');
|
||||
|
||||
// Ouvrir la boîte avec le type approprié
|
||||
if (boxName == AppKeys.userBoxName) {
|
||||
await Hive.openBox<UserModel>(boxName);
|
||||
} else if (boxName == AppKeys.amicaleBoxName) {
|
||||
await Hive.openBox<AmicaleModel>(boxName);
|
||||
} else if (boxName == AppKeys.clientsBoxName) {
|
||||
await Hive.openBox<ClientModel>(boxName);
|
||||
} else if (boxName == AppKeys.regionsBoxName) {
|
||||
// Ouvrir la boîte des régions sans type spécifique pour l'instant
|
||||
// car RegionModelAdapter n'est pas encore enregistré
|
||||
await Hive.openBox(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.passagesBoxName) {
|
||||
await Hive.openBox<PassageModel>(boxName);
|
||||
} else if (boxName == AppKeys.membresBoxName) {
|
||||
await Hive.openBox<MembreModel>(boxName);
|
||||
} else if (boxName == AppKeys.userSectorBoxName) {
|
||||
await Hive.openBox<UserSectorModel>(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);
|
||||
}
|
||||
|
||||
debugPrint('Boîte $boxName ouverte avec succès');
|
||||
} else {
|
||||
debugPrint('Boîte $boxName déjà ouverte');
|
||||
}
|
||||
|
||||
// Ajouter une temporisation entre chaque ouverture
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
await _restoreUsers(existingUsers);
|
||||
}
|
||||
|
||||
// Mettre à jour la barre de progression à 0.2 (20%) à la fin
|
||||
// Étape 6: Vérification finale
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = 'Toutes les boîtes sont prêtes';
|
||||
_progress = 0.8;
|
||||
_statusMessage = "Vérification des bases de données...";
|
||||
_progress = 0.90;
|
||||
});
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
debugPrint('✅ RESET COMPLET terminé avec succès');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur lors du reset complet: $e');
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Préparation de l'application...";
|
||||
_progress = 0.9;
|
||||
});
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
// Finalisation
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Erreur critique - Redémarrage recommandé";
|
||||
_progress = 1.0;
|
||||
_isInitializing = false;
|
||||
_showButtons = true;
|
||||
_progress = 1.0;
|
||||
});
|
||||
}
|
||||
debugPrint('Toutes les boîtes Hive sont maintenant ouvertes');
|
||||
} catch (e) {
|
||||
debugPrint('Erreur lors de l\'initialisation des boîtes Hive: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// En cas d'erreur, mettre à jour le message
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = 'Erreur lors de l\'initialisation des données';
|
||||
});
|
||||
/// Ferme toutes les boîtes connues
|
||||
Future<void> _closeAllKnownBoxes() async {
|
||||
try {
|
||||
final allKnownBoxes = [
|
||||
AppKeys.userBoxName,
|
||||
AppKeys.amicaleBoxName,
|
||||
AppKeys.clientsBoxName,
|
||||
AppKeys.regionsBoxName,
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.membresBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.settingsBoxName,
|
||||
AppKeys.chatConversationsBoxName,
|
||||
AppKeys.chatMessagesBoxName,
|
||||
// Boîtes potentiellement problématiques
|
||||
'auth', 'locations', 'messages', 'temp'
|
||||
];
|
||||
|
||||
debugPrint('🔒 Fermeture de ${allKnownBoxes.length} boîtes connues...');
|
||||
|
||||
for (final boxName in allKnownBoxes) {
|
||||
try {
|
||||
if (Hive.isBoxOpen(boxName)) {
|
||||
await Hive.box(boxName).close();
|
||||
debugPrint('✅ Boîte $boxName fermée');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur fermeture $boxName: $e');
|
||||
// Continuer même en cas d'erreur
|
||||
}
|
||||
}
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 1000));
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur fermeture des boîtes: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Suppression RADICALE de tout Hive
|
||||
Future<void> _nukeHiveCompletely() async {
|
||||
try {
|
||||
debugPrint('💥 DESTRUCTION NUCLÉAIRE de Hive...');
|
||||
|
||||
if (kIsWeb) {
|
||||
// En version web, supprimer toutes les boîtes possibles une par une
|
||||
final allPossibleBoxes = [
|
||||
AppKeys.userBoxName,
|
||||
AppKeys.amicaleBoxName,
|
||||
AppKeys.clientsBoxName,
|
||||
AppKeys.regionsBoxName,
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.membresBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.settingsBoxName,
|
||||
AppKeys.chatConversationsBoxName,
|
||||
AppKeys.chatMessagesBoxName,
|
||||
// Toutes les boîtes potentiellement corrompues
|
||||
'auth', 'locations', 'messages', 'temp', 'cache', 'data'
|
||||
];
|
||||
|
||||
for (final boxName in allPossibleBoxes) {
|
||||
try {
|
||||
await Hive.deleteBoxFromDisk(boxName);
|
||||
debugPrint('✅ Boîte $boxName DÉTRUITE');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur destruction $boxName: $e');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Sur mobile/desktop, destruction totale
|
||||
try {
|
||||
await Hive.deleteFromDisk();
|
||||
debugPrint('✅ Hive COMPLÈTEMENT DÉTRUIT');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur destruction totale: $e');
|
||||
// Fallback : supprimer boîte par boîte
|
||||
await _deleteBoxesOneByOne();
|
||||
}
|
||||
}
|
||||
|
||||
// Attendre pour s'assurer que tout est détruit
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur destruction Hive: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Fallback : supprimer les boîtes une par une
|
||||
Future<void> _deleteBoxesOneByOne() async {
|
||||
final allBoxes = [
|
||||
AppKeys.userBoxName,
|
||||
AppKeys.amicaleBoxName,
|
||||
AppKeys.clientsBoxName,
|
||||
AppKeys.regionsBoxName,
|
||||
AppKeys.operationsBoxName,
|
||||
AppKeys.sectorsBoxName,
|
||||
AppKeys.passagesBoxName,
|
||||
AppKeys.membresBoxName,
|
||||
AppKeys.userSectorBoxName,
|
||||
AppKeys.settingsBoxName,
|
||||
AppKeys.chatConversationsBoxName,
|
||||
AppKeys.chatMessagesBoxName,
|
||||
];
|
||||
|
||||
for (final boxName in allBoxes) {
|
||||
try {
|
||||
await Hive.deleteBoxFromDisk(boxName);
|
||||
debugPrint('✅ Boîte $boxName supprimée (fallback)');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur suppression fallback $boxName: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recrée toutes les boîtes VIDES et PROPRES
|
||||
Future<void> _createAllBoxesFresh() async {
|
||||
try {
|
||||
debugPrint('🆕 Création de toutes les boîtes vides...');
|
||||
|
||||
final boxesToCreate = [
|
||||
{'name': AppKeys.userBoxName, 'type': 'UserModel'},
|
||||
{'name': AppKeys.amicaleBoxName, 'type': 'AmicaleModel'},
|
||||
{'name': AppKeys.clientsBoxName, 'type': 'ClientModel'},
|
||||
{'name': AppKeys.regionsBoxName, 'type': 'dynamic'},
|
||||
{'name': AppKeys.operationsBoxName, 'type': 'OperationModel'},
|
||||
{'name': AppKeys.sectorsBoxName, 'type': 'SectorModel'},
|
||||
{'name': AppKeys.passagesBoxName, 'type': 'PassageModel'},
|
||||
{'name': AppKeys.membresBoxName, 'type': 'MembreModel'},
|
||||
{'name': AppKeys.userSectorBoxName, 'type': 'UserSectorModel'},
|
||||
{'name': AppKeys.settingsBoxName, 'type': 'dynamic'},
|
||||
{'name': AppKeys.chatConversationsBoxName, 'type': 'ConversationModel'},
|
||||
{'name': AppKeys.chatMessagesBoxName, 'type': 'MessageModel'},
|
||||
];
|
||||
|
||||
final progressIncrement = 0.35 / boxesToCreate.length; // De 0.40 à 0.75
|
||||
|
||||
for (int i = 0; i < boxesToCreate.length; i++) {
|
||||
final boxInfo = boxesToCreate[i];
|
||||
final boxName = boxInfo['name'] as String;
|
||||
final boxType = boxInfo['type'] as String;
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_statusMessage = "Création de $boxName...";
|
||||
_progress = 0.40 + (progressIncrement * i);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Créer la boîte avec le bon type
|
||||
switch (boxType) {
|
||||
case 'UserModel':
|
||||
await Hive.openBox<UserModel>(boxName);
|
||||
break;
|
||||
case 'AmicaleModel':
|
||||
await Hive.openBox<AmicaleModel>(boxName);
|
||||
break;
|
||||
case 'ClientModel':
|
||||
await Hive.openBox<ClientModel>(boxName);
|
||||
break;
|
||||
case 'OperationModel':
|
||||
await Hive.openBox<OperationModel>(boxName);
|
||||
break;
|
||||
case 'SectorModel':
|
||||
await Hive.openBox<SectorModel>(boxName);
|
||||
break;
|
||||
case 'PassageModel':
|
||||
await Hive.openBox<PassageModel>(boxName);
|
||||
break;
|
||||
case 'MembreModel':
|
||||
await Hive.openBox<MembreModel>(boxName);
|
||||
break;
|
||||
case 'UserSectorModel':
|
||||
await Hive.openBox<UserSectorModel>(boxName);
|
||||
break;
|
||||
case 'ConversationModel':
|
||||
await Hive.openBox<ConversationModel>(boxName);
|
||||
break;
|
||||
case 'MessageModel':
|
||||
await Hive.openBox<MessageModel>(boxName);
|
||||
break;
|
||||
default:
|
||||
await Hive.openBox(boxName);
|
||||
}
|
||||
|
||||
debugPrint('✅ Boîte $boxName créée (type: $boxType)');
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur création $boxName: $e');
|
||||
// En cas d'erreur, essayer sans type
|
||||
try {
|
||||
await Hive.openBox(boxName);
|
||||
debugPrint('⚠️ Boîte $boxName créée sans type');
|
||||
} catch (e2) {
|
||||
debugPrint('❌ Échec total création $boxName: $e2');
|
||||
}
|
||||
}
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur création des boîtes: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Restaure les utilisateurs sauvegardés
|
||||
Future<void> _restoreUsers(Map<dynamic, UserModel> users) async {
|
||||
try {
|
||||
if (Hive.isBoxOpen(AppKeys.userBoxName)) {
|
||||
final userBox = Hive.box<UserModel>(AppKeys.userBoxName);
|
||||
|
||||
for (final entry in users.entries) {
|
||||
try {
|
||||
await userBox.put(entry.key, entry.value);
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ Erreur restauration utilisateur ${entry.key}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('✅ ${users.length} utilisateurs restaurés');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur restauration utilisateurs: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +475,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
children: [
|
||||
const Spacer(flex: 2),
|
||||
|
||||
// Logo avec animation de réduction
|
||||
// Logo avec animation
|
||||
AnimatedBuilder(
|
||||
animation: _scaleAnimation,
|
||||
builder: (context, child) {
|
||||
@@ -306,13 +486,13 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
},
|
||||
child: Image.asset(
|
||||
'assets/images/logo-geosector-1024.png',
|
||||
height: 180, // Augmenté de 140 à 180
|
||||
height: 180,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Titre avec animation fade-in
|
||||
// Titre
|
||||
AnimatedOpacity(
|
||||
opacity: _isInitializing ? 0.9 : 1.0,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
@@ -328,7 +508,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Sous-titre avec nouveau slogan
|
||||
// Sous-titre
|
||||
AnimatedOpacity(
|
||||
opacity: _isInitializing ? 0.8 : 1.0,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
@@ -356,7 +536,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
theme.colorScheme.primary,
|
||||
),
|
||||
minHeight: 10, // Augmenté de 6 à 10
|
||||
minHeight: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -369,7 +549,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
),
|
||||
],
|
||||
|
||||
// Boutons après l'initialisation
|
||||
// Boutons (reste identique)
|
||||
if (_showButtons) ...[
|
||||
// Bouton Connexion Utilisateur
|
||||
AnimatedOpacity(
|
||||
@@ -377,7 +557,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.go('/login/user'); // Utiliser la route spécifique
|
||||
context.go('/login/user');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
@@ -402,13 +582,13 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Bouton Connexion Administrateur
|
||||
// Bouton Connexion Administrateur
|
||||
AnimatedOpacity(
|
||||
opacity: _showButtons ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
context.go('/login/admin'); // Utiliser la route spécifique
|
||||
context.go('/login/admin');
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
@@ -432,7 +612,7 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 32), // 2 espaces sous le bouton précédent
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Bouton d'inscription
|
||||
AnimatedOpacity(
|
||||
@@ -472,7 +652,6 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
// Déterminer l'URL du site web en fonction de l'environnement
|
||||
String webUrl = 'https://geosector.fr';
|
||||
|
||||
if (kIsWeb) {
|
||||
@@ -486,7 +665,6 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
}
|
||||
}
|
||||
|
||||
// Ouvrir l'URL dans une nouvelle fenêtre/onglet
|
||||
launchUrl(
|
||||
Uri.parse(webUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
@@ -514,7 +692,8 @@ class _SplashPageState extends State<SplashPage> with SingleTickerProviderStateM
|
||||
),
|
||||
),
|
||||
),
|
||||
// Badge de version en bas à droite
|
||||
|
||||
// Badge de version
|
||||
if (_appVersion.isNotEmpty)
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
|
||||
@@ -98,14 +98,23 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
|
||||
// Appeler l'API pour mettre à jour l'entité
|
||||
Future<void> _updateAmicale(AmicaleModel amicale) async {
|
||||
if (!mounted) return;
|
||||
|
||||
try {
|
||||
// Afficher un indicateur de chargement
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
return const AlertDialog(
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(height: 16),
|
||||
Text('Mise à jour en cours...'),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -121,9 +130,9 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
'phone': amicale.phone,
|
||||
'mobile': amicale.mobile,
|
||||
'email': amicale.email,
|
||||
'chk_copie_mail_recu': amicale.chkCopieMailRecu,
|
||||
'chk_accept_sms': amicale.chkAcceptSms,
|
||||
'chk_stripe': amicale.chkStripe,
|
||||
'chk_copie_mail_recu': amicale.chkCopieMailRecu ? 1 : 0,
|
||||
'chk_accept_sms': amicale.chkAcceptSms ? 1 : 0,
|
||||
'chk_stripe': amicale.chkStripe ? 1 : 0,
|
||||
};
|
||||
|
||||
// Ajouter les champs réservés aux administrateurs si l'utilisateur est admin
|
||||
@@ -132,63 +141,81 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
data['gps_lat'] = amicale.gpsLat;
|
||||
data['gps_lng'] = amicale.gpsLng;
|
||||
data['stripe_id'] = amicale.stripeId;
|
||||
data['chk_demo'] = amicale.chkDemo;
|
||||
data['chk_active'] = amicale.chkActive;
|
||||
data['chk_demo'] = amicale.chkDemo ? 1 : 0;
|
||||
data['chk_active'] = amicale.chkActive ? 1 : 0;
|
||||
}
|
||||
|
||||
// Fermer l'indicateur de chargement
|
||||
Navigator.of(context).pop();
|
||||
debugPrint('🔧 Données à envoyer à l\'API: $data');
|
||||
|
||||
bool apiSuccess = false;
|
||||
String? errorMessage;
|
||||
|
||||
// Appeler l'API si le service est disponible
|
||||
if (widget.apiService != null) {
|
||||
try {
|
||||
await widget.apiService!.post('/entite/update', data: data);
|
||||
debugPrint('📡 Appel API pour mise à jour amicale...');
|
||||
|
||||
// Afficher un message de succès
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Amicale mise à jour avec succès'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
// Version RESTful correcte avec PUT
|
||||
final response = await widget.apiService!.put('/entites/${amicale.id}', data: data);
|
||||
|
||||
// Alternative avec PATCH si votre API le supporte
|
||||
// final response = await widget.apiService!.patch('/entites/${amicale.id}', data: data);
|
||||
|
||||
debugPrint('📡 Réponse API: ${response.statusCode}');
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
apiSuccess = true;
|
||||
} else {
|
||||
errorMessage = 'Erreur serveur: ${response.statusCode}';
|
||||
}
|
||||
} catch (error) {
|
||||
// Afficher un message d'erreur
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur lors de la mise à jour de l\'amicale: $error'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
return; // Sortir de la fonction en cas d'erreur
|
||||
}
|
||||
} else {
|
||||
// Pas d'API service, afficher un message d'information
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Modifications enregistrées localement'),
|
||||
backgroundColor: Colors.blue,
|
||||
),
|
||||
);
|
||||
debugPrint('❌ Erreur API: $error');
|
||||
errorMessage = 'Erreur lors de la communication avec le serveur: $error';
|
||||
}
|
||||
}
|
||||
|
||||
// Appeler la fonction onSubmit si elle existe
|
||||
if (widget.onSubmit != null) {
|
||||
widget.onSubmit!(amicale);
|
||||
}
|
||||
|
||||
// Fermer le formulaire
|
||||
if (mounted) {
|
||||
// Fermer l'indicateur de chargement
|
||||
if (mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (apiSuccess) {
|
||||
// Appeler la fonction onSubmit si elle existe
|
||||
if (widget.onSubmit != null) {
|
||||
widget.onSubmit!(amicale);
|
||||
}
|
||||
|
||||
// Afficher un message de succès
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(widget.apiService != null ? 'Amicale mise à jour avec succès' : 'Modifications enregistrées localement'),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
|
||||
// Fermer le formulaire après un délai pour que l'utilisateur voie le message
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
if (mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} else {
|
||||
// Afficher un message d'erreur
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(errorMessage ?? 'Erreur lors de la mise à jour'),
|
||||
backgroundColor: Colors.red,
|
||||
duration: const Duration(seconds: 4),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ Erreur générale dans _updateAmicale: $e');
|
||||
|
||||
// Fermer l'indicateur de chargement si encore ouvert
|
||||
if (Navigator.of(context).canPop()) {
|
||||
if (mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
@@ -196,8 +223,9 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur: ${e.toString()}'),
|
||||
content: Text('Erreur inattendue: ${e.toString()}'),
|
||||
backgroundColor: Colors.red,
|
||||
duration: const Duration(seconds: 4),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -205,9 +233,14 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
}
|
||||
|
||||
void _submitForm() {
|
||||
debugPrint('🔧 _submitForm appelée');
|
||||
|
||||
if (_formKey.currentState!.validate()) {
|
||||
debugPrint('🔧 Formulaire valide');
|
||||
|
||||
// Vérifier qu'au moins un numéro de téléphone est renseigné
|
||||
if (_phoneController.text.isEmpty && _mobileController.text.isEmpty) {
|
||||
debugPrint('⚠️ Aucun numéro de téléphone renseigné');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Veuillez renseigner au moins un numéro de téléphone'),
|
||||
@@ -217,6 +250,8 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint('🔧 Création de l\'objet AmicaleModel...');
|
||||
|
||||
final amicale = widget.amicale?.copyWith(
|
||||
name: _nameController.text,
|
||||
adresse1: _adresse1Controller.text,
|
||||
@@ -258,10 +293,13 @@ class _AmicaleFormState extends State<AmicaleForm> {
|
||||
chkActive: _chkActive,
|
||||
);
|
||||
|
||||
debugPrint('🔧 AmicaleModel créé: ${amicale.name}');
|
||||
debugPrint('🔧 Appel de _updateAmicale...');
|
||||
|
||||
// Appeler l'API pour mettre à jour l'amicale
|
||||
_updateAmicale(amicale);
|
||||
|
||||
// Ne pas appeler widget.onSubmit ici car c'est fait dans _updateAmicale
|
||||
} else {
|
||||
debugPrint('❌ Formulaire invalide');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class AmicaleTableWidget extends StatelessWidget {
|
||||
final Function(AmicaleModel)? onDelete;
|
||||
final AmicaleRepository amicaleRepository;
|
||||
final UserRepository userRepository; // Nouveau paramètre
|
||||
final ApiService? apiService; // Nouveau paramètre optionnel
|
||||
final ApiService? apiService;
|
||||
final bool isLoading;
|
||||
final String? emptyMessage;
|
||||
final bool readOnly;
|
||||
@@ -35,7 +35,7 @@ class AmicaleTableWidget extends StatelessWidget {
|
||||
required this.userRepository, // Requis
|
||||
this.onEdit,
|
||||
this.onDelete,
|
||||
this.apiService, // Optionnel
|
||||
this.apiService,
|
||||
this.isLoading = false,
|
||||
this.emptyMessage,
|
||||
this.readOnly = false,
|
||||
@@ -83,9 +83,13 @@ class AmicaleTableWidget extends StatelessWidget {
|
||||
readOnly: false,
|
||||
userRepository: userRepository,
|
||||
apiService: apiService,
|
||||
onSubmit: (updatedAmicale) {
|
||||
onSubmit: (updatedAmicale) async {
|
||||
// Sauvegarder l'amicale mise à jour dans le repository
|
||||
debugPrint('🔄 Sauvegarde de l\'amicale mise à jour: ${updatedAmicale.name}');
|
||||
await amicaleRepository.saveAmicale(updatedAmicale);
|
||||
debugPrint('✅ Amicale sauvegardée dans le repository');
|
||||
|
||||
Navigator.of(dialogContext).pop();
|
||||
// La mise à jour sera gérée par les ValueListenableBuilder
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -227,7 +231,12 @@ class AmicaleTableWidget extends StatelessWidget {
|
||||
readOnly: true,
|
||||
userRepository: userRepository,
|
||||
apiService: apiService,
|
||||
onSubmit: (updatedAmicale) {
|
||||
onSubmit: (updatedAmicale) async {
|
||||
// Sauvegarder l'amicale mise à jour dans le repository
|
||||
debugPrint('🔄 Sauvegarde de l\'amicale mise à jour: ${updatedAmicale.name}');
|
||||
await amicaleRepository.saveAmicale(updatedAmicale);
|
||||
debugPrint('✅ Amicale sauvegardée dans le repository');
|
||||
|
||||
Navigator.of(dialogContext).pop();
|
||||
},
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
import 'package:geosector_app/app.dart'; // Pour accéder à l'instance globale de ApiService
|
||||
import 'package:geosector_app/core/services/api_service.dart'; // Import du service singleton
|
||||
|
||||
/// Widget de carte réutilisable utilisant Mapbox
|
||||
///
|
||||
@@ -105,11 +105,10 @@ class _MapboxMapState extends State<MapboxMap> {
|
||||
Widget build(BuildContext context) {
|
||||
// Déterminer l'URL du template de tuiles Mapbox
|
||||
// Utiliser l'environnement actuel pour obtenir la bonne clé API
|
||||
final String environment = apiService.getCurrentEnvironment();
|
||||
final String environment = ApiService.instance.getCurrentEnvironment();
|
||||
final String mapboxToken = AppKeys.getMapboxApiKey(environment);
|
||||
final String mapStyle = widget.mapStyle ?? 'mapbox/streets-v11';
|
||||
final String urlTemplate =
|
||||
'https://api.mapbox.com/styles/v1/$mapStyle/tiles/256/{z}/{x}/{y}@2x?access_token=$mapboxToken';
|
||||
final String urlTemplate = 'https://api.mapbox.com/styles/v1/$mapStyle/tiles/256/{z}/{x}/{y}@2x?access_token=$mapboxToken';
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
@@ -145,12 +144,10 @@ class _MapboxMapState extends State<MapboxMap> {
|
||||
),
|
||||
|
||||
// Polygones
|
||||
if (widget.polygons != null && widget.polygons!.isNotEmpty)
|
||||
PolygonLayer(polygons: widget.polygons!),
|
||||
if (widget.polygons != null && widget.polygons!.isNotEmpty) PolygonLayer(polygons: widget.polygons!),
|
||||
|
||||
// Marqueurs
|
||||
if (widget.markers != null && widget.markers!.isNotEmpty)
|
||||
MarkerLayer(markers: widget.markers!),
|
||||
if (widget.markers != null && widget.markers!.isNotEmpty) MarkerLayer(markers: widget.markers!),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class MembreRowWidget extends StatelessWidget {
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
membre.firstName,
|
||||
membre.firstName ?? '',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -54,7 +54,7 @@ class MembreRowWidget extends StatelessWidget {
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
membre.name,
|
||||
membre.name ?? '',
|
||||
style: theme.textTheme.bodyMedium,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -70,31 +70,31 @@ class MembreRowWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
|
||||
// Rôle (fkRole)
|
||||
// Rôle (role au lieu de fkRole)
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
_getRoleName(membre.fkRole),
|
||||
_getRoleName(membre.role),
|
||||
style: theme.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
|
||||
// Statut (actif/inactif)
|
||||
// Statut (isActive au lieu de chkActive)
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: membre.chkActive == 1 ? Colors.green.withOpacity(0.1) : Colors.red.withOpacity(0.1),
|
||||
color: membre.isActive ? Colors.green.withOpacity(0.1) : Colors.red.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: membre.chkActive == 1 ? Colors.green.withOpacity(0.3) : Colors.red.withOpacity(0.3),
|
||||
color: membre.isActive ? Colors.green.withOpacity(0.3) : Colors.red.withOpacity(0.3),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
membre.chkActive == 1 ? 'Actif' : 'Inactif',
|
||||
membre.isActive ? 'Actif' : 'Inactif',
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: membre.chkActive == 1 ? Colors.green[700] : Colors.red[700],
|
||||
color: membre.isActive ? Colors.green[700] : Colors.red[700],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
@@ -161,18 +161,20 @@ class MembreRowWidget extends StatelessWidget {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('${membre.firstName} ${membre.name}'),
|
||||
title: Text('${membre.firstName ?? ''} ${membre.name ?? ''}'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildDetailRow('ID', membre.id.toString()),
|
||||
_buildDetailRow('Email', membre.email),
|
||||
_buildDetailRow('Username', membre.username),
|
||||
_buildDetailRow('Rôle', _getRoleName(membre.fkRole)),
|
||||
_buildDetailRow('Titre', membre.fkTitre.toString()),
|
||||
_buildDetailRow('Username', membre.username ?? 'Non défini'),
|
||||
_buildDetailRow('Rôle', _getRoleName(membre.role)),
|
||||
_buildDetailRow('Titre', membre.fkTitre?.toString() ?? 'Non défini'),
|
||||
_buildDetailRow('Secteur', membre.sectName ?? 'Non défini'),
|
||||
_buildDetailRow('Statut', membre.chkActive == 1 ? 'Actif' : 'Inactif'),
|
||||
_buildDetailRow('Statut', membre.isActive ? 'Actif' : 'Inactif'),
|
||||
_buildDetailRow('Téléphone', membre.phone ?? 'Non défini'),
|
||||
_buildDetailRow('Mobile', membre.mobile ?? 'Non défini'),
|
||||
if (membre.dateNaissance != null)
|
||||
_buildDetailRow('Date de naissance', '${membre.dateNaissance!.day}/${membre.dateNaissance!.month}/${membre.dateNaissance!.year}'),
|
||||
if (membre.dateEmbauche != null)
|
||||
|
||||
Reference in New Issue
Block a user