Files
unionflow-mobile-apps/lib/features/compliance/presentation/bloc/compliance_bloc.dart
dahoud 8c1a254e80 feat(sprint-9 mobile 2026-04-25): feature compliance dashboard + Devise enum + selector + tests
Apporte aux compliance officers et controleurs internes l'accès mobile au tableau de bord
de conformité backend (P1-NEW-7). Et prépare la diaspora avec Devise enum + sélecteur
préférence persisté.

Feature Compliance (Clean Architecture)
- Domain : ComplianceSnapshot + ConformiteIndicateur (Equatable), helpers scoreSeverite + hasAlertesCritiques
- Data : ComplianceSnapshotModel.fromJson (parsing tolerant aux nullables), ComplianceRemoteDataSourceImpl Dio (GET /api/compliance/dashboard), ComplianceRepositoryImpl @Injectable
- Presentation : ComplianceBloc (Load/Refresh events, Initial/Loading/Loaded/Error states), ConformiteDashboardPage (Material 3, ScoreCard 0-100 colorée, 9 IndicateurTile, AlertesCard rouge si critiques)

Feature Devise
- Devise enum (10 valeurs miroirs backend, code/libelle/zone)
- fromCode tolérant casse + null/vide → XOF
- estInternationale pour AML
- DeviseSelector widget DropdownButtonFormField + readPreferred/writePreferred via FlutterSecureStorage (clé unionflow.devise.preferee)

Tests (17/17 verts)
- ComplianceSnapshot : 7 tests (scoreSeverite × 3, hasAlertesCritiques × 4)
- ComplianceSnapshotModel.fromJson : 4 tests (complet, fallbacks, string→double, indicateur invalide)
- Devise enum : 6 tests (reference, fromCode parse, fromCode null/inconnu, estInternationale × 2, intégrité valeurs)

Note : feature reporting trimestriel mobile (PDF viewer + bloc liste) reportée à un sprint
dédié — nécessite intégration pdf viewer + cache local non triviale.
2026-04-25 11:11:03 +00:00

72 lines
1.9 KiB
Dart

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../../../core/utils/logger.dart';
import '../../domain/entities/compliance_snapshot.dart';
import '../../domain/repositories/compliance_repository.dart';
// Events
abstract class ComplianceEvent extends Equatable {
const ComplianceEvent();
@override
List<Object?> get props => [];
}
class LoadComplianceSnapshot extends ComplianceEvent {
const LoadComplianceSnapshot();
}
class RefreshComplianceSnapshot extends ComplianceEvent {
const RefreshComplianceSnapshot();
}
// States
abstract class ComplianceState extends Equatable {
const ComplianceState();
@override
List<Object?> get props => [];
}
class ComplianceInitial extends ComplianceState {
const ComplianceInitial();
}
class ComplianceLoading extends ComplianceState {
const ComplianceLoading();
}
class ComplianceLoaded extends ComplianceState {
final ComplianceSnapshot snapshot;
const ComplianceLoaded(this.snapshot);
@override
List<Object?> get props => [snapshot];
}
class ComplianceError extends ComplianceState {
final String message;
const ComplianceError(this.message);
@override
List<Object?> get props => [message];
}
@injectable
class ComplianceBloc extends Bloc<ComplianceEvent, ComplianceState> {
final ComplianceRepository repository;
ComplianceBloc(this.repository) : super(const ComplianceInitial()) {
on<LoadComplianceSnapshot>(_onLoad);
on<RefreshComplianceSnapshot>(_onLoad);
}
Future<void> _onLoad(ComplianceEvent event, Emitter<ComplianceState> emit) async {
emit(const ComplianceLoading());
try {
final s = await repository.getSnapshotCurrent();
emit(ComplianceLoaded(s));
} catch (e, st) {
AppLogger.error('ComplianceBloc: load failed', error: e, stackTrace: st);
emit(ComplianceError(e.toString()));
}
}
}