Files
petitspas/frontend/lib/widgets/admin/validation_dossier_modal.dart
T
jmartinandCursor c8c9cfbc4d feat(#135): mode édition dossier (PATCH fiche, co-parent, enfants).
Wizard edit famille/AM depuis la liste Dossiers ; save parents +
co-parent ; enfants existants en PATCH (photo multipart) sans POST doublon.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 16:07:23 +02:00

201 lines
5.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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/am_dossier_wizard.dart';
import 'package:p_tits_pas/widgets/admin/parent_dossier_wizard.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 (review), #135 (`openAsEdit`).
class ValidationDossierModal extends StatefulWidget {
final String numeroDossier;
final VoidCallback onClose;
final VoidCallback? onSuccess;
/// Liste Dossiers actifs → mode edit (#135). Pending reste en review (défaut).
final bool openAsEdit;
const ValidationDossierModal({
super.key,
required this.numeroDossier,
required this.onClose,
this.onSuccess,
this.openAsEdit = false,
});
@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 lappelant dans onSuccess (Navigator.pop).
}
/// Largeur modale = 1,5 × 620.
static const double _modalWidth = 930; // 620 * 1.5
double get _bodyHeight {
final d = _dossier;
if (d != null && d.isAm) return AmDossierWizard.shellBodyHeight;
// Aligné create (#129) / edit (#135) — évite overflow IdentityBlock (8px).
return ParentDossierWizard.shellBodyHeight;
}
@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) {
if (widget.openAsEdit) {
return AmDossierWizard.edit(
dossier: d.asAm,
onClose: widget.onClose,
onSuccess: _onSuccess,
onStepChanged: _onStepChanged,
);
}
return ValidationAmWizard(
dossier: d.asAm,
onClose: widget.onClose,
onSuccess: _onSuccess,
onStepChanged: _onStepChanged,
);
}
if (widget.openAsEdit) {
return ParentDossierWizard.edit(
dossier: d.asFamily,
onClose: widget.onClose,
onSuccess: _onSuccess,
onStepChanged: _onStepChanged,
);
}
return ValidationFamilyWizard(
dossier: d.asFamily,
onClose: widget.onClose,
onSuccess: _onSuccess,
onStepChanged: _onStepChanged,
);
}
}