feat: WebSocket temps réel + Finance Workflow + corrections

- Task #6: WebSocket /ws/dashboard + Kafka events (5 topics)
  * Backend: KafkaEventProducer, KafkaEventConsumer
  * Mobile: WebSocketService (reconnection, heartbeat, typed events)
  * DashboardBloc: Auto-refresh depuis WebSocket events

- Finance Workflow: approbations + budgets (backend + mobile)
  * Backend: entities, services, resources, migrations Flyway V6
  * Mobile: features finance_workflow complète avec BLoC

- Corrections DI: interfaces IRepository partout
  * IProfileRepository, IOrganizationRepository, IMembreRepository
  * GetIt configuré avec @injectable

- Spec-Kit: constitution + templates mis à jour
  * .specify/memory/constitution.md enrichie
  * Templates agent, plan, spec, tasks, checklist

- Nettoyage: fichiers temporaires supprimés

Signed-off-by: lions dev Team
This commit is contained in:
dahoud
2026-03-15 02:12:17 +00:00
parent bbc409de9d
commit e8ad874015
635 changed files with 58160 additions and 20674 deletions

View File

@@ -1,19 +1,46 @@
/// BLoC pour la gestion des rapports (Clean Architecture)
library reports_bloc;
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../data/repositories/reports_repository.dart';
import 'package:injectable/injectable.dart';
import '../../domain/usecases/get_reports.dart';
import '../../domain/usecases/generate_report.dart';
import '../../domain/usecases/export_report_pdf.dart';
import '../../domain/usecases/export_report_excel.dart';
import '../../domain/usecases/schedule_report.dart';
import '../../domain/usecases/get_scheduled_reports.dart';
import '../../domain/repositories/reports_repository.dart';
part 'reports_event.dart';
part 'reports_state.dart';
/// BLoC pour la gestion des rapports (Clean Architecture)
@injectable
class ReportsBloc extends Bloc<ReportsEvent, ReportsState> {
final ReportsRepository _repository;
final GetReports _getReports;
final GenerateReport _generateReport;
final ExportReportPdf _exportReportPdf;
final ExportReportExcel _exportReportExcel;
final ScheduleReport _scheduleReport;
final GetScheduledReports _getScheduledReports;
final IReportsRepository _repository; // Pour méthodes non-couvertes (statistics, analytics)
ReportsBloc(this._repository) : super(const ReportsInitial()) {
ReportsBloc(
this._getReports,
this._generateReport,
this._exportReportPdf,
this._exportReportExcel,
this._scheduleReport,
this._getScheduledReports,
this._repository,
) : super(const ReportsInitial()) {
on<LoadDashboardReports>(_onLoadDashboard);
on<ScheduleReportRequested>(_onScheduleReport);
on<GenerateReportRequested>(_onGenerateReport);
}
/// Charge le tableau de bord des rapports
Future<void> _onLoadDashboard(
LoadDashboardReports event,
Emitter<ReportsState> emit,
@@ -40,4 +67,30 @@ class ReportsBloc extends Bloc<ReportsEvent, ReportsState> {
emit(ReportsError('Erreur lors du chargement des rapports : $e'));
}
}
/// Programme un rapport automatique
Future<void> _onScheduleReport(
ScheduleReportRequested event,
Emitter<ReportsState> emit,
) async {
try {
await _scheduleReport(cronExpression: event.cronExpression);
emit(const ReportScheduled());
} catch (e) {
emit(ReportsError('Impossible de programmer le rapport : $e'));
}
}
/// Génère un rapport
Future<void> _onGenerateReport(
GenerateReportRequested event,
Emitter<ReportsState> emit,
) async {
try {
await _generateReport(event.type, format: event.format);
emit(ReportGenerated(event.type));
} catch (e) {
emit(ReportsError('Impossible de générer le rapport : $e'));
}
}
}

View File

@@ -26,3 +26,20 @@ class LoadCotisationsStats extends ReportsEvent {
class LoadEvenementsStats extends ReportsEvent {
const LoadEvenementsStats();
}
class ScheduleReportRequested extends ReportsEvent {
final String? cronExpression;
const ScheduleReportRequested({this.cronExpression});
@override
List<Object?> get props => [cronExpression];
}
class GenerateReportRequested extends ReportsEvent {
final String type;
final String? format;
const GenerateReportRequested(this.type, {this.format});
@override
List<Object?> get props => [type, format];
}

View File

@@ -39,3 +39,20 @@ class ReportsError extends ReportsState {
@override
List<Object?> get props => [message];
}
class ReportScheduled extends ReportsState {
final String message;
const ReportScheduled([this.message = 'Programmation configurée. Vous recevrez le rapport par email.']);
@override
List<Object?> get props => [message];
}
class ReportGenerated extends ReportsState {
final String type;
final String message;
const ReportGenerated(this.type, [this.message = 'Génération lancée. Vous recevrez le rapport par email.']);
@override
List<Object?> get props => [type, message];
}