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 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_USER_COLUMN_WIDTH = 40;
const MOBILE_DEFAULT_CONTENT_WIDTH = 236;
@ -82,6 +93,9 @@ const WeeklySummaryPage = ({ userId }) => {
const [availableScheduleHeight, setAvailableScheduleHeight] =
useState(620);
const [mobileContainerHeight, setMobileContainerHeight] =
useState(0);
const [isMobile, setIsMobile] = useState(false);
const [mobileContainerWidth, setMobileContainerWidth] =
@ -428,14 +442,26 @@ const WeeklySummaryPage = ({ userId }) => {
);
/*
* Le mobile utilise directement la hauteur du conteneur
* définie par 60vh. Il ne faut surtout pas recalculer
* une seconde hauteur à partir de window.innerHeight.
* MOBILE
*
* 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) {
setMobileContainerHeight(
scheduleTable.clientHeight,
);
return;
}
/*
* DESKTOP
*/
const cardContent =
scheduleTable.closest(
'[data-slot="card-content"]',
@ -498,11 +524,29 @@ const WeeklySummaryPage = ({ userId }) => {
updateScheduleDimensions,
);
let resizeObserver = null;
if (
typeof ResizeObserver !== 'undefined' &&
scheduleTableRef.current
) {
resizeObserver =
new ResizeObserver(() => {
updateScheduleDimensions();
});
resizeObserver.observe(
scheduleTableRef.current,
);
}
return () => {
window.removeEventListener(
'resize',
updateScheduleDimensions,
);
resizeObserver?.disconnect();
};
}, [
loading,
@ -1022,32 +1066,44 @@ const WeeklySummaryPage = ({ userId }) => {
*
* 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
* d'heures.
*
* Il n'y a aucune hauteur min/max par heure.
* On conserve le calcul dynamique existant.
*/
const mobilePlanningViewportHeight =
typeof window !== 'undefined'
? window.innerHeight * 0.6
: 0;
const mobileScheduleHeight =
const mobileAvailableBodyHeight =
Math.max(
0,
mobilePlanningViewportHeight -
mobileContainerHeight -
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
? mobileScheduleHeight
: Math.max(
@ -1294,10 +1350,10 @@ const WeeklySummaryPage = ({ userId }) => {
</div>
</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 */}
{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">
<label
htmlFor="weekly-selected-user"
@ -1370,7 +1426,7 @@ const WeeklySummaryPage = ({ userId }) => {
{isAdmin &&
showAllUsers &&
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">
<span className="mr-2 text-sm font-medium">
Légende :
@ -1410,18 +1466,18 @@ const WeeklySummaryPage = ({ userId }) => {
<div
ref={scheduleTableRef}
className={`relative rounded-lg border ${
className={`relative min-h-0 rounded-lg border ${
isMobile
? 'h-[60vh] max-h-[60vh] overflow-x-auto overflow-y-auto'
? 'flex-1 overflow-auto'
: 'overflow-x-auto overflow-y-hidden'
}`}
style={{
height: isMobile
? '60vh'
: `${totalPlanningHeight}px`,
maxHeight: isMobile
? '60vh'
: `${totalPlanningHeight}px`,
...(isMobile
? {}
: {
height: `${totalPlanningHeight}px`,
maxHeight: `${totalPlanningHeight}px`,
}),
WebkitOverflowScrolling:
'touch',
touchAction:
@ -1445,7 +1501,7 @@ const WeeklySummaryPage = ({ userId }) => {
>
{/* Header */}
<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={{
gridColumn: '1 / -1',
height: `${SCHEDULE_HEADER_HEIGHT}px`,