Skip to content

Commit

Permalink
Feature: Useable Events / Schedule Page (#106)
Browse files Browse the repository at this point in the history
* update: created getSchedule file

* update: synced sanity data with schedule component

* feat: update event schema

* feat: group events by day

* feat: format date ranges

* fix: group events by America/Los_Angeles day

* fix: event order asc

* fix: remove resources from nav

---------

Co-authored-by: Tyler Yu <tyleryy@uci.edu>
  • Loading branch information
alexanderl19 and tyleryy committed Oct 31, 2023
1 parent d1e4446 commit 3f0fc0e
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 50 deletions.
28 changes: 22 additions & 6 deletions apps/sanity/schemas/event.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,50 @@
import { defineType, defineField } from "sanity";
import { defineType, defineField, defineArrayMember } from "sanity";
import { CalendarClock } from "lucide-react";

export default defineType({
name: "event",
title: "Event",
icon: CalendarClock,
type: "document",
fields: [
defineField({
name: "title",
title: "Title",
type: "string",
validation: (Rule) => Rule.required(),
}),
defineField({
name: "location",
title: "Location",
type: "string",
}),
defineField({
name: "virtual",
title: "Virtual Meeting Link",
type: "url",
}),
defineField({
name: "startTime",
title: "Start Time",
type: "datetime",
validation: (Rule) => Rule.required(),
}),
defineField({
name: "endTime",
title: "End Time",
type: "datetime",
validation: (Rule) => Rule.required(),
}),
defineField({
name: "category",
title: "Category",
name: "organization",
title: "Organization",
type: "string",
}),
defineField({
name: "host",
title: "Host",
type: "string",
name: "hosts",
title: "Hosts",
type: "array",
of: [defineArrayMember({ type: "string" })],
}),
defineField({
name: "description",
Expand Down
1 change: 1 addition & 0 deletions apps/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@types/react": "18.2.20",
"@types/react-dom": "^18.2.0",
"bootstrap": "^5.3.1",
"dayjs": "^1.11.10",
"eslint": "8.46.0",
"eslint-config-next": "^13.4.1",
"framer-motion": "^10.16.4",
Expand Down
4 changes: 2 additions & 2 deletions apps/site/src/components/NavBar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function NavBar() {
>
Home
</Link>
<Link
{/* <Link
href="/resources"
className={
activeRoute === "/resources"
Expand All @@ -40,7 +40,7 @@ export default function NavBar() {
}
>
Resources
</Link>
</Link> */}
<Link
href="/schedule"
className={
Expand Down
8 changes: 8 additions & 0 deletions apps/site/src/lib/day.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";

dayjs.extend(utc);
dayjs.extend(timezone);

export default dayjs;
26 changes: 13 additions & 13 deletions apps/site/src/views/Schedule/Schedule.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import ClipboardSchedule from "./sections/ClipboardSchedule/ClipboardSchedule";
import { getSchedule } from "./getSchedule";
import { PortableText } from "@portabletext/react";

import styles from "./Schedule.module.scss";

export default function Schedule() {
export default async function Schedule() {
const days = await getSchedule();

const schedule = days.map((events) =>
events.map(({ description, ...event }) => ({
...event,
description: <PortableText value={description} />,
})),
);

return (
<div className={styles.schedule}>
<h1>Schedule</h1>
<ClipboardSchedule
schedule={[
{
_key: "test",
title: "test",
description: "test",
location: "test",
startDate: new Date(),
endDate: new Date(),
},
]}
/>
<ClipboardSchedule schedule={schedule} />
</div>
);
}
64 changes: 64 additions & 0 deletions apps/site/src/views/Schedule/getSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { z } from "zod";
import { cache } from "react";
import { client } from "@/lib/sanity/client";
import { SanityDocument } from "@/lib/sanity/types";
import dayjs from "@/lib/day";

const Events = z.array(
SanityDocument.extend({
_type: z.literal("event"),
title: z.string(),
location: z.string().optional(),
virtual: z.string().url().optional(),
startTime: z
.string()
.datetime()
.transform((time) => new Date(time)),
endTime: z
.string()
.datetime()
.transform((time) => new Date(time)),
organization: z.string().optional(),
hosts: z.array(z.string()).optional(),
description: z.array(
z.object({
_key: z.string(),
markDefs: z.array(
z.object({
_type: z.string(),
href: z.optional(z.string()),
_key: z.string(),
}),
),
children: z.array(
z.object({
text: z.string(),
_key: z.string(),
_type: z.literal("span"),
marks: z.array(z.string()),
}),
),
_type: z.literal("block"),
style: z.literal("normal"),
}),
),
}),
);

export const getSchedule = cache(async () => {
const events = Events.parse(
await client.fetch("*[_type == 'event'] | order(startTime asc)"),
);
const eventsByDay = new Map<string, z.infer<typeof Events>>();

events.forEach((event) => {
const date = dayjs
.utc(event.startTime)
.tz("America/Los_Angeles")
.format("YYYY-MM-DD");

eventsByDay.set(date, [...(eventsByDay.get(date) ?? []), event]);
});

return Array.from(eventsByDay.values());
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@
border-top: 2px solid theme.$light-blue;
border-bottom: 2px solid theme.$light-blue;
}

.date {
margin-top: 36px;
}

.eventTitle {
font-size: 24px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,29 @@ import Image from "next/image";

import Accordion from "react-bootstrap/Accordion";
import Container from "react-bootstrap/Container";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";

import clip from "@/assets/images/clip.svg";

import styles from "./ClipboardSchedule.module.scss";

interface ClipboardScheduleProps {
schedule: {
_key: string;
title: string;
description: string;
location: string;
startDate: Date;
endDate: Date;
}[];
location?: string | undefined;
virtual?: string | undefined;
startTime: Date;
endTime: Date;
organization?: string | undefined;
hosts?: string[] | undefined;
description: JSX.Element;
}[][];
}

const dateTimeFormat = new Intl.DateTimeFormat("en", {
hour: "numeric",
minute: "numeric",
});

function ClipboardSchedule({ schedule }: ClipboardScheduleProps) {
return (
<Container
Expand All @@ -31,28 +36,33 @@ function ClipboardSchedule({ schedule }: ClipboardScheduleProps) {
<div className={styles.clip}>
<Image src={clip} alt="Clipboard clip" className={styles.clip} />
</div>
<h2 className="mb-5">Countdown Timer</h2>
<Accordion defaultActiveKey="0" className={styles.accordion}>
{schedule.map(
(
{ _key, title, description, location, startDate, endDate },
index,
) => (
<Accordion.Item
key={_key}
eventKey={`${index}`}
className={styles.accordionItem}
>
<Accordion.Header className={styles.accordionHeader}>
<h3>{title}</h3>
<span className="text-end ms-auto">
{location}, {startDate.toString()} - {endDate.toString()}
</span>
</Accordion.Header>
<Accordion.Body>{description}</Accordion.Body>
</Accordion.Item>
),
)}
{schedule.map((day, i) => (
<div key={i}>
<h2 className={styles.date}>
{day[0].startTime.toLocaleDateString()}
</h2>
{day.map(
({ title, description, location, hosts, startTime, endTime }) => (
<Accordion.Item
key={title}
eventKey={title}
className={styles.accordionItem}
>
<Accordion.Header className={styles.accordionHeader}>
<h3 className={styles.eventTitle}>{title}</h3>
{/* <span>{hosts?.join()}</span> */}
<span className="text-end ms-auto">
{location},{" "}
{dateTimeFormat.formatRange(startTime, endTime)}
</span>
</Accordion.Header>
<Accordion.Body>{description}</Accordion.Body>
</Accordion.Item>
),
)}
</div>
))}
</Accordion>
</Container>
);
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3f0fc0e

Please sign in to comment.