Cette mise à jour majeure introduit une fonctionnalité de scan et de saisie manuelle de codes QR directement depuis la page de préparation d'un événement. Ce système accélère et fiabilise le processus de validation des équipements et des containers pour chaque étape (préparation, chargement, etc.), tout en ajoutant des retours sonores, haptiques et visuels pour une expérience utilisateur améliorée.
**Fonctionnalités et améliorations principales :**
- **Scan et saisie manuelle en préparation d'événement :**
- Ajout d'un champ de "Saisie manuelle" et d'un bouton "Scanner QR Code" sur la page de préparation (`EventPreparationPage`).
- Le scanner peut fonctionner en mode "multi-scan", permettant de valider plusieurs éléments à la suite sans fermer la caméra.
- Le système gère à la fois les équipements individuels et les containers (qui valident automatiquement tout leur contenu).
- **Logique de traitement intelligente (`QRCodeProcessingService`) :**
- Un nouveau service centralise la logique de traitement des codes.
- Pour les équipements quantitatifs, chaque scan incrémente la quantité jusqu'à atteindre la cible requise pour l'étape en cours.
- Pour les équipements non quantitatifs, le premier scan valide l'élément.
- Les scans multiples d'un élément déjà validé ou dont la quantité est atteinte génèrent une erreur.
- **Ajout dynamique d'équipements :**
- Si un code scanné n'est pas assigné à l'événement, une boîte de dialogue propose de rechercher l'équipement ou le container dans la base de données et de l'ajouter à l'événement en cours.
- **Feedbacks utilisateur :**
- Création d'un `AudioFeedbackService` pour fournir des retours sonores (succès/erreur) et haptiques (vibration) lors de chaque scan.
- Des `Snackbars` claires (vertes pour succès, orange pour erreur) informent l'utilisateur du résultat de chaque action.
- **Optimisation du chargement des données :**
- Nouvel endpoint backend `getEventWithDetails` qui charge un événement et toutes ses dépendances (équipements, containers et leurs enfants) en un seul appel, optimisant drastiquement les temps de chargement des pages de préparation et de modification d'événement.
- Le frontend (`EventPreparationPage`, `EventAssignedEquipmentSection`) utilise ce nouvel endpoint, éliminant les chargements multiples et fiabilisant l'affichage des données.
**Refactorisation et corrections :**
- **Structure du code :**
- La logique de traitement des codes est extraite dans le `QRCodeProcessingService`.
- Création de widgets dédiés (`CodeNotFoundDialog`, `AddEquipmentToEventDialog`) pour gérer les nouveaux flux utilisateurs.
- **Fiabilisation de l'état :**
- Mise à jour optimiste de l'UI lors du changement de statut d'un événement (`EventStatusButton`) pour une meilleure réactivité.
- Correction d'un bug dans la suppression d'un container d'un événement, qui pouvait retirer des équipements partagés avec d'autres containers.
- Correction d'un bug lors de l'ajout d'un container à un événement, qui n'ajoutait pas automatiquement ses équipements enfants.
- **Optimisations des performances UI :**
- Amélioration de la fluidité du défilement infini sur la page de gestion des équipements grâce à `RepaintBoundary` et à une gestion optimisée du chargement.
**Déploiement et version :**
- **Scripts de déploiement :** Ajout d'un script PowerShell (`deploy_hosting.ps1`) et amélioration du script Node.js pour automatiser et fiabiliser les déploiements sur Firebase Hosting.
- **Configuration CORS :** Les en-têtes CORS sont désormais configurés pour `version.json`, assurant le bon fonctionnement du mécanisme de mise à jour de l'application.
- **Version de l'application :** Incrémentée à `1.0.6`.
677 lines
23 KiB
Dart
677 lines
23 KiB
Dart
import 'package:em2rp/services/api_service.dart';
|
|
import 'package:em2rp/utils/debug_log.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 événement
|
|
Future<void> updateEvent(String eventId, Map<String, dynamic> data) async {
|
|
try {
|
|
// Correction : fusionner eventId et les champs de data à la racine
|
|
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 équipement
|
|
Future<void> createEquipment(String equipmentId, Map<String, dynamic> data) async {
|
|
try {
|
|
// S'assurer que l'ID est dans les données
|
|
final equipmentData = Map<String, dynamic>.from(data);
|
|
equipmentData['id'] = equipmentId;
|
|
|
|
await _apiService.call('createEquipment', equipmentData);
|
|
} 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 {
|
|
await _apiService.call('updateEquipment', {
|
|
'equipmentId': equipmentId,
|
|
'data': data,
|
|
});
|
|
} 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 un événement avec tous les détails (équipements complets + containers avec enfants)
|
|
Future<Map<String, dynamic>> getEventWithDetails(String eventId) async {
|
|
try {
|
|
print('[DataService] Getting event with details: $eventId');
|
|
final result = await _apiService.call('getEventWithDetails', {
|
|
'eventId': eventId,
|
|
});
|
|
|
|
final event = result['event'] as Map<String, dynamic>?;
|
|
final equipments = result['equipments'] as Map<String, dynamic>? ?? {};
|
|
final containers = result['containers'] as Map<String, dynamic>? ?? {};
|
|
|
|
if (event == null) {
|
|
throw Exception('Event not found');
|
|
}
|
|
|
|
print('[DataService] Event loaded with ${equipments.length} equipments and ${containers.length} containers');
|
|
|
|
return {
|
|
'event': event,
|
|
'equipments': equipments,
|
|
'containers': containers,
|
|
};
|
|
} catch (e) {
|
|
print('[DataService] Error getting event with details: $e');
|
|
throw Exception('Erreur lors de la récupération de l\'événement avec détails: $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 plusieurs équipements par leurs IDs
|
|
Future<List<Map<String, dynamic>>> getEquipmentsByIds(List<String> equipmentIds) async {
|
|
try {
|
|
if (equipmentIds.isEmpty) return [];
|
|
|
|
print('[DataService] Getting equipments by IDs: ${equipmentIds.length} items');
|
|
final result = await _apiService.call('getEquipmentsByIds', {
|
|
'equipmentIds': equipmentIds,
|
|
});
|
|
final equipments = result['equipments'] as List<dynamic>?;
|
|
if (equipments == null) {
|
|
print('[DataService] No equipments in result');
|
|
return [];
|
|
}
|
|
print('[DataService] Found ${equipments.length} equipments by IDs');
|
|
return equipments.map((e) => e as Map<String, dynamic>).toList();
|
|
} catch (e) {
|
|
print('[DataService] Error getting equipments by IDs: $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 plusieurs containers par leurs IDs
|
|
Future<List<Map<String, dynamic>>> getContainersByIds(List<String> containerIds) async {
|
|
try {
|
|
if (containerIds.isEmpty) return [];
|
|
|
|
print('[DataService] Getting containers by IDs: ${containerIds.length} items');
|
|
final result = await _apiService.call('getContainersByIds', {
|
|
'containerIds': containerIds,
|
|
});
|
|
final containers = result['containers'] as List<dynamic>?;
|
|
if (containers == null) {
|
|
print('[DataService] No containers in result');
|
|
return [];
|
|
}
|
|
print('[DataService] Found ${containers.length} containers by IDs');
|
|
return containers.map((e) => e as Map<String, dynamic>).toList();
|
|
} catch (e) {
|
|
print('[DataService] Error getting containers by IDs: $e');
|
|
throw Exception('Erreur lors de la récupération des containers: $e');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// EQUIPMENTS & CONTAINERS - Pagination
|
|
// ============================================================================
|
|
|
|
/// Récupère les équipements avec pagination et filtrage
|
|
Future<Map<String, dynamic>> getEquipmentsPaginated({
|
|
int limit = 20,
|
|
String? startAfter,
|
|
String? category,
|
|
String? status,
|
|
String? searchQuery,
|
|
String sortBy = 'id',
|
|
String sortOrder = 'asc',
|
|
}) async {
|
|
try {
|
|
final params = <String, dynamic>{
|
|
'limit': limit,
|
|
'sortBy': sortBy,
|
|
'sortOrder': sortOrder,
|
|
};
|
|
|
|
if (startAfter != null) params['startAfter'] = startAfter;
|
|
if (category != null) params['category'] = category;
|
|
if (status != null) params['status'] = status;
|
|
if (searchQuery != null && searchQuery.isNotEmpty) {
|
|
params['searchQuery'] = searchQuery;
|
|
}
|
|
|
|
final result = await (_apiService as FirebaseFunctionsApiService).callPaginated(
|
|
'getEquipmentsPaginated',
|
|
params,
|
|
);
|
|
|
|
return {
|
|
'equipments': (result['equipments'] as List<dynamic>?)
|
|
?.map((e) => e as Map<String, dynamic>)
|
|
.toList() ?? [],
|
|
'hasMore': result['hasMore'] as bool? ?? false,
|
|
'lastVisible': result['lastVisible'] as String?,
|
|
'total': result['total'] as int? ?? 0,
|
|
};
|
|
} catch (e) {
|
|
DebugLog.error('[DataService] Error in getEquipmentsPaginated', e);
|
|
throw Exception('Erreur lors de la récupération paginée des équipements: $e');
|
|
}
|
|
}
|
|
|
|
/// Récupère les containers avec pagination et filtrage
|
|
Future<Map<String, dynamic>> getContainersPaginated({
|
|
int limit = 20,
|
|
String? startAfter,
|
|
String? type,
|
|
String? status,
|
|
String? searchQuery,
|
|
String? category,
|
|
String sortBy = 'id',
|
|
String sortOrder = 'asc',
|
|
}) async {
|
|
try {
|
|
final params = <String, dynamic>{
|
|
'limit': limit,
|
|
'sortBy': sortBy,
|
|
'sortOrder': sortOrder,
|
|
};
|
|
|
|
if (startAfter != null) params['startAfter'] = startAfter;
|
|
if (type != null) params['type'] = type;
|
|
if (status != null) params['status'] = status;
|
|
if (category != null) params['category'] = category;
|
|
if (searchQuery != null && searchQuery.isNotEmpty) {
|
|
params['searchQuery'] = searchQuery;
|
|
}
|
|
|
|
final result = await (_apiService as FirebaseFunctionsApiService).callPaginated(
|
|
'getContainersPaginated',
|
|
params,
|
|
);
|
|
|
|
return {
|
|
'containers': (result['containers'] as List<dynamic>?)
|
|
?.map((e) => e as Map<String, dynamic>)
|
|
.toList() ?? [],
|
|
'hasMore': result['hasMore'] as bool? ?? false,
|
|
'lastVisible': result['lastVisible'] as String?,
|
|
'total': result['total'] as int? ?? 0,
|
|
};
|
|
} catch (e) {
|
|
DebugLog.error('[DataService] Error in getContainersPaginated', e);
|
|
throw Exception('Erreur lors de la récupération paginée des containers: $e');
|
|
}
|
|
}
|
|
|
|
/// Recherche rapide (autocomplétion)
|
|
Future<List<Map<String, dynamic>>> quickSearch(
|
|
String query, {
|
|
int limit = 10,
|
|
bool includeEquipments = true,
|
|
bool includeContainers = true,
|
|
}) async {
|
|
try {
|
|
return await (_apiService as FirebaseFunctionsApiService).quickSearch(
|
|
query,
|
|
limit: limit,
|
|
includeEquipments: includeEquipments,
|
|
includeContainers: includeContainers,
|
|
);
|
|
} catch (e) {
|
|
DebugLog.error('[DataService] Error in quickSearch', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// USER - Current User
|
|
// ============================================================================
|
|
|
|
/// Récupère l'utilisateur actuellement authentifié avec son rôle
|
|
Future<Map<String, dynamic>> getCurrentUser() async {
|
|
try {
|
|
print('[DataService] Calling getCurrentUser API...');
|
|
final result = await _apiService.call('getCurrentUser', {});
|
|
print('[DataService] Current user loaded successfully');
|
|
return result['user'] as Map<String, dynamic>;
|
|
} catch (e) {
|
|
print('[DataService] Error getting current user: $e');
|
|
throw Exception('Erreur lors de la récupération de l\'utilisateur actuel: $e');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// ALERTS
|
|
// ============================================================================
|
|
|
|
/// Récupère toutes 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');
|
|
}
|
|
}
|
|
|
|
/// Marque une alerte comme lue
|
|
Future<void> markAlertAsRead(String alertId) async {
|
|
try {
|
|
await _apiService.call('markAlertAsRead', {'alertId': alertId});
|
|
} catch (e) {
|
|
throw Exception('Erreur lors du marquage de l\'alerte comme lue: $e');
|
|
}
|
|
}
|
|
|
|
/// Supprime une alerte
|
|
Future<void> deleteAlert(String alertId) async {
|
|
try {
|
|
await _apiService.call('deleteAlert', {'alertId': alertId});
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la suppression de l\'alerte: $e');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// EQUIPMENT AVAILABILITY
|
|
// ============================================================================
|
|
|
|
/// Vérifie la disponibilité d'un équipement
|
|
Future<Map<String, dynamic>> checkEquipmentAvailability({
|
|
required String equipmentId,
|
|
required DateTime startDate,
|
|
required DateTime endDate,
|
|
String? excludeEventId,
|
|
}) async {
|
|
try {
|
|
final result = await _apiService.call('checkEquipmentAvailability', {
|
|
'equipmentId': equipmentId,
|
|
'startDate': startDate.toIso8601String(),
|
|
'endDate': endDate.toIso8601String(),
|
|
if (excludeEventId != null) 'excludeEventId': excludeEventId,
|
|
});
|
|
return result;
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la vérification de disponibilité: $e');
|
|
}
|
|
}
|
|
|
|
/// Récupère tous les IDs d'équipements et conteneurs en conflit pour une période
|
|
/// Optimisé : une seule requête au lieu d'une par équipement
|
|
Future<Map<String, dynamic>> getConflictingEquipmentIds({
|
|
required DateTime startDate,
|
|
required DateTime endDate,
|
|
String? excludeEventId,
|
|
int installationTime = 0,
|
|
int disassemblyTime = 0,
|
|
}) async {
|
|
try {
|
|
final result = await _apiService.call('getConflictingEquipmentIds', {
|
|
'startDate': startDate.toIso8601String(),
|
|
'endDate': endDate.toIso8601String(),
|
|
if (excludeEventId != null) 'excludeEventId': excludeEventId,
|
|
'installationTime': installationTime,
|
|
'disassemblyTime': disassemblyTime,
|
|
});
|
|
return result;
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la récupération des équipements en conflit: $e');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// MAINTENANCES
|
|
// ============================================================================
|
|
|
|
/// Récupère toutes les maintenances
|
|
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');
|
|
}
|
|
}
|
|
|
|
/// Supprime une maintenance
|
|
Future<void> deleteMaintenance(String maintenanceId) async {
|
|
try {
|
|
await _apiService.call('deleteMaintenance', {'maintenanceId': maintenanceId});
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la suppression de la maintenance: $e');
|
|
}
|
|
}
|
|
|
|
/// Récupère les containers contenant un équipement
|
|
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: $e');
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// USERS
|
|
// ============================================================================
|
|
|
|
/// Récupère tous les utilisateurs (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 un utilisateur spécifique
|
|
Future<Map<String, dynamic>> getUser(String userId) async {
|
|
try {
|
|
final result = await _apiService.call('getUser', {'userId': userId});
|
|
return result['user'] as Map<String, dynamic>;
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la récupération de l\'utilisateur: $e');
|
|
}
|
|
}
|
|
|
|
/// Supprime un utilisateur (Auth + Firestore)
|
|
Future<void> deleteUser(String userId) async {
|
|
try {
|
|
await _apiService.call('deleteUser', {'userId': userId});
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la suppression de l\'utilisateur: $e');
|
|
}
|
|
}
|
|
|
|
/// Met à jour un utilisateur
|
|
Future<void> updateUser(String userId, Map<String, dynamic> data) async {
|
|
try {
|
|
await _apiService.call('updateUser', {
|
|
'userId': userId,
|
|
'data': data,
|
|
});
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la mise à jour de l\'utilisateur: $e');
|
|
}
|
|
}
|
|
|
|
/// Crée un utilisateur avec invitation par email
|
|
Future<Map<String, dynamic>> createUserWithInvite({
|
|
required String email,
|
|
required String firstName,
|
|
required String lastName,
|
|
String? phoneNumber,
|
|
required String roleId,
|
|
}) async {
|
|
try {
|
|
final result = await _apiService.call('createUserWithInvite', {
|
|
'email': email,
|
|
'firstName': firstName,
|
|
'lastName': lastName,
|
|
'phoneNumber': phoneNumber ?? '',
|
|
'roleId': roleId,
|
|
});
|
|
return result;
|
|
} catch (e) {
|
|
throw Exception('Erreur lors de la création de l\'utilisateur: $e');
|
|
}
|
|
}
|
|
}
|