321 lines
9.2 KiB
Dart
321 lines
9.2 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:google_fonts/google_fonts.dart';
|
||
import 'package:p_tits_pas/widgets/app_footer.dart';
|
||
import 'package:p_tits_pas/widgets/quotidien/quotidien_bandeau.dart';
|
||
import 'package:p_tits_pas/widgets/quotidien/quotidien_theme.dart';
|
||
|
||
/// Coquille « Cahier de liaison » quotidien 3 colonnes + bandeau (#166).
|
||
/// L’AM (#169) réutilise ce widget en injectant ses slots.
|
||
class QuotidienShell extends StatelessWidget {
|
||
final QuotidienNavSection selectedSection;
|
||
final ValueChanged<QuotidienNavSection> onSectionSelected;
|
||
final String userDisplayName;
|
||
final String? userEmail;
|
||
final VoidCallback? onProfileTap;
|
||
final VoidCallback? onSearchAmTap;
|
||
final VoidCallback? onSettingsTap;
|
||
final VoidCallback? onLogout;
|
||
|
||
/// Colonne gauche (couple + cartes + actions).
|
||
final Widget leftColumn;
|
||
|
||
/// Colonne milieu (blog).
|
||
final Widget centerColumn;
|
||
|
||
/// Colonne droite (messagerie).
|
||
final Widget rightColumn;
|
||
|
||
/// Corps affiché hors Cahier de liaison (Agenda / Contrat stubs).
|
||
final Widget? agendaBody;
|
||
final Widget? contratBody;
|
||
|
||
const QuotidienShell({
|
||
super.key,
|
||
required this.selectedSection,
|
||
required this.onSectionSelected,
|
||
required this.userDisplayName,
|
||
required this.leftColumn,
|
||
required this.centerColumn,
|
||
required this.rightColumn,
|
||
this.userEmail,
|
||
this.onProfileTap,
|
||
this.onSearchAmTap,
|
||
this.onSettingsTap,
|
||
this.onLogout,
|
||
this.agendaBody,
|
||
this.contratBody,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: QuotidienTheme.ivory,
|
||
body: Container(
|
||
decoration: QuotidienTheme.paperBackground(),
|
||
child: Column(
|
||
children: [
|
||
QuotidienBandeau(
|
||
selectedSection: selectedSection,
|
||
onSectionSelected: onSectionSelected,
|
||
userDisplayName: userDisplayName,
|
||
userEmail: userEmail,
|
||
onProfileTap: onProfileTap,
|
||
onSearchAmTap: onSearchAmTap,
|
||
onSettingsTap: onSettingsTap,
|
||
onLogout: onLogout,
|
||
),
|
||
const QuotidienPencilDivider(),
|
||
Expanded(child: _bodyForSection(context)),
|
||
const QuotidienPencilDivider(),
|
||
const AppFooter(showTopBorder: false),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _bodyForSection(BuildContext context) {
|
||
switch (selectedSection) {
|
||
case QuotidienNavSection.liaison:
|
||
return _ThreeColumns(
|
||
left: leftColumn,
|
||
center: centerColumn,
|
||
right: rightColumn,
|
||
);
|
||
case QuotidienNavSection.agenda:
|
||
return agendaBody ??
|
||
const QuotidienStubPage(
|
||
title: 'Agenda',
|
||
message: 'Page Agenda — à venir.',
|
||
);
|
||
case QuotidienNavSection.contrat:
|
||
return contratBody ??
|
||
const QuotidienStubPage(
|
||
title: 'Contrat',
|
||
message: 'Page Contrat — à venir.',
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Trait « crayon gris » dessiné à la main : sépare bandeau / corps / footer
|
||
/// (horizontal) et les 3 colonnes (vertical). Le PNG (2400×28) est étiré dans
|
||
/// le sens du trait seulement : sur un trait, c'est invisible.
|
||
class QuotidienPencilDivider extends StatelessWidget {
|
||
final Axis axis;
|
||
|
||
/// Épaisseur de la zone du trait (hauteur si horizontal, largeur sinon).
|
||
final double thickness;
|
||
final EdgeInsets padding;
|
||
|
||
const QuotidienPencilDivider({
|
||
super.key,
|
||
this.axis = Axis.horizontal,
|
||
this.thickness = 14,
|
||
this.padding = const EdgeInsets.symmetric(horizontal: 24),
|
||
});
|
||
|
||
const QuotidienPencilDivider.vertical({
|
||
super.key,
|
||
this.thickness = 14,
|
||
this.padding = const EdgeInsets.symmetric(vertical: 16),
|
||
}) : axis = Axis.vertical;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final horizontal = axis == Axis.horizontal;
|
||
return Padding(
|
||
padding: padding,
|
||
child: SizedBox(
|
||
width: horizontal ? double.infinity : thickness,
|
||
height: horizontal ? thickness : double.infinity,
|
||
child: Image.asset(
|
||
horizontal
|
||
? QuotidienTheme.pencilLineAsset
|
||
: QuotidienTheme.pencilLineVerticalAsset,
|
||
fit: BoxFit.fill,
|
||
filterQuality: FilterQuality.medium,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class _ThreeColumns extends StatelessWidget {
|
||
final Widget left;
|
||
final Widget center;
|
||
final Widget right;
|
||
|
||
const _ThreeColumns({
|
||
required this.left,
|
||
required this.center,
|
||
required this.right,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return LayoutBuilder(
|
||
builder: (context, constraints) {
|
||
final wide = constraints.maxWidth >= 900;
|
||
if (!wide) {
|
||
// Socle mobile temporaire (#188 = swipe dédié) : pile verticale.
|
||
return ListView(
|
||
padding: const EdgeInsets.fromLTRB(12, 4, 12, 12),
|
||
children: [
|
||
_ColumnPanel(child: left),
|
||
const QuotidienPencilDivider(
|
||
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 4),
|
||
),
|
||
_ColumnPanel(child: center),
|
||
const QuotidienPencilDivider(
|
||
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 4),
|
||
),
|
||
_ColumnPanel(child: right),
|
||
],
|
||
);
|
||
}
|
||
return Padding(
|
||
padding: const EdgeInsets.fromLTRB(16, 4, 16, 12),
|
||
child: Row(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
Expanded(child: _ColumnPanel(child: left)),
|
||
const QuotidienPencilDivider.vertical(),
|
||
Expanded(child: _ColumnPanel(child: center)),
|
||
const QuotidienPencilDivider.vertical(),
|
||
Expanded(child: _ColumnPanel(child: right)),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
}
|
||
|
||
/// Panneau colonne (carte pastel semi-transparente).
|
||
class _ColumnPanel extends StatelessWidget {
|
||
final Widget child;
|
||
|
||
const _ColumnPanel({required this.child});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Container(
|
||
// Pas de bordure Material : la séparation est assurée par les traits
|
||
// crayon, on garde juste un léger fond.
|
||
decoration: BoxDecoration(
|
||
color: QuotidienTheme.columnCardFill.withOpacity(0.82),
|
||
borderRadius: BorderRadius.circular(16),
|
||
),
|
||
clipBehavior: Clip.antiAlias,
|
||
child: child,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// Placeholder de colonne métier (branchable par tickets C/D/E).
|
||
class QuotidienColumnPlaceholder extends StatelessWidget {
|
||
final String title;
|
||
final String subtitle;
|
||
final IconData icon;
|
||
|
||
const QuotidienColumnPlaceholder({
|
||
super.key,
|
||
required this.title,
|
||
required this.subtitle,
|
||
required this.icon,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Padding(
|
||
padding: const EdgeInsets.all(20),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
Row(
|
||
children: [
|
||
Icon(icon, color: QuotidienTheme.lavender, size: 22),
|
||
const SizedBox(width: 8),
|
||
Text(
|
||
title,
|
||
style: GoogleFonts.merienda(
|
||
fontSize: 18,
|
||
fontWeight: FontWeight.w600,
|
||
color: QuotidienTheme.ink,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
Expanded(
|
||
child: Center(
|
||
child: Text(
|
||
subtitle,
|
||
textAlign: TextAlign.center,
|
||
style: GoogleFonts.merriweather(
|
||
fontSize: 13,
|
||
color: QuotidienTheme.muted,
|
||
height: 1.4,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// Stub pleine page (Agenda / Contrat) — contenu riche hors #166.
|
||
class QuotidienStubPage extends StatelessWidget {
|
||
final String title;
|
||
final String message;
|
||
|
||
const QuotidienStubPage({
|
||
super.key,
|
||
required this.title,
|
||
required this.message,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Center(
|
||
child: ConstrainedBox(
|
||
constraints: const BoxConstraints(maxWidth: 420),
|
||
child: Container(
|
||
margin: const EdgeInsets.all(24),
|
||
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 32),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white.withOpacity(0.85),
|
||
borderRadius: BorderRadius.circular(16),
|
||
border: Border.all(color: Colors.grey.shade300),
|
||
),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Text(
|
||
title,
|
||
style: GoogleFonts.merienda(
|
||
fontSize: 22,
|
||
fontWeight: FontWeight.w600,
|
||
color: QuotidienTheme.ink,
|
||
),
|
||
),
|
||
const SizedBox(height: 12),
|
||
Text(
|
||
message,
|
||
textAlign: TextAlign.center,
|
||
style: GoogleFonts.merriweather(
|
||
fontSize: 14,
|
||
color: QuotidienTheme.muted,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|