Files
EM2_ERP/em2rp/lib/models/alert_model.dart

274 lines
9.0 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
/// Type d'alerte
enum AlertType {
lowStock, // Stock faible
maintenanceDue, // Maintenance à venir
conflict, // Conflit disponibilité
lost, // Équipement perdu
eventCreated, // Événement créé
eventModified, // Événement modifié
eventCancelled, // Événement annulé
eventAssigned, // Assigné à un événement
maintenanceReminder, // Rappel maintenance périodique
equipmentMissing, // Équipement manquant à une étape
quantityMismatch, // Quantité incorrecte
damaged, // Équipement endommagé
workforceAdded, // Ajouté à la workforce d'un événement
}
/// Gravité de l'alerte
enum AlertSeverity {
info, // Information (bleu)
warning, // Avertissement (orange)
critical, // Critique (rouge)
}
String alertTypeToString(AlertType type) {
switch (type) {
case AlertType.lowStock:
return 'LOW_STOCK';
case AlertType.maintenanceDue:
return 'MAINTENANCE_DUE';
case AlertType.conflict:
return 'CONFLICT';
case AlertType.lost:
return 'LOST';
case AlertType.eventCreated:
return 'EVENT_CREATED';
case AlertType.eventModified:
return 'EVENT_MODIFIED';
case AlertType.eventCancelled:
return 'EVENT_CANCELLED';
case AlertType.eventAssigned:
return 'EVENT_ASSIGNED';
case AlertType.maintenanceReminder:
return 'MAINTENANCE_REMINDER';
case AlertType.equipmentMissing:
return 'EQUIPMENT_MISSING';
case AlertType.quantityMismatch:
return 'QUANTITY_MISMATCH';
case AlertType.damaged:
return 'DAMAGED';
case AlertType.workforceAdded:
return 'WORKFORCE_ADDED';
}
}
AlertType alertTypeFromString(String? type) {
switch (type) {
case 'LOW_STOCK':
return AlertType.lowStock;
case 'MAINTENANCE_DUE':
return AlertType.maintenanceDue;
case 'CONFLICT':
return AlertType.conflict;
case 'LOST':
return AlertType.lost;
case 'EVENT_CREATED':
return AlertType.eventCreated;
case 'EVENT_MODIFIED':
return AlertType.eventModified;
case 'EVENT_CANCELLED':
return AlertType.eventCancelled;
case 'EVENT_ASSIGNED':
return AlertType.eventAssigned;
case 'MAINTENANCE_REMINDER':
return AlertType.maintenanceReminder;
case 'EQUIPMENT_MISSING':
return AlertType.equipmentMissing;
case 'QUANTITY_MISMATCH':
return AlertType.quantityMismatch;
case 'DAMAGED':
return AlertType.damaged;
case 'WORKFORCE_ADDED':
return AlertType.workforceAdded;
default:
return AlertType.conflict;
}
}
String alertSeverityToString(AlertSeverity severity) {
switch (severity) {
case AlertSeverity.info:
return 'INFO';
case AlertSeverity.warning:
return 'WARNING';
case AlertSeverity.critical:
return 'CRITICAL';
}
}
AlertSeverity alertSeverityFromString(String? severity) {
switch (severity) {
case 'INFO':
return AlertSeverity.info;
case 'WARNING':
return AlertSeverity.warning;
case 'CRITICAL':
return AlertSeverity.critical;
default:
return AlertSeverity.info;
}
}
class AlertModel {
final String id; // ID généré automatiquement
final AlertType type; // Type d'alerte
final AlertSeverity severity; // Gravité de l'alerte
final String message; // Message de l'alerte
final List<String> assignedToUserIds; // Utilisateurs concernés
final String? eventId; // ID de l'événement concerné (optionnel)
final String? equipmentId; // ID de l'équipement concerné (optionnel)
final String? createdByUserId; // Qui a déclenché l'alerte
final DateTime createdAt; // Date de création
final DateTime? dueDate; // Date d'échéance (pour maintenance)
final String? actionUrl; // URL de redirection (deep link)
final bool isRead; // Statut lu/non lu
final bool isResolved; // Résolue ou non
final String? resolution; // Message de résolution
final DateTime? resolvedAt; // Date de résolution
final String? resolvedByUserId; // Qui a résolu
AlertModel({
required this.id,
required this.type,
this.severity = AlertSeverity.info,
required this.message,
this.assignedToUserIds = const [],
this.eventId,
this.equipmentId,
this.createdByUserId,
required this.createdAt,
this.dueDate,
this.actionUrl,
this.isRead = false,
this.isResolved = false,
this.resolution,
this.resolvedAt,
this.resolvedByUserId,
});
factory AlertModel.fromMap(Map<String, dynamic> map, String id) {
// Fonction helper pour convertir Timestamp ou String ISO en DateTime
DateTime parseDate(dynamic value) {
if (value == null) return DateTime.now();
if (value is Timestamp) return value.toDate();
if (value is String) return DateTime.tryParse(value) ?? DateTime.now();
return DateTime.now();
}
// Parser les assignedToUserIds (peut être List ou null)
List<String> parseUserIds(dynamic value) {
if (value == null) return [];
if (value is List) return value.map((e) => e.toString()).toList();
return [];
}
return AlertModel(
id: id,
type: alertTypeFromString(map['type']),
severity: alertSeverityFromString(map['severity']),
message: map['message'] ?? '',
assignedToUserIds: parseUserIds(map['assignedToUserIds'] ?? map['assignedTo']),
eventId: map['eventId'],
equipmentId: map['equipmentId'],
createdByUserId: map['createdByUserId'] ?? map['createdBy'],
createdAt: parseDate(map['createdAt']),
dueDate: map['dueDate'] != null ? parseDate(map['dueDate']) : null,
actionUrl: map['actionUrl'],
isRead: map['isRead'] ?? false,
isResolved: map['isResolved'] ?? false,
resolution: map['resolution'],
resolvedAt: map['resolvedAt'] != null ? parseDate(map['resolvedAt']) : null,
resolvedByUserId: map['resolvedByUserId'],
);
}
/// Factory depuis un document Firestore
factory AlertModel.fromFirestore(DocumentSnapshot doc) {
final data = doc.data() as Map<String, dynamic>?;
if (data == null) {
throw Exception('Document vide: ${doc.id}');
}
return AlertModel.fromMap(data, doc.id);
}
Map<String, dynamic> toMap() {
return {
'type': alertTypeToString(type),
'severity': alertSeverityToString(severity),
'message': message,
'assignedToUserIds': assignedToUserIds,
if (eventId != null) 'eventId': eventId,
if (equipmentId != null) 'equipmentId': equipmentId,
if (createdByUserId != null) 'createdByUserId': createdByUserId,
'createdAt': Timestamp.fromDate(createdAt),
if (dueDate != null) 'dueDate': Timestamp.fromDate(dueDate!),
if (actionUrl != null) 'actionUrl': actionUrl,
'isRead': isRead,
'isResolved': isResolved,
if (resolution != null) 'resolution': resolution,
if (resolvedAt != null) 'resolvedAt': Timestamp.fromDate(resolvedAt!),
if (resolvedByUserId != null) 'resolvedByUserId': resolvedByUserId,
};
}
AlertModel copyWith({
String? id,
AlertType? type,
AlertSeverity? severity,
String? message,
List<String>? assignedToUserIds,
String? eventId,
String? equipmentId,
String? createdByUserId,
DateTime? createdAt,
DateTime? dueDate,
String? actionUrl,
bool? isRead,
bool? isResolved,
String? resolution,
DateTime? resolvedAt,
String? resolvedByUserId,
}) {
return AlertModel(
id: id ?? this.id,
type: type ?? this.type,
severity: severity ?? this.severity,
message: message ?? this.message,
assignedToUserIds: assignedToUserIds ?? this.assignedToUserIds,
eventId: eventId ?? this.eventId,
equipmentId: equipmentId ?? this.equipmentId,
createdByUserId: createdByUserId ?? this.createdByUserId,
createdAt: createdAt ?? this.createdAt,
dueDate: dueDate ?? this.dueDate,
actionUrl: actionUrl ?? this.actionUrl,
isRead: isRead ?? this.isRead,
isResolved: isResolved ?? this.isResolved,
resolution: resolution ?? this.resolution,
resolvedAt: resolvedAt ?? this.resolvedAt,
resolvedByUserId: resolvedByUserId ?? this.resolvedByUserId,
);
}
/// Helper : Retourne true si l'alerte est pour un événement
bool get isEventAlert =>
type == AlertType.eventCreated ||
type == AlertType.eventModified ||
type == AlertType.eventCancelled ||
type == AlertType.eventAssigned;
/// Helper : Retourne true si l'alerte est pour la maintenance
bool get isMaintenanceAlert =>
type == AlertType.maintenanceDue ||
type == AlertType.maintenanceReminder;
/// Helper : Retourne true si l'alerte est pour un équipement
bool get isEquipmentAlert =>
type == AlertType.lost ||
type == AlertType.equipmentMissing ||
type == AlertType.lowStock;
}