From 0b59f86977d5586ddfa24ea6a81d1b7c6ca1f098 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 12:02:52 -0400 Subject: [PATCH 01/46] Add share button --- .../app/(event)/[event-code]/page-client.tsx | 33 ++++++++++++++++++- 1 file changed, 32 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 431eaae12..b2314e39b 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -2,11 +2,12 @@ import { useState } from "react"; -import { PencilIcon, SquarePenIcon } from "lucide-react"; +import { PencilIcon, ShareIcon, SquarePenIcon } from "lucide-react"; import CopyToastButton from "@/components/copy-toast-button"; import KebabMenu from "@/components/kebab-menu"; import { EventInformation } from "@/core/event/types"; +import ActionButton from "@/features/button/components/action"; import LinkButton from "@/features/button/components/link"; import ScheduleGrid from "@/features/event/grid/grid"; import AttendeesPanel from "@/features/event/results/attendee-panel/panel"; @@ -20,6 +21,7 @@ import ResultsDrawer from "@/features/event/results/drawer"; import { ResultsInformation } from "@/features/event/results/lib/types"; import HeaderSpacer from "@/features/header/components/header-spacer"; import { useHeaderSize } from "@/features/header/context"; +import { useToast } from "@/features/system-feedback"; import { cn } from "@/lib/utils/classname"; export default function ClientPage({ @@ -57,6 +59,9 @@ function EventResults({ eventData }: { eventData: EventInformation }) { timeslots, } = eventData; + /* TOAST PROVIDER */ + const { addToast } = useToast(); + /* TIMEZONE HANDLING */ const handleTZChange = (newTZ: string | number) => { setTimezone(newTZ.toString()); @@ -106,6 +111,30 @@ function EventResults({ eventData }: { eventData: EventInformation }) { /> ); + const shareButton = (buttonStyle: "frosted glass inset" | "secondary") => ( + } + label="Share Event" + onClick={async () => { + try { + await navigator.share({ + title: eventTitle, + url: window.location.href, + }); + } catch (error) { + // An error is thrown if sharing is cancelled, ignore that + if (error instanceof Error && error.name !== "AbortError") { + addToast( + "error", + "Sharing is not supported on this browser, sorry!", + ); + } + } + }} + /> + ); + const copyButton = (buttonStyle: "frosted glass inset" | "secondary") => ( ); @@ -121,12 +150,14 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
{isCreator && editButton("frosted glass inset")} + {shareButton("frosted glass inset")} {copyButton("frosted glass inset")}
{isCreator && editButton("secondary")} + {shareButton("secondary")} {copyButton("secondary")} {paintingButton}
From 88335f149ca07760f0cc6b90748e5521741282cc Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 12:30:08 -0400 Subject: [PATCH 02/46] Add check for web share support --- .../app/(event)/[event-code]/page-client.tsx | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index b2314e39b..7f179a920 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -22,6 +22,7 @@ import { ResultsInformation } from "@/features/event/results/lib/types"; import HeaderSpacer from "@/features/header/components/header-spacer"; import { useHeaderSize } from "@/features/header/context"; import { useToast } from "@/features/system-feedback"; +import { MESSAGES } from "@/lib/messages"; import { cn } from "@/lib/utils/classname"; export default function ClientPage({ @@ -111,29 +112,33 @@ function EventResults({ eventData }: { eventData: EventInformation }) { /> ); - const shareButton = (buttonStyle: "frosted glass inset" | "secondary") => ( - } - label="Share Event" - onClick={async () => { - try { - await navigator.share({ - title: eventTitle, - url: window.location.href, - }); - } catch (error) { - // An error is thrown if sharing is cancelled, ignore that - if (error instanceof Error && error.name !== "AbortError") { - addToast( - "error", - "Sharing is not supported on this browser, sorry!", - ); - } - } - }} - /> - ); + const shareButton = (buttonStyle: "frosted glass inset" | "secondary") => { + if (typeof navigator !== "undefined" && !navigator.share) { + // Don't show the button if sharing isn't supported + return null; + } else { + return ( + } + label="Share Event" + onClick={async () => { + try { + await navigator.share({ + title: eventTitle, + url: window.location.href, + }); + } catch (error) { + // An error is thrown if sharing is cancelled, ignore that + if (error instanceof Error && error.name !== "AbortError") { + addToast("error", MESSAGES.ERROR_GENERIC); + } + } + }} + /> + ); + } + }; const copyButton = (buttonStyle: "frosted glass inset" | "secondary") => ( From 33f64fc4f04f27b2ae9edd7f010a33839303d105 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 12:47:58 -0400 Subject: [PATCH 03/46] Update share button check comment --- frontend/src/app/(event)/[event-code]/page-client.tsx | 10 +++++++++- 1 file changed, 9 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 7f179a920..3630960ac 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -113,8 +113,16 @@ function EventResults({ eventData }: { eventData: EventInformation }) { ); const shareButton = (buttonStyle: "frosted glass inset" | "secondary") => { + // Check if sharing is supported if (typeof navigator !== "undefined" && !navigator.share) { - // Don't show the button if sharing isn't supported + /* This condition means it will be rendered until mounted on the client, then it + * disappears if not supported. There are more browsers that support the API than + * don't, so this is a better trade-off than having the button appear after initial + * mount on supported browsers. + * + * This also won't be visible on mobile anyway, since the buttons are hidden in the + * kebab menu. + */ return null; } else { return ( From 60d5b26c3809336354bb3b718354d5492b4d07f3 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 12:49:01 -0400 Subject: [PATCH 04/46] Centralize button style type --- frontend/src/app/(event)/[event-code]/page-client.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index 3630960ac..04eecdb2f 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -39,6 +39,8 @@ export default function ClientPage({ ); } +type HeaderButtonStyle = "frosted glass inset" | "secondary"; + function EventResults({ eventData }: { eventData: EventInformation }) { const { hoveredSlot, @@ -103,7 +105,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { /> ); - const editButton = (buttonStyle: "frosted glass inset" | "secondary") => ( + const editButton = (buttonStyle: HeaderButtonStyle) => ( } @@ -112,7 +114,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { /> ); - const shareButton = (buttonStyle: "frosted glass inset" | "secondary") => { + const shareButton = (buttonStyle: HeaderButtonStyle) => { // Check if sharing is supported if (typeof navigator !== "undefined" && !navigator.share) { /* This condition means it will be rendered until mounted on the client, then it @@ -148,7 +150,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { } }; - const copyButton = (buttonStyle: "frosted glass inset" | "secondary") => ( + const copyButton = (buttonStyle: HeaderButtonStyle) => ( ); From 654d0c5d0ed3d1025a1d441b2c19c2c9203e3eef Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 14:56:35 -0400 Subject: [PATCH 05/46] Change hovered slot scaling to a fixed amount --- frontend/src/features/event/grid/time-slot.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index c2aa0c142..6ca047316 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -45,7 +45,7 @@ function TimeSlot({ cellClasses, "select-none", isHovered && - "z-5 scale-y-130 scale-x-110 rounded-full border-none shadow-xl ring-1 md:scale-x-105", + "z-5 -inset-1 h-[calc(100%+0.5rem)] w-[calc(100%+0.5rem)] rounded-full border-none shadow-xl ring-1", )} style={{ gridColumn, From 81b0c8f098c98cd2944abef7e4a6075db4572b15 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 14:56:46 -0400 Subject: [PATCH 06/46] Increase hovered slot border thickness --- frontend/src/features/event/grid/time-slot.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index 6ca047316..1b4b24922 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -45,7 +45,7 @@ function TimeSlot({ cellClasses, "select-none", isHovered && - "z-5 -inset-1 h-[calc(100%+0.5rem)] w-[calc(100%+0.5rem)] rounded-full border-none shadow-xl ring-1", + "z-5 -inset-1 h-[calc(100%+0.5rem)] w-[calc(100%+0.5rem)] rounded-full border-none shadow-xl ring-2", )} style={{ gridColumn, From cb37ab183d8c1b7f3632d409783967de99566065 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 14:58:13 -0400 Subject: [PATCH 07/46] Slightly decrease hovered slot height --- frontend/src/features/event/grid/time-slot.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index 1b4b24922..a8dfa8e4a 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -45,7 +45,7 @@ function TimeSlot({ cellClasses, "select-none", isHovered && - "z-5 -inset-1 h-[calc(100%+0.5rem)] w-[calc(100%+0.5rem)] rounded-full border-none shadow-xl ring-2", + "z-5 -inset-x-1 -inset-y-0.5 h-[calc(100%+0.25rem)] w-[calc(100%+0.5rem)] rounded-full border-none shadow-xl ring-2", )} style={{ gridColumn, From a13d47eddbef87e2d5f15c2b3b8db6c276a38bf1 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 15:53:07 -0400 Subject: [PATCH 08/46] Add icon prop to time slot component --- .../src/features/event/grid/time-slot.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index a8dfa8e4a..a492698be 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -15,6 +15,8 @@ interface TimeSlotProps { gridColumn: number; gridRow: number; + icon?: React.ReactNode; + cellClasses?: string; // Event handlers @@ -31,9 +33,22 @@ function TimeSlot({ dynamicStyle: style, gridColumn, gridRow, + icon, cellClasses = "", ...eventHandlers }: TimeSlotProps) { + if (icon) { + icon = React.cloneElement( + icon as React.ReactElement<{ className: string }>, + { + className: cn( + (icon as React.ReactElement<{ className: string }>).props.className, + "h-4 w-4", + ), + }, + ); + } + return (
+ > + {icon && ( +
{icon}
+ )} +
); } From 014d319ce7eb2b54b02a7b645a634ea3f38008da Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 15:56:16 -0400 Subject: [PATCH 09/46] Add sparkles icon for best times --- .../src/features/event/grid/timeblocks/results.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frontend/src/features/event/grid/timeblocks/results.tsx b/frontend/src/features/event/grid/timeblocks/results.tsx index cc9c7b0e0..4753bda08 100644 --- a/frontend/src/features/event/grid/timeblocks/results.tsx +++ b/frontend/src/features/event/grid/timeblocks/results.tsx @@ -1,3 +1,5 @@ +import { SparklesIcon } from "lucide-react"; + import TimeSlot from "@/features/event/grid/time-slot"; import BaseTimeBlock from "@/features/event/grid/timeblocks/base"; import { ResultsTimeBlockProps } from "@/features/event/grid/timeblocks/props"; @@ -41,6 +43,14 @@ export default function ResultsTimeBlock({ "bg-[color-mix(in_srgb,var(--color-accent)_var(--opacity-percent),var(--color-background))]", ); + // icon + const iconColorClass = + opacityPercent > 50 ? "text-white" : "text-foreground"; + const icon = + matchCount === numParticipants && numParticipants > 1 ? ( + + ) : undefined; + return ( { onHoverSlot?.(iso); }} From 8da41b3453afe53889740262ad8fd52cbf650612 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 16:19:16 -0400 Subject: [PATCH 10/46] Create getHighestMatchCount util function --- frontend/src/features/event/results/lib/utils.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frontend/src/features/event/results/lib/utils.ts b/frontend/src/features/event/results/lib/utils.ts index ad33d9d4b..1b915e1c1 100644 --- a/frontend/src/features/event/results/lib/utils.ts +++ b/frontend/src/features/event/results/lib/utils.ts @@ -51,3 +51,19 @@ export function findConsensusAndConflicts( return { allAvailableSlots, noOneAvailableSlots }; } + +/** + * Finds the highest number of participants available for any single timeslot. + * + * @returns The maximum count of participants available in any timeslot. + */ +export function getHighestMatchCount(availabilities: ResultsAvailabilityMap): number { + let highestCount = 0; + for (const slot in availabilities) { + const count = availabilities[slot].length; + if (count > highestCount) { + highestCount = count; + } + } + return highestCount; +} From 3154ec313db94789da70aa44747eb9b3bf8135b0 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 11 Apr 2026 16:24:43 -0400 Subject: [PATCH 11/46] Add thumbs up icon for best but not ideal times --- frontend/src/features/event/grid/grid.tsx | 2 ++ frontend/src/features/event/grid/timeblocks/props.ts | 1 + .../src/features/event/grid/timeblocks/results.tsx | 11 ++++++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 5971cad7f..d5772c78a 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -14,6 +14,7 @@ import TimeColumn from "@/features/event/grid/time-column"; import InteractiveTimeBlock from "@/features/event/grid/timeblocks/interactive"; import PreviewTimeBlock from "@/features/event/grid/timeblocks/preview"; import ResultsTimeBlock from "@/features/event/grid/timeblocks/results"; +import { getHighestMatchCount } from "@/features/event/results/lib/utils"; import useCheckMobile from "@/lib/hooks/use-check-mobile"; import { cn } from "@/lib/utils/classname"; @@ -171,6 +172,7 @@ export default function ScheduleGrid({ hoveredSlot={hoveredSlot} availabilities={availabilities} numParticipants={numParticipants} + highestMatchCount={getHighestMatchCount(availabilities)} onHoverSlot={setHoveredSlot} /> ); diff --git a/frontend/src/features/event/grid/timeblocks/props.ts b/frontend/src/features/event/grid/timeblocks/props.ts index d0d10c176..b0f63fb7a 100644 --- a/frontend/src/features/event/grid/timeblocks/props.ts +++ b/frontend/src/features/event/grid/timeblocks/props.ts @@ -37,5 +37,6 @@ export type ResultsTimeBlockProps = CommonBlockProps & { hoveredSlot: string | null | undefined; availabilities: ResultsAvailabilityMap; numParticipants: number; + highestMatchCount: number; onHoverSlot?: (iso: string | null) => void; }; diff --git a/frontend/src/features/event/grid/timeblocks/results.tsx b/frontend/src/features/event/grid/timeblocks/results.tsx index 4753bda08..f0f54ffb2 100644 --- a/frontend/src/features/event/grid/timeblocks/results.tsx +++ b/frontend/src/features/event/grid/timeblocks/results.tsx @@ -1,4 +1,4 @@ -import { SparklesIcon } from "lucide-react"; +import { SparklesIcon, ThumbsUpIcon } from "lucide-react"; import TimeSlot from "@/features/event/grid/time-slot"; import BaseTimeBlock from "@/features/event/grid/timeblocks/base"; @@ -10,6 +10,7 @@ export default function ResultsTimeBlock({ numVisibleDays, availabilities, numParticipants, + highestMatchCount, hoveredSlot, onHoverSlot, hasNext = false, @@ -47,8 +48,12 @@ export default function ResultsTimeBlock({ const iconColorClass = opacityPercent > 50 ? "text-white" : "text-foreground"; const icon = - matchCount === numParticipants && numParticipants > 1 ? ( - + highestMatchCount > 1 && matchCount === highestMatchCount ? ( + highestMatchCount === numParticipants ? ( + + ) : ( + + ) ) : undefined; return ( From 288722cb4f03f6428d2562c6f96e45127a7195c5 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 12 Apr 2026 17:08:13 -0400 Subject: [PATCH 12/46] Change grid to scrollable on all desktop views --- frontend/src/app/(event)/[event-code]/page-client.tsx | 4 ++-- .../src/app/(event)/[event-code]/painting/page-client.tsx | 4 ++-- frontend/src/features/event/grid/grid.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index 431eaae12..3776196a2 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -111,7 +111,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { ); return ( -
+
{/* Header */} @@ -134,7 +134,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
{banners}
-
+
+
{/* Rate Limit Error */} @@ -264,7 +264,7 @@ export default function ClientPage({
{/* Main Content */} -
+
{/* Left Panel */}
From 176ce4292e60273da145327f54321362eb897701 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 12 Apr 2026 17:15:17 -0400 Subject: [PATCH 13/46] Add extra bottom padding for painting and results --- frontend/src/features/event/grid/grid.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 26ae5fccc..530b8f9d0 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -116,8 +116,9 @@ export default function ScheduleGrid({
From 56c69cc8e2d319096bdc1a2506d1fbaf0619c343 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 12 Apr 2026 17:18:39 -0400 Subject: [PATCH 14/46] Remove sticky classes from results and painting --- frontend/src/app/(event)/[event-code]/page-client.tsx | 11 +---------- .../app/(event)/[event-code]/painting/page-client.tsx | 7 +------ 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/(event)/[event-code]/page-client.tsx b/frontend/src/app/(event)/[event-code]/page-client.tsx index 3776196a2..2ae9e05af 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -19,7 +19,6 @@ import DisplaySettings from "@/features/event/results/display-settings"; import ResultsDrawer from "@/features/event/results/drawer"; import { ResultsInformation } from "@/features/event/results/lib/types"; import HeaderSpacer from "@/features/header/components/header-spacer"; -import { useHeaderSize } from "@/features/header/context"; import { cn } from "@/lib/utils/classname"; export default function ClientPage({ @@ -75,9 +74,6 @@ function EventResults({ eventData }: { eventData: EventInformation }) { return drawerSnap; }; - /* HEADER SPACING */ - const { topMarginClass } = useHeaderSize(); - /* BANNERS */ const banners = getResultBanners( availabilities, @@ -169,12 +165,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { )} > {banners} -
+
{/* Left Panel */} -
+

Date: Sun, 12 Apr 2026 17:19:38 -0400 Subject: [PATCH 15/46] Remove unused variables and imports --- .../src/app/(event)/[event-code]/painting/page-client.tsx | 5 ----- 1 file changed, 5 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 38125dff7..62583db21 100644 --- a/frontend/src/app/(event)/[event-code]/painting/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/painting/page-client.tsx @@ -17,7 +17,6 @@ import { validateAvailabilityData } from "@/features/event/availability/validate import TimeZoneSelector from "@/features/event/components/selectors/timezone"; import { ScheduleGrid } from "@/features/event/grid"; import HeaderSpacer from "@/features/header/components/header-spacer"; -import { useHeaderSize } from "@/features/header/context"; import { ConfirmationDialog, RateLimitBanner, @@ -28,7 +27,6 @@ import { clientPost } from "@/lib/utils/api/client-fetch"; import { ROUTES } from "@/lib/utils/api/endpoints"; import { ApiErrorResponse } from "@/lib/utils/api/fetch-wrapper"; import { SelfAvailability } from "@/lib/utils/api/types"; -import { cn } from "@/lib/utils/classname"; import { timeslotToISOString } from "@/lib/utils/date-time-format"; export default function ClientPage({ @@ -46,9 +44,6 @@ export default function ClientPage({ }) { const router = useRouter(); - // HEADER SIZE CONTEXT - const { topMarginClass } = useHeaderSize(); - // AVAILABILITY STATE const { state, setDisplayName, setTimeZone, toggleSlot } = useAvailability( initialData, From b0827ffc3acdb076d9413e6d56c848412f2e27d1 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 12 Apr 2026 19:10:27 -0400 Subject: [PATCH 16/46] Change participant chips to lion color --- .../results/attendee-panel/participant-chip.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/event/results/attendee-panel/participant-chip.tsx b/frontend/src/features/event/results/attendee-panel/participant-chip.tsx index 4ccf4be3e..04ca6137e 100644 --- a/frontend/src/features/event/results/attendee-panel/participant-chip.tsx +++ b/frontend/src/features/event/results/attendee-panel/participant-chip.tsx @@ -52,15 +52,19 @@ export default function ParticipantChip({ // Availability Styling !isAvailable && "bg-gray-200/25 line-through opacity-50", - isAvailable && "bg-accent/25 text-accent-text opacity-100", + isAvailable && "bg-lion text-violet opacity-100", + + !isAvailable && isSelected && "text-violet", // Selection Styling isSelected && - "bg-accent ring-accent ring-offset-background text-white ring-2 ring-offset-1", - areSelected && !isSelected && "bg-gray-200/25", + "bg-lion ring-lion ring-offset-background ring-2 ring-offset-1", + areSelected && !isSelected && "text-foreground bg-gray-200/25", // Hover Styling - !isRemoving && !isSelected && "hover:bg-accent hover:text-white", + !isRemoving && + !isSelected && + "hover:ring-lion/50 hover:ring-offset-background hover:ring-2 hover:ring-offset-1", // Wiggle/Remove Styling isRemoving && From 9136841647cdf2e9b9353e0f599b514da71616cc Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Wed, 15 Apr 2026 18:09:04 -0400 Subject: [PATCH 17/46] Remove transition from morphing drawer --- frontend/src/features/drawer/components/base.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/drawer/components/base.tsx b/frontend/src/features/drawer/components/base.tsx index 7f1d0650f..006f987ea 100644 --- a/frontend/src/features/drawer/components/base.tsx +++ b/frontend/src/features/drawer/components/base.tsx @@ -146,7 +146,7 @@ export default function BaseDrawer({ style={{ zIndex: contentZIndex }} >

Date: Wed, 15 Apr 2026 19:23:44 -0400 Subject: [PATCH 18/46] Slightly decrease floating drawer side padding --- frontend/src/features/drawer/components/base.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/drawer/components/base.tsx b/frontend/src/features/drawer/components/base.tsx index 006f987ea..2cbaecab0 100644 --- a/frontend/src/features/drawer/components/base.tsx +++ b/frontend/src/features/drawer/components/base.tsx @@ -172,7 +172,7 @@ export default function BaseDrawer({ className={cn( "mx-auto flex w-full flex-col transition-[max-width,border-radius,margin,padding] duration-300", isPill - ? "border-foreground/10 rounded-4xl mb-4 max-h-[calc(100svh-2rem)] max-w-[calc(100%-2rem)] overflow-hidden border" + ? "border-foreground/10 rounded-4xl mb-4 max-h-[calc(100svh-2rem)] max-w-[calc(100%-1rem)] overflow-hidden border" : "border-foreground/10 max-w-full overflow-hidden rounded-t-[32px] border", frostedGlass ? "frosted-glass" : "bg-panel", (_type === "floating" || !isPill) && From 0f7bd999b0b6b69f05ae5903c3f637581e0c68fd Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 14:08:54 -0400 Subject: [PATCH 19/46] Fix drawer content scaling pop-in --- .../src/features/drawer/components/base.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/drawer/components/base.tsx b/frontend/src/features/drawer/components/base.tsx index 2cbaecab0..b025acddf 100644 --- a/frontend/src/features/drawer/components/base.tsx +++ b/frontend/src/features/drawer/components/base.tsx @@ -252,16 +252,26 @@ export default function BaseDrawer({
- {(!isPill || _type !== "morphing") && children} + {/* Wrap in an extra div to manage the opacity transition separately */} +
+ {(!isPill || _type !== "morphing") && children} +
{footerContent && ( From fb50c488b5239b1168b70968304f37d8cbf9d7f9 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 14:19:56 -0400 Subject: [PATCH 20/46] Fix padding transition definition --- frontend/src/features/drawer/components/base.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/drawer/components/base.tsx b/frontend/src/features/drawer/components/base.tsx index b025acddf..391cf03cc 100644 --- a/frontend/src/features/drawer/components/base.tsx +++ b/frontend/src/features/drawer/components/base.tsx @@ -252,7 +252,7 @@ export default function BaseDrawer({
Date: Fri, 17 Apr 2026 14:46:33 -0400 Subject: [PATCH 21/46] Move time slot icon to right side --- frontend/src/features/event/grid/time-slot.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index a492698be..ddeca958b 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -16,6 +16,7 @@ interface TimeSlotProps { gridRow: number; icon?: React.ReactNode; + number?: number; cellClasses?: string; @@ -74,7 +75,7 @@ function TimeSlot({ {...eventHandlers} > {icon && ( -
{icon}
+
{icon}
)}
); From 7bcec58f87768a241a5a46885ee7e200086ba04c Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 14:47:22 -0400 Subject: [PATCH 22/46] Update time slot icons --- frontend/src/features/event/grid/timeblocks/results.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/event/grid/timeblocks/results.tsx b/frontend/src/features/event/grid/timeblocks/results.tsx index f0f54ffb2..a4f3eb4ab 100644 --- a/frontend/src/features/event/grid/timeblocks/results.tsx +++ b/frontend/src/features/event/grid/timeblocks/results.tsx @@ -1,4 +1,4 @@ -import { SparklesIcon, ThumbsUpIcon } from "lucide-react"; +import { CircleSmallIcon, ThumbsUpIcon } from "lucide-react"; import TimeSlot from "@/features/event/grid/time-slot"; import BaseTimeBlock from "@/features/event/grid/timeblocks/base"; @@ -50,9 +50,9 @@ export default function ResultsTimeBlock({ const icon = highestMatchCount > 1 && matchCount === highestMatchCount ? ( highestMatchCount === numParticipants ? ( - - ) : ( + ) : ( + ) ) : undefined; From 40b470edb5ddec6d2d1b6b0a3e9654b12129643a Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 17:18:42 -0400 Subject: [PATCH 23/46] Update banners for no mutual times --- frontend/src/features/event/results/banners.tsx | 14 ++++++++++++-- frontend/src/lib/messages.ts | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/event/results/banners.tsx b/frontend/src/features/event/results/banners.tsx index 34c8c4c53..ba561cef6 100644 --- a/frontend/src/features/event/results/banners.tsx +++ b/frontend/src/features/event/results/banners.tsx @@ -1,5 +1,8 @@ import { ResultsAvailabilityMap } from "@/core/availability/types"; -import { hasMutualAvailability } from "@/features/event/results/lib/utils"; +import { + getHighestMatchCount, + hasMutualAvailability, +} from "@/features/event/results/lib/utils"; import { Banner } from "@/features/system-feedback/banner/base"; import { MESSAGES } from "@/lib/messages"; @@ -30,9 +33,16 @@ export function getResultBanners( ); } else if (!hasMutualAvailability(availabilities, participants)) { + if (getHighestMatchCount(availabilities) <= 1) { + return ( + +

{MESSAGES.INFO_NO_MUTUAL_AVAILABILITY}

+
+ ); + } return ( -

{MESSAGES.INFO_NO_MUTUAL_AVAILABILITY}

+

{MESSAGES.INFO_NO_IDEAL_TIMES}

); } else if (participated && participants.length === 1) { diff --git a/frontend/src/lib/messages.ts b/frontend/src/lib/messages.ts index b13e6a154..e8fe92807 100644 --- a/frontend/src/lib/messages.ts +++ b/frontend/src/lib/messages.ts @@ -58,5 +58,6 @@ export const MESSAGES = { INFO_ADD_AVAILABILITY: "Add your availability by clicking the button above.", INFO_ADD_AVAILABILITY_MOBILE: "Add your availability by clicking the button below.", INFO_COPY_SHARE_LINK: "Copy and share the link so others can join!", - INFO_NO_MUTUAL_AVAILABILITY: "There is no time where everyone is available.", + INFO_NO_MUTUAL_AVAILABILITY: "There are no times with more than 1 person available. The plans are NOT making it out of the group chat...", + INFO_NO_IDEAL_TIMES: "There is no time where everyone is available. Times with an indicator are the best options.", }; From 9ea74643d3cad0d40643a6503989daf1e269bf4b Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 18:03:35 -0400 Subject: [PATCH 24/46] Add separate message for results page banner --- frontend/src/features/event/results/banners.tsx | 2 +- frontend/src/lib/messages.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/event/results/banners.tsx b/frontend/src/features/event/results/banners.tsx index ba561cef6..b3e5908aa 100644 --- a/frontend/src/features/event/results/banners.tsx +++ b/frontend/src/features/event/results/banners.tsx @@ -42,7 +42,7 @@ export function getResultBanners( } return ( -

{MESSAGES.INFO_NO_IDEAL_TIMES}

+

{MESSAGES.INFO_NO_IDEAL_TIMES_BANNER}

); } else if (participated && participants.length === 1) { diff --git a/frontend/src/lib/messages.ts b/frontend/src/lib/messages.ts index e8fe92807..4d08f4ff9 100644 --- a/frontend/src/lib/messages.ts +++ b/frontend/src/lib/messages.ts @@ -59,5 +59,6 @@ export const MESSAGES = { INFO_ADD_AVAILABILITY_MOBILE: "Add your availability by clicking the button below.", INFO_COPY_SHARE_LINK: "Copy and share the link so others can join!", INFO_NO_MUTUAL_AVAILABILITY: "There are no times with more than 1 person available. The plans are NOT making it out of the group chat...", - INFO_NO_IDEAL_TIMES: "There is no time where everyone is available. Times with an indicator are the best options.", + INFO_NO_IDEAL_TIMES: "There are no times where everyone is available.", + INFO_NO_IDEAL_TIMES_BANNER: "There are no times where everyone is available. Times with an indicator are the best options.", }; From bce1483df4cbaf9fbd0e49f7ac62732ab98e6034 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 18:04:39 -0400 Subject: [PATCH 25/46] Remove unused time slot property --- frontend/src/features/event/grid/time-slot.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index ddeca958b..af53f7bad 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -16,7 +16,6 @@ interface TimeSlotProps { gridRow: number; icon?: React.ReactNode; - number?: number; cellClasses?: string; From d6ca305ffa807fc737a1aee80465adc6dd1e9bad Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Fri, 17 Apr 2026 18:05:44 -0400 Subject: [PATCH 26/46] Tighten icon prop type --- frontend/src/features/event/grid/time-slot.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/event/grid/time-slot.tsx b/frontend/src/features/event/grid/time-slot.tsx index af53f7bad..2c0d60b7d 100644 --- a/frontend/src/features/event/grid/time-slot.tsx +++ b/frontend/src/features/event/grid/time-slot.tsx @@ -15,7 +15,7 @@ interface TimeSlotProps { gridColumn: number; gridRow: number; - icon?: React.ReactNode; + icon?: React.ReactElement; cellClasses?: string; From 8dc019ef3c1962ee73bb535c3fcfd657b778ebae Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 14:08:52 -0400 Subject: [PATCH 27/46] Remove unnecessary side panel classes --- frontend/src/app/(event)/[event-code]/page-client.tsx | 2 +- 1 file changed, 1 insertion(+), 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 2ae9e05af..84385f943 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -160,7 +160,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) {
From 49c2754c6d913da9dc768e1f163ce7b7a1e97a72 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 14:08:58 -0400 Subject: [PATCH 28/46] Adjust side panel max height --- frontend/src/app/(event)/[event-code]/page-client.tsx | 2 +- 1 file changed, 1 insertion(+), 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 84385f943..8afa9fc07 100644 --- a/frontend/src/app/(event)/[event-code]/page-client.tsx +++ b/frontend/src/app/(event)/[event-code]/page-client.tsx @@ -165,7 +165,7 @@ function EventResults({ eventData }: { eventData: EventInformation }) { )} > {banners} -
+
Date: Sun, 3 May 2026 14:40:34 -0400 Subject: [PATCH 29/46] Add dynamic schedule header for scrollbar spacing --- frontend/src/features/event/grid/grid.tsx | 21 ++++++++++++++++++- .../features/event/grid/schedule-header.tsx | 5 ++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/event/grid/grid.tsx b/frontend/src/features/event/grid/grid.tsx index 530b8f9d0..6f86abfe1 100644 --- a/frontend/src/features/event/grid/grid.tsx +++ b/frontend/src/features/event/grid/grid.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import { TriangleAlertIcon } from "lucide-react"; @@ -94,6 +94,23 @@ export default function ScheduleGrid({ const hasPrevPage = currentPage > 0; const hasNextPage = currentPage < totalPages - 1; + // Check if the scrollbar is present to pass to the header + const [scrollbarPresent, setScrollbarPresent] = useState(false); + const scrollRef = useRef(null); + useEffect(() => { + const el = scrollRef.current; + if (!el) return; + + const checkScrollbar = () => + // Include mobile check because it will report true even if the scrollbar is hidden + setScrollbarPresent(el.scrollHeight > el.clientHeight && !isMobile); + const resizeObserver = new ResizeObserver(checkScrollbar); + resizeObserver.observe(el); + checkScrollbar(); + + return () => resizeObserver.disconnect(); + }); + if (error) return ; return ( @@ -108,6 +125,7 @@ export default function ScheduleGrid({ visibleDays={visibleDays} currentPage={currentPage} totalPages={totalPages} + scrollbarPresent={scrollbarPresent} isWeekdayEvent={isWeekdayEvent} onPrevPage={() => paginate(-1)} onNextPage={() => paginate(1)} @@ -115,6 +133,7 @@ export default function ScheduleGrid({ />
void; onNextPage: () => void; @@ -42,6 +43,7 @@ export default function ScheduleHeader({ visibleDays, currentPage, totalPages, + scrollbarPresent = false, isWeekdayEvent = false, onPrevPage, onNextPage, @@ -52,7 +54,8 @@ export default function ScheduleHeader({ return (
Date: Sun, 3 May 2026 15:01:11 -0400 Subject: [PATCH 30/46] Create kebab menu for theme picker --- .../header/components/theme-toggle.tsx | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-toggle.tsx index 113bda349..edacd3406 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-toggle.tsx @@ -1,28 +1,40 @@ "use client"; -import { MoonIcon, SunIcon } from "lucide-react"; +import { MonitorIcon, MoonIcon, SunIcon, SunMoonIcon } from "lucide-react"; import { useTheme } from "next-themes"; -import ActionButton from "@/features/button/components/action"; +import KebabMenu from "@/components/kebab-menu"; +import SegmentedControl from "@/components/segmented-control"; +import EmptyButton from "@/features/button/components/empty"; import ShrinkingHeaderButton from "@/features/header/components/shrinking-header-button"; export default function ThemeToggle() { - const { setTheme, resolvedTheme } = useTheme(); - - const toggleTheme = () => { - setTheme(resolvedTheme === "dark" ? "light" : "dark"); - }; + const { theme = "system", setTheme } = useTheme(); return ( : } + icon={} > - : } - onClick={toggleTheme} - /> + } + /> + } + > + }, + { value: "light", label: }, + { value: "dark", label: }, + ]} + value={theme} + onChange={setTheme} + className="frosted-glass-inset" + /> + ); } From 5d19a812b4a9125834399f570446a66bac4ac26b Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 15:32:49 -0400 Subject: [PATCH 31/46] Add header activeMenu functionality --- frontend/src/features/header/components/theme-toggle.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-toggle.tsx index edacd3406..0d52fd61a 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-toggle.tsx @@ -7,16 +7,23 @@ import KebabMenu from "@/components/kebab-menu"; import SegmentedControl from "@/components/segmented-control"; import EmptyButton from "@/features/button/components/empty"; import ShrinkingHeaderButton from "@/features/header/components/shrinking-header-button"; +import { useHeaderSize } from "@/features/header/context"; export default function ThemeToggle() { + const { activeMenu, setActiveMenu } = useHeaderSize(); const { theme = "system", setTheme } = useTheme(); + const isMenuOpen = activeMenu === "theme"; + return ( } > setActiveMenu(isOpen ? "theme" : null)} trigger={ Date: Sun, 3 May 2026 19:18:08 -0400 Subject: [PATCH 32/46] Add closeOnClick option to KebabMenu --- frontend/src/components/kebab-menu.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/kebab-menu.tsx b/frontend/src/components/kebab-menu.tsx index 1052e56df..a96b1f01b 100644 --- a/frontend/src/components/kebab-menu.tsx +++ b/frontend/src/components/kebab-menu.tsx @@ -21,12 +21,14 @@ export default function KebabMenu({ open: controlledOpen, onOpenChange, nested = false, + closeOnClick = true, }: { children: React.ReactNode; trigger?: React.ReactNode; open?: boolean; onOpenChange?: (open: boolean) => void; nested?: boolean; + closeOnClick?: boolean; }) { const id = useId(); const containerRef = useRef(null); @@ -112,10 +114,11 @@ export default function KebabMenu({ onClick={(e) => { const target = e.target as HTMLElement; if ( - target.closest("button") || - target.closest("a") || - target.closest('[role="button"]') || - target.closest('[role="menuitem"]') + (target.closest("button") || + target.closest("a") || + target.closest('[role="button"]') || + target.closest('[role="menuitem"]')) && + closeOnClick ) { handleOpenChange(false); } From e406168127d6aff6651751e1f4d29c870dcc24a8 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 19:18:18 -0400 Subject: [PATCH 33/46] Set closeOnClick on theme picker --- frontend/src/features/header/components/theme-toggle.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-toggle.tsx index 0d52fd61a..540c802a0 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-toggle.tsx @@ -30,6 +30,7 @@ export default function ThemeToggle() { icon={} /> } + closeOnClick={false} > Date: Sun, 3 May 2026 20:07:38 -0400 Subject: [PATCH 34/46] Add anchor point options to kebab menu --- frontend/src/components/kebab-menu.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/kebab-menu.tsx b/frontend/src/components/kebab-menu.tsx index a96b1f01b..9f9e6ebc0 100644 --- a/frontend/src/components/kebab-menu.tsx +++ b/frontend/src/components/kebab-menu.tsx @@ -15,11 +15,14 @@ const morphTransition: Transition = { mass: 0.8, }; +export type AnchorPoint = "top-left" | "top-center" | "top-right"; + export default function KebabMenu({ children, trigger, open: controlledOpen, onOpenChange, + anchorPoint = "top-right", nested = false, closeOnClick = true, }: { @@ -27,6 +30,7 @@ export default function KebabMenu({ trigger?: React.ReactNode; open?: boolean; onOpenChange?: (open: boolean) => void; + anchorPoint?: AnchorPoint; nested?: boolean; closeOnClick?: boolean; }) { @@ -85,7 +89,12 @@ export default function KebabMenu({ key="trigger" layoutId={"popover-morph-" + id} transition={morphTransition} - className="absolute right-0 top-0 z-10 inline-block cursor-pointer rounded-full" + className={cn( + "absolute top-0 z-10 inline-block cursor-pointer rounded-full", + anchorPoint === "top-right" && "right-0", + anchorPoint === "top-left" && "left-0", + anchorPoint === "top-center" && "left-1/2 -translate-x-1/2", + )} aria-haspopup="menu" aria-expanded={open} onClick={() => handleOpenChange(true)} @@ -106,8 +115,12 @@ export default function KebabMenu({ layoutId={"popover-morph-" + id} transition={morphTransition} className={cn( - "absolute right-2 top-1 z-[100]", - "flex min-w-[200px] origin-top-right flex-col gap-2", + "absolute top-1 z-[100]", + anchorPoint === "top-right" && "right-2 origin-top-right", + anchorPoint === "top-left" && "left-2 origin-top-left", + anchorPoint === "top-center" && + "left-1/2 origin-top -translate-x-1/2", + "flex min-w-[200px] flex-col gap-2", "frosted-glass overflow-hidden rounded-3xl p-4 shadow-lg", nested && "scale-110", )} From aeddf24a6cd746166aaff87afb6c343af6896767 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 20:07:46 -0400 Subject: [PATCH 35/46] Add top center anchor point to theme picker --- frontend/src/features/header/components/theme-toggle.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-toggle.tsx index 540c802a0..04bc0e1bb 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-toggle.tsx @@ -24,6 +24,7 @@ export default function ThemeToggle() { nested open={isMenuOpen} onOpenChange={(isOpen) => setActiveMenu(isOpen ? "theme" : null)} + anchorPoint="top-center" trigger={ Date: Sun, 3 May 2026 20:21:24 -0400 Subject: [PATCH 36/46] Add more content to theme picker --- frontend/src/features/header/components/theme-toggle.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-toggle.tsx index 04bc0e1bb..50dca5e78 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-toggle.tsx @@ -33,6 +33,7 @@ export default function ThemeToggle() { } closeOnClick={false} > +
Theme
}, @@ -43,6 +44,13 @@ export default function ThemeToggle() { onChange={setTheme} className="frosted-glass-inset" /> +
+ {theme === "system" + ? "Match System" + : theme === "light" + ? "Light" + : "Dark"} +
); From 8272a3c0bdc6b80e9bb8e0e43442e72105965a7d Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 20:30:16 -0400 Subject: [PATCH 37/46] Add aria-label to theme picker button --- frontend/src/features/header/components/theme-toggle.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-toggle.tsx index 50dca5e78..0c641616c 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-toggle.tsx @@ -29,6 +29,7 @@ export default function ThemeToggle() { } + aria-label="Choose Site Theme" /> } closeOnClick={false} From ccf831d52726c31245e66046f0cfde3d979e9d6e Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 20:51:35 -0400 Subject: [PATCH 38/46] Rename ThemeToggle to ThemePicker --- frontend/src/features/header/components/header.tsx | 4 ++-- .../header/components/{theme-toggle.tsx => theme-picker.tsx} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename frontend/src/features/header/components/{theme-toggle.tsx => theme-picker.tsx} (97%) diff --git a/frontend/src/features/header/components/header.tsx b/frontend/src/features/header/components/header.tsx index 1aad1b1cc..cf6ee7161 100644 --- a/frontend/src/features/header/components/header.tsx +++ b/frontend/src/features/header/components/header.tsx @@ -8,7 +8,7 @@ import AccountButton from "@/features/header/components/account-button"; import DashboardButton from "@/features/header/components/dashboard-button"; import LogoArea from "@/features/header/components/logo-area"; import NewEventButton from "@/features/header/components/new-event-button"; -import ThemeToggle from "@/features/header/components/theme-toggle"; +import ThemePicker from "@/features/header/components/theme-picker"; import { useHeaderSize } from "@/features/header/context"; import useCheckMobile from "@/lib/hooks/use-check-mobile"; import { cn } from "@/lib/utils/classname"; @@ -107,7 +107,7 @@ export default function Header() { /> - + diff --git a/frontend/src/features/header/components/theme-toggle.tsx b/frontend/src/features/header/components/theme-picker.tsx similarity index 97% rename from frontend/src/features/header/components/theme-toggle.tsx rename to frontend/src/features/header/components/theme-picker.tsx index 0c641616c..45804a43a 100644 --- a/frontend/src/features/header/components/theme-toggle.tsx +++ b/frontend/src/features/header/components/theme-picker.tsx @@ -9,7 +9,7 @@ import EmptyButton from "@/features/button/components/empty"; import ShrinkingHeaderButton from "@/features/header/components/shrinking-header-button"; import { useHeaderSize } from "@/features/header/context"; -export default function ThemeToggle() { +export default function ThemePicker() { const { activeMenu, setActiveMenu } = useHeaderSize(); const { theme = "system", setTheme } = useTheme(); From 7434318505021ad79c5b588aa98ab7ff49d5850c Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 23:03:29 -0400 Subject: [PATCH 39/46] Add ariaLabel to segmented control options --- frontend/src/components/segmented-control.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/segmented-control.tsx b/frontend/src/components/segmented-control.tsx index fafa6301b..d77ff513c 100644 --- a/frontend/src/components/segmented-control.tsx +++ b/frontend/src/components/segmented-control.tsx @@ -5,7 +5,7 @@ import { useMemo } from "react"; import { cn } from "@/lib/utils/classname"; type SegmentedControlProps = { - options: { label: React.ReactNode; value: T }[]; + options: { label: React.ReactNode; value: T; ariaLabel?: string }[]; value: T; onChange: (value: T) => void; className?: string; @@ -53,6 +53,7 @@ export default function SegmentedControl({ key={option.value} type="button" onClick={() => onChange(option.value)} + aria-label={option.ariaLabel} className={cn( "z-10 flex w-full items-center justify-center rounded-full py-2 text-sm font-medium transition-colors duration-300 focus:outline-none", isSelected From e9d383dbe6901d6fdd6a94ea503e3b53336c4aa8 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sun, 3 May 2026 23:05:32 -0400 Subject: [PATCH 40/46] Add ariaLabels to theme picker --- .../src/features/header/components/theme-picker.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/header/components/theme-picker.tsx b/frontend/src/features/header/components/theme-picker.tsx index 45804a43a..8a82b8435 100644 --- a/frontend/src/features/header/components/theme-picker.tsx +++ b/frontend/src/features/header/components/theme-picker.tsx @@ -37,9 +37,13 @@ export default function ThemePicker() {
Theme
}, - { value: "light", label: }, - { value: "dark", label: }, + { + value: "system", + label: , + ariaLabel: "Match System Theme", + }, + { value: "light", label: , ariaLabel: "Light Theme" }, + { value: "dark", label: , ariaLabel: "Dark Theme" }, ]} value={theme} onChange={setTheme} From 8e0c9c7ffa451b29658a8a3cdc669b4ac11adafb Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 4 May 2026 13:16:02 -0400 Subject: [PATCH 41/46] Replace leave event icon with door open --- .../features/event/results/attendee-panel/panel-header.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/event/results/attendee-panel/panel-header.tsx b/frontend/src/features/event/results/attendee-panel/panel-header.tsx index ebf26fc35..a7027d805 100644 --- a/frontend/src/features/event/results/attendee-panel/panel-header.tsx +++ b/frontend/src/features/event/results/attendee-panel/panel-header.tsx @@ -1,4 +1,4 @@ -import { CheckIcon, EraserIcon, LogOutIcon, Undo2Icon } from "lucide-react"; +import { CheckIcon, DoorOpenIcon, EraserIcon, Undo2Icon } from "lucide-react"; import ActionButton from "@/features/button/components/action"; import { useResultsContext } from "@/features/event/results/context"; @@ -125,7 +125,7 @@ export default function PanelHeader({ {showSelfRemove && ( } + icon={} onClick={() => { promptRemove(currentUser); }} From 055ce772241aff8f53626a5a0c407dbcb16c6bf9 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 4 May 2026 13:17:48 -0400 Subject: [PATCH 42/46] Reword self removal --- .../features/event/results/attendee-panel/panel-header.tsx | 2 +- .../src/features/event/results/attendee-panel/panel.tsx | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/features/event/results/attendee-panel/panel-header.tsx b/frontend/src/features/event/results/attendee-panel/panel-header.tsx index a7027d805..1123fe19b 100644 --- a/frontend/src/features/event/results/attendee-panel/panel-header.tsx +++ b/frontend/src/features/event/results/attendee-panel/panel-header.tsx @@ -129,7 +129,7 @@ export default function PanelHeader({ onClick={() => { promptRemove(currentUser); }} - aria-label="Remove Self from Event" + aria-label="Leave Event" className="hover:bg-error/25 hover:text-error active:bg-error/40 shrink-0" /> )} diff --git a/frontend/src/features/event/results/attendee-panel/panel.tsx b/frontend/src/features/event/results/attendee-panel/panel.tsx index 592d46d20..66077242f 100644 --- a/frontend/src/features/event/results/attendee-panel/panel.tsx +++ b/frontend/src/features/event/results/attendee-panel/panel.tsx @@ -58,13 +58,11 @@ export default function AttendeesPanel() { type="delete" autoClose={true} title={ - personToRemove === currentUser - ? "Remove Yourself" - : "Remove Participant" + personToRemove === currentUser ? "Leave Event" : "Remove Participant" } description={ personToRemove == currentUser ? ( - "Are you sure you want to remove yourself from this event?" + "Are you sure you want to leave this event?" ) : ( Are you sure you want to remove{" "} From 2fafb5e89b56470abe51ef8e75ff80f584972c90 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 4 May 2026 13:24:37 -0400 Subject: [PATCH 43/46] Update mobile self removal text --- frontend/src/features/event/results/drawer.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/event/results/drawer.tsx b/frontend/src/features/event/results/drawer.tsx index 4adc81ce7..a9810a01e 100644 --- a/frontend/src/features/event/results/drawer.tsx +++ b/frontend/src/features/event/results/drawer.tsx @@ -131,13 +131,11 @@ export default function ResultsDrawer({ type="delete" autoClose={true} title={ - personToRemove === currentUser - ? "Remove Yourself" - : "Remove Participant" + personToRemove === currentUser ? "Leave Event" : "Remove Participant" } description={ personToRemove == currentUser ? ( - "Are you sure you want to remove yourself from this event?" + "Are you sure you want to leave this event?" ) : ( Are you sure you want to remove{" "} From f511fc31b0a5ad46e64892dc1be10cb59cb9c1ea Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Mon, 4 May 2026 13:24:56 -0400 Subject: [PATCH 44/46] Update equality check for consistency --- frontend/src/features/event/results/attendee-panel/panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/features/event/results/attendee-panel/panel.tsx b/frontend/src/features/event/results/attendee-panel/panel.tsx index 66077242f..5e3c6a9ca 100644 --- a/frontend/src/features/event/results/attendee-panel/panel.tsx +++ b/frontend/src/features/event/results/attendee-panel/panel.tsx @@ -61,7 +61,7 @@ export default function AttendeesPanel() { personToRemove === currentUser ? "Leave Event" : "Remove Participant" } description={ - personToRemove == currentUser ? ( + personToRemove === currentUser ? ( "Are you sure you want to leave this event?" ) : ( From 0cb2b159f988f5229adb36e959184ff00e8ed791 Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Tue, 5 May 2026 15:45:59 -0400 Subject: [PATCH 45/46] Add isort to requirements.txt --- backend/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/requirements.txt b/backend/requirements.txt index f9548a77b..1c686fa96 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -16,6 +16,7 @@ django-environ==0.12.0 django-ses==4.4.0 djangorestframework==3.16.0 gunicorn==23.0.0 +isort==8.0.1 jmespath==1.0.1 kombu==5.5.4 packaging==25.0 From 4d4d1dd16cbeef82f425b808c6665f846e34fdda Mon Sep 17 00:00:00 2001 From: jzgom067 Date: Sat, 9 May 2026 12:04:34 -0400 Subject: [PATCH 46/46] Add version data --- frontend/src/features/version-history/data.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/version-history/data.ts b/frontend/src/features/version-history/data.ts index adf4cf0b7..350c33307 100644 --- a/frontend/src/features/version-history/data.ts +++ b/frontend/src/features/version-history/data.ts @@ -35,7 +35,7 @@ export function getVersionHistoryData(): VersionHistoryData { "Added a version history page", "Updated error handling across the site", "Updated certain parts of the event editor", - 'Improved readability of transparent components', + "Improved readability of transparent components", "Temporarily disabled weekday events for fixes", ], bugFixes: ["Fixed the theme transition on the landing page"], @@ -131,7 +131,7 @@ export function getVersionHistoryData(): VersionHistoryData { "Updated the event results page to reduce clutter", "Updated the appearance of events on the dashboard", "Updated the event grid to list the ending hour on the left axis", - "Removed \"Intended Duration\" from events", + 'Removed "Intended Duration" from events', ], bugFixes: [ "Fixed an issue where an invalid custom event code would not show an error", @@ -159,8 +159,21 @@ export function getVersionHistoryData(): VersionHistoryData { "Fixed the attendee count display when there is only one participant", ], }, + { + version: "v0.4.3", + releaseDate: { year: 2026, month: 4, day: 9 }, + changes: [ + "Added icons to the results grid to indicate the best times", + "Added a share button to the results page", + 'Added a new theme picker, which includes a "Match System" option', + 'Updated the "Leave Event" button icon', + "Adjusted the scroll functionality of the event grid", + "Changed the color of event attendees on the results page", + "Fixed the drawer transition on the mobile results page", + ], + }, ], - } + }, ]; }