feat(front): implement AM dashboard shell with 3 columns (#169)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-24 12:13:20 +02:00
co-authored by Cursor
parent 2380aa9826
commit 8b1b9e7917
2 changed files with 63 additions and 54 deletions
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:p_tits_pas/models/user.dart';
import 'package:p_tits_pas/services/auth_service.dart';
import 'package:p_tits_pas/widgets/app_footer.dart';
import 'package:p_tits_pas/widgets/dashboard/dashboard_bandeau.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_shell.dart';
import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart';
/// Dashboard assistante maternelle – page blanche avec bandeau générique.
/// Contenu détaillé à venir.
/// Dashboard assistante maternelle – coquille 3 colonnes quotidien (#169).
/// Colonne gauche : sélecteur de couple enfant–parent(s) (à venir #170).
/// Métier cartes / blog / messagerie : tickets C/D/E.
class AmDashboardScreen extends StatefulWidget {
const AmDashboardScreen({super.key});
@@ -14,7 +15,7 @@ class AmDashboardScreen extends StatefulWidget {
}
class _AmDashboardScreenState extends State<AmDashboardScreen> {
int selectedTabIndex = 0;
QuotidienNavSection _section = QuotidienNavSection.liaison;
AppUser? _user;
@override
@@ -28,51 +29,58 @@ class _AmDashboardScreenState extends State<AmDashboardScreen> {
if (mounted) setState(() => _user = user);
}
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 'Assistante maternelle';
}
void _soon(String label) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$label — à venir')),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(60.0),
child: DashboardBandeau(
tabItems: const [
DashboardTabItem(label: 'Mon tableau de bord'),
DashboardTabItem(label: 'Paramètres'),
],
selectedTabIndex: selectedTabIndex,
onTabSelected: (index) => setState(() => selectedTabIndex = index),
userDisplayName: _user?.fullName.isNotEmpty == true
? _user!.fullName
: 'Assistante maternelle',
userEmail: _user?.email,
userRole: _user?.role,
onProfileTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Modification du profil – à venir')),
);
},
onSettingsTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Paramètres – à venir')),
);
},
onLogout: () {},
showLogoutConfirmation: true,
),
return QuotidienShell(
selectedSection: _section,
onSectionSelected: (s) => setState(() => _section = s),
userDisplayName: _displayName,
userEmail: _user?.email,
onProfileTap: () => _soon('Profil'),
onSettingsTap: () => _soon('Paramètres'),
// L'AM n'a pas de bouton "Recherche AM" dans le bandeau
onSearchAmTap: null,
leftColumn: const QuotidienColumnPlaceholder(
title: 'Cartes',
subtitle:
'Couple enfant–parent(s) et flux de cartes\n(à brancher — tickets #170 / #174).',
icon: Icons.style_outlined,
),
body: Column(
children: [
Expanded(
child: Center(
child: Text(
'Dashboard AM – à venir',
style: Theme.of(context).textTheme.titleLarge,
),
),
),
const AppFooter(),
],
centerColumn: const QuotidienColumnPlaceholder(
title: 'Blog',
subtitle:
'Fil du quotidien (affichage par défaut)\n(à brancher — ticket #179).',
icon: Icons.auto_stories_outlined,
),
rightColumn: const QuotidienColumnPlaceholder(
title: 'Messagerie',
subtitle:
'Mess. Parents · Mess. RPE\n(à brancher — ticket #185).',
icon: Icons.chat_bubble_outline,
),
agendaBody: const QuotidienStubPage(
title: 'Agenda',
message: 'Agenda — contenu à venir (stub #187).',
),
contratBody: const QuotidienStubPage(
title: 'Contrat',
message: 'Contrat — contenu à venir (stub #187).',
),
);
}
}
@@ -183,15 +183,16 @@ class _UserMenu extends StatelessWidget {
title: Text('Profil'),
),
),
const PopupMenuItem(
value: 'search_am',
child: ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.search, size: 20),
title: Text('Recherche AM'),
if (onSearchAmTap != null)
const PopupMenuItem(
value: 'search_am',
child: ListTile(
dense: true,
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.search, size: 20),
title: Text('Recherche AM'),
),
),
),
const PopupMenuItem(
value: 'settings',
child: ListTile(