Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0eca81e
Add server cookie forwarding
jzgom067 Oct 15, 2025
07e813e
Add no attendees message to results
jzgom067 Oct 15, 2025
c5d6149
Change cookies to optional in fetchEventDetails
jzgom067 Oct 15, 2025
2708797
Move availability page layout to components
jzgom067 Oct 15, 2025
31116e1
Create EventEditor component
jzgom067 Oct 15, 2025
52e05fb
Simplify auth cookie util function
jzgom067 Oct 16, 2025
abca3c2
Add data pre-population to edit event page
jzgom067 Oct 16, 2025
8d57cb0
Add functionality to edit event button
jzgom067 Oct 16, 2025
34168cd
Fix bad navigation on edit success
jzgom067 Oct 16, 2025
ac4594d
Simplify initial data code
jzgom067 Oct 16, 2025
0ccd9e8
Fix edit event date inconsistency
jzgom067 Oct 16, 2025
a9e0fce
Fix cursor on results page timeslots
jzgom067 Oct 16, 2025
947c31e
Add early start date to calendar picker
jzgom067 Oct 16, 2025
b7e5766
Add event date length checking to selector
jzgom067 Oct 17, 2025
6eed579
Remove custom code functionality for event edit
jzgom067 Oct 17, 2025
08adf91
Add availability prepopulation
jzgom067 Oct 17, 2025
f96b498
Fix hydration mismatch error
jzgom067 Oct 17, 2025
a0de72b
Remove console.log statements
jzgom067 Oct 17, 2025
b2b0822
Adjust button text on painting page
jzgom067 Oct 17, 2025
3fd5cd3
Change edit event button to Link
jzgom067 Oct 17, 2025
c3791f2
Add edit/add availability button to results page
jzgom067 Oct 17, 2025
f2e037c
Remove duration display when not present
jzgom067 Oct 17, 2025
f6a7bc8
Change duration to optional in event editor
jzgom067 Oct 17, 2025
d382399
Disable event type switching on edit
jzgom067 Oct 17, 2025
f2a5569
Rename disabled property to editing
jzgom067 Oct 17, 2025
7dc502d
Disable event type switching on mobile edit
jzgom067 Oct 17, 2025
f720c3b
Disable event code changes on mobile edit
jzgom067 Oct 17, 2025
3033779
Add event date length check on submit
jzgom067 Oct 17, 2025
a074239
Change mobile availability add button icon
jzgom067 Oct 17, 2025
41d1805
Change mobile duration default
jzgom067 Oct 17, 2025
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 app/[event-code]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { fetchEventDetails } from "@/app/_utils/fetch-data";
import { processEventData } from "@/app/_utils/process-event-data";
import EventEditor from "../../ui/layout/event-editor";
import notFound from "../not-found";

export default async function Page({
params,
}: {
params: { "event-code": string };
}) {
const { "event-code": eventCode } = await params;

if (!eventCode) {
notFound();
}

const eventData = await fetchEventDetails(eventCode);
const { eventName, eventRange } = processEventData(eventData);

return (
<EventEditor
type="edit"
initialData={{ title: eventName, code: eventCode, eventRange }}
/>
);
}
12 changes: 0 additions & 12 deletions app/[event-code]/layout.tsx

This file was deleted.

10 changes: 8 additions & 2 deletions app/[event-code]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
import { notFound } from "next/navigation";
import { fetchEventDetails } from "../_utils/fetch-data";
import { fetchEventDetails, fetchSelfAvailability } from "../_utils/fetch-data";
import { processEventData } from "../_utils/process-event-data";

import AvailabilityClientPage from "@/app/ui/layout/availability-page";
import { getAuthCookieString } from "../_utils/cookie-utils";

export default async function Page({
params,
}: {
params: { "event-code": string };
}) {
const { "event-code": eventCode } = await params;
const authCookies = await getAuthCookieString();

if (!eventCode) {
notFound();
}

const eventData = await fetchEventDetails(eventCode);
const [eventData, initialAvailabilityData] = await Promise.all([
fetchEventDetails(eventCode),
fetchSelfAvailability(eventCode, authCookies),
]);
const { eventName, eventRange } = processEventData(eventData);

return (
<AvailabilityClientPage
eventCode={eventCode}
eventName={eventName}
eventRange={eventRange}
initialData={initialAvailabilityData}
/>
);
}
6 changes: 4 additions & 2 deletions app/[event-code]/results/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@ import {
import { processEventData } from "@/app/_utils/process-event-data";

import ResultsPage from "@/app/ui/layout/results-page";
import { getAuthCookieString } from "@/app/_utils/cookie-utils";

export default async function Page({
params,
}: {
params: { "event-code": string };
}) {
const { "event-code": eventCode } = await params;
const authCookies = await getAuthCookieString();

if (!eventCode) {
notFound();
}

const [initialEventData, availabilityData] = await Promise.all([
fetchEventDetails(eventCode),
fetchAvailabilityData(eventCode),
fetchEventDetails(eventCode, authCookies),
fetchAvailabilityData(eventCode, authCookies),
]);

// Process the data here, on the server!
Expand Down
21 changes: 13 additions & 8 deletions app/_lib/availability/use-availability.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { useReducer, useCallback } from "react";
import { availabilityReducer, AvailabilityState } from "./availability-reducer";
import { createEmptyUserAvailability } from "./utils";
import { createUserAvailability } from "./utils";

export function useAvailability(initialData: any = null) {
const initialTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const isoStrings = [];
if (initialData && initialData.available_dates) {
for (const dateStr of initialData.available_dates) {
isoStrings.push(new Date(dateStr).toISOString());
}
}

export function useAvailability(
initialDisplayName = "",
initialTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone,
) {
const initialState: AvailabilityState = {
displayName: initialDisplayName,
timeZone: initialTimeZone,
userAvailability: createEmptyUserAvailability(),
displayName: initialData?.display_name || "",
timeZone: initialData?.time_zone || initialTimeZone,
userAvailability: createUserAvailability(isoStrings),
};

const [state, dispatch] = useReducer(availabilityReducer, initialState);
Expand Down
6 changes: 6 additions & 0 deletions app/_lib/availability/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export const createEmptyUserAvailability = (): AvailabilitySet => {
return new Set<string>();
};

export const createUserAvailability = (
data: Array<string>,
): AvailabilitySet => {
return new Set<string>(data);
};

// Toggles a single time slot in the user's availability
export function toggleUtcSlot(
prev: AvailabilitySet,
Expand Down
18 changes: 11 additions & 7 deletions app/_lib/schedule/use-event-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { EventInfoReducer } from "./event-info-reducer";
import { EventInformation, EventRange, WeekdayMap } from "./types";
import { DateRange } from "react-day-picker";

export function useEventInfo() {
const initalState: EventInformation = {
title: "",
customCode: "",
eventRange: {
export function useEventInfo(initialData?: any) {
const initialState: EventInformation = {
title: initialData?.title || "",
customCode: initialData?.code || "",
eventRange: initialData?.eventRange || {
type: "specific",
duration: 60,
duration: 0,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
dateRange: {
from: new Date().toISOString(),
Expand All @@ -22,7 +22,11 @@ export function useEventInfo() {
},
};

const [state, dispatch] = useReducer(EventInfoReducer, initalState);
if (!initialData?.eventRange?.duration) {
initialState.eventRange.duration = 0;
}

const [state, dispatch] = useReducer(EventInfoReducer, initialState);

// DISPATCHERS
const setTitle = useCallback((title: string) => {
Expand Down
13 changes: 11 additions & 2 deletions app/_lib/schedule/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "@/app/_lib/schedule/types";
import { formatInTimeZone, fromZonedTime, toZonedTime } from "date-fns-tz";
import { getHours, getMinutes, startOfDay } from "date-fns";
import { DateRange } from "react-day-picker";

/* WEEKDAY SPECIFIC UTILITIES */

Expand Down Expand Up @@ -116,9 +117,7 @@ export function getSelectedWeekdaysInTimezone(
const selectedDatesUTC: WeekdayTimeRange[] = [];
for (let i = 0; i < 7; i++) {
const currentDay = new Date(startOfWeekInViewerTz);
// console.log({ currentDay });
currentDay.setDate(startOfWeekInViewerTz.getDate() + i);
// console.log({ currentDay });
if (selectedDayIndexes.has(currentDay.getDay())) {
const dateString = formatInTimeZone(
currentDay,
Expand Down Expand Up @@ -216,3 +215,13 @@ function generateSlotsForWeekdayRange(range: WeekdayRange): Date[] {

return slots;
}

export function checkInvalidDateRangeLength(
range: DateRange | undefined,
): boolean {
if (range?.from && range?.to) {
const diffTime = range.to.getTime() - range.from.getTime();
return diffTime > 30 * 24 * 60 * 60 * 1000; // more than 30 days
}
return false;
}
3 changes: 3 additions & 0 deletions app/_lib/types/date-range-props.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { EventRange, WeekdayMap } from "@/app/_lib/schedule/types";
import { DateRange } from "react-day-picker";

export type DateRangeProps = {
earliestDate?: Date;
eventRange: EventRange;
tooManyDays?: boolean;
editing?: boolean;

// update functions
setTitle?: (title: string) => void;
Expand Down
14 changes: 14 additions & 0 deletions app/_utils/cookie-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { cookies } from "next/headers";

export async function getAuthCookieString(): Promise<string> {
const cookieStore = await cookies();
let cookieHeader = "";
const authCookieNames = ["auth_sess_token", "guest_sess_token"];
authCookieNames.forEach((name) => {
const cookie = cookieStore.get(name);
if (cookie) {
cookieHeader += `${name}=${cookie.value}; `;
}
});
return cookieHeader.trim();
}
44 changes: 40 additions & 4 deletions app/_utils/fetch-data.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import formatApiError from "@/app/_utils/format-api-error";

export async function fetchEventDetails(eventCode: string): Promise<any> {
export async function fetchEventDetails(
eventCode: string,
cookieHeader?: string,
): Promise<any> {
const baseUrl = process.env.NEXT_PUBLIC_API_URL;
const res = await fetch(
`${baseUrl}/event/get-details/?event_code=${eventCode}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
headers: {
"Content-Type": "application/json",
Cookie: cookieHeader ? cookieHeader : "",
},
cache: "no-store",
},
);
Expand All @@ -19,13 +25,19 @@ export async function fetchEventDetails(eventCode: string): Promise<any> {
return res.json();
}

export async function fetchAvailabilityData(eventCode: string): Promise<any> {
export async function fetchAvailabilityData(
eventCode: string,
cookieHeader: string,
): Promise<any> {
const baseUrl = process.env.NEXT_PUBLIC_API_URL;
const res = await fetch(
`${baseUrl}/availability/get-all/?event_code=${eventCode}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
headers: {
"Content-Type": "application/json",
Cookie: cookieHeader,
},
cache: "no-store",
},
);
Expand All @@ -37,3 +49,27 @@ export async function fetchAvailabilityData(eventCode: string): Promise<any> {

return res.json();
}

export async function fetchSelfAvailability(
eventCode: string,
cookieHeader: string,
): Promise<any> {
const baseUrl = process.env.NEXT_PUBLIC_API_URL;
const res = await fetch(
`${baseUrl}/availability/get-self/?event_code=${eventCode}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Cookie: cookieHeader,
},
cache: "no-store",
},
);

if (!res.ok) {
return null;
}

return res.json();
}
Loading