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.
71 lines
2.0 KiB
Dart
71 lines
2.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import '../../domain/entities/devise.dart';
|
|
|
|
const _kDevisePrefereeKey = 'unionflow.devise.preferee';
|
|
|
|
/// Widget de sélection de la devise préférée (persistée en secure storage).
|
|
///
|
|
/// Utilisé sur la page Profil/Paramètres. Notifie via [onChanged] avec la
|
|
/// nouvelle devise quand l'utilisateur fait un choix.
|
|
class DeviseSelector extends StatefulWidget {
|
|
final Devise? initial;
|
|
final ValueChanged<Devise>? onChanged;
|
|
|
|
const DeviseSelector({super.key, this.initial, this.onChanged});
|
|
|
|
/// Lit la devise préférée du secure storage. Fallback sur XOF.
|
|
static Future<Devise> readPreferred(FlutterSecureStorage storage) async {
|
|
try {
|
|
final v = await storage.read(key: _kDevisePrefereeKey);
|
|
return Devise.fromCode(v);
|
|
} catch (_) {
|
|
return Devise.reference();
|
|
}
|
|
}
|
|
|
|
/// Persiste la devise préférée.
|
|
static Future<void> writePreferred(
|
|
FlutterSecureStorage storage, Devise devise) async {
|
|
await storage.write(key: _kDevisePrefereeKey, value: devise.code);
|
|
}
|
|
|
|
@override
|
|
State<DeviseSelector> createState() => _DeviseSelectorState();
|
|
}
|
|
|
|
class _DeviseSelectorState extends State<DeviseSelector> {
|
|
late Devise _selected;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_selected = widget.initial ?? Devise.reference();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return DropdownButtonFormField<Devise>(
|
|
value: _selected,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Devise préférée',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.account_balance_wallet_outlined),
|
|
),
|
|
items: Devise.values
|
|
.map(
|
|
(d) => DropdownMenuItem<Devise>(
|
|
value: d,
|
|
child: Text('${d.code} — ${d.libelle}'),
|
|
),
|
|
)
|
|
.toList(),
|
|
onChanged: (d) {
|
|
if (d == null) return;
|
|
setState(() => _selected = d);
|
|
widget.onChanged?.call(d);
|
|
},
|
|
);
|
|
}
|
|
}
|