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

@@ -3,17 +3,21 @@ library adhesions_bloc;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:injectable/injectable.dart';
import '../../../core/utils/logger.dart';
import '../data/models/adhesion_model.dart';
import '../data/repositories/adhesion_repository.dart';
part 'adhesions_event.dart';
part 'adhesions_state.dart';
@injectable
class AdhesionsBloc extends Bloc<AdhesionsEvent, AdhesionsState> {
final AdhesionRepository _repository;
AdhesionsBloc(this._repository) : super(const AdhesionsState()) {
on<LoadAdhesions>(_onLoadAdhesions);
on<LoadAdhesionsByMembre>(_onLoadAdhesionsByMembre);
on<LoadAdhesionsEnAttente>(_onLoadAdhesionsEnAttente);
on<LoadAdhesionsByStatut>(_onLoadAdhesionsByStatut);
on<LoadAdhesionById>(_onLoadAdhesionById);
@@ -34,6 +38,17 @@ class AdhesionsBloc extends Bloc<AdhesionsEvent, AdhesionsState> {
}
}
Future<void> _onLoadAdhesionsByMembre(LoadAdhesionsByMembre event, Emitter<AdhesionsState> emit) async {
emit(state.copyWith(status: AdhesionsStatus.loading, message: 'Chargement...'));
try {
final list = await _repository.getByMembre(event.membreId, page: event.page, size: event.size);
emit(state.copyWith(status: AdhesionsStatus.loaded, adhesions: list));
} catch (e) {
emit(state.copyWith(status: AdhesionsStatus.error, message: e.toString(), error: e));
}
}
Future<void> _onLoadAdhesionsEnAttente(LoadAdhesionsEnAttente event, Emitter<AdhesionsState> emit) async {
emit(state.copyWith(status: AdhesionsStatus.loading, message: 'Chargement...'));
try {
@@ -116,6 +131,13 @@ class AdhesionsBloc extends Bloc<AdhesionsEvent, AdhesionsState> {
try {
final stats = await _repository.getStats();
emit(state.copyWith(stats: stats));
} catch (_) {}
} catch (e, st) {
AppLogger.error('AdhesionsBloc: chargement stats échoué', error: e, stackTrace: st);
emit(state.copyWith(
status: AdhesionsStatus.error,
message: e.toString(),
error: e,
));
}
}
}

View File

@@ -14,6 +14,15 @@ class LoadAdhesions extends AdhesionsEvent {
List<Object?> get props => [page, size];
}
class LoadAdhesionsByMembre extends AdhesionsEvent {
final String membreId;
final int page;
final int size;
const LoadAdhesionsByMembre(this.membreId, {this.page = 0, this.size = 20});
@override
List<Object?> get props => [membreId, page, size];
}
class LoadAdhesionsEnAttente extends AdhesionsEvent {
final int page;
final int size;