This commit is contained in:
Louis Mazin 2026-09-20 03:01:43 +02:00
parent 862ed7f279
commit 9c3b068646

View File

@ -27,6 +27,17 @@ const DEFAULT_END_HOUR = 18;
const SCHEDULE_HEADER_HEIGHT = 58; const SCHEDULE_HEADER_HEIGHT = 58;
const MIN_SCHEDULE_HEIGHT = 180; const MIN_SCHEDULE_HEIGHT = 180;
/*
* Hauteur minimale d'une heure sur mobile.
*
* Si l'espace disponible permet de faire plus grand :
* les heures prennent toute la place disponible.
*
* Si l'espace disponible est trop petit :
* le planning devient scrollable.
*/
const MOBILE_MIN_HOUR_HEIGHT = 52;
const MOBILE_TIME_COLUMN_WIDTH = 64; const MOBILE_TIME_COLUMN_WIDTH = 64;
const MOBILE_USER_COLUMN_WIDTH = 40; const MOBILE_USER_COLUMN_WIDTH = 40;
const MOBILE_DEFAULT_CONTENT_WIDTH = 236; const MOBILE_DEFAULT_CONTENT_WIDTH = 236;
@ -82,6 +93,9 @@ const WeeklySummaryPage = ({ userId }) => {
const [availableScheduleHeight, setAvailableScheduleHeight] = const [availableScheduleHeight, setAvailableScheduleHeight] =
useState(620); useState(620);
const [mobileContainerHeight, setMobileContainerHeight] =
useState(0);
const [isMobile, setIsMobile] = useState(false); const [isMobile, setIsMobile] = useState(false);
const [mobileContainerWidth, setMobileContainerWidth] = const [mobileContainerWidth, setMobileContainerWidth] =
@ -428,14 +442,26 @@ const WeeklySummaryPage = ({ userId }) => {
); );
/* /*
* Le mobile utilise directement la hauteur du conteneur * MOBILE
* définie par 60vh. Il ne faut surtout pas recalculer *
* une seconde hauteur à partir de window.innerHeight. * Le conteneur est maintenant un flex-item qui prend
* toute la hauteur disponible.
*
* On mesure donc sa vraie hauteur au lieu d'utiliser
* une valeur arbitraire comme 60vh.
*/ */
if (window.innerWidth < 768) { if (window.innerWidth < 768) {
setMobileContainerHeight(
scheduleTable.clientHeight,
);
return; return;
} }
/*
* DESKTOP
*/
const cardContent = const cardContent =
scheduleTable.closest( scheduleTable.closest(
'[data-slot="card-content"]', '[data-slot="card-content"]',
@ -498,11 +524,29 @@ const WeeklySummaryPage = ({ userId }) => {
updateScheduleDimensions, updateScheduleDimensions,
); );
let resizeObserver = null;
if (
typeof ResizeObserver !== 'undefined' &&
scheduleTableRef.current
) {
resizeObserver =
new ResizeObserver(() => {
updateScheduleDimensions();
});
resizeObserver.observe(
scheduleTableRef.current,
);
}
return () => { return () => {
window.removeEventListener( window.removeEventListener(
'resize', 'resize',
updateScheduleDimensions, updateScheduleDimensions,
); );
resizeObserver?.disconnect();
}; };
}, [ }, [
loading, loading,
@ -1022,32 +1066,44 @@ const WeeklySummaryPage = ({ userId }) => {
* *
* MOBILE * MOBILE
* *
* Le conteneur visible fait exactement 60vh. * Le conteneur visible prend maintenant toute la place
* disponible grâce au flex.
* *
* Le header occupe 58px. * Si suffisamment de place est disponible :
* -> les heures sont réparties sur toute la hauteur.
* *
* Le corps du planning prend donc : * Si la hauteur disponible est insuffisante :
* -> chaque heure garde au minimum
* MOBILE_MIN_HOUR_HEIGHT.
* -> le contenu devient alors scrollable.
* *
* 60vh - 58px * DESKTOP
* *
* Puis cette hauteur est divisée exactement par le nombre * On conserve le calcul dynamique existant.
* d'heures.
*
* Il n'y a aucune hauteur min/max par heure.
*/ */
const mobilePlanningViewportHeight = const mobileAvailableBodyHeight =
typeof window !== 'undefined'
? window.innerHeight * 0.6
: 0;
const mobileScheduleHeight =
Math.max( Math.max(
0, 0,
mobilePlanningViewportHeight - mobileContainerHeight -
SCHEDULE_HEADER_HEIGHT, SCHEDULE_HEADER_HEIGHT,
); );
const mobileNaturalScheduleHeight =
hourCount *
MOBILE_MIN_HOUR_HEIGHT;
const mobileScheduleHeight =
mobileAvailableBodyHeight > 0
? Math.max(
mobileAvailableBodyHeight,
mobileNaturalScheduleHeight,
)
: Math.max(
MIN_SCHEDULE_HEIGHT,
mobileNaturalScheduleHeight,
);
const scheduleHeight = isMobile const scheduleHeight = isMobile
? mobileScheduleHeight ? mobileScheduleHeight
: Math.max( : Math.max(
@ -1294,10 +1350,10 @@ const WeeklySummaryPage = ({ userId }) => {
</div> </div>
</CardHeader> </CardHeader>
<CardContent className="min-h-0 flex-1 overflow-visible"> <CardContent className="flex min-h-0 flex-1 flex-col overflow-hidden">
{/* Sélection utilisateur */} {/* Sélection utilisateur */}
{isAdmin && ( {isAdmin && (
<div className="flex flex-col gap-3 pb-4 md:flex-row md:items-center"> <div className="shrink-0 flex flex-col gap-3 pb-4 md:flex-row md:items-center">
<div className="flex min-w-0 flex-1 items-center gap-2"> <div className="flex min-w-0 flex-1 items-center gap-2">
<label <label
htmlFor="weekly-selected-user" htmlFor="weekly-selected-user"
@ -1370,7 +1426,7 @@ const WeeklySummaryPage = ({ userId }) => {
{isAdmin && {isAdmin &&
showAllUsers && showAllUsers &&
legendUsers.length > 0 && ( legendUsers.length > 0 && (
<div className="mb-4 rounded-lg bg-muted p-3"> <div className="mb-4 shrink-0 rounded-lg bg-muted p-3">
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<span className="mr-2 text-sm font-medium"> <span className="mr-2 text-sm font-medium">
Légende : Légende :
@ -1410,18 +1466,18 @@ const WeeklySummaryPage = ({ userId }) => {
<div <div
ref={scheduleTableRef} ref={scheduleTableRef}
className={`relative rounded-lg border ${ className={`relative min-h-0 rounded-lg border ${
isMobile isMobile
? 'h-[60vh] max-h-[60vh] overflow-x-auto overflow-y-auto' ? 'flex-1 overflow-auto'
: 'overflow-x-auto overflow-y-hidden' : 'overflow-x-auto overflow-y-hidden'
}`} }`}
style={{ style={{
height: isMobile ...(isMobile
? '60vh' ? {}
: `${totalPlanningHeight}px`, : {
maxHeight: isMobile height: `${totalPlanningHeight}px`,
? '60vh' maxHeight: `${totalPlanningHeight}px`,
: `${totalPlanningHeight}px`, }),
WebkitOverflowScrolling: WebkitOverflowScrolling:
'touch', 'touch',
touchAction: touchAction:
@ -1445,7 +1501,7 @@ const WeeklySummaryPage = ({ userId }) => {
> >
{/* Header */} {/* Header */}
<div <div
className="flex items-center justify-center border-b bg-background p-2 text-center font-medium" className="sticky top-0 z-20 flex items-center justify-center border-b bg-background p-2 text-center font-medium"
style={{ style={{
gridColumn: '1 / -1', gridColumn: '1 / -1',
height: `${SCHEDULE_HEADER_HEIGHT}px`, height: `${SCHEDULE_HEADER_HEIGHT}px`,