Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7adfb4a
Create useGridPageDays hook with constants
jzgom067 Jul 16, 2026
ca98591
Add state to useGridPageDays
jzgom067 Jul 16, 2026
12ab942
Create grid page days selector
jzgom067 Jul 16, 2026
83f0e2e
Add pageDays prop to ScheduleGrid
jzgom067 Jul 16, 2026
d099ce4
Merge branch 'grid-preview-removal' into page-days-option
jzgom067 Jul 16, 2026
4ac3fe1
Add grid page days selector to grid pages
jzgom067 Jul 16, 2026
da0666a
Dynamically hide grid preview footer
jzgom067 Jul 16, 2026
054b138
Add mobile grid page day selectors
jzgom067 Jul 16, 2026
90cd1bd
Add compact grid header functionality
jzgom067 Jul 16, 2026
c7272a9
Remove colon for consistency
jzgom067 Jul 16, 2026
c58016e
Add adaptive compact header for desktop
jzgom067 Jul 17, 2026
ee93107
Fix mobile display settings showing on desktop
jzgom067 Jul 20, 2026
92fbf25
Simplify logic for visiting all pages
jzgom067 Jul 20, 2026
6d587aa
Fix out of bounds pagination bug
jzgom067 Jul 20, 2026
0a196b6
Add preview message to mobile editor
jzgom067 Jul 20, 2026
40dd443
Add sticky grid header to mobile preview
jzgom067 Jul 20, 2026
3e0073e
Update preview text for event editing
jzgom067 Jul 20, 2026
f53dbb6
Reorder ScheduleGrid props
jzgom067 Jul 20, 2026
39993ec
Merge branch 'v0.4.7' into page-days-option
jzgom067 Jul 20, 2026
8b47ac4
Add useCallback to paginate
jzgom067 Jul 20, 2026
b6de935
Add unique id to GridPageDaysSelector
jzgom067 Jul 20, 2026
216b556
Fix stale page value reference
jzgom067 Jul 20, 2026
ee962be
Optimize header condensation
jzgom067 Jul 20, 2026
22bc0ac
Merge branch 'page-days-option' of https://github.com/plan-cake/planc…
jzgom067 Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions frontend/src/app/(event)/[event-code]/page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -64,6 +66,14 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
setTimezone(newTZ.toString());
};

/* GRID PAGE DAYS */
const {
gridPageDays,
gridPageDaysOptions,
usingMaxGridPageDays,
setGridPageDays,
} = useGridPageDays();

/* MOBILE DRAWER SPACING */
const [drawerSnap, setDrawerSnap] = useState<number | string | null>(0.22);
const getSpacerHeight = () => {
Expand Down Expand Up @@ -119,6 +129,18 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
/>
);

const daysPerPageSelector = (
<div className="mb-2">
<p>Days per page</p>
<GridPageDaysSelector
id="grid-page-days-selector"
value={gridPageDays}
options={gridPageDaysOptions}
onChange={setGridPageDays}
/>
</div>
);

return (
<div className="flex flex-col space-y-4 pl-6 pr-6 md:h-screen">
<HeaderSpacer />
Expand Down Expand Up @@ -152,9 +174,12 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
availabilities={filteredAvailabilities}
numParticipants={gridNumParticipants}
timeslots={timeslots}
pageDays={gridPageDays}
useCompactHeader={usingMaxGridPageDays}
/>

<div className="bg-panel shrink-0 rounded-3xl p-6 text-sm md:hidden">
{daysPerPageSelector}
<DisplaySettings
timezone={timezone}
onTimezoneChange={handleTZChange}
Expand Down Expand Up @@ -186,6 +211,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
<div className="flex max-h-[calc(100vh-18rem)] flex-col gap-y-4">
<AttendeesPanel />
<div className="bg-panel shrink-0 rounded-3xl p-6 text-sm">
{daysPerPageSelector}
<DisplaySettings
timezone={timezone}
onTimezoneChange={handleTZChange}
Expand Down
61 changes: 39 additions & 22 deletions frontend/src/app/(event)/[event-code]/painting/page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import ActionButton from "@/features/button/components/action";
import LinkButton from "@/features/button/components/link";
import { MAX_DISPLAY_NAME_LENGTH } from "@/features/event/availability/constants";
import { validateAvailabilityData } from "@/features/event/availability/validate-data";
import GridPageDaysSelector from "@/features/event/components/selectors/grid-page-days";
import TimeZoneSelector from "@/features/event/components/selectors/timezone";
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 {
ConfirmationDialog,
Expand Down Expand Up @@ -55,14 +57,20 @@ export default function ClientPage({
);
const { displayName, timeZone, userAvailability } = state;

// GRID PAGE DAYS
const {
gridPageDays,
gridPageDaysOptions,
usingMaxGridPageDays,
setGridPageDays,
} = useGridPageDays();

// TOASTS AND ERROR STATES
const { addToast } = useToast();
const [errors, setErrors] = useState<Record<string, string>>({});

// 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);

Expand Down Expand Up @@ -249,6 +257,28 @@ export default function ClientPage({
/>
);

const displaySettings = (
<div className="bg-panel flex flex-col gap-2 rounded-3xl p-6 text-sm">
<div>
<p>Days per page</p>
<GridPageDaysSelector
id="grid-page-days-selector"
value={gridPageDays}
options={gridPageDaysOptions}
onChange={setGridPageDays}
/>
</div>
<div>
<p>Displaying event in</p>
<TimeZoneSelector
id="timezone-select"
value={timeZone}
onChange={setTimeZone}
/>
</div>
</div>
);

return (
<div className="flex flex-col space-y-4 pl-6 pr-6 md:h-screen">
<HeaderSpacer />
Expand Down Expand Up @@ -280,14 +310,7 @@ export default function ClientPage({
setSaveDefaultName={setSaveDefaultName}
/>

<div className="bg-panel rounded-3xl p-6 text-sm">
Displaying event in
<TimeZoneSelector
id="timezone-select"
value={timeZone}
onChange={setTimeZone}
/>
</div>
{displaySettings}
</div>

{/* Right Panel */}
Expand All @@ -299,21 +322,15 @@ 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}
useCompactHeader={usingMaxGridPageDays}
/>

<div className="bg-panel rounded-3xl p-6 text-sm md:hidden">
Displaying event in
<TimeZoneSelector
id="timezone-select"
value={timeZone}
onChange={setTimeZone}
/>
</div>
<div className="md:hidden">{displaySettings}</div>
</div>

{/* This z-index is necessary to avoid the time column overlapping */}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Selector from "@/features/selector/components/selector";

export default function GridPageDaysSelector({
id,
value,
options,
onChange,
}: {
id: string;
value: number;
options: number[];
onChange: (value: number) => void;
}) {
return (
<Selector
id={id}
value={value}
options={options.map((option) => ({
value: option,
label: `${option} days`,
}))}
onChange={onChange}
dialogTitle="Select Days per Page"
dialogDescription="Select the number of days to display per page"
/>
);
}
Comment thread
jzgom067 marked this conversation as resolved.
57 changes: 53 additions & 4 deletions frontend/src/features/event/editor/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ 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";
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";
Expand Down Expand Up @@ -57,6 +59,13 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
setEndTime,
} = useEventContext();
const { title, customCode, eventRange, timeslots } = state;
const {
gridPageDays,
gridPageDaysOptions,
usingMaxGridPageDays,
setGridPageDays,
} = useGridPageDays();
const [gridDisplayed, setGridDisplayed] = useState(false);
const router = useRouter();

const [mobileTab, setMobileTab] = useState<SegmentedControlOption>("details");
Expand Down Expand Up @@ -111,9 +120,21 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
unselectedRange={checkUnselectedRange(eventRange)}
timezone={eventRange.timezone}
timeslots={timeslots}
pageDays={gridPageDays}
useCompactHeader={usingMaxGridPageDays}
setGridDisplayed={setGridDisplayed}
/>
);

const previewText = (
<p className="opacity-75">
This is a preview!{" "}
{type === "edit"
? "Manage availability from the results page."
: "You'll add your availability later."}
</p>
);

return (
<div className="flex min-h-dvh flex-col space-y-4 pl-6 pr-6">
<HeaderSpacer />
Expand Down Expand Up @@ -190,20 +211,48 @@ function EventEditorContent({ type, initialData }: EventEditorProps) {
<div className="h-16 md:hidden" />
<div className="hidden flex-1 md:col-start-2 md:row-span-9 md:row-start-2 md:block">
<div className="relative h-full w-full grow">
<div className="bg-panel absolute inset-0 flex rounded-3xl pb-4 pl-2 pr-4 pt-4">
<div className="flex1 min-h-0 grow space-y-4">{grid}</div>
<div className="bg-panel absolute inset-0 flex flex-col justify-between gap-2 rounded-3xl px-2 py-4">
<div className="min-h-0 flex-1 pr-2">{grid}</div>
{gridDisplayed && (
<div className="flex items-center justify-between pl-4 pr-2 text-sm">
{previewText}
<div className="flex flex-none items-center gap-2">
<p className="opacity-75">Days per page:</p>
<GridPageDaysSelector
id="grid-page-days-selector-desktop"
value={gridPageDays}
options={gridPageDaysOptions}
onChange={setGridPageDays}
/>
</div>
</div>
)}
</div>
</div>
</div>
</div>

<div
className={cn(
"md:hidden",
mobileTab === "details" ? "hidden" : "block",
"flex-col gap-4 md:hidden",
mobileTab === "details" ? "hidden" : "flex",
)}
>
{grid}
{gridDisplayed && (
<div className="bg-panel flex flex-col gap-2 rounded-3xl p-6 text-sm">
{previewText}
<div>
<p>Days per page</p>
<GridPageDaysSelector
id="grid-page-days-selector-mobile"
value={gridPageDays}
options={gridPageDaysOptions}
onChange={setGridPageDays}
/>
</div>
</div>
)}
Comment thread
jzgom067 marked this conversation as resolved.
</div>

{/* This z-index is necessary to avoid the time column overlapping */}
Expand Down
25 changes: 22 additions & 3 deletions frontend/src/features/event/grid/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ interface ScheduleGridProps {
mode: "paint" | "view" | "preview";
timeslots: Date[];
timezone: string;
pageDays: number;
useCompactHeader: boolean;
isWeekdayEvent?: boolean;

// for "preview" mode
unselectedRange?: boolean;
setGridDisplayed?: (displayed: boolean) => void;

// for "view" mode
availabilities?: ResultsAvailabilityMap;
Expand Down Expand Up @@ -61,9 +65,12 @@ const variants = {
export default function ScheduleGrid({
timeslots,
timezone,
pageDays,
useCompactHeader,
mode = "preview",
isWeekdayEvent = false,
unselectedRange = false,
setGridDisplayed = () => {},
availabilities = {},
numParticipants = 0,
hoveredSlot,
Expand All @@ -82,17 +89,23 @@ 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
const reportedTotalPages = useRef<number | null>(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]);
}, [onPageUpdate, currentPage, totalPages, paginate]);
Comment thread
jzgom067 marked this conversation as resolved.

const hasPrevPage = currentPage > 0;
const hasNextPage = currentPage < totalPages - 1;
Expand All @@ -114,6 +127,11 @@ export default function ScheduleGrid({
return () => resizeObserver.disconnect();
});

const isGridDisplayed = !unselectedRange && !error;
useEffect(() => {
setGridDisplayed(isGridDisplayed);
}, [isGridDisplayed, setGridDisplayed]);

if (unselectedRange)
return (
<GridMessage
Expand All @@ -133,6 +151,7 @@ export default function ScheduleGrid({
<ScheduleHeader
preview={mode === "preview"}
visibleDays={visibleDays}
compact={useCompactHeader}
currentPage={currentPage}
totalPages={totalPages}
scrollbarPresent={scrollbarPresent}
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/features/event/grid/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -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";
Loading
Loading