Skip to content

Bare-bones Schedule renderer #233

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;

Activity

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

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