- Dossiers unifiés #119, pending-families enrichi, validation admin (wizards) - Front: modèles dossier_unifie / pending_family, NIR, auth - Migrations dossier_famille, scripts de test API - Résolution conflits: parents.*, docs tickets, auth_service, nir_utils Made-with: Cursor
174 lines
5.0 KiB
Dart
174 lines
5.0 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:p_tits_pas/models/dossier_unifie.dart';
|
||
import 'package:p_tits_pas/services/user_service.dart';
|
||
import 'package:p_tits_pas/widgets/admin/validation_am_wizard.dart';
|
||
import 'package:p_tits_pas/widgets/admin/validation_family_wizard.dart';
|
||
|
||
/// Modale (dialog) : charge le dossier par numéro puis affiche le wizard AM ou Famille. Ticket #107, #119.
|
||
class ValidationDossierModal extends StatefulWidget {
|
||
final String numeroDossier;
|
||
final VoidCallback onClose;
|
||
final VoidCallback? onSuccess;
|
||
|
||
const ValidationDossierModal({
|
||
super.key,
|
||
required this.numeroDossier,
|
||
required this.onClose,
|
||
this.onSuccess,
|
||
});
|
||
|
||
@override
|
||
State<ValidationDossierModal> createState() => _ValidationDossierModalState();
|
||
}
|
||
|
||
class _ValidationDossierModalState extends State<ValidationDossierModal> {
|
||
bool _loading = true;
|
||
String? _error;
|
||
DossierUnifie? _dossier;
|
||
int? _stepIndex;
|
||
int? _stepTotal;
|
||
|
||
void _onStepChanged(int step, int total) {
|
||
// step = 0-based dans les wizards, affichage 1-based dans l'en-tête.
|
||
if (!mounted) return;
|
||
setState(() {
|
||
_stepIndex = step;
|
||
_stepTotal = total;
|
||
});
|
||
}
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_load();
|
||
}
|
||
|
||
Future<void> _load() async {
|
||
setState(() {
|
||
_loading = true;
|
||
_error = null;
|
||
_dossier = null;
|
||
_stepIndex = null;
|
||
_stepTotal = null;
|
||
});
|
||
try {
|
||
final d = await UserService.getDossier(widget.numeroDossier);
|
||
if (!mounted) return;
|
||
setState(() {
|
||
_dossier = d;
|
||
_loading = false;
|
||
});
|
||
} catch (e) {
|
||
if (!mounted) return;
|
||
setState(() {
|
||
_error = e is Exception
|
||
? e.toString().replaceFirst('Exception: ', '')
|
||
: 'Erreur inconnue';
|
||
_loading = false;
|
||
});
|
||
}
|
||
}
|
||
|
||
void _onSuccess() {
|
||
widget.onSuccess?.call();
|
||
// La modale est fermée par l’appelant dans onSuccess (Navigator.pop).
|
||
}
|
||
|
||
/// Largeur modale = 1,5 × 620.
|
||
static const double _modalWidth = 930; // 620 * 1.5
|
||
// Hauteur uniforme (ajustée +5px pour éviter l'overflow des étapes parents sans scroll).
|
||
static const double _bodyHeight = 435;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final maxH = MediaQuery.of(context).size.height * 0.85;
|
||
final showStep =
|
||
_stepIndex != null && _stepTotal != null && (_stepTotal ?? 0) > 0;
|
||
return Dialog(
|
||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||
child: ConstrainedBox(
|
||
constraints: BoxConstraints(maxWidth: _modalWidth, maxHeight: maxH),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Padding(
|
||
padding: const EdgeInsets.fromLTRB(18, 18, 0, 12),
|
||
child: Text(
|
||
'Dossier ${widget.numeroDossier}',
|
||
style: const TextStyle(
|
||
fontSize: 18, fontWeight: FontWeight.w700),
|
||
),
|
||
),
|
||
const Spacer(),
|
||
if (showStep) ...[
|
||
Text(
|
||
'Étape ${(_stepIndex ?? 0) + 1}/${_stepTotal ?? 1}',
|
||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||
color: Colors.black54,
|
||
fontStyle: FontStyle.italic,
|
||
),
|
||
),
|
||
const SizedBox(width: 8),
|
||
],
|
||
IconButton(
|
||
icon: const Icon(Icons.close),
|
||
onPressed: widget.onClose,
|
||
tooltip: 'Fermer',
|
||
),
|
||
],
|
||
),
|
||
const Divider(height: 1),
|
||
SizedBox(
|
||
height: _bodyHeight,
|
||
child: _buildBody(),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildBody() {
|
||
if (_loading) {
|
||
return const Padding(
|
||
padding: EdgeInsets.all(48),
|
||
child: Center(child: CircularProgressIndicator()),
|
||
);
|
||
}
|
||
if (_error != null && _error!.isNotEmpty) {
|
||
return Padding(
|
||
padding: const EdgeInsets.all(24),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text(_error!,
|
||
style: const TextStyle(color: Colors.red),
|
||
textAlign: TextAlign.center),
|
||
const SizedBox(height: 16),
|
||
ElevatedButton(onPressed: _load, child: const Text('Réessayer')),
|
||
const SizedBox(height: 8),
|
||
TextButton(onPressed: widget.onClose, child: const Text('Fermer')),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
final d = _dossier!;
|
||
if (d.isAm) {
|
||
return ValidationAmWizard(
|
||
dossier: d.asAm,
|
||
onClose: widget.onClose,
|
||
onSuccess: _onSuccess,
|
||
onStepChanged: _onStepChanged,
|
||
);
|
||
}
|
||
return ValidationFamilyWizard(
|
||
dossier: d.asFamily,
|
||
onClose: widget.onClose,
|
||
onSuccess: _onSuccess,
|
||
onStepChanged: _onStepChanged,
|
||
);
|
||
}
|
||
}
|