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>
188 lines
5.6 KiB
Dart
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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|