Initial commit: unionflow-mobile-apps

Application Flutter complète (sans build artifacts).

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 16:30:08 +00:00
commit d094d6db9c
1790 changed files with 507435 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
/// Entité métier Conversation
///
/// Représente une conversation (fil de messages) dans UnionFlow
library conversation;
import 'package:equatable/equatable.dart';
import 'message.dart';
/// Type de conversation
enum ConversationType {
/// Conversation individuelle (1-1)
individual,
/// Conversation de groupe
group,
/// Canal broadcast (lecture seule pour la plupart)
broadcast,
/// Canal d'annonces organisation
announcement,
}
/// Entité Conversation
class Conversation extends Equatable {
final String id;
final String name;
final String? description;
final ConversationType type;
final List<String> participantIds;
final String? organizationId;
final Message? lastMessage;
final int unreadCount;
final bool isMuted;
final bool isPinned;
final bool isArchived;
final DateTime createdAt;
final DateTime? updatedAt;
final String? avatarUrl;
final Map<String, dynamic>? metadata;
const Conversation({
required this.id,
required this.name,
this.description,
required this.type,
required this.participantIds,
this.organizationId,
this.lastMessage,
this.unreadCount = 0,
this.isMuted = false,
this.isPinned = false,
this.isArchived = false,
required this.createdAt,
this.updatedAt,
this.avatarUrl,
this.metadata,
});
/// Vérifie si la conversation a des messages non lus
bool get hasUnread => unreadCount > 0;
/// Vérifie si c'est une conversation individuelle
bool get isIndividual => type == ConversationType.individual;
/// Vérifie si c'est un broadcast
bool get isBroadcast => type == ConversationType.broadcast;
/// Nombre de participants
int get participantCount => participantIds.length;
/// Copie avec modifications
Conversation copyWith({
String? id,
String? name,
String? description,
ConversationType? type,
List<String>? participantIds,
String? organizationId,
Message? lastMessage,
int? unreadCount,
bool? isMuted,
bool? isPinned,
bool? isArchived,
DateTime? createdAt,
DateTime? updatedAt,
String? avatarUrl,
Map<String, dynamic>? metadata,
}) {
return Conversation(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
type: type ?? this.type,
participantIds: participantIds ?? this.participantIds,
organizationId: organizationId ?? this.organizationId,
lastMessage: lastMessage ?? this.lastMessage,
unreadCount: unreadCount ?? this.unreadCount,
isMuted: isMuted ?? this.isMuted,
isPinned: isPinned ?? this.isPinned,
isArchived: isArchived ?? this.isArchived,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
avatarUrl: avatarUrl ?? this.avatarUrl,
metadata: metadata ?? this.metadata,
);
}
@override
List<Object?> get props => [
id,
name,
description,
type,
participantIds,
organizationId,
lastMessage,
unreadCount,
isMuted,
isPinned,
isArchived,
createdAt,
updatedAt,
avatarUrl,
metadata,
];
}

View File

@@ -0,0 +1,173 @@
/// Entité métier Message
///
/// Représente un message dans le système de communication UnionFlow
library message;
import 'package:equatable/equatable.dart';
/// Type de message
enum MessageType {
/// Message individuel (membre à membre)
individual,
/// Broadcast organisation (OrgAdmin → tous)
broadcast,
/// Message ciblé par rôle (Moderator → groupe)
targeted,
/// Notification système
system,
}
/// Statut de lecture du message
enum MessageStatus {
/// Envoyé mais non lu
sent,
/// Livré (reçu par le serveur)
delivered,
/// Lu par le destinataire
read,
/// Échec d'envoi
failed,
}
/// Priorité du message
enum MessagePriority {
/// Priorité normale
normal,
/// Priorité élevée (important)
high,
/// Priorité urgente (critique)
urgent,
}
/// Entité Message
class Message extends Equatable {
final String id;
final String conversationId;
final String senderId;
final String senderName;
final String? senderAvatar;
final String content;
final MessageType type;
final MessageStatus status;
final MessagePriority priority;
final List<String> recipientIds;
final List<String>? recipientRoles;
final String? organizationId;
final DateTime createdAt;
final DateTime? readAt;
final Map<String, dynamic>? metadata;
final List<String>? attachments;
final bool isEdited;
final DateTime? editedAt;
final bool isDeleted;
const Message({
required this.id,
required this.conversationId,
required this.senderId,
required this.senderName,
this.senderAvatar,
required this.content,
required this.type,
required this.status,
this.priority = MessagePriority.normal,
required this.recipientIds,
this.recipientRoles,
this.organizationId,
required this.createdAt,
this.readAt,
this.metadata,
this.attachments,
this.isEdited = false,
this.editedAt,
this.isDeleted = false,
});
/// Vérifie si le message a été lu
bool get isRead => status == MessageStatus.read;
/// Vérifie si le message est urgent
bool get isUrgent => priority == MessagePriority.urgent;
/// Vérifie si le message est un broadcast
bool get isBroadcast => type == MessageType.broadcast;
/// Vérifie si le message a des pièces jointes
bool get hasAttachments => attachments != null && attachments!.isNotEmpty;
/// Copie avec modifications
Message copyWith({
String? id,
String? conversationId,
String? senderId,
String? senderName,
String? senderAvatar,
String? content,
MessageType? type,
MessageStatus? status,
MessagePriority? priority,
List<String>? recipientIds,
List<String>? recipientRoles,
String? organizationId,
DateTime? createdAt,
DateTime? readAt,
Map<String, dynamic>? metadata,
List<String>? attachments,
bool? isEdited,
DateTime? editedAt,
bool? isDeleted,
}) {
return Message(
id: id ?? this.id,
conversationId: conversationId ?? this.conversationId,
senderId: senderId ?? this.senderId,
senderName: senderName ?? this.senderName,
senderAvatar: senderAvatar ?? this.senderAvatar,
content: content ?? this.content,
type: type ?? this.type,
status: status ?? this.status,
priority: priority ?? this.priority,
recipientIds: recipientIds ?? this.recipientIds,
recipientRoles: recipientRoles ?? this.recipientRoles,
organizationId: organizationId ?? this.organizationId,
createdAt: createdAt ?? this.createdAt,
readAt: readAt ?? this.readAt,
metadata: metadata ?? this.metadata,
attachments: attachments ?? this.attachments,
isEdited: isEdited ?? this.isEdited,
editedAt: editedAt ?? this.editedAt,
isDeleted: isDeleted ?? this.isDeleted,
);
}
@override
List<Object?> get props => [
id,
conversationId,
senderId,
senderName,
senderAvatar,
content,
type,
status,
priority,
recipientIds,
recipientRoles,
organizationId,
createdAt,
readAt,
metadata,
attachments,
isEdited,
editedAt,
isDeleted,
];
}

View File

@@ -0,0 +1,154 @@
/// Entité métier Template de Message
///
/// Templates réutilisables pour notifications et broadcasts
library message_template;
import 'package:equatable/equatable.dart';
/// Catégorie de template
enum TemplateCategory {
/// Événements
events,
/// Finances
finances,
/// Adhésions
membership,
/// Solidarité
solidarity,
/// Système
system,
/// Personnalisé
custom,
}
/// Variables dynamiques dans les templates
class TemplateVariable {
final String name;
final String description;
final String placeholder;
final bool required;
const TemplateVariable({
required this.name,
required this.description,
required this.placeholder,
this.required = true,
});
}
/// Entité Template de Message
class MessageTemplate extends Equatable {
final String id;
final String name;
final String description;
final TemplateCategory category;
final String subject;
final String body;
final List<TemplateVariable> variables;
final String? organizationId;
final String createdBy;
final DateTime createdAt;
final DateTime? updatedAt;
final bool isActive;
final bool isSystem;
final int usageCount;
final Map<String, dynamic>? metadata;
const MessageTemplate({
required this.id,
required this.name,
required this.description,
required this.category,
required this.subject,
required this.body,
this.variables = const [],
this.organizationId,
required this.createdBy,
required this.createdAt,
this.updatedAt,
this.isActive = true,
this.isSystem = false,
this.usageCount = 0,
this.metadata,
});
/// Vérifie si le template est éditable (pas système)
bool get isEditable => !isSystem;
/// Génère un message à partir du template avec des valeurs
String generateMessage(Map<String, String> values) {
String result = body;
for (final variable in variables) {
final value = values[variable.name];
if (value != null) {
result = result.replaceAll('{{${variable.name}}}', value);
} else if (variable.required) {
throw ArgumentError('Variable requise manquante: ${variable.name}');
}
}
return result;
}
/// Copie avec modifications
MessageTemplate copyWith({
String? id,
String? name,
String? description,
TemplateCategory? category,
String? subject,
String? body,
List<TemplateVariable>? variables,
String? organizationId,
String? createdBy,
DateTime? createdAt,
DateTime? updatedAt,
bool? isActive,
bool? isSystem,
int? usageCount,
Map<String, dynamic>? metadata,
}) {
return MessageTemplate(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
category: category ?? this.category,
subject: subject ?? this.subject,
body: body ?? this.body,
variables: variables ?? this.variables,
organizationId: organizationId ?? this.organizationId,
createdBy: createdBy ?? this.createdBy,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
isActive: isActive ?? this.isActive,
isSystem: isSystem ?? this.isSystem,
usageCount: usageCount ?? this.usageCount,
metadata: metadata ?? this.metadata,
);
}
@override
List<Object?> get props => [
id,
name,
description,
category,
subject,
body,
variables,
organizationId,
createdBy,
createdAt,
updatedAt,
isActive,
isSystem,
usageCount,
metadata,
];
}