refactor: Remplacement de l'accès direct à Firestore par des Cloud Functions
Migration complète du backend pour utiliser des Cloud Functions comme couche API sécurisée, en remplacement des appels directs à Firestore depuis le client.
**Backend (Cloud Functions):**
- **Centralisation CORS :** Ajout d'un middleware `withCors` et d'une configuration `httpOptions` pour gérer uniformément les en-têtes CORS et les requêtes `OPTIONS` sur toutes les fonctions.
- **Nouvelles Fonctions de Lecture (GET) :**
- `getEquipments`, `getContainers`, `getEvents`, `getUsers`, `getOptions`, `getEventTypes`, `getRoles`, `getMaintenances`, `getAlerts`.
- Ces fonctions gèrent les permissions côté serveur, masquant les données sensibles (ex: prix des équipements) pour les utilisateurs non-autorisés.
- `getEvents` retourne également une map des utilisateurs (`usersMap`) pour optimiser le chargement des données de la main d'œuvre.
- **Nouvelle Fonction de Recherche :**
- `getContainersByEquipment` : Endpoint dédié pour trouver efficacement tous les containers qui contiennent un équipement spécifique.
- **Nouvelles Fonctions d'Écriture (CRUD) :**
- Fonctions CRUD complètes pour `eventTypes` (`create`, `update`, `delete`), incluant la validation (unicité du nom, vérification des événements futurs avant suppression).
- **Mise à jour de Fonctions Existantes :**
- Toutes les fonctions CRUD existantes (`create/update/deleteEquipment`, `create/update/deleteContainer`, etc.) sont wrappées avec le nouveau gestionnaire CORS.
**Frontend (Flutter):**
- **Introduction du `DataService` :** Nouveau service centralisant tous les appels aux Cloud Functions, servant d'intermédiaire entre l'UI/Providers et l'API.
- **Refactorisation des Providers :**
- `EquipmentProvider`, `ContainerProvider`, `EventProvider`, `UsersProvider`, `MaintenanceProvider` et `AlertProvider` ont été refactorisés pour utiliser le `DataService` au lieu d'accéder directement à Firestore.
- Les `Stream` Firestore sont remplacés par des chargements de données via des méthodes `Future` (`loadEquipments`, `loadEvents`, etc.).
- **Gestion des Relations Équipement-Container :**
- Le modèle `EquipmentModel` ne stocke plus `parentBoxIds`.
- La relation est maintenant gérée par le `ContainerModel` qui contient `equipmentIds`.
- Le `ContainerEquipmentService` est introduit pour utiliser la nouvelle fonction `getContainersByEquipment`.
- L'affichage des boîtes parentes (`EquipmentParentContainers`) et le formulaire d'équipement (`EquipmentFormPage`) ont été mis à jour pour refléter ce nouveau modèle de données, synchronisant les ajouts/suppressions d'équipements dans les containers.
- **Amélioration de l'UI :**
- Nouveau widget `ParentBoxesSelector` pour une sélection améliorée et visuelle des boîtes parentes dans le formulaire d'équipement.
- Refonte visuelle de `EquipmentParentContainers` pour une meilleure présentation.
This commit is contained in:
52
em2rp/lib/services/container_equipment_service.dart
Normal file
52
em2rp/lib/services/container_equipment_service.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:em2rp/models/container_model.dart';
|
||||
import 'package:em2rp/services/data_service.dart';
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
|
||||
/// Service pour gérer la relation entre containers et équipements
|
||||
/// Utilise le principe : seul le container stocke la référence aux équipements
|
||||
class ContainerEquipmentService {
|
||||
final DataService _dataService = DataService(apiService);
|
||||
|
||||
/// Récupère tous les containers contenant un équipement spécifique
|
||||
/// Utilise une Cloud Function avec authentification et permissions
|
||||
Future<List<ContainerModel>> getContainersByEquipment(String equipmentId) async {
|
||||
try {
|
||||
final containersData = await _dataService.getContainersByEquipment(equipmentId);
|
||||
|
||||
return containersData.map((data) {
|
||||
// L'ID est dans le champ 'id' retourné par la fonction
|
||||
final id = data['id'] as String;
|
||||
return ContainerModel.fromMap(data, id);
|
||||
}).toList();
|
||||
} catch (e) {
|
||||
print('[ContainerEquipmentService] Error getting containers for equipment $equipmentId: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Vérifie si un équipement est dans au moins un container
|
||||
Future<bool> isEquipmentInAnyContainer(String equipmentId) async {
|
||||
try {
|
||||
final containers = await getContainersByEquipment(equipmentId);
|
||||
return containers.isNotEmpty;
|
||||
} catch (e) {
|
||||
print('[ContainerEquipmentService] Error checking if equipment is in container: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère le nombre de containers contenant un équipement
|
||||
Future<int> getContainerCountForEquipment(String equipmentId) async {
|
||||
try {
|
||||
final containers = await getContainersByEquipment(equipmentId);
|
||||
return containers.length;
|
||||
} catch (e) {
|
||||
print('[ContainerEquipmentService] Error getting container count: $e');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Instance globale singleton
|
||||
final containerEquipmentService = ContainerEquipmentService();
|
||||
|
||||
339
em2rp/lib/services/data_service.dart
Normal file
339
em2rp/lib/services/data_service.dart
Normal file
@@ -0,0 +1,339 @@
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
|
||||
/// Service générique pour les opérations de lecture de données via Cloud Functions
|
||||
class DataService {
|
||||
final ApiService _apiService;
|
||||
|
||||
DataService(this._apiService);
|
||||
|
||||
/// Récupère toutes les options
|
||||
Future<List<Map<String, dynamic>>> getOptions() async {
|
||||
try {
|
||||
final result = await _apiService.call('getOptions', {});
|
||||
final options = result['options'] as List<dynamic>?;
|
||||
if (options == null) return [];
|
||||
return options.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des options: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère tous les types d'événements
|
||||
Future<List<Map<String, dynamic>>> getEventTypes() async {
|
||||
try {
|
||||
final result = await _apiService.call('getEventTypes', {});
|
||||
final eventTypes = result['eventTypes'] as List<dynamic>?;
|
||||
if (eventTypes == null) return [];
|
||||
return eventTypes.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des types d\'événements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère tous les rôles
|
||||
Future<List<Map<String, dynamic>>> getRoles() async {
|
||||
try {
|
||||
final result = await _apiService.call('getRoles', {});
|
||||
final roles = result['roles'] as List<dynamic>?;
|
||||
if (roles == null) return [];
|
||||
return roles.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des rôles: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour les équipements d'un événement
|
||||
Future<void> updateEventEquipment({
|
||||
required String eventId,
|
||||
List<Map<String, dynamic>>? assignedEquipment,
|
||||
String? preparationStatus,
|
||||
String? loadingStatus,
|
||||
String? unloadingStatus,
|
||||
String? returnStatus,
|
||||
}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{'eventId': eventId};
|
||||
|
||||
if (assignedEquipment != null) data['assignedEquipment'] = assignedEquipment;
|
||||
if (preparationStatus != null) data['preparationStatus'] = preparationStatus;
|
||||
if (loadingStatus != null) data['loadingStatus'] = loadingStatus;
|
||||
if (unloadingStatus != null) data['unloadingStatus'] = unloadingStatus;
|
||||
if (returnStatus != null) data['returnStatus'] = returnStatus;
|
||||
|
||||
await _apiService.call('updateEventEquipment', data);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour des équipements de l\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour uniquement le statut d'un équipement
|
||||
Future<void> updateEquipmentStatusOnly({
|
||||
required String equipmentId,
|
||||
String? status,
|
||||
int? availableQuantity,
|
||||
}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{'equipmentId': equipmentId};
|
||||
|
||||
if (status != null) data['status'] = status;
|
||||
if (availableQuantity != null) data['availableQuantity'] = availableQuantity;
|
||||
|
||||
await _apiService.call('updateEquipmentStatusOnly', data);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour du statut de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour un utilisateur
|
||||
Future<void> updateUser(String userId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final requestData = {'userId': userId, ...data};
|
||||
await _apiService.call('updateUser', requestData);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'utilisateur: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour un événement
|
||||
Future<void> updateEvent(String eventId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final requestData = {'eventId': eventId, ...data};
|
||||
await _apiService.call('updateEvent', requestData);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime un événement
|
||||
Future<void> deleteEvent(String eventId) async {
|
||||
try {
|
||||
await _apiService.call('deleteEvent', {'eventId': eventId});
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de l\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée un nouvel équipement
|
||||
Future<void> createEquipment(String equipmentId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final requestData = {'equipmentId': equipmentId, ...data};
|
||||
await _apiService.call('createEquipment', requestData);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la création de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour un équipement
|
||||
Future<void> updateEquipment(String equipmentId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final requestData = {'equipmentId': equipmentId, ...data};
|
||||
await _apiService.call('updateEquipment', requestData);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime un équipement
|
||||
Future<void> deleteEquipment(String equipmentId) async {
|
||||
try {
|
||||
await _apiService.call('deleteEquipment', {'equipmentId': equipmentId});
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les événements utilisant un type d'événement donné
|
||||
Future<List<Map<String, dynamic>>> getEventsByEventType(String eventTypeId) async {
|
||||
try {
|
||||
final result = await _apiService.call('getEventsByEventType', {'eventTypeId': eventTypeId});
|
||||
final events = result['events'] as List<dynamic>?;
|
||||
if (events == null) return [];
|
||||
return events.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des événements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée un type d'événement
|
||||
Future<String> createEventType({
|
||||
required String name,
|
||||
required double defaultPrice,
|
||||
}) async {
|
||||
try {
|
||||
final result = await _apiService.call('createEventType', {
|
||||
'name': name,
|
||||
'defaultPrice': defaultPrice,
|
||||
});
|
||||
return result['id'] as String;
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la création du type d\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour un type d'événement
|
||||
Future<void> updateEventType({
|
||||
required String eventTypeId,
|
||||
String? name,
|
||||
double? defaultPrice,
|
||||
}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{'eventTypeId': eventTypeId};
|
||||
if (name != null) data['name'] = name;
|
||||
if (defaultPrice != null) data['defaultPrice'] = defaultPrice;
|
||||
|
||||
await _apiService.call('updateEventType', data);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour du type d\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime un type d'événement
|
||||
Future<void> deleteEventType(String eventTypeId) async {
|
||||
try {
|
||||
await _apiService.call('deleteEventType', {'eventTypeId': eventTypeId});
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression du type d\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Crée une option
|
||||
Future<String> createOption(String code, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final requestData = {'code': code, ...data};
|
||||
final result = await _apiService.call('createOption', requestData);
|
||||
return result['id'] as String? ?? code;
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la création de l\'option: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Met à jour une option
|
||||
Future<void> updateOption(String optionId, Map<String, dynamic> data) async {
|
||||
try {
|
||||
final requestData = {'optionId': optionId, ...data};
|
||||
await _apiService.call('updateOption', requestData);
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la mise à jour de l\'option: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Supprime une option
|
||||
Future<void> deleteOption(String optionId) async {
|
||||
try {
|
||||
await _apiService.call('deleteOption', {'optionId': optionId});
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la suppression de l\'option: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// LECTURE DES DONNÉES (avec permissions côté serveur)
|
||||
// ============================================================================
|
||||
|
||||
/// Récupère tous les événements (filtrés selon permissions)
|
||||
/// Retourne { events: List<Map>, users: Map<String, Map> }
|
||||
Future<Map<String, dynamic>> getEvents({String? userId}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{};
|
||||
if (userId != null) data['userId'] = userId;
|
||||
|
||||
final result = await _apiService.call('getEvents', data);
|
||||
|
||||
// Extraire events et users
|
||||
final events = result['events'] as List<dynamic>? ?? [];
|
||||
final users = result['users'] as Map<String, dynamic>? ?? {};
|
||||
|
||||
return {
|
||||
'events': events.map((e) => e as Map<String, dynamic>).toList(),
|
||||
'users': users,
|
||||
};
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des événements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère tous les équipements (avec masquage des prix selon permissions)
|
||||
Future<List<Map<String, dynamic>>> getEquipments() async {
|
||||
try {
|
||||
print('[DataService] Calling getEquipments API...');
|
||||
final result = await _apiService.call('getEquipments', {});
|
||||
print('[DataService] API call successful, parsing result...');
|
||||
final equipments = result['equipments'] as List<dynamic>?;
|
||||
if (equipments == null) {
|
||||
print('[DataService] No equipments in result');
|
||||
return [];
|
||||
}
|
||||
print('[DataService] Found ${equipments.length} equipments');
|
||||
return equipments.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
print('[DataService] Error getting equipments: $e');
|
||||
throw Exception('Erreur lors de la récupération des équipements: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère tous les conteneurs
|
||||
Future<List<Map<String, dynamic>>> getContainers() async {
|
||||
try {
|
||||
final result = await _apiService.call('getContainers', {});
|
||||
final containers = result['containers'] as List<dynamic>?;
|
||||
if (containers == null) return [];
|
||||
return containers.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des conteneurs: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les maintenances (optionnellement filtrées par équipement)
|
||||
Future<List<Map<String, dynamic>>> getMaintenances({String? equipmentId}) async {
|
||||
try {
|
||||
final data = <String, dynamic>{};
|
||||
if (equipmentId != null) data['equipmentId'] = equipmentId;
|
||||
|
||||
final result = await _apiService.call('getMaintenances', data);
|
||||
final maintenances = result['maintenances'] as List<dynamic>?;
|
||||
if (maintenances == null) return [];
|
||||
return maintenances.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des maintenances: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les alertes
|
||||
Future<List<Map<String, dynamic>>> getAlerts() async {
|
||||
try {
|
||||
final result = await _apiService.call('getAlerts', {});
|
||||
final alerts = result['alerts'] as List<dynamic>?;
|
||||
if (alerts == null) return [];
|
||||
return alerts.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des alertes: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les utilisateurs (filtrés selon permissions)
|
||||
Future<List<Map<String, dynamic>>> getUsers() async {
|
||||
try {
|
||||
final result = await _apiService.call('getUsers', {});
|
||||
final users = result['users'] as List<dynamic>?;
|
||||
if (users == null) return [];
|
||||
return users.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des utilisateurs: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupère les containers contenant un équipement spécifique
|
||||
Future<List<Map<String, dynamic>>> getContainersByEquipment(String equipmentId) async {
|
||||
try {
|
||||
final result = await _apiService.call('getContainersByEquipment', {
|
||||
'equipmentId': equipmentId,
|
||||
});
|
||||
final containers = result['containers'] as List<dynamic>?;
|
||||
if (containers == null) return [];
|
||||
return containers.map((e) => e as Map<String, dynamic>).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Erreur lors de la récupération des containers pour l\'équipement: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:em2rp/models/equipment_model.dart';
|
||||
import 'package:em2rp/models/container_model.dart';
|
||||
import 'package:em2rp/models/alert_model.dart';
|
||||
import 'package:em2rp/models/maintenance_model.dart';
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
import 'package:em2rp/services/data_service.dart';
|
||||
|
||||
class EquipmentService {
|
||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||
final ApiService _apiService = apiService;
|
||||
final DataService _dataService = DataService(apiService);
|
||||
|
||||
// Collection references (utilisées seulement pour les lectures)
|
||||
CollectionReference get _equipmentCollection => _firestore.collection('equipments');
|
||||
@@ -134,7 +137,7 @@ class EquipmentService {
|
||||
.get();
|
||||
|
||||
for (var eventDoc in eventsQuery.docs) {
|
||||
final eventData = eventDoc.data() as Map<String, dynamic>;
|
||||
final eventData = eventDoc.data();
|
||||
final assignedEquipmentRaw = eventData['assignedEquipment'] ?? [];
|
||||
|
||||
if (assignedEquipmentRaw is List) {
|
||||
@@ -170,7 +173,7 @@ class EquipmentService {
|
||||
|
||||
for (var doc in equipmentQuery.docs) {
|
||||
final equipment = EquipmentModel.fromMap(
|
||||
doc.data() as Map<String, dynamic>,
|
||||
doc.data(),
|
||||
doc.id,
|
||||
);
|
||||
|
||||
@@ -230,7 +233,7 @@ class EquipmentService {
|
||||
|
||||
for (var doc in equipmentQuery.docs) {
|
||||
final equipment = EquipmentModel.fromMap(
|
||||
doc.data() as Map<String, dynamic>,
|
||||
doc.data(),
|
||||
doc.id,
|
||||
);
|
||||
|
||||
@@ -285,7 +288,7 @@ class EquipmentService {
|
||||
final models = <String>{};
|
||||
|
||||
for (var doc in equipmentQuery.docs) {
|
||||
final data = doc.data() as Map<String, dynamic>;
|
||||
final data = doc.data();
|
||||
final model = data['model'] as String?;
|
||||
if (model != null && model.isNotEmpty) {
|
||||
models.add(model);
|
||||
@@ -306,7 +309,7 @@ class EquipmentService {
|
||||
final brands = <String>{};
|
||||
|
||||
for (var doc in equipmentQuery.docs) {
|
||||
final data = doc.data() as Map<String, dynamic>;
|
||||
final data = doc.data();
|
||||
final brand = data['brand'] as String?;
|
||||
if (brand != null && brand.isNotEmpty) {
|
||||
brands.add(brand);
|
||||
@@ -329,7 +332,7 @@ class EquipmentService {
|
||||
final models = <String>{};
|
||||
|
||||
for (var doc in equipmentQuery.docs) {
|
||||
final data = doc.data() as Map<String, dynamic>;
|
||||
final data = doc.data();
|
||||
final model = data['model'] as String?;
|
||||
if (model != null && model.isNotEmpty) {
|
||||
models.add(model);
|
||||
@@ -354,26 +357,16 @@ class EquipmentService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Récupérer toutes les boîtes (équipements qui peuvent contenir d'autres équipements)
|
||||
Future<List<EquipmentModel>> getBoxes() async {
|
||||
/// Récupérer toutes les boîtes/containers disponibles
|
||||
Future<List<ContainerModel>> getBoxes() async {
|
||||
try {
|
||||
// Les boîtes sont généralement des équipements de catégorie "structure" ou "other"
|
||||
// On pourrait aussi ajouter un champ spécifique "isBox" dans le modèle
|
||||
final equipmentQuery = await _firestore.collection('equipments')
|
||||
.where('category', whereIn: [
|
||||
equipmentCategoryToString(EquipmentCategory.structure),
|
||||
equipmentCategoryToString(EquipmentCategory.other),
|
||||
])
|
||||
.get();
|
||||
final containersData = await _dataService.getContainers();
|
||||
|
||||
final boxes = <EquipmentModel>[];
|
||||
for (var doc in equipmentQuery.docs) {
|
||||
final equipment = EquipmentModel.fromMap(
|
||||
doc.data() as Map<String, dynamic>,
|
||||
doc.id,
|
||||
);
|
||||
// On pourrait ajouter un filtre supplémentaire ici si besoin
|
||||
boxes.add(equipment);
|
||||
final boxes = <ContainerModel>[];
|
||||
for (var data in containersData) {
|
||||
final id = data['id'] as String;
|
||||
final container = ContainerModel.fromMap(data, id);
|
||||
boxes.add(container);
|
||||
}
|
||||
|
||||
return boxes;
|
||||
@@ -401,7 +394,7 @@ class EquipmentService {
|
||||
for (var doc in query.docs) {
|
||||
equipments.add(
|
||||
EquipmentModel.fromMap(
|
||||
doc.data() as Map<String, dynamic>,
|
||||
doc.data(),
|
||||
doc.id,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
@@ -8,32 +7,34 @@ import 'package:em2rp/models/event_model.dart';
|
||||
import 'package:em2rp/models/event_type_model.dart';
|
||||
import 'package:em2rp/models/user_model.dart';
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
import 'package:em2rp/services/data_service.dart';
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
class EventFormService {
|
||||
static final ApiService _apiService = apiService;
|
||||
static final DataService _dataService = DataService(FirebaseFunctionsApiService());
|
||||
|
||||
// ============================================================================
|
||||
// READ Operations - Utilise Firestore (peut rester en lecture directe)
|
||||
// READ Operations - Utilise l'API (sécurisé avec permissions côté serveur)
|
||||
// ============================================================================
|
||||
|
||||
static Future<List<EventTypeModel>> fetchEventTypes() async {
|
||||
developer.log('Fetching event types from Firestore...', name: 'EventFormService');
|
||||
developer.log('Fetching event types via API...', name: 'EventFormService');
|
||||
try {
|
||||
final snapshot = await FirebaseFirestore.instance.collection('eventTypes').get();
|
||||
final eventTypes = snapshot.docs.map((doc) => EventTypeModel.fromMap(doc.data(), doc.id)).toList();
|
||||
final eventTypesData = await _dataService.getEventTypes();
|
||||
final eventTypes = eventTypesData.map((data) => EventTypeModel.fromMap(data, data['id'] as String)).toList();
|
||||
developer.log('${eventTypes.length} event types loaded.', name: 'EventFormService');
|
||||
return eventTypes;
|
||||
} catch (e, s) {
|
||||
developer.log('Error fetching event types', name: 'EventFormService', error: e, stackTrace: s);
|
||||
throw Exception("Could not load event types. Please check Firestore permissions.");
|
||||
throw Exception("Could not load event types. Please check permissions.");
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<UserModel>> fetchUsers() async {
|
||||
try {
|
||||
final snapshot = await FirebaseFirestore.instance.collection('users').get();
|
||||
return snapshot.docs.map((doc) => UserModel.fromMap(doc.data(), doc.id)).toList();
|
||||
final usersData = await _dataService.getUsers();
|
||||
return usersData.map((data) => UserModel.fromMap(data, data['id'] as String)).toList();
|
||||
} catch (e) {
|
||||
developer.log('Error fetching users', name: 'EventFormService', error: e);
|
||||
throw Exception("Could not load users.");
|
||||
@@ -171,9 +172,15 @@ class EventFormService {
|
||||
}
|
||||
|
||||
static Future<void> updateEventDocuments(String eventId, List<Map<String, String>> documents) async {
|
||||
await FirebaseFirestore.instance
|
||||
.collection('events')
|
||||
.doc(eventId)
|
||||
.update({'documents': documents});
|
||||
// Utiliser l'API pour mettre à jour les documents
|
||||
try {
|
||||
await _apiService.call('updateEvent', {
|
||||
'eventId': eventId,
|
||||
'documents': documents,
|
||||
});
|
||||
} catch (e) {
|
||||
developer.log('Error updating event documents', name: 'EventFormService', error: e);
|
||||
throw Exception("Could not update event documents.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,19 +66,30 @@ END:VCALENDAR''';
|
||||
}
|
||||
|
||||
/// Récupère les détails de la main d'œuvre
|
||||
static Future<List<String>> _getWorkforceDetails(List<DocumentReference> workforce) async {
|
||||
static Future<List<String>> _getWorkforceDetails(List<dynamic> workforce) async {
|
||||
final List<String> workforceNames = [];
|
||||
|
||||
for (final ref in workforce) {
|
||||
try {
|
||||
final doc = await ref.get();
|
||||
if (doc.exists) {
|
||||
final data = doc.data() as Map<String, dynamic>?;
|
||||
if (data != null) {
|
||||
final firstName = data['firstName'] ?? '';
|
||||
final lastName = data['lastName'] ?? '';
|
||||
if (firstName.isNotEmpty || lastName.isNotEmpty) {
|
||||
workforceNames.add('$firstName $lastName'.trim());
|
||||
DocumentReference? docRef;
|
||||
|
||||
// Gérer String (UID) ou DocumentReference
|
||||
if (ref is String) {
|
||||
docRef = FirebaseFirestore.instance.collection('users').doc(ref);
|
||||
} else if (ref is DocumentReference) {
|
||||
docRef = ref;
|
||||
}
|
||||
|
||||
if (docRef != null) {
|
||||
final doc = await docRef.get();
|
||||
if (doc.exists) {
|
||||
final data = doc.data() as Map<String, dynamic>?;
|
||||
if (data != null) {
|
||||
final firstName = data['firstName'] ?? '';
|
||||
final lastName = data['lastName'] ?? '';
|
||||
if (firstName.isNotEmpty || lastName.isNotEmpty) {
|
||||
workforceNames.add('$firstName $lastName'.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,48 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
|
||||
import '../models/user_model.dart';
|
||||
import 'package:em2rp/services/data_service.dart';
|
||||
import 'package:em2rp/services/api_service.dart';
|
||||
|
||||
/// @deprecated Ce service est obsolète. Utilisez UsersProvider avec DataService à la place.
|
||||
/// Ce service reste pour compatibilité mais toutes les opérations passent par l'API.
|
||||
class UserService {
|
||||
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
|
||||
final DataService _dataService = DataService(FirebaseFunctionsApiService());
|
||||
|
||||
/// @deprecated Utilisez UsersProvider.fetchUsers() à la place
|
||||
Future<List<UserModel>> fetchUsers() async {
|
||||
try {
|
||||
final snapshot = await _firestore.collection('users').get();
|
||||
return snapshot.docs
|
||||
.map((doc) => UserModel.fromMap(doc.data(), doc.id))
|
||||
.toList();
|
||||
final usersData = await _dataService.getUsers();
|
||||
return usersData.map((data) => UserModel.fromMap(data, data['id'] as String)).toList();
|
||||
} catch (e) {
|
||||
print("Erreur: $e");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// @deprecated Utilisez DataService.updateUser() à la place
|
||||
Future<void> updateUser(UserModel user) async {
|
||||
try {
|
||||
await _firestore.collection('users').doc(user.uid).update(user.toMap());
|
||||
await _dataService.updateUser(user.uid, user.toMap());
|
||||
} catch (e) {
|
||||
print("Erreur mise à jour: $e");
|
||||
}
|
||||
}
|
||||
|
||||
/// @deprecated Utilisez API deleteUser à la place
|
||||
Future<void> deleteUser(String uid) async {
|
||||
try {
|
||||
await _firestore.collection('users').doc(uid).delete();
|
||||
// TODO: Créer une Cloud Function deleteUser
|
||||
print("Suppression d'utilisateur non implémentée via API");
|
||||
} catch (e) {
|
||||
print("Erreur suppression: $e");
|
||||
}
|
||||
}
|
||||
|
||||
/// Firebase Auth reste OK (pas Firestore)
|
||||
Future<void> resetPassword(String email) async {
|
||||
try {
|
||||
// Firebase Auth est OK, ce n'est pas Firestore
|
||||
await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
|
||||
print("Email de réinitialisation envoyé à $email");
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user