Skip to content

Schedule table component#239

Open
ExistentialKevin wants to merge 2 commits intodevfrom
schedule-table-component
Open

Schedule table component#239
ExistentialKevin wants to merge 2 commits intodevfrom
schedule-table-component

Conversation

@ExistentialKevin
Copy link

@ExistentialKevin ExistentialKevin commented Mar 19, 2026

Why

  • A table of events should be simple and display all the necessary information to the viewer.

What

  • Created a table of events that groups by day.
  • Events link to to their respective event pages for more information on the event.

Satisfies

HK-215

Summary by CodeRabbit

Release Notes

  • New Features

    • Schedule now displays as a table organized by day, with event times and descriptions.
  • Style

    • Reduced schedule page heading size for improved layout balance.
    • Enhanced responsive design for mobile and desktop viewports.
    • Adjusted table cell spacing for better visual hierarchy.

@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

📝 Walkthrough

Walkthrough

The pull request replaces the schedule page's timeline-based UI with a table-based component. A new ScheduleTable component groups events by day and renders them in table format with formatted times and event details. Default cell padding was removed from the base table component, and styling on the schedule page was adjusted.

Changes

Cohort / File(s) Summary
Schedule Page Refactor
apps/web/src/app/schedule/page.tsx
Switched from ScheduleTimeline to ScheduleTable component, removed timezone resolution dependencies, reduced heading size (text-8xltext-5xl), and adjusted responsive width classes.
Schedule Table Component
apps/web/src/components/schedule/ScheduleTable.tsx
Added new component that groups events by day-of-week using startTime and renders a table with day headers and per-event rows. EventRow subcomponent formats times, styles badges by event type, and links to detail pages.
UI Component Updates
apps/web/src/components/shadcn/ui/table.tsx, apps/web/src/components/shared/NavBarLinksGrouper.tsx
Removed default p-4 padding class from TableCell to allow caller-specified spacing; minor whitespace adjustment in NavBarLinksGrouper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

  • Bare-bones Schedule renderer #233: This PR implements day-split schedule rendering with per-event row components, directly addressing the objective of replacing timeline-based rendering with table-based day-grouped display using config.hackathonTimezone for time formatting.

Poem

🐰 From timelines tangled, a table takes shape,
Days grouped in neat rows, no schedule escape!
With badges and links, the events align,
A hop-friendly view—now the schedule's divine! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Schedule table component' directly and clearly summarizes the main change: adding a new ScheduleTable component to replace the previous ScheduleTimeline, as evidenced by the new ScheduleTable.tsx file and schedule/page.tsx modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch schedule-table-component
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/components/shadcn/ui/table.tsx (1)

84-97: ⚠️ Potential issue | 🟠 Major

Remove default padding from TableCell without updating dependent components breaks existing tables.

Removing p-4 from TableCell is a breaking change affecting all tables using this component. While ScheduleTable was updated with explicit className="p-2", the tables in EventDataTable, UserDataTable, and NavItemsManager were not updated and now lack cell padding. Either keep the default padding and override where needed, or update all affected tables to specify explicit padding.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/shadcn/ui/table.tsx` around lines 84 - 97, The
TableCell component was changed to remove the default padding (previously
"p-4"), which breaks tables that weren't updated; either restore the original
default padding in TableCell (add "p-4" back into the cn(...) call in the
TableCell forwardRef) or update every dependent table component (e.g.,
ScheduleTable, EventDataTable, UserDataTable, NavItemsManager) to explicitly
pass a padding class (like className="p-2" or "p-4") when rendering TableCell so
existing tables keep their expected spacing.
🧹 Nitpick comments (3)
apps/web/src/components/schedule/ScheduleTable.tsx (3)

84-96: Stale comment and unnecessary option.

  • Line 84: The comment // Needs timezone prop appears outdated since the component uses the module-level userTimeZone.
  • Line 94: useAdditionalDayOfYearTokens is only needed for D/DD tokens; the format "hh:mm a" doesn't use these.
Proposed cleanup
-// Needs timezone prop
 type eventRowProps = {
 	event: Event;
 };
 export function EventRow({ event }: eventRowProps) {
 	const startTimeFormatted = formatInTimeZone(
 		event.startTime,
 		userTimeZone,
 		"hh:mm a",
-		{
-			useAdditionalDayOfYearTokens: true,
-		},
 	);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/schedule/ScheduleTable.tsx` around lines 84 - 96,
Remove the stale comment and the unnecessary option: delete the "// Needs
timezone prop" comment above the eventRowProps/EventRow definitions since
EventRow already uses the module-level userTimeZone, and remove the
useAdditionalDayOfYearTokens option passed to formatInTimeZone in EventRow (the
format "hh:mm a" doesn't require that token). Keep the call to formatInTimeZone
with event.startTime and userTimeZone unchanged otherwise.

106-108: Redundant key prop.

The key is already provided when rendering EventRow at line 70. This inner key on TableRow is unnecessary.

Proposed fix
 	return (
 		<TableRow
-			key={event.id}
 			className="flex flex-col justify-around bg-transparent px-4 pb-1 odd:bg-white/5"
 		>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/schedule/ScheduleTable.tsx` around lines 106 - 108,
Remove the redundant key prop from the inner TableRow element: the unique key is
already assigned to EventRow where the list is mapped, so delete the
key={event.id} on TableRow (the JSX element named TableRow in ScheduleTable.tsx)
and keep the existing key on EventRow to avoid duplicate/unused keys.

46-50: Fragile date formatting with hardcoded substring.

Trimming the last 6 characters assumes a specific locale format. Consider using a format pattern that produces the desired output directly, such as "EEEE, MMMM d" for "Friday, March 20".

Proposed fix
 function eventDateString(arr: Event[], timezone: string) {
-	const date = formatInTimeZone(arr[0].startTime, timezone, "PPPP");
-	const retString = date.substring(0, date.length - 6);
-	return retString;
+	return formatInTimeZone(arr[0].startTime, timezone, "EEEE, MMMM d");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/schedule/ScheduleTable.tsx` around lines 46 - 50, The
eventDateString function uses a fragile substring to trim the formatted date;
replace this by calling formatInTimeZone with an explicit format string (e.g.,
"EEEE, MMMM d") so the output is directly "Friday, March 20" and not dependent
on locale length; update eventDateString to formatInTimeZone(arr[0].startTime,
timezone, "EEEE, MMMM d") and also add a small guard to handle empty arr to
avoid runtime errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/components/schedule/ScheduleTable.tsx`:
- Line 103: The code gets color via const color = (c.eventTypes as
Record<string, string>)[event.type] which can be undefined for unknown
event.type; change this to use a fallback (same approach used in
EventDetails.tsx) so color defaults to a safe value (e.g., an empty string or a
default color key) when event.type is not present in c.eventTypes; update the
reference in ScheduleTable.tsx where color is used (the badge border rendering)
to rely on this fallback to ensure the badge always renders.
- Around line 58-77: The mapped fragment uses a shorthand <> but the key is
placed on the inner TableBody which causes a React "missing key on fragment"
warning; change the outer fragment to an explicit React.Fragment and move the
key to that fragment (i.e., replace <> with React.Fragment key={dateID}) so the
key is on the outermost element returned from the
Array.from(splitByDay(schedule).entries()).map callback (referencing splitByDay,
dateID, TableBody, and EventRow to locate the code).

In `@apps/web/src/components/shared/NavBarLinksGrouper.tsx`:
- Line 15: Remove the trailing whitespace after the closing brace in
NavBarLinksGrouper.tsx (the extra space following the '}' in the component/file)
so Prettier passes; simply delete the trailing spaces or run Prettier/format on
the file (e.g., target the NavBarLinksGrouper component/definition) and commit
the formatted file.

---

Outside diff comments:
In `@apps/web/src/components/shadcn/ui/table.tsx`:
- Around line 84-97: The TableCell component was changed to remove the default
padding (previously "p-4"), which breaks tables that weren't updated; either
restore the original default padding in TableCell (add "p-4" back into the
cn(...) call in the TableCell forwardRef) or update every dependent table
component (e.g., ScheduleTable, EventDataTable, UserDataTable, NavItemsManager)
to explicitly pass a padding class (like className="p-2" or "p-4") when
rendering TableCell so existing tables keep their expected spacing.

---

Nitpick comments:
In `@apps/web/src/components/schedule/ScheduleTable.tsx`:
- Around line 84-96: Remove the stale comment and the unnecessary option: delete
the "// Needs timezone prop" comment above the eventRowProps/EventRow
definitions since EventRow already uses the module-level userTimeZone, and
remove the useAdditionalDayOfYearTokens option passed to formatInTimeZone in
EventRow (the format "hh:mm a" doesn't require that token). Keep the call to
formatInTimeZone with event.startTime and userTimeZone unchanged otherwise.
- Around line 106-108: Remove the redundant key prop from the inner TableRow
element: the unique key is already assigned to EventRow where the list is
mapped, so delete the key={event.id} on TableRow (the JSX element named TableRow
in ScheduleTable.tsx) and keep the existing key on EventRow to avoid
duplicate/unused keys.
- Around line 46-50: The eventDateString function uses a fragile substring to
trim the formatted date; replace this by calling formatInTimeZone with an
explicit format string (e.g., "EEEE, MMMM d") so the output is directly "Friday,
March 20" and not dependent on locale length; update eventDateString to
formatInTimeZone(arr[0].startTime, timezone, "EEEE, MMMM d") and also add a
small guard to handle empty arr to avoid runtime errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac00d3e6-1c47-4768-8362-6c7a00cd671b

📥 Commits

Reviewing files that changed from the base of the PR and between 105d85c and ba55972.

📒 Files selected for processing (4)
  • apps/web/src/app/schedule/page.tsx
  • apps/web/src/components/schedule/ScheduleTable.tsx
  • apps/web/src/components/shadcn/ui/table.tsx
  • apps/web/src/components/shared/NavBarLinksGrouper.tsx

Comment on lines +58 to +77
{Array.from(splitByDay(schedule).entries()).map(
([dateID, arr]): ReactNode => (
<>
<TableBody key={dateID} className="border">
<TableHeader className="flex justify-start">
<span className="m-1 p-4 text-center text-xl font-bold lg:text-4xl">
<p>{`${eventDateString(arr, userTimeZone)}`}</p>
</span>
</TableHeader>
{arr.map(
(event): ReactNode => (
<EventRow
key={event.id}
event={event}
/>
),
)}
</TableBody>
</>
),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing key on Fragment causes React warning.

When mapping, the key should be on the outermost element. The fragment <> is outermost, but the key is on TableBody inside. Use React.Fragment with an explicit key instead.

Proposed fix
+import React, { type ReactNode } from "react";
-import { ReactNode } from "react";
 				{Array.from(splitByDay(schedule).entries()).map(
 					([dateID, arr]): ReactNode => (
-						<>
-							<TableBody key={dateID} className="border">
+						<React.Fragment key={dateID}>
+							<TableBody className="border">
 								<TableHeader className="flex justify-start">
 									<span className="m-1 p-4 text-center text-xl font-bold lg:text-4xl">
 										<p>{`${eventDateString(arr, userTimeZone)}`}</p>
 									</span>
 								</TableHeader>
 								{arr.map(
 									(event): ReactNode => (
 										<EventRow
 											key={event.id}
 											event={event}
 										/>
 									),
 								)}
 							</TableBody>
-						</>
+						</React.Fragment>
 					),
 				)}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{Array.from(splitByDay(schedule).entries()).map(
([dateID, arr]): ReactNode => (
<>
<TableBody key={dateID} className="border">
<TableHeader className="flex justify-start">
<span className="m-1 p-4 text-center text-xl font-bold lg:text-4xl">
<p>{`${eventDateString(arr, userTimeZone)}`}</p>
</span>
</TableHeader>
{arr.map(
(event): ReactNode => (
<EventRow
key={event.id}
event={event}
/>
),
)}
</TableBody>
</>
),
{Array.from(splitByDay(schedule).entries()).map(
([dateID, arr]): ReactNode => (
<React.Fragment key={dateID}>
<TableBody className="border">
<TableHeader className="flex justify-start">
<span className="m-1 p-4 text-center text-xl font-bold lg:text-4xl">
<p>{`${eventDateString(arr, userTimeZone)}`}</p>
</span>
</TableHeader>
{arr.map(
(event): ReactNode => (
<EventRow
key={event.id}
event={event}
/>
),
)}
</TableBody>
</React.Fragment>
),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/schedule/ScheduleTable.tsx` around lines 58 - 77, The
mapped fragment uses a shorthand <> but the key is placed on the inner TableBody
which causes a React "missing key on fragment" warning; change the outer
fragment to an explicit React.Fragment and move the key to that fragment (i.e.,
replace <> with React.Fragment key={dateID}) so the key is on the outermost
element returned from the Array.from(splitByDay(schedule).entries()).map
callback (referencing splitByDay, dateID, TableBody, and EventRow to locate the
code).

userTimeZone,
"h:mm a",
);
const color = (c.eventTypes as Record<string, string>)[event.type];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add fallback for unknown event types.

If event.type is not a key in c.eventTypes, color will be undefined and the badge border won't render. Add a fallback like EventDetails.tsx does (see apps/web/src/components/events/admin/EventDetails.tsx:17-18).

Proposed fix
-	const color = (c.eventTypes as Record<string, string>)[event.type];
+	const color = (c.eventTypes as Record<string, string>)[event.type] || c.eventTypes.Other;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const color = (c.eventTypes as Record<string, string>)[event.type];
const color = (c.eventTypes as Record<string, string>)[event.type] || c.eventTypes.Other;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/schedule/ScheduleTable.tsx` at line 103, The code
gets color via const color = (c.eventTypes as Record<string,
string>)[event.type] which can be undefined for unknown event.type; change this
to use a fallback (same approach used in EventDetails.tsx) so color defaults to
a safe value (e.g., an empty string or a default color key) when event.type is
not present in c.eventTypes; update the reference in ScheduleTable.tsx where
color is used (the badge border rendering) to rely on this fallback to ensure
the badge always renders.

);
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix trailing whitespace causing Prettier CI failure.

Line 15 contains extra trailing whitespace after the closing brace. Please remove it (or run Prettier on the file) so Prettier --check passes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/shared/NavBarLinksGrouper.tsx` at line 15, Remove the
trailing whitespace after the closing brace in NavBarLinksGrouper.tsx (the extra
space following the '}' in the component/file) so Prettier passes; simply delete
the trailing spaces or run Prettier/format on the file (e.g., target the
NavBarLinksGrouper component/definition) and commit the formatted file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant