Files
petitspas/frontend/lib/screens/home/parent_screen/ParentDashboardScreen.dart
T
jmartinandCursor d53682110d feat(#173): feed bulles parent branché sur GET /cards
Affiche la file d’attention pastel (assets card_*_h) filtrée
par couple courant, avec accept / refus motivé / ack.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 17:10:40 +02:00

188 lines
5.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:p_tits_pas/models/couple_garde.dart';
import 'package:p_tits_pas/models/user.dart';
import 'package:p_tits_pas/services/auth_service.dart';
import 'package:p_tits_pas/services/couple_garde_service.dart';
import 'package:p_tits_pas/widgets/quotidien/cartes_feed.dart';
import 'package:p_tits_pas/widgets/quotidien/couple_selector_bandeau.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_shell.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart';
import 'package:p_tits_pas/screens/home/parent_screen/agenda_absences_stub.dart';
/// Tableau de bord parent — coquille 3 colonnes quotidien (#166).
/// Colonne gauche : sélecteur de couple (#167) + feed bulles (#173).
class ParentDashboardScreen extends StatefulWidget {
const ParentDashboardScreen({super.key});
@override
State<ParentDashboardScreen> createState() => _ParentDashboardScreenState();
}
class _ParentDashboardScreenState extends State<ParentDashboardScreen> {
QuotidienNavSection _section = QuotidienNavSection.liaison;
AppUser? _user;
List<CoupleGarde> _couples = const [];
String? _selectedCoupleId;
bool _couplesLoading = true;
String? _couplesError;
@override
void initState() {
super.initState();
_loadUser();
_loadCouples();
}
Future<void> _loadUser() async {
final user = await AuthService.getCurrentUser();
if (mounted) setState(() => _user = user);
}
Future<void> _loadCouples() async {
setState(() {
_couplesLoading = true;
_couplesError = null;
});
try {
final res = await CoupleGardeService.getCouplesGarde();
if (!mounted) return;
setState(() {
_couples = res.couples;
_selectedCoupleId = res.coupleCourant?.id;
_couplesLoading = false;
});
} catch (e) {
if (!mounted) return;
setState(() {
_couplesError = e.toString().replaceFirst('Exception: ', '');
_couplesLoading = false;
});
}
}
Future<void> _selectCouple(CoupleGarde couple) async {
if (couple.id == _selectedCoupleId) return;
setState(() => _selectedCoupleId = couple.id);
try {
final res = await CoupleGardeService.definirCoupleCourant(couple.id);
if (!mounted) return;
setState(() {
_couples = res.couples;
_selectedCoupleId = res.coupleCourant?.id ?? couple.id;
});
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Impossible de changer de garde : '
'${e.toString().replaceFirst('Exception: ', '')}',
),
),
);
}
}
String get _displayName {
final n = _user?.fullName.trim() ?? '';
if (n.isNotEmpty) return n;
final email = _user?.email.trim() ?? '';
if (email.isNotEmpty) return email.split('@').first;
return 'Parent';
}
void _soon(String label) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$label — à venir')),
);
}
@override
Widget build(BuildContext context) {
return QuotidienShell(
selectedSection: _section,
onSectionSelected: (s) => setState(() => _section = s),
userDisplayName: _displayName,
userEmail: _user?.email,
onProfileTap: () => _soon('Profil'),
onSearchAmTap: () => _soon('Recherche AM'),
onSettingsTap: () => _soon('Paramètres'),
leftColumn: _LeftColumn(
couples: _couples,
selectedCoupleId: _selectedCoupleId,
loading: _couplesLoading,
error: _couplesError,
onRetry: _loadCouples,
onCoupleSelected: _selectCouple,
onAjouterBulle: () => _soon('Ajouter une bulle'),
),
centerColumn: const QuotidienColumnPlaceholder(
title: 'Blog',
subtitle:
'Fil du quotidien (affichage par défaut)\n(à brancher — ticket #178).',
icon: Icons.auto_stories_outlined,
),
rightColumn: const QuotidienColumnPlaceholder(
title: 'Messagerie',
subtitle:
'Mess. AM · Mess. RPE\n(à brancher — ticket #184).',
icon: Icons.chat_bubble_outline,
),
agendaBody: AgendaAbsencesStub(placementId: _selectedCoupleId),
contratBody: const QuotidienStubPage(
title: 'Contrat',
message: 'Contrat — contenu à venir (stub #187).',
),
);
}
}
/// Colonne gauche : bandeau couple (#167) puis flux de cartes (#173).
class _LeftColumn extends StatelessWidget {
final List<CoupleGarde> couples;
final String? selectedCoupleId;
final bool loading;
final String? error;
final VoidCallback onRetry;
final ValueChanged<CoupleGarde> onCoupleSelected;
final VoidCallback onAjouterBulle;
const _LeftColumn({
required this.couples,
required this.selectedCoupleId,
required this.loading,
required this.error,
required this.onRetry,
required this.onCoupleSelected,
required this.onAjouterBulle,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CoupleSelectorBandeau(
couples: couples,
selectedCoupleId: selectedCoupleId,
loading: loading,
errorMessage: error,
onRetry: onRetry,
onCoupleSelected: onCoupleSelected,
),
const SizedBox(height: 14),
Expanded(
child: CartesFeed(
placementId: selectedCoupleId,
onAjouterBulle: onAjouterBulle,
),
),
],
),
);
}
}