feat(admin): onglet À valider, dossier unifié et modales de validation
- Onglet « À valider » (AM + familles), pending-families et détail dossier par numéro. - Wizards validation AM et famille, modale commune, chargement via GET /dossiers/:num. - UI : cartes enfants, photo AM (cadre uniforme, ratio), NIR affiché formaté (espaces autour du tiret). - Backend : DTO / routes parents pending ; scripts de test et mise à jour issue Gitea. Tickets #107, #119. Made-with: Cursor
This commit is contained in:
@@ -17,7 +17,7 @@ class LoginScreen extends StatefulWidget {
|
||||
State<LoginScreen> createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
class _LoginPageState extends State<LoginScreen> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
@@ -27,31 +27,30 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
|
||||
static const double _mobileBreakpoint = 900.0;
|
||||
|
||||
/// Une seule fois : évite de relancer `_getImageDimensions()` à chaque rebuild (sinon sur web,
|
||||
/// tout événement de layout / métriques recréait un Future et pouvait provoquer des erreurs DWDS
|
||||
/// ou des comportements bizarres pendant la saisie dans les champs).
|
||||
late final Future<ImageDimensions> _desktopRiverLogoDimensionsFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_desktopRiverLogoDimensionsFuture = _getImageDimensions();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
super.didChangeMetrics();
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
String? _validateEmail(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
final v = value ?? '';
|
||||
if (v.isEmpty) {
|
||||
return 'Veuillez entrer votre email';
|
||||
}
|
||||
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
|
||||
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(v)) {
|
||||
return 'Veuillez entrer un email valide';
|
||||
}
|
||||
return null;
|
||||
@@ -116,7 +115,7 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
TextInput.finishAutofillContext(shouldSave: true);
|
||||
|
||||
// Rediriger selon le rôle de l'utilisateur
|
||||
_redirectUserByRole(user.role);
|
||||
_redirectUserByRole(user.role.isEmpty ? null : user.role);
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
@@ -126,9 +125,10 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
/// Redirige l'utilisateur selon son rôle (GoRouter : context.go).
|
||||
void _redirectUserByRole(String role) {
|
||||
void _redirectUserByRole(String? role) {
|
||||
setState(() => _isLoading = false);
|
||||
switch (role.toLowerCase()) {
|
||||
final r = (role ?? '').toLowerCase();
|
||||
switch (r) {
|
||||
case 'super_admin':
|
||||
case 'administrateur':
|
||||
context.go('/admin-dashboard');
|
||||
@@ -162,8 +162,8 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
builder: (context, constraints) {
|
||||
final w = constraints.maxWidth;
|
||||
final h = constraints.maxHeight;
|
||||
return FutureBuilder(
|
||||
future: _getImageDimensions(),
|
||||
return FutureBuilder<ImageDimensions>(
|
||||
future: _desktopRiverLogoDimensionsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
@@ -203,7 +203,7 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
bottom: 0,
|
||||
width: w * 0.6, // 60% de la largeur de l'écran
|
||||
height: h * 0.5, // 50% de la hauteur de l'écran
|
||||
child: Padding(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(w * 0.02), // 2% de padding
|
||||
child: AutofillGroup(
|
||||
child: Form(
|
||||
@@ -240,7 +240,7 @@ class _LoginPageState extends State<LoginScreen> with WidgetsBindingObserver {
|
||||
hintText: 'Votre mot de passe',
|
||||
obscureText: true,
|
||||
autofillHints: const [
|
||||
AutofillHints.password
|
||||
AutofillHints.password,
|
||||
],
|
||||
textInputAction: TextInputAction.done,
|
||||
onFieldSubmitted:
|
||||
|
||||
Reference in New Issue
Block a user