diff --git a/app/assignments/page.tsx b/app/assignments/page.tsx index 13f801d..3f422fe 100644 --- a/app/assignments/page.tsx +++ b/app/assignments/page.tsx @@ -52,7 +52,7 @@ export const formattedAssessments = allAssessments.map(a => { { name: "Scheduled", date, - specifyTime: true, + specifyTime: false, } ], sortDate: date, diff --git a/app/menu.tsx b/app/menu.tsx index 0e8bf49..b181ca6 100644 --- a/app/menu.tsx +++ b/app/menu.tsx @@ -24,6 +24,12 @@ export const menuItems: MenuItemProps[] = [ icon: "📄", href: "/syllabus", }, + { + id: "schedule", + title: "Schedule", + icon: "📅", + href: "/schedule", + }, { id: "assignments", title: "Assignments", @@ -53,12 +59,12 @@ const MenuContext = createContext(null) export function MenuContextProvider({ children }: { children: ReactNode }) { const pathname = usePathname() - const [activeState, setActiveState] = useState<{ item: string | null, pathname: string }>({ item: null, pathname }) + const [activeState, setActiveState] = useState(null) return {children} @@ -78,10 +84,9 @@ export type MenuItemActivatorProps = { export function MenuItemActivator({ item }: MenuItemActivatorProps) { const context = useMenuContext() - const pathname = usePathname() useEffect(() => { context.setActiveItem(item) - }, [pathname]) + }, []) return null } diff --git a/app/schedule/content.tsx b/app/schedule/content.tsx new file mode 100644 index 0000000..db1e996 --- /dev/null +++ b/app/schedule/content.tsx @@ -0,0 +1,46 @@ +"use client" + +import { Card } from "@/components/Card" +import { ScheduleTable, getSchedule } from "@/components/Schedule" +import { useEffect, useState } from "react" +import sections from "@/sections" + +export function SchedulePageContent() { + const [section, setSection] = useState(null) + const [isClient, setIsClient] = useState(false) + + useEffect(() => { + setSection(localStorage.getItem("cis1951.section.24sp")) + setIsClient(true) + }, []) + + const setSectionAndStore = (section: string) => { + localStorage.setItem("cis1951.section.24sp", section) + setSection(section) + } + + if (!isClient) return null + + if (!section) { + return
+
Please choose a section:
+
+ {sections.map(section => )} +
+
You can change this later.
+
+ } + + const schedule = getSchedule(section) + + return
+ + You're currently viewing the schedule for section {section}. + + Schedule}> + + +
+} \ No newline at end of file diff --git a/app/schedule/page.tsx b/app/schedule/page.tsx new file mode 100644 index 0000000..320134c --- /dev/null +++ b/app/schedule/page.tsx @@ -0,0 +1,13 @@ +import { Card } from "@/components/Card"; +import { MenuItemActivator } from "../menu"; +import { SchedulePageContent } from "./content"; + +export default function SchedulePage() { + return <> + + + + +} \ No newline at end of file diff --git a/components/Schedule.tsx b/components/Schedule.tsx new file mode 100644 index 0000000..8816a90 --- /dev/null +++ b/components/Schedule.tsx @@ -0,0 +1,98 @@ +import { allAssessments, allHomework, allLectures } from "contentlayer/generated" +import { ReactNode } from "react" +import { FormattedDate } from "./FormattedDate" +import Link from "next/link" + +export type LectureScheduleItemDetails = { + type: "lecture" + title: string + body: typeof allLectures[0]["body"] +} + +export type AssignmentScheduleItemDetails = { + type: "assignment" + title: string + event?: string + href: string + isReleased: boolean + releaseDate?: Date +} + +export type ScheduleItem = { + date: Date +} & (LectureScheduleItemDetails | AssignmentScheduleItemDetails) + +export function getSchedule(section: string | null): ScheduleItem[] { + const homework = allHomework.flatMap(hw => { + const shared = { + type: "assignment" as const, + title: hw.title, + href: `/assignments/hw/${hw.slug}`, + isReleased: hw.isReleased, + releaseDate: hw.releaseDate && new Date(hw.releaseDate), + } + + return [ + { + ...shared, + event: "Due", + date: new Date(hw.dueDate), + }, + ...(hw.auxiliaryDates ?? []).map(aux => ({ + ...shared, + event: aux.name, + date: new Date(aux.date), + })), + ] + }) + + const assessments = allAssessments.map(a => ({ + type: "assignment" as const, + title: a.title, + href: `/assignments/assessment/${a.slug}`, + isReleased: a.isReleased, + releaseDate: a.releaseDate && new Date(a.releaseDate), + date: new Date(a.assessmentDate), + })) + + const lectures = allLectures.map(l => ({ + type: "lecture" as const, + title: l.title, + body: l.body, + date: l.dates[section] && new Date(l.dates[section]), + })).filter(l => l.date) + + const schedule = [...homework, ...assessments, ...lectures] + return schedule.toSorted((a, b) => a.date.getTime() - b.date.getTime()) +} + +export function ScheduleRow({ date, ...details }: ScheduleItem) { + let content: ReactNode = null + if (details.type === "lecture") { + content = <>🧑‍🏫 {details.title} + } else if (details.type === "assignment") { + const title = details.isReleased ? + {details.title} : + {details.title}{details.releaseDate && <> (available )} + content = 📋 {title}{details.event && ` - ${details.event}`} @ + } + + return + + {content} + +} + +export function ScheduleTable({ items }: { items: ScheduleItem[] }) { + return + + + + + + + + {items.map((item, index) => )} + +
DateEvent
+} \ No newline at end of file diff --git a/components/StaffGrid.tsx b/components/StaffGrid.tsx index a7e4299..4076593 100644 --- a/components/StaffGrid.tsx +++ b/components/StaffGrid.tsx @@ -1,6 +1,6 @@ export type StaffMemberProps = { name: string - section?: "501" | "502" + section?: "201" | "202" flavorText?: string pennkey: string school: "sas" | "seas" | "wharton" // Sorry, forgot what Penn Medicine has diff --git a/components/UpcomingAssignments.tsx b/components/UpcomingAssignments.tsx index be07a74..f6d6e80 100644 --- a/components/UpcomingAssignments.tsx +++ b/components/UpcomingAssignments.tsx @@ -1,20 +1,26 @@ -import { formattedAssessments, formattedHomework, AssignmentTable } from "@/app/assignments/page" +"use client" -const threshold = 3 * 7 * 24 * 60 * 60 * 1000 // 3 weeks +import { useState } from "react" +import { ScheduleTable, getSchedule } from "./Schedule" + +const threshold = 5 * 7 * 24 * 60 * 60 * 1000 // 3 weeks const thresholdText = "3 weeks" -const upcoming = [...formattedHomework, ...formattedAssessments].filter(({ sortDate }) => { - const remainingTime = sortDate.getTime() - new Date().getTime() - return remainingTime >= 0 && remainingTime <= threshold -}) +const schedule = getSchedule(null) export function UpcomingAssignments() { + const [now] = useState(new Date()) + const upcoming = schedule.filter(item => { + const diff = item.date.getTime() - now.getTime() + return diff >= 0 && diff < threshold + }) + if (!upcoming.length) return
There are no upcoming assignments in the next {thresholdText}.
return <>
There are {upcoming.length} {upcoming.length === 1 ? "assignment" : "assignments"} in the next {thresholdText}:
- + } \ No newline at end of file diff --git a/content/lectures/01-intro.mdx b/content/lectures/01-intro.mdx new file mode 100644 index 0000000..c322731 --- /dev/null +++ b/content/lectures/01-intro.mdx @@ -0,0 +1,6 @@ +--- +title: "Lecture 1: Intro to iOS & Xcode" +dates: + "201": 2024-01-18T19:00:00-05:00 + "202": 2024-01-22T17:15:00-05:00 +--- \ No newline at end of file diff --git a/content/pages/index.mdx b/content/pages/index.mdx index 368ff7f..7f21931 100644 --- a/content/pages/index.mdx +++ b/content/pages/index.mdx @@ -11,8 +11,6 @@ customLayout: true Upcoming Assignments} margin> - **NOTE:** This component is currently broken. It only shows upcoming assignments at page build time, not at view time. - @@ -21,14 +19,14 @@ customLayout: true ({ name: 'Page', @@ -65,13 +66,29 @@ export const Assessment = defineDocumentType(() => ({ }, })) +const LectureDates = defineNestedType(() => { + const fields = {} + sections.forEach(section => { + fields[section] = { type: 'date', required: false } + }) + + return { + name: 'LectureDates', + fields, + } +}) + export const Lecture = defineDocumentType(() => ({ name: 'Lecture', filePathPattern: `lectures/**/*.mdx`, contentType: 'mdx', fields: { title: { type: 'string', required: true }, - date: { type: 'date', required: true }, + dates: { + type: 'nested', + of: LectureDates, + required: true, + }, }, computedFields: { slug: { type: 'string', resolve: page => page._raw.flattenedPath.slice("lectures/".length) }, diff --git a/sections.json b/sections.json new file mode 100644 index 0000000..f8da1be --- /dev/null +++ b/sections.json @@ -0,0 +1 @@ +["201", "202"] \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index e6c9299..e9ce951 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,8 @@ "@/app/*": ["app/*"], "@/components/*": ["components/*"], "@/styles/*": ["styles/*"], - "contentlayer/generated": ["./.contentlayer/generated"] + "contentlayer/generated": ["./.contentlayer/generated"], + "@/sections": ["sections.json"], }, }, "include": [