Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0842f66df8 | ||
|
|
c3fd098464 | ||
|
|
da6836cda3 | ||
|
|
91f2c3e1ef |
@@ -208,7 +208,7 @@ export class CardsService {
|
|||||||
id_card: card.id,
|
id_card: card.id,
|
||||||
id_utilisateur: userId,
|
id_utilisateur: userId,
|
||||||
action: dto.action,
|
action: dto.action,
|
||||||
comment: dto.comment?.trim() || null,
|
comment: dto.comment?.trim() || undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (card.id_absence) {
|
if (card.id_absence) {
|
||||||
|
|||||||
@@ -39,13 +39,15 @@ class CoupleMembre {
|
|||||||
class CoupleGarde {
|
class CoupleGarde {
|
||||||
final String id;
|
final String id;
|
||||||
final CoupleMembre enfant;
|
final CoupleMembre enfant;
|
||||||
final CoupleMembre am;
|
final CoupleMembre? am;
|
||||||
|
final List<CoupleMembre> parents;
|
||||||
final bool courant;
|
final bool courant;
|
||||||
|
|
||||||
const CoupleGarde({
|
const CoupleGarde({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.enfant,
|
required this.enfant,
|
||||||
required this.am,
|
this.am,
|
||||||
|
this.parents = const [],
|
||||||
this.courant = false,
|
this.courant = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,9 +57,15 @@ class CoupleGarde {
|
|||||||
enfant: CoupleMembre.fromJson(
|
enfant: CoupleMembre.fromJson(
|
||||||
Map<String, dynamic>.from(json['enfant'] ?? const {}),
|
Map<String, dynamic>.from(json['enfant'] ?? const {}),
|
||||||
),
|
),
|
||||||
am: CoupleMembre.fromJson(
|
am: json['am'] != null
|
||||||
Map<String, dynamic>.from(json['am'] ?? const {}),
|
? CoupleMembre.fromJson(Map<String, dynamic>.from(json['am']))
|
||||||
),
|
: null,
|
||||||
|
parents: json['parents'] != null
|
||||||
|
? (json['parents'] as List)
|
||||||
|
.whereType<Map>()
|
||||||
|
.map((e) => CoupleMembre.fromJson(Map<String, dynamic>.from(e)))
|
||||||
|
.toList()
|
||||||
|
: const [],
|
||||||
courant: json['courant'] == true,
|
courant: json['courant'] == true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -67,6 +75,7 @@ class CoupleGarde {
|
|||||||
id: id,
|
id: id,
|
||||||
enfant: enfant,
|
enfant: enfant,
|
||||||
am: am,
|
am: am,
|
||||||
|
parents: parents,
|
||||||
courant: courant ?? this.courant,
|
courant: courant ?? this.courant,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ class _AmDashboardScreenState extends State<AmDashboardScreen> {
|
|||||||
_couplesError = null;
|
_couplesError = null;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
// TODO: Pour l'instant on utilise le service générique de test (CoupleGardeService.getCouplesGarde).
|
final res = await CoupleGardeService.getAmCouplesGarde();
|
||||||
// Dans le futur, l'API pour l'AM (enfants_accueillis) fournira la vraie liste.
|
|
||||||
final res = await CoupleGardeService.getCouplesGarde();
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_couples = res.couples;
|
_couples = res.couples;
|
||||||
@@ -67,7 +65,7 @@ class _AmDashboardScreenState extends State<AmDashboardScreen> {
|
|||||||
if (couple.id == _selectedCoupleId) return;
|
if (couple.id == _selectedCoupleId) return;
|
||||||
setState(() => _selectedCoupleId = couple.id);
|
setState(() => _selectedCoupleId = couple.id);
|
||||||
try {
|
try {
|
||||||
final res = await CoupleGardeService.definirCoupleCourant(couple.id);
|
final res = await CoupleGardeService.definirAmCoupleCourant(couple.id);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_couples = res.couples;
|
_couples = res.couples;
|
||||||
|
|||||||
@@ -67,7 +67,11 @@ class ApiConfig {
|
|||||||
static const String parentsCoupleGardeCourant =
|
static const String parentsCoupleGardeCourant =
|
||||||
'/parents/me/couples-garde/courant';
|
'/parents/me/couples-garde/courant';
|
||||||
static const String assistantesMaternelles = '/assistantes-maternelles';
|
static const String assistantesMaternelles = '/assistantes-maternelles';
|
||||||
/// Création dossier AM actif par le staff (#156) — body type register AM.
|
/// Couples de garde de l'AM connectée (#170 / #171).
|
||||||
|
static const String assistantesMaternellesCouplesGarde =
|
||||||
|
'/assistantes-maternelles/me/couples-garde';
|
||||||
|
static const String assistantesMaternellesCoupleGardeCourant =
|
||||||
|
'/assistantes-maternelles/me/couples-garde/courant';
|
||||||
static const String assistantesMaternellesDossier =
|
static const String assistantesMaternellesDossier =
|
||||||
'/assistantes-maternelles/dossier';
|
'/assistantes-maternelles/dossier';
|
||||||
static const String enfants = '/enfants';
|
static const String enfants = '/enfants';
|
||||||
|
|||||||
@@ -73,4 +73,45 @@ class CoupleGardeService {
|
|||||||
Map<String, dynamic>.from(decoded as Map),
|
Map<String, dynamic>.from(decoded as Map),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Liste les couples de garde et le couple courant de l'AM connectée.
|
||||||
|
static Future<CouplesGardeResponse> getAmCouplesGarde() async {
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.assistantesMaternellesCouplesGarde}'),
|
||||||
|
headers: await _headers(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception(
|
||||||
|
_extractError(response.body, 'Erreur chargement des couples de garde (AM)'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final decoded = jsonDecode(response.body);
|
||||||
|
return CouplesGardeResponse.fromJson(
|
||||||
|
Map<String, dynamic>.from(decoded as Map),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Persiste le couple courant (AM) et renvoie la liste à jour.
|
||||||
|
static Future<CouplesGardeResponse> definirAmCoupleCourant(
|
||||||
|
String coupleId,
|
||||||
|
) async {
|
||||||
|
final response = await http.put(
|
||||||
|
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.assistantesMaternellesCoupleGardeCourant}'),
|
||||||
|
headers: await _headers(),
|
||||||
|
body: jsonEncode({'couple_id': coupleId}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception(
|
||||||
|
_extractError(response.body, 'Erreur sélection du couple de garde (AM)'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final decoded = jsonDecode(response.body);
|
||||||
|
return CouplesGardeResponse.fromJson(
|
||||||
|
Map<String, dynamic>.from(decoded as Map),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class CoupleSelectorBandeau extends StatelessWidget {
|
|||||||
|
|
||||||
CoupleGarde? get _selected {
|
CoupleGarde? get _selected {
|
||||||
if (couples.isEmpty) return null;
|
if (couples.isEmpty) return null;
|
||||||
if (selectedCoupleId != null) {
|
if (selectedCoupleId != null) {
|
||||||
for (final c in couples) {
|
for (final c in couples) {
|
||||||
if (c.id == selectedCoupleId) return c;
|
if (c.id == selectedCoupleId) return c;
|
||||||
}
|
}
|
||||||
@@ -133,17 +133,46 @@ class _CoupleCard extends StatelessWidget {
|
|||||||
required this.colorIndex,
|
required this.colorIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Libellé parents côté AM : « Sophie » ou « Thomas et Claire ».
|
||||||
|
static String _parentsLabel(List<CoupleMembre> parents) {
|
||||||
|
if (parents.isEmpty) return 'Parents';
|
||||||
|
String prenomOf(CoupleMembre p) {
|
||||||
|
final prenom = (p.prenom ?? '').trim();
|
||||||
|
if (prenom.isNotEmpty) return prenom;
|
||||||
|
return p.displayName(fallback: 'Parent');
|
||||||
|
}
|
||||||
|
if (parents.length == 1) return prenomOf(parents.first);
|
||||||
|
return '${prenomOf(parents[0])} et ${prenomOf(parents[1])}';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Parent mode: enfant | AM
|
// Parent mode: enfant | AM (photo + prénom)
|
||||||
// AM mode: enfant | parent(s)
|
// AM mode: enfant | parent(s) — texte seul, pas de vignette parent
|
||||||
final isParentMode = mode == CoupleBandeauMode.parent;
|
final isParentMode = mode == CoupleBandeauMode.parent;
|
||||||
final fallbackRoleIcon = isParentMode ? Icons.volunteer_activism : Icons.person_outline;
|
|
||||||
final fallbackRoleName = isParentMode ? 'Nounou' : 'Parent';
|
Widget amOrParentsWidget;
|
||||||
|
|
||||||
// TODO: In AM mode, we should ideally display "parent1+parent2 empilés" or centered.
|
if (isParentMode) {
|
||||||
// For now, CoupleGarde only gives us `am` (which in AM mode might just represent one parent, or the system needs to feed both parents here).
|
amOrParentsWidget = _MembreTile(
|
||||||
// Assuming backend will populate `am` field with the parent data when called by AM.
|
photoUrl: couple.am?.photoUrl,
|
||||||
|
name: (couple.am?.prenom != null && couple.am!.prenom!.isNotEmpty)
|
||||||
|
? couple.am!.prenom!
|
||||||
|
: (couple.am?.displayName(fallback: 'Nounou') ?? 'Nounou'),
|
||||||
|
fallbackIcon: Icons.volunteer_activism,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
amOrParentsWidget = Text(
|
||||||
|
_parentsLabel(couple.parents),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: GoogleFonts.merienda(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: QuotidienTheme.ink,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
height: 90,
|
height: 90,
|
||||||
@@ -181,13 +210,7 @@ class _CoupleCard extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 12),
|
padding: const EdgeInsets.only(left: 12),
|
||||||
child: _MembreTile(
|
child: amOrParentsWidget,
|
||||||
photoUrl: couple.am.photoUrl,
|
|
||||||
name: (couple.am.prenom != null && couple.am.prenom!.isNotEmpty)
|
|
||||||
? couple.am.prenom!
|
|
||||||
: couple.am.displayName(fallback: fallbackRoleName),
|
|
||||||
fallbackIcon: fallbackRoleIcon,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|||||||
Reference in New Issue
Block a user