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,54 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../../../core/utils/logger.dart';
import '../../data/repositories/notification_feed_repository.dart';
import 'notification_event.dart';
import 'notification_state.dart';
@injectable
class NotificationBloc extends Bloc<NotificationEvent, NotificationState> {
final NotificationFeedRepository _repository;
NotificationBloc(this._repository) : super(NotificationInitial()) {
on<LoadNotificationsRequested>(_onLoadNotificationsRequested);
on<NotificationMarkedAsRead>(_onNotificationMarkedAsRead);
}
Future<void> _onLoadNotificationsRequested(LoadNotificationsRequested event, Emitter<NotificationState> emit) async {
emit(NotificationLoading());
try {
final items = await _repository.getNotifications();
emit(NotificationLoaded(items: items));
} catch (e, st) {
AppLogger.error('NotificationBloc: chargement notifications échoué', error: e, stackTrace: st);
emit(NotificationError('Erreur de chargement: $e'));
}
}
Future<void> _onNotificationMarkedAsRead(NotificationMarkedAsRead event, Emitter<NotificationState> emit) async {
if (state is NotificationLoaded) {
final currentState = state as NotificationLoaded;
try {
await _repository.markAsRead(event.id);
final updatedItems = currentState.items.map((item) {
if (item.id == event.id) {
return NotificationItem(
id: item.id,
title: item.title,
body: item.body,
date: item.date,
isRead: true,
category: item.category,
);
}
return item;
}).toList();
emit(NotificationLoaded(items: updatedItems));
} catch (e, st) {
AppLogger.error('NotificationBloc: marquer comme lu échoué', error: e, stackTrace: st);
emit(NotificationError('Impossible de marquer comme lu'));
}
}
}
}

View File

@@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
abstract class NotificationEvent extends Equatable {
const NotificationEvent();
@override
List<Object> get props => [];
}
class LoadNotificationsRequested extends NotificationEvent {}
class NotificationMarkedAsRead extends NotificationEvent {
final String id;
const NotificationMarkedAsRead(this.id);
@override
List<Object> get props => [id];
}

View File

@@ -0,0 +1,50 @@
import 'package:equatable/equatable.dart';
class NotificationItem extends Equatable {
final String id;
final String title;
final String body;
final DateTime date;
final bool isRead;
final String category; // 'finance', 'event', 'system'
const NotificationItem({
required this.id,
required this.title,
required this.body,
required this.date,
this.isRead = false,
required this.category,
});
@override
List<Object?> get props => [id, title, body, date, isRead, category];
}
abstract class NotificationState extends Equatable {
const NotificationState();
@override
List<Object?> get props => [];
}
class NotificationInitial extends NotificationState {}
class NotificationLoading extends NotificationState {}
class NotificationLoaded extends NotificationState {
final List<NotificationItem> items;
const NotificationLoaded({required this.items});
@override
List<Object?> get props => [items];
}
class NotificationError extends NotificationState {
final String message;
const NotificationError(this.message);
@override
List<Object?> get props => [message];
}

View File

@@ -0,0 +1,92 @@
library notifications_bloc;
import 'package:equatable/equatable.dart';
import 'package:injectable/injectable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dio/dio.dart';
import '../../../../core/utils/logger.dart';
import '../../data/models/notification_model.dart';
import '../../data/repositories/notification_repository.dart';
part 'notifications_event.dart';
part 'notifications_state.dart';
@injectable
class NotificationsBloc extends Bloc<NotificationsEvent, NotificationsState> {
final NotificationRepository _repository;
NotificationsBloc(this._repository) : super(const NotificationsInitial()) {
on<LoadNotifications>(_onLoadNotifications);
on<MarkNotificationAsRead>(_onMarkAsRead);
on<RefreshNotifications>(_onRefresh);
}
Future<void> _onLoadNotifications(
LoadNotifications event,
Emitter<NotificationsState> emit,
) async {
try {
emit(const NotificationsLoading());
final notifications = (event.membreId != null && event.membreId!.isNotEmpty)
? await _repository.getNotificationsByMembre(event.membreId!)
: await _repository.getMesNotifications();
final nonLues = notifications.where((n) => !n.estLue).length;
emit(NotificationsLoaded(notifications: notifications, nonLuesCount: nonLues));
} on DioException catch (e) {
emit(NotificationsError(_networkError(e)));
} catch (e) {
emit(NotificationsError('Erreur lors du chargement : $e'));
}
}
Future<void> _onMarkAsRead(
MarkNotificationAsRead event,
Emitter<NotificationsState> emit,
) async {
try {
await _repository.marquerCommeLue(event.notificationId);
final currentState = state;
if (currentState is NotificationsLoaded) {
final updated = currentState.notifications.map((n) {
if (n.id == event.notificationId) {
return NotificationModel(
id: n.id,
typeNotification: n.typeNotification,
priorite: n.priorite,
statut: 'LUE',
sujet: n.sujet,
corps: n.corps,
dateEnvoiPrevue: n.dateEnvoiPrevue,
dateEnvoi: n.dateEnvoi,
dateLecture: DateTime.now(),
donneesAdditionnelles: n.donneesAdditionnelles,
membreId: n.membreId,
organisationId: n.organisationId,
);
}
return n;
}).toList();
final nonLues = updated.where((n) => !n.estLue).length;
emit(NotificationMarkedAsRead(notifications: updated, nonLuesCount: nonLues));
}
} catch (e, st) {
AppLogger.error('NotificationsBloc: marquer comme lu échoué', error: e, stackTrace: st);
emit(NotificationsError('Impossible de marquer comme lu'));
}
}
Future<void> _onRefresh(
RefreshNotifications event,
Emitter<NotificationsState> emit,
) async {
add(LoadNotifications(membreId: event.membreId));
}
String _networkError(DioException e) {
final code = e.response?.statusCode;
if (code == 401) return 'Non autorisé.';
if (code == 403) return 'Accès refusé.';
if (code != null && code >= 500) return 'Erreur serveur.';
return 'Erreur réseau. Vérifiez votre connexion.';
}
}

View File

@@ -0,0 +1,34 @@
part of 'notifications_bloc.dart';
abstract class NotificationsEvent extends Equatable {
const NotificationsEvent();
@override
List<Object?> get props => [];
}
class LoadNotifications extends NotificationsEvent {
/// Si null ou vide, utilise GET /api/notifications/me (membre connecté).
final String? membreId;
final bool onlyUnread;
const LoadNotifications({this.membreId, this.onlyUnread = false});
@override
List<Object?> get props => [membreId, onlyUnread];
}
class MarkNotificationAsRead extends NotificationsEvent {
final String notificationId;
const MarkNotificationAsRead(this.notificationId);
@override
List<Object?> get props => [notificationId];
}
class RefreshNotifications extends NotificationsEvent {
final String? membreId;
const RefreshNotifications([this.membreId]);
@override
List<Object?> get props => [membreId];
}

View File

@@ -0,0 +1,50 @@
part of 'notifications_bloc.dart';
abstract class NotificationsState extends Equatable {
const NotificationsState();
@override
List<Object?> get props => [];
}
class NotificationsInitial extends NotificationsState {
const NotificationsInitial();
}
class NotificationsLoading extends NotificationsState {
const NotificationsLoading();
}
class NotificationsLoaded extends NotificationsState {
final List<NotificationModel> notifications;
final int nonLuesCount;
const NotificationsLoaded({
required this.notifications,
required this.nonLuesCount,
});
@override
List<Object?> get props => [notifications, nonLuesCount];
}
class NotificationMarkedAsRead extends NotificationsState {
final List<NotificationModel> notifications;
final int nonLuesCount;
const NotificationMarkedAsRead({
required this.notifications,
required this.nonLuesCount,
});
@override
List<Object?> get props => [notifications, nonLuesCount];
}
class NotificationsError extends NotificationsState {
final String message;
const NotificationsError(this.message);
@override
List<Object?> get props => [message];
}