From 7adfb4ab6478335bdda5d197f59d873102d6c9c4 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 16:23:21 -0400 Subject: [PATCH 01/21] Create useGridPageDays hook with constants --- .../src/features/event/grid/lib/constants.ts | 5 +++ .../features/event/grid/lib/use-page-days.ts | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 frontend/src/features/event/grid/lib/use-page-days.ts diff --git a/frontend/src/features/event/grid/lib/constants.ts b/frontend/src/features/event/grid/lib/constants.ts index b2077f135..d29ea3399 100644 --- a/frontend/src/features/event/grid/lib/constants.ts +++ b/frontend/src/features/event/grid/lib/constants.ts @@ -1,2 +1,7 @@ export const SIDE_WIDTH = 30; export const TIME_LABEL_WIDTH = 50; + +export const GRID_PAGE_DAYS_MOBILE_OPTIONS = [4, 7]; +export const GRID_PAGE_DAYS_DESKTOP_OPTIONS = [7, 14]; +export const GRID_PAGE_DAYS_MOBILE_KEY = "grid-page-days-mobile"; +export const GRID_PAGE_DAYS_DESKTOP_KEY = "grid-page-days-desktop"; diff --git a/frontend/src/features/event/grid/lib/use-page-days.ts b/frontend/src/features/event/grid/lib/use-page-days.ts new file mode 100644 index 000000000..c723586d6 --- /dev/null +++ b/frontend/src/features/event/grid/lib/use-page-days.ts @@ -0,0 +1,41 @@ +"use client"; + +import { + GRID_PAGE_DAYS_DESKTOP_OPTIONS, + GRID_PAGE_DAYS_MOBILE_OPTIONS, + GRID_PAGE_DAYS_DESKTOP_KEY, + GRID_PAGE_DAYS_MOBILE_KEY, +} from "@/features/event/grid/lib/constants"; +import useCheckMobile from "@/lib/hooks/use-check-mobile"; + +export default function useGridPageDays() { + const isMobile = useCheckMobile(); + + const gridPageDaysOptions: number[] = isMobile + ? GRID_PAGE_DAYS_MOBILE_OPTIONS + : GRID_PAGE_DAYS_DESKTOP_OPTIONS; + + const validateValue = (value: string | null) => { + if (!value) return gridPageDaysOptions[0]; + const parsedValue = parseInt(value); + return gridPageDaysOptions.includes(parsedValue) + ? parsedValue + : gridPageDaysOptions[0]; + }; + + const localStorageKey = isMobile + ? GRID_PAGE_DAYS_MOBILE_KEY + : GRID_PAGE_DAYS_DESKTOP_KEY; + + let storedValue: string | null = null; + try { + storedValue = localStorage.getItem(localStorageKey); + } catch {} + const gridPageDays = validateValue(storedValue); + + const setGridPageDays = (value: number) => { + localStorage.setItem(localStorageKey, value.toString()); + }; + + return { gridPageDays, gridPageDaysOptions, setGridPageDays }; +} From ca985910493ba5b275a6e2e0400c6eda8577a99b Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 17:02:05 -0400 Subject: [PATCH 02/21] Add state to useGridPageDays --- .../features/event/grid/lib/use-page-days.ts | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/frontend/src/features/event/grid/lib/use-page-days.ts b/frontend/src/features/event/grid/lib/use-page-days.ts index c723586d6..833493c66 100644 --- a/frontend/src/features/event/grid/lib/use-page-days.ts +++ b/frontend/src/features/event/grid/lib/use-page-days.ts @@ -1,10 +1,12 @@ "use client"; +import { useCallback, useEffect, useState } from "react"; + import { - GRID_PAGE_DAYS_DESKTOP_OPTIONS, - GRID_PAGE_DAYS_MOBILE_OPTIONS, GRID_PAGE_DAYS_DESKTOP_KEY, + GRID_PAGE_DAYS_DESKTOP_OPTIONS, GRID_PAGE_DAYS_MOBILE_KEY, + GRID_PAGE_DAYS_MOBILE_OPTIONS, } from "@/features/event/grid/lib/constants"; import useCheckMobile from "@/lib/hooks/use-check-mobile"; @@ -15,26 +17,41 @@ export default function useGridPageDays() { ? GRID_PAGE_DAYS_MOBILE_OPTIONS : GRID_PAGE_DAYS_DESKTOP_OPTIONS; - const validateValue = (value: string | null) => { - if (!value) return gridPageDaysOptions[0]; - const parsedValue = parseInt(value); - return gridPageDaysOptions.includes(parsedValue) - ? parsedValue - : gridPageDaysOptions[0]; - }; - const localStorageKey = isMobile ? GRID_PAGE_DAYS_MOBILE_KEY : GRID_PAGE_DAYS_DESKTOP_KEY; - let storedValue: string | null = null; - try { - storedValue = localStorage.getItem(localStorageKey); - } catch {} - const gridPageDays = validateValue(storedValue); + const validateValue = useCallback( + (value: string | null) => { + if (!value) return gridPageDaysOptions[0]; + const parsedValue = parseInt(value); + return gridPageDaysOptions.includes(parsedValue) + ? parsedValue + : gridPageDaysOptions[0]; + }, + [gridPageDaysOptions], + ); + + const [gridPageDays, setGridPageDaysState] = useState( + gridPageDaysOptions[0], + ); + + useEffect(() => { + try { + const storedValue = localStorage.getItem(localStorageKey); + setGridPageDaysState(validateValue(storedValue)); + } catch { + setGridPageDaysState(gridPageDaysOptions[0]); + } + }, [localStorageKey, isMobile, gridPageDaysOptions, validateValue]); const setGridPageDays = (value: number) => { - localStorage.setItem(localStorageKey, value.toString()); + try { + localStorage.setItem(localStorageKey, value.toString()); + } catch { + // Ignore error + } + setGridPageDaysState(value); }; return { gridPageDays, gridPageDaysOptions, setGridPageDays }; From 12ab94231c40216d987e65b14514674ae50099fd Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 17:17:25 -0400 Subject: [PATCH 03/21] Create grid page days selector --- .../components/selectors/grid-page-days.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/src/features/event/components/selectors/grid-page-days.tsx diff --git a/frontend/src/features/event/components/selectors/grid-page-days.tsx b/frontend/src/features/event/components/selectors/grid-page-days.tsx new file mode 100644 index 000000000..6f758ce43 --- /dev/null +++ b/frontend/src/features/event/components/selectors/grid-page-days.tsx @@ -0,0 +1,25 @@ +import Selector from "@/features/selector/components/selector"; + +export default function GridPageDaysSelector({ + value, + options, + onChange, +}: { + value: number; + options: number[]; + onChange: (value: number) => void; +}) { + return ( + ({ + value: option, + label: `${option} days`, + }))} + onChange={onChange} + dialogTitle="Select Days per Page" + dialogDescription="Select the number of days to display per page" + /> + ); +} From 83f0e2e89110c95db956f9a14a226e09b04eecf6 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 17:17:38 -0400 Subject: [PATCH 04/21] Add pageDays prop to ScheduleGrid --- frontend/src/features/event/grid/grid.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 207dd9f02..677849813 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -23,6 +23,7 @@ interface ScheduleGridProps { mode: "paint" | "view" | "preview"; timeslots: Date[]; timezone: string; + pageDays: number; isWeekdayEvent?: boolean; unselectedRange?: boolean; @@ -61,6 +62,7 @@ const variants = { export default function ScheduleGrid({ timeslots, timezone, + pageDays, mode = "preview", isWeekdayEvent = false, unselectedRange = false, @@ -82,7 +84,7 @@ export default function ScheduleGrid({ direction, paginate, error, - } = useGridinfo(timeslots, timezone, isMobile ? 4 : 7, onPageUpdate); + } = useGridinfo(timeslots, timezone, pageDays, onPageUpdate); // Initial onPageUpdate callback to report pagination info to parent // Also triggers if the user changes between mobile and desktop layouts From 4ac3fe188a17f43c9d95859b31eb892bfe89df49 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 18:00:21 -0400 Subject: [PATCH 05/21] Add grid page days selector to grid pages --- .../app/(event)/[event-code]/page-client.tsx | 20 ++++++++++++ .../[event-code]/painting/page-client.tsx | 31 ++++++++++++++----- frontend/src/features/event/editor/editor.tsx | 22 +++++++++++-- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index 15b4ef6d0..dbda67008 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -8,7 +8,9 @@ import KebabMenu from "@/components/kebab-menu"; import { EventInformation } from "@/core/event/types"; import EmptyButton from "@/features/button/components/empty"; import LinkButton from "@/features/button/components/link"; +import GridPageDaysSelector from "@/features/event/components/selectors/grid-page-days"; import ScheduleGrid from "@/features/event/grid/grid"; +import useGridPageDays from "@/features/event/grid/lib/use-page-days"; import AttendeesPanel from "@/features/event/results/attendees/desktop-panel"; import AttendeesDrawer from "@/features/event/results/attendees/mobile-drawer"; import { getResultBanners } from "@/features/event/results/components/banners"; @@ -65,6 +67,10 @@ function EventResults({ eventData }: { eventData: EventInformation }) { setTimezone(newTZ.toString()); }; + /* GRID PAGE DAYS */ + const { gridPageDays, gridPageDaysOptions, setGridPageDays } = + useGridPageDays(); + /* MOBILE DRAWER SPACING */ const [drawerSnap, setDrawerSnap] = useState(0.22); const getSpacerHeight = () => { @@ -123,6 +129,17 @@ function EventResults({ eventData }: { eventData: EventInformation }) { ); + const daysPerPageSelector = ( +
+

Days per page

+ +
+ ); + return (
@@ -156,9 +173,11 @@ function EventResults({ eventData }: { eventData: EventInformation }) { availabilities={filteredAvailabilities} numParticipants={gridNumParticipants} timeslots={timeslots} + pageDays={gridPageDays} />
+ {daysPerPageSelector}
+ {daysPerPageSelector} >({}); @@ -280,13 +286,23 @@ export default function ClientPage({ setSaveDefaultName={setSaveDefaultName} /> -
- Displaying event in - +
+
+

Days per page

+ +
+
+

Displaying event in

+ +
@@ -304,6 +320,7 @@ export default function ClientPage({ setMaxVisitedPage(index); } }} + pageDays={gridPageDays} />
diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index e462d4a04..64aaaada7 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -13,6 +13,7 @@ import checkUnselectedRange from "@/core/event/lib/unselected-range"; import { EventInformation } from "@/core/event/types"; import ActionButton from "@/features/button/components/action"; import LinkButton from "@/features/button/components/link"; +import GridPageDaysSelector from "@/features/event/components/selectors/grid-page-days"; import TimeSelector from "@/features/event/components/selectors/time"; import AdvancedOptions from "@/features/event/editor/advanced-options"; import { MAX_TITLE_LENGTH } from "@/features/event/editor/constants"; @@ -20,6 +21,7 @@ import DateRangeSelection from "@/features/event/editor/date-range/selector"; import { EventEditorType } from "@/features/event/editor/types"; import { validateEventData } from "@/features/event/editor/validate-data"; import { ScheduleGrid } from "@/features/event/grid"; +import useGridPageDays from "@/features/event/grid/lib/use-page-days"; import HeaderSpacer from "@/features/header/components/header-spacer"; import FormSelectorField from "@/features/selector/components/selector-field"; import { RateLimitBanner } from "@/features/system-feedback"; @@ -57,6 +59,8 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { setEndTime, } = useEventContext(); const { title, customCode, eventRange, timeslots } = state; + const { gridPageDays, gridPageDaysOptions, setGridPageDays } = + useGridPageDays(); const router = useRouter(); const [mobileTab, setMobileTab] = useState("details"); @@ -111,6 +115,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { unselectedRange={checkUnselectedRange(eventRange)} timezone={eventRange.timezone} timeslots={timeslots} + pageDays={gridPageDays} /> ); @@ -188,8 +193,21 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
-
-
{grid}
+
+
{grid}
+
+

+ This is a preview! You{"'"}ll add your availability later. +

+
+

Days per page:

+ +
+
From da0666af72c9c78a1e23b57f9bd7f3dc30612c83 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 18:10:47 -0400 Subject: [PATCH 06/21] Dynamically hide grid preview footer --- frontend/src/features/event/editor/editor.tsx | 28 +++++++++++-------- frontend/src/features/event/grid/grid.tsx | 7 +++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index 64aaaada7..760c89c1a 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -61,6 +61,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { const { title, customCode, eventRange, timeslots } = state; const { gridPageDays, gridPageDaysOptions, setGridPageDays } = useGridPageDays(); + const [gridDisplayed, setGridDisplayed] = useState(false); const router = useRouter(); const [mobileTab, setMobileTab] = useState("details"); @@ -116,6 +117,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { timezone={eventRange.timezone} timeslots={timeslots} pageDays={gridPageDays} + setGridDisplayed={setGridDisplayed} /> ); @@ -195,19 +197,21 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
{grid}
-
-

- This is a preview! You{"'"}ll add your availability later. -

-
-

Days per page:

- + {gridDisplayed && ( +
+

+ This is a preview! You{"'"}ll add your availability later. +

+
+

Days per page:

+ +
-
+ )}
diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 677849813..c0c068453 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -33,6 +33,7 @@ interface ScheduleGridProps { numParticipants?: number; hoveredSlot?: string | null; setHoveredSlot?: (slotIso: string | null) => void; + setGridDisplayed?: (displayed: boolean) => void; // for "paint" mode userAvailability?: AvailabilitySet; @@ -70,6 +71,7 @@ export default function ScheduleGrid({ numParticipants = 0, hoveredSlot, setHoveredSlot = () => {}, + setGridDisplayed = () => {}, userAvailability = createEmptyUserAvailability(), onToggleSlot = () => {}, onPageUpdate = () => {}, @@ -116,6 +118,11 @@ export default function ScheduleGrid({ return () => resizeObserver.disconnect(); }); + const isGridDisplayed = !unselectedRange && !error; + useEffect(() => { + setGridDisplayed(isGridDisplayed); + }, [isGridDisplayed, setGridDisplayed]); + if (unselectedRange) return ( Date: Thu, 16 Jul 2026 18:26:17 -0400 Subject: [PATCH 07/21] Add mobile grid page day selectors --- .../[event-code]/painting/page-client.tsx | 49 +++++++++---------- frontend/src/features/event/editor/editor.tsx | 14 +++++- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx index 790726ceb..6e2b8dcd9 100644 --- a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx @@ -255,6 +255,27 @@ export default function ClientPage({ /> ); + const displaySettings = ( +
+
+

Days per page

+ +
+
+

Displaying event in

+ +
+
+ ); + return (
@@ -286,24 +307,7 @@ export default function ClientPage({ setSaveDefaultName={setSaveDefaultName} /> -
-
-

Days per page

- -
-
-

Displaying event in

- -
-
+ {displaySettings}
{/* Right Panel */} @@ -323,14 +327,7 @@ export default function ClientPage({ pageDays={gridPageDays} /> -
- Displaying event in - -
+ {displaySettings}
{/* This z-index is necessary to avoid the time column overlapping */} diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index 760c89c1a..4db82d368 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -219,11 +219,21 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
{grid} + {gridDisplayed && ( +
+

Days per page:

+ +
+ )}
{/* This z-index is necessary to avoid the time column overlapping */} From 90cd1bd2401145cec8bf9bf846b26ea42c43c5e3 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 19:16:57 -0400 Subject: [PATCH 08/21] Add compact grid header functionality --- .../app/(event)/[event-code]/page-client.tsx | 9 ++++-- .../[event-code]/painting/page-client.tsx | 9 ++++-- frontend/src/features/event/editor/editor.tsx | 9 ++++-- frontend/src/features/event/grid/grid.tsx | 3 ++ .../features/event/grid/lib/use-page-days.ts | 10 +++++- .../features/event/grid/schedule-header.tsx | 31 +++++++++++++++++-- 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index dbda67008..e25a1db8e 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -68,8 +68,12 @@ function EventResults({ eventData }: { eventData: EventInformation }) { }; /* GRID PAGE DAYS */ - const { gridPageDays, gridPageDaysOptions, setGridPageDays } = - useGridPageDays(); + const { + gridPageDays, + gridPageDaysOptions, + usingMaxGridPageDays, + setGridPageDays, + } = useGridPageDays(); /* MOBILE DRAWER SPACING */ const [drawerSnap, setDrawerSnap] = useState(0.22); @@ -174,6 +178,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { numParticipants={gridNumParticipants} timeslots={timeslots} pageDays={gridPageDays} + useCompactHeader={usingMaxGridPageDays} />
diff --git a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx index 6e2b8dcd9..c18467902 100644 --- a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx @@ -58,8 +58,12 @@ export default function ClientPage({ const { displayName, timeZone, userAvailability } = state; // GRID PAGE DAYS - const { gridPageDays, gridPageDaysOptions, setGridPageDays } = - useGridPageDays(); + const { + gridPageDays, + gridPageDaysOptions, + usingMaxGridPageDays, + setGridPageDays, + } = useGridPageDays(); // TOASTS AND ERROR STATES const { addToast } = useToast(); @@ -325,6 +329,7 @@ export default function ClientPage({ } }} pageDays={gridPageDays} + useCompactHeader={usingMaxGridPageDays} /> {displaySettings} diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index 4db82d368..34c7acd19 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -59,8 +59,12 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { setEndTime, } = useEventContext(); const { title, customCode, eventRange, timeslots } = state; - const { gridPageDays, gridPageDaysOptions, setGridPageDays } = - useGridPageDays(); + const { + gridPageDays, + gridPageDaysOptions, + usingMaxGridPageDays, + setGridPageDays, + } = useGridPageDays(); const [gridDisplayed, setGridDisplayed] = useState(false); const router = useRouter(); @@ -117,6 +121,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { timezone={eventRange.timezone} timeslots={timeslots} pageDays={gridPageDays} + useCompactHeader={usingMaxGridPageDays} setGridDisplayed={setGridDisplayed} /> ); diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index c0c068453..5d45d3446 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -24,6 +24,7 @@ interface ScheduleGridProps { timeslots: Date[]; timezone: string; pageDays: number; + useCompactHeader: boolean; isWeekdayEvent?: boolean; unselectedRange?: boolean; @@ -64,6 +65,7 @@ export default function ScheduleGrid({ timeslots, timezone, pageDays, + useCompactHeader, mode = "preview", isWeekdayEvent = false, unselectedRange = false, @@ -142,6 +144,7 @@ export default function ScheduleGrid({ {visibleDays.map(({ dayDisplay }, i) => { const [weekday, month, day] = dayDisplay.split(" "); + const condenseWeekday = (weekday: string) => { + return weekday === "Thu" + ? "R" + : weekday === "Sun" + ? "U" + : weekday[0]; + }; + const condenseMonth = (month: string) => { + const date = new Date(`${month} 1, 2020`); + return date.getMonth() + 1; + }; return (
-
{isWeekdayEvent ? weekday.toUpperCase() : weekday}
+
+ {compact + ? condenseWeekday(weekday) + : isWeekdayEvent + ? weekday.toUpperCase() + : weekday} +
{!isWeekdayEvent && (
- {month} {day.replace(/^0+/, "")} + {compact ? condenseMonth(month) : month} + {compact ? "/" : " "} + {day.replace(/^0+/, "")}
)}
From c7272a9654f3a546db3150b672399207dde8b07a Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Thu, 16 Jul 2026 19:17:45 -0400 Subject: [PATCH 09/21] Remove colon for consistency --- frontend/src/features/event/editor/editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index 34c7acd19..793a91ad5 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -231,7 +231,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { {grid} {gridDisplayed && (
-

Days per page:

+

Days per page

Date: Thu, 16 Jul 2026 20:57:03 -0400 Subject: [PATCH 10/21] Add adaptive compact header for desktop --- .../features/event/grid/schedule-header.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/frontend/src/features/event/grid/schedule-header.tsx b/frontend/src/features/event/grid/schedule-header.tsx index 51779c014..974b76f5f 100644 --- a/frontend/src/features/event/grid/schedule-header.tsx +++ b/frontend/src/features/event/grid/schedule-header.tsx @@ -118,17 +118,24 @@ export default function ScheduleHeader({ )} >
- {compact - ? condenseWeekday(weekday) - : isWeekdayEvent - ? weekday.toUpperCase() - : weekday} + + {condenseWeekday(weekday)} + + + {isWeekdayEvent ? weekday.toUpperCase() : weekday} +
{!isWeekdayEvent && ( -
- {compact ? condenseMonth(month) : month} - {compact ? "/" : " "} - {day.replace(/^0+/, "")} +
+ + {condenseMonth(month)}/ + + + {month} + + {day.replace(/^0+/, "")}
)}
From ee93107ff2607ae039682344fad89e62a388c6bf Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 15:08:20 -0400 Subject: [PATCH 11/21] Fix mobile display settings showing on desktop --- frontend/src/app/(event)/[event-code]/painting/page-client.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx index c18467902..8705d02a5 100644 --- a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx @@ -332,7 +332,7 @@ export default function ClientPage({ useCompactHeader={usingMaxGridPageDays} /> - {displaySettings} +
{displaySettings}
{/* This z-index is necessary to avoid the time column overlapping */} From 92fbf25ade096794d39dc248e2f058dd3f96764e Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 15:08:31 -0400 Subject: [PATCH 12/21] Simplify logic for visiting all pages --- .../app/(event)/[event-code]/painting/page-client.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx index 8705d02a5..3bed49619 100644 --- a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx @@ -70,9 +70,7 @@ export default function ClientPage({ const [errors, setErrors] = useState>({}); // VISITED LAST PAGE STATE - const [maxVisitedPage, setMaxVisitedPage] = useState(0); - const [numPages, setNumPages] = useState(1); - const visitedLastPage = maxVisitedPage >= numPages - 1; + const [visitedLastPage, setVisitedLastPage] = useState(false); const [confirmationOpen, setConfirmationOpen] = useState(false); const dialogResolver = useRef<((confirmed: boolean) => void) | null>(null); @@ -323,9 +321,8 @@ export default function ClientPage({ userAvailability={userAvailability} timeslots={timeslots} onPageUpdate={(index, pages) => { - setNumPages(pages); - if (index > maxVisitedPage) { - setMaxVisitedPage(index); + if (index >= pages - 1) { + setVisitedLastPage(true); } }} pageDays={gridPageDays} From 6d587aa0a077e6477f0565e63098cd10d79b1985 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 15:15:53 -0400 Subject: [PATCH 13/21] Fix out of bounds pagination bug --- frontend/src/features/event/grid/grid.tsx | 6 +++++- frontend/src/features/event/grid/lib/use-grid.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 5d45d3446..17009aec9 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -94,11 +94,15 @@ export default function ScheduleGrid({ // Also triggers if the user changes between mobile and desktop layouts const reportedTotalPages = useRef(null); useEffect(() => { + if (currentPage >= totalPages) { + // If out of bounds, go to the last page + paginate(totalPages - 1 - currentPage); + } if (reportedTotalPages.current !== totalPages) { onPageUpdate(currentPage, totalPages); reportedTotalPages.current = totalPages; } - }, [onPageUpdate, currentPage, totalPages]); + }, [onPageUpdate, currentPage, totalPages, paginate]); const hasPrevPage = currentPage > 0; const hasNextPage = currentPage < totalPages - 1; diff --git a/frontend/src/features/event/grid/lib/use-grid.ts b/frontend/src/features/event/grid/lib/use-grid.ts index 132a33a71..eeebb9678 100644 --- a/frontend/src/features/event/grid/lib/use-grid.ts +++ b/frontend/src/features/event/grid/lib/use-grid.ts @@ -46,8 +46,8 @@ export default function useGridInfo( const paginate = (newDirection: number) => { if ( - (newDirection === -1 && currentPage > 0) || - (newDirection === 1 && currentPage < view.totalPages - 1) + (newDirection < 0 && currentPage > 0) || + (newDirection > 0 && currentPage < view.totalPages - 1) ) { setCurrentPage([currentPage + newDirection, newDirection]); onPaginate(currentPage + newDirection, view.totalPages); From 0a196b691a13bab2888350a4caf184e2b3683b73 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 15:30:53 -0400 Subject: [PATCH 14/21] Add preview message to mobile editor --- frontend/src/features/event/editor/editor.tsx | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index 793a91ad5..144f0b41e 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -126,6 +126,12 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { /> ); + const previewText = ( +

+ This is a preview! You{"'"}ll add your availability later. +

+ ); + return (
@@ -204,9 +210,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
{grid}
{gridDisplayed && (
-

- This is a preview! You{"'"}ll add your availability later. -

+ {previewText}

Days per page:

{grid} {gridDisplayed && ( -
-

Days per page

- +
+ {previewText} +
+

Days per page

+ +
)}
From 40dd443401cafc840407487a5c1cc44b284d2873 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 15:34:12 -0400 Subject: [PATCH 15/21] Add sticky grid header to mobile preview --- frontend/src/features/event/grid/schedule-header.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/schedule-header.tsx b/frontend/src/features/event/grid/schedule-header.tsx index 974b76f5f..a2b624140 100644 --- a/frontend/src/features/event/grid/schedule-header.tsx +++ b/frontend/src/features/event/grid/schedule-header.tsx @@ -56,7 +56,8 @@ export default function ScheduleHeader({ return (
Date: Mon, 20 Jul 2026 16:39:35 -0400 Subject: [PATCH 16/21] Update preview text for event editing --- frontend/src/features/event/editor/editor.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index 144f0b41e..aecbd2bdc 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -128,7 +128,10 @@ function EventEditorContent({ type, initialData }: EventEditorProps) { const previewText = (

- This is a preview! You{"'"}ll add your availability later. + This is a preview!{" "} + {type === "edit" + ? "Manage availability from the results page." + : "You'll add your availability later."}

); From f53dbb6266ba896c2e945444986f7f44caccb9f3 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 16:46:59 -0400 Subject: [PATCH 17/21] Reorder ScheduleGrid props --- frontend/src/features/event/grid/grid.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 17009aec9..397123940 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -27,14 +27,15 @@ interface ScheduleGridProps { useCompactHeader: boolean; isWeekdayEvent?: boolean; + // for "preview" mode unselectedRange?: boolean; + setGridDisplayed?: (displayed: boolean) => void; // for "view" mode availabilities?: ResultsAvailabilityMap; numParticipants?: number; hoveredSlot?: string | null; setHoveredSlot?: (slotIso: string | null) => void; - setGridDisplayed?: (displayed: boolean) => void; // for "paint" mode userAvailability?: AvailabilitySet; @@ -69,11 +70,11 @@ export default function ScheduleGrid({ mode = "preview", isWeekdayEvent = false, unselectedRange = false, + setGridDisplayed = () => {}, availabilities = {}, numParticipants = 0, hoveredSlot, setHoveredSlot = () => {}, - setGridDisplayed = () => {}, userAvailability = createEmptyUserAvailability(), onToggleSlot = () => {}, onPageUpdate = () => {}, From 8b47ac4c0fd3697eb86f45449f19720ab3bf31c5 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 17:16:32 -0400 Subject: [PATCH 18/21] Add useCallback to paginate --- .../src/features/event/grid/lib/use-grid.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/src/features/event/grid/lib/use-grid.ts b/frontend/src/features/event/grid/lib/use-grid.ts index eeebb9678..7297584ae 100644 --- a/frontend/src/features/event/grid/lib/use-grid.ts +++ b/frontend/src/features/event/grid/lib/use-grid.ts @@ -1,4 +1,4 @@ -import { useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { formatInTimeZone, toZonedTime } from "date-fns-tz"; @@ -44,15 +44,18 @@ export default function useGridInfo( [data, currentPage, daysPerPage], ); - const paginate = (newDirection: number) => { - if ( - (newDirection < 0 && currentPage > 0) || - (newDirection > 0 && currentPage < view.totalPages - 1) - ) { - setCurrentPage([currentPage + newDirection, newDirection]); - onPaginate(currentPage + newDirection, view.totalPages); - } - }; + const paginate = useCallback( + (newDirection: number) => { + if ( + (newDirection < 0 && currentPage > 0) || + (newDirection > 0 && currentPage < view.totalPages - 1) + ) { + setCurrentPage([currentPage + newDirection, newDirection]); + onPaginate(currentPage + newDirection, view.totalPages); + } + }, + [currentPage, view.totalPages, onPaginate], + ); return { ...view, currentPage, direction, paginate }; } From b6de935bae07d1aa286ac0e9631fac8f39ea6e01 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 17:18:10 -0400 Subject: [PATCH 19/21] Add unique id to GridPageDaysSelector --- frontend/src/app/(event)/[event-code]/page-client.tsx | 1 + .../src/app/(event)/[event-code]/painting/page-client.tsx | 1 + .../features/event/components/selectors/grid-page-days.tsx | 4 +++- frontend/src/features/event/editor/editor.tsx | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index e25a1db8e..d2e5fd5bc 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -137,6 +137,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) {

Days per page

Days per page

void; }) { return ( ({ value: option, diff --git a/frontend/src/features/event/editor/editor.tsx b/frontend/src/features/event/editor/editor.tsx index aecbd2bdc..3bb2872b6 100644 --- a/frontend/src/features/event/editor/editor.tsx +++ b/frontend/src/features/event/editor/editor.tsx @@ -217,6 +217,7 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {

Days per page:

Days per page

Date: Mon, 20 Jul 2026 17:20:57 -0400 Subject: [PATCH 20/21] Fix stale page value reference --- frontend/src/features/event/grid/grid.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 397123940..73246c3b2 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -95,12 +95,14 @@ export default function ScheduleGrid({ // Also triggers if the user changes between mobile and desktop layouts const reportedTotalPages = useRef(null); useEffect(() => { + let effectiveCurrentPage = currentPage; if (currentPage >= totalPages) { // If out of bounds, go to the last page + effectiveCurrentPage = totalPages - 1; paginate(totalPages - 1 - currentPage); } if (reportedTotalPages.current !== totalPages) { - onPageUpdate(currentPage, totalPages); + onPageUpdate(effectiveCurrentPage, totalPages); reportedTotalPages.current = totalPages; } }, [onPageUpdate, currentPage, totalPages, paginate]); From ee962be5cf6f40fb40fd49f794e42b1df60dbaa8 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 20 Jul 2026 17:25:51 -0400 Subject: [PATCH 21/21] Optimize header condensation --- .../features/event/grid/schedule-header.tsx | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/frontend/src/features/event/grid/schedule-header.tsx b/frontend/src/features/event/grid/schedule-header.tsx index a2b624140..3f699b95f 100644 --- a/frontend/src/features/event/grid/schedule-header.tsx +++ b/frontend/src/features/event/grid/schedule-header.tsx @@ -39,6 +39,27 @@ const variants = { }), }; +const MONTH_TO_NUM: Record = { + Jan: 1, + Feb: 2, + Mar: 3, + Apr: 4, + May: 5, + Jun: 6, + Jul: 7, + Aug: 8, + Sep: 9, + Oct: 10, + Nov: 11, + Dec: 12, +} as const; +const condenseMonth = (month: string) => { + return MONTH_TO_NUM[month] ?? month; +}; +const condenseWeekday = (weekday: string) => { + return weekday === "Thu" ? "R" : weekday === "Sun" ? "U" : weekday[0]; +}; + export default function ScheduleHeader({ preview = false, visibleDays, @@ -97,17 +118,6 @@ export default function ScheduleHeader({ > {visibleDays.map(({ dayDisplay }, i) => { const [weekday, month, day] = dayDisplay.split(" "); - const condenseWeekday = (weekday: string) => { - return weekday === "Thu" - ? "R" - : weekday === "Sun" - ? "U" - : weekday[0]; - }; - const condenseMonth = (month: string) => { - const date = new Date(`${month} 1, 2020`); - return date.getMonth() + 1; - }; return (