feat(unionflow): ajout Spec-Kit, constitution, mission mutuelles

- Config Spec-Kit pour Spec-Driven Development
- CONSTITUTION.md + .specify/memory/constitution.md
- Commandes Cursor /speckit.*, règles projet
- Mission: associations + mutuelles d'épargne et de financement
- .gitignore: versionner config spec-kit unionflow

Made-with: Cursor
This commit is contained in:
dahoud
2026-02-27 14:41:07 +00:00
parent 144b68f8e7
commit b1957c1c81
631 changed files with 104070 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
/// Modèle pour un utilisateur admin (Keycloak) - aligné sur l'API /api/admin/users
library admin_user_model;
class AdminUserModel {
final String id;
final String? username;
final String? email;
final String? prenom;
final String? nom;
final bool? enabled;
final List<String>? realmRoles;
AdminUserModel({
required this.id,
this.username,
this.email,
this.prenom,
this.nom,
this.enabled,
this.realmRoles,
});
String get displayName {
if (prenom != null && nom != null) return '$prenom $nom';
if (prenom != null) return prenom!;
if (nom != null) return nom!;
return username ?? email ?? id;
}
factory AdminUserModel.fromJson(Map<String, dynamic> json) {
final roles = json['realmRoles'] as List<dynamic>?;
return AdminUserModel(
id: json['id'] as String? ?? '',
username: json['username'] as String?,
email: json['email'] as String?,
prenom: json['prenom'] as String?,
nom: json['nom'] as String?,
enabled: json['enabled'] as bool?,
realmRoles: roles?.map((e) => e is Map ? (e['name'] as String?) ?? e.toString() : e.toString()).toList(),
);
}
Map<String, dynamic> toJson() => {
'id': id,
'username': username,
'email': email,
'prenom': prenom,
'nom': nom,
'enabled': enabled,
'realmRoles': realmRoles,
};
}
class AdminRoleModel {
final String id;
final String name;
final String? description;
AdminRoleModel({required this.id, required this.name, this.description});
factory AdminRoleModel.fromJson(Map<String, dynamic> json) => AdminRoleModel(
id: json['id'] as String? ?? '',
name: json['name'] as String? ?? '',
description: json['description'] as String?,
);
}

View File

@@ -0,0 +1,85 @@
/// Repository pour la gestion des utilisateurs admin (API /api/admin/users)
library admin_user_repository;
import 'package:dio/dio.dart';
import '../models/admin_user_model.dart';
abstract class AdminUserRepository {
Future<AdminUserSearchResult> search({int page = 0, int size = 20, String? search});
Future<AdminUserModel?> getById(String id);
Future<List<AdminRoleModel>> getRealmRoles();
Future<List<AdminRoleModel>> getUserRoles(String userId);
Future<void> setUserRoles(String userId, List<String> roleNames);
}
class AdminUserSearchResult {
final List<AdminUserModel> users;
final int totalCount;
final int currentPage;
final int pageSize;
final int totalPages;
AdminUserSearchResult({
required this.users,
required this.totalCount,
required this.currentPage,
required this.pageSize,
required this.totalPages,
});
}
class AdminUserRepositoryImpl implements AdminUserRepository {
final Dio _dio;
static const String _base = '/api/admin/users';
AdminUserRepositoryImpl(this._dio);
@override
Future<AdminUserSearchResult> search({int page = 0, int size = 20, String? search}) async {
final query = <String, dynamic>{'page': page, 'size': size};
if (search != null && search.isNotEmpty) query['search'] = search;
final response = await _dio.get(_base, queryParameters: query);
if (response.statusCode != 200) throw Exception('Erreur ${response.statusCode}');
final data = response.data as Map<String, dynamic>;
final list = data['users'] as List<dynamic>? ?? [];
return AdminUserSearchResult(
users: list.map((e) => AdminUserModel.fromJson(e as Map<String, dynamic>)).toList(),
totalCount: (data['totalCount'] as num?)?.toInt() ?? 0,
currentPage: (data['currentPage'] as num?)?.toInt() ?? 0,
pageSize: (data['pageSize'] as num?)?.toInt() ?? size,
totalPages: (data['totalPages'] as num?)?.toInt() ?? 0,
);
}
@override
Future<AdminUserModel?> getById(String id) async {
final response = await _dio.get('$_base/$id');
if (response.statusCode == 200) {
return AdminUserModel.fromJson(response.data as Map<String, dynamic>);
}
if (response.statusCode == 404) return null;
throw Exception('Erreur ${response.statusCode}');
}
@override
Future<List<AdminRoleModel>> getRealmRoles() async {
final response = await _dio.get('$_base/roles');
if (response.statusCode != 200) return [];
final list = response.data as List<dynamic>? ?? [];
return list.map((e) => AdminRoleModel.fromJson(e as Map<String, dynamic>)).toList();
}
@override
Future<List<AdminRoleModel>> getUserRoles(String userId) async {
final response = await _dio.get('$_base/$userId/roles');
if (response.statusCode != 200) return [];
final list = response.data as List<dynamic>? ?? [];
return list.map((e) => AdminRoleModel.fromJson(e as Map<String, dynamic>)).toList();
}
@override
Future<void> setUserRoles(String userId, List<String> roleNames) async {
final response = await _dio.put('$_base/$userId/roles', data: roleNames);
if (response.statusCode != 200) throw Exception('Erreur ${response.statusCode}');
}
}