Skip to content

Bare-bones Schedule renderer #233

@linear

Description

@linear

Goal

Make a minimal wrapper for the ScheduleTimeline pattern that:

  • accepts schedule (full event objects, not just a minimal type)
  • computes / accepts timezone
  • splits events by day
  • iterates and renders using caller-provided components so pages can swap UI without re-writing the logic
  • passes the already-calculated timezone into the custom components (so they don't have to figure it out)

Props (recommended)

  • schedule: Event[] (same shape you already use in ScheduleTimeline)
  • timezone?: string
  • DayHeader?: React.ComponentType<{ dayName: string }>
  • RenderEventRow: React.ComponentType<{ event: Event; userTimeZone: string }>

If timezone is not provided, compute it internally:

  • const userTimeZone = getClientTimeZone(c.hackathonTimezone)

Acceptance Criteria

  • New component exists (e.g. ScheduleTimelineBare.tsx)
  • Uses existing splitByDay(schedule) logic
  • Iterates days, then iterates events within each day
  • Calls RenderEventRow for each event and passes:
    • event
    • userTimeZone (already computed)
  • Optional DayHeader can be overridden
  • Works with existing EventRow without changes

Example Implementation

components/schedule/ScheduleTimelineBare.tsx

import { type EventType as Event } from "@/lib/types/events";
import { getClientTimeZone } from "@/lib/utils/client/shared";
import c from "config";
import { ReactNode } from "react";

const daysOfWeek = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
];

function splitByDay(schedule: Event[]) {
  const days: Map<string, Event[]> = new Map();
  schedule.forEach((event) => {
    const day = daysOfWeek[event.startTime.getDay()];
    const existing = days.get(day);
    if (existing) existing.push(event);
    else days.set(day, [event]);
  });
  return days;
}

type DayHeaderProps = { dayName: string };
const DefaultDayHeader = ({ dayName }: DayHeaderProps) => (
  <tr key={dayName + " title"} className="py-8">
    <td></td>
    <td className="w-1" style={{ backgroundColor: `hsl(var(--secondary))` }} />
    <td>
      <h2 className="ml-16 w-full border-b py-4 text-6xl font-black">
        {dayName}
      </h2>
    </td>
  </tr>
);

type RowProps = { event: Event; userTimeZone: string };

export default function ScheduleTimelineBare({
  schedule,
  timezone,
  DayHeader = DefaultDayHeader,
  RenderEventRow,
}: {
  schedule: Event[];
  timezone?: string;
  DayHeader?: React.ComponentType<DayHeaderProps>;
  RenderEventRow: React.ComponentType<RowProps>;
}) {
  const userTimeZone = timezone ?? getClientTimeZone(c.hackathonTimezone);
  const days = Array.from(splitByDay(schedule).entries());

  return (
    <div className="mx-auto mt-5 w-3/4">
      <table className="p-4">
        <tbody>
          {days.map(([dayName, arr]): ReactNode => (
            <>
              <DayHeader dayName={dayName} />
              {arr.map((event) => (
                <RenderEventRow
                  key={event.id}
                  event={event}
                  userTimeZone={userTimeZone}
                />
              ))}
            </>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Usage example (your current page + existing EventRow)

import ScheduleTimelineBare from "@/components/schedule/ScheduleTimelineBare";
import { EventRow } from "../dash/schedule/schedule-timeline";
import { getAllEvents } from "db/functions";
import { getClientTimeZone } from "@/lib/utils/client/shared";
import c from "config";

export default async function Page() {
  const sched = await getAllEvents();
  const userTimeZone = getClientTimeZone(c.hackathonTimezone);

  return (
    <>
      <h1 className="mx-auto my-8 w-3/4 text-8xl font-black">Schedule</h1>
      <ScheduleTimelineBare
        schedule={sched}
        timezone={userTimeZone}
        RenderEventRow={EventRow}
      />
    </>
  );
}

export const revalidate = 60;

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions