diff --git a/components/course-revision/CourseRevisionMiniCard.tsx b/components/course-revision/CourseRevisionMiniCard.tsx index 4e11e00..bd3a3d9 100644 --- a/components/course-revision/CourseRevisionMiniCard.tsx +++ b/components/course-revision/CourseRevisionMiniCard.tsx @@ -1,10 +1,10 @@ import { styled } from "@stitches/react"; import { Text } from "components/Text"; -import { CourseRevisionOffering } from "contentlayer/generated"; +import { CourseRevisionOffering, WorkshopsOffering } from "contentlayer/generated"; type PropTypes = { - courseOffering: CourseRevisionOffering + courseOffering: CourseRevisionOffering | WorkshopsOffering } const CourseRevisionMiniCardStyled = styled('div', { diff --git a/components/course-revision/CourseRevisionSidebar.tsx b/components/course-revision/CourseRevisionSidebar.tsx index 9c059b8..e889892 100644 --- a/components/course-revision/CourseRevisionSidebar.tsx +++ b/components/course-revision/CourseRevisionSidebar.tsx @@ -1,5 +1,5 @@ import { styled } from "@stitches/react" -import { CourseRevisionExercise, CourseRevisionOffering } from "contentlayer/generated" +import { CourseRevisionExercise, CourseRevisionOffering, WorkshopsOffering, WorkshopsExercise } from "contentlayer/generated" import Link from "next/link" import { ArrowLeft, CaretDown, CaretUp } from "phosphor-react" import { useState } from "react" @@ -11,10 +11,10 @@ type PropTypes = { courseOfferingTitle: string, /* Home page for this course offering e.g. 1511 22T3 */ - courseOfferingContent: CourseRevisionOffering, + courseOfferingContent: CourseRevisionOffering | WorkshopsOffering, /* List of exercises content */ - contentList: CourseRevisionExercise[], + contentList: CourseRevisionExercise[] | WorkshopsExercise[] /* Index of the currently selected content in contentList */ currentContentIdx: number, @@ -111,6 +111,16 @@ const ExerciseButton = styled("button", { }, }) +const determineCourseRevisionOrWorkshopsOffering = (toBeDetermined: WorkshopsOffering | CourseRevisionOffering) => { + + if (toBeDetermined.type === "WorkshopsOffering") { + return true; + } + + return false; + +} + const CourseRevisionSidebar = ({ courseOfferingTitle, courseOfferingContent, contentList, currentContentIdx }: PropTypes) => { /* ONLY applies when width < 768px (in mobile view) */ const [showContent, setShowContent] = useState(false) @@ -120,11 +130,23 @@ const CourseRevisionSidebar = ({ courseOfferingTitle, courseOfferingContent, con justifyContent: "space-between", alignItems: "center", }}> - + {determineCourseRevisionOrWorkshopsOffering(courseOfferingContent) ? + + + Other Workshops + + : + + + Other Courses + + + } + {/* Other Courses - + */} setShowContent((val) => !val)}> {showContent ? (<>Hide Exercises) : <>Show Exercises} diff --git a/components/workshops/WorkshopsContainerHomePage.tsx b/components/workshops/WorkshopsContainerHomePage.tsx new file mode 100644 index 0000000..88cd3e2 --- /dev/null +++ b/components/workshops/WorkshopsContainerHomePage.tsx @@ -0,0 +1,40 @@ +import { styled } from "@stitches/react" +import { WorkshopsOffering } from "contentlayer/generated" +import Link from "next/link" +import CourseRevisionMiniCard from "../course-revision/CourseRevisionMiniCard" + +type PropTypes = { + allWorkshopsOffering: WorkshopsOffering[] +} + +const CourseRevisionsContainer = styled('div', { + width: "100%", + display: 'flex', + flexFlow: "row wrap", + justifyContent: "center", + rowGap: '$1', + columnGap: '$4', +}) + +const WorkshopsContainerHomePage = (props: PropTypes) => { + return ( + + { + props.allWorkshopsOffering?.map((offering) => ( + +
+ +
+ + )) + + }
+ ) +} + +export default WorkshopsContainerHomePage + diff --git a/contentlayer.config.js b/contentlayer.config.js index 2774da6..f2067eb 100644 --- a/contentlayer.config.js +++ b/contentlayer.config.js @@ -69,22 +69,26 @@ export const CourseRevisionOffering = defineDocumentType(() => ({ fields: { title: { type: 'string', - description: 'The title of the exercise set e.g. COMP1511 22T3 Revision Session', + description: + 'The title of the exercise set e.g. COMP1511 22T3 Revision Session', required: true }, desc: { type: 'string', - description: 'A brief 1-2 sentence description of what this course revision contains', + description: + 'A brief 1-2 sentence description of what this course revision contains', required: true }, course: { type: 'string', - description: 'The course that the revision set relates to (COMP1511, COMP2521, ...)', + description: + 'The course that the revision set relates to (COMP1511, COMP2521, ...)', required: true }, offering: { type: 'string', - description: 'The offering of the course that the revision set is intended for (22T3, 23T1, ...)', + description: + 'The offering of the course that the revision set is intended for (22T3, 23T1, ...)', required: true } }, @@ -108,7 +112,71 @@ export const CourseRevisionExercise = defineDocumentType(() => ({ }, class: { type: 'string', - description: 'The class the exercise relates to (COMP1511, COMP2521, etc)', + description: + 'The class the exercise relates to (COMP1511, COMP2521, etc)', + required: true + }, + difficulty: { + type: 'number', + description: 'The difficulty of the exercise (1=Easy, 2=Medium, 3=Hard)', + required: true + } + }, + computedFields +})) + +export const WorkshopsOffering = defineDocumentType(() => ({ + name: 'WorkshopsOffering', + filePathPattern: `workshops/*.mdx`, + contentType: 'mdx', + fields: { + title: { + type: 'string', + description: + 'The title of the exercise set e.g. COMP2521 Fundamentals Workshop', + required: true + }, + desc: { + type: 'string', + description: + 'A brief 1-2 sentence description of what this workshop contains', + required: true + }, + course: { + type: 'string', + description: + 'The course that the revision set relates to (COMP1511, COMP2521, ...)', + required: true + }, + offering: { + type: 'string', + description: + 'The offering of the course that the revision set is intended for (22T3, 23T1, ...)', + required: true + } + }, + computedFields +})) + +export const WorkshopsExercise = defineDocumentType(() => ({ + name: 'WorkshopsExercise', + filePathPattern: `workshops/**/*.mdx`, + contentType: 'mdx', + fields: { + title: { + type: 'string', + description: 'The title of the exercise', + required: true + }, + desc: { + type: 'string', + description: 'One sentence that summarises the exercise objective.', + required: true + }, + class: { + type: 'string', + description: + 'The class the exercise relates to (COMP1511, COMP2521, etc)', required: true }, difficulty: { @@ -122,7 +190,13 @@ export const CourseRevisionExercise = defineDocumentType(() => ({ export default makeSource({ contentDirPath: 'data', - documentTypes: [ArticleType, CourseRevisionOffering, CourseRevisionExercise], + documentTypes: [ + ArticleType, + CourseRevisionOffering, + CourseRevisionExercise, + WorkshopsOffering, + WorkshopsExercise + ], mdx: { remarkPlugins: [remarkGfm], rehypePlugins: [ diff --git a/pages/articles.tsx b/pages/articles.tsx index 1db11a7..e7df9d4 100644 --- a/pages/articles.tsx +++ b/pages/articles.tsx @@ -3,7 +3,7 @@ import { Flex } from 'components/Flex' import { Text } from 'components/Text' import { Button } from 'components/Button' import ArticleCard from 'components/ArticleCard' -import { ArticleType, allArticleTypes, allCourseRevisionOfferings } from 'contentlayer/generated' +import { ArticleType, allArticleTypes, allCourseRevisionOfferings, allWorkshopsOfferings } from 'contentlayer/generated' import { compareDesc } from 'date-fns' import { NextPage } from 'next' import Head from 'next/head' @@ -13,6 +13,7 @@ import { MagnifyingGlass } from 'phosphor-react' import { ArticleRow } from '../components/ArticleRow' import { ArticlesCarousel } from 'components/ArticlesCarousel' import CourseRevisionContainerHomePage from 'components/course-revision/CourseRevisionContainerHomePage' +import WorkshopsContainerHomePage from 'components/workshops/WorkshopsContainerHomePage' export async function getStaticProps() { const articles = allArticleTypes.sort((a, b) => { @@ -23,7 +24,7 @@ export async function getStaticProps() { ) const flattenedTags = tagLists.flat(1) const allTags = [...['All Topics'], ...new Set(flattenedTags)] - return { props: { articles, allTags, courseOfferingContent: allCourseRevisionOfferings } } + return { props: { articles, allTags, courseOfferingContent: allCourseRevisionOfferings, workshopOfferingContent: allWorkshopsOfferings } } } const SearchBar = styled('input', { @@ -43,7 +44,7 @@ const TagsContainer = styled('div', { }) // I'm tired, I didn't type this properly ok -const Articles: NextPage = ({ articles, allTags, courseOfferingContent }: any) => { +const Articles: NextPage = ({ articles, allTags, courseOfferingContent, workshopOfferingContent }: any) => { const [currentTag, setCurrentTag] = useState('All Topics') const [currentSearch, setCurrentSearch] = useState('') const [filteredArticles, setFilteredArticles] = useState(articles) @@ -135,6 +136,29 @@ const Articles: NextPage = ({ articles, allTags, courseOfferingContent }: any) = + {/* Uncomment once we have content for workshops */} + + {/* + + Workshops + + + Explore the many workshops our Education Team has curated to become big brain. + + + */} + diff --git a/pages/index.tsx b/pages/index.tsx index 1bd1c91..8fe70bb 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -3,7 +3,7 @@ import { Button } from 'components/Button' import { Card } from 'components/Card' import { Flex } from 'components/Flex' import { Text } from 'components/Text' -import { allArticleTypes, allCourseRevisionOfferings, ArticleType, CourseRevisionOffering } from 'contentlayer/generated' +import { allArticleTypes, allCourseRevisionOfferings, ArticleType, CourseRevisionOffering, WorkshopsOffering, allWorkshopsOfferings } from 'contentlayer/generated' import { compareAsc, compareDesc } from 'date-fns' import type { NextPage } from 'next' import Head from 'next/head' @@ -15,20 +15,22 @@ import CourseRevisionCard from 'components/course-revision/CourseRevisionCard' import { styled } from '@stitches/react' import CourseRevisionMiniCard from 'components/course-revision/CourseRevisionMiniCard' import CourseRevisionContainerHomePage from 'components/course-revision/CourseRevisionContainerHomePage' +import WorkshopsContainerHomePage from 'components/workshops/WorkshopsContainerHomePage' type PropTypes = { articles: ArticleType[], courseOfferingContent: CourseRevisionOffering[], + workshopOfferingContent: WorkshopsOffering[] } export async function getStaticProps() { const articles = allArticleTypes.sort((a, b) => { return compareAsc(new Date(b.date), new Date(a.date)) }) - return { props: { articles, courseOfferingContent: allCourseRevisionOfferings } } + return { props: { articles, courseOfferingContent: allCourseRevisionOfferings, workshopOfferingContent: allWorkshopsOfferings } } } -const Home: NextPage = ({ articles, courseOfferingContent }: PropTypes) => { +const Home: NextPage = ({ articles, courseOfferingContent, workshopOfferingContent }: PropTypes) => { return ( @@ -87,6 +89,28 @@ const Home: NextPage = ({ articles, courseOfferingContent }: PropTypes) => { + {/* Uncomment once we have content for workshops */} + + {/* + + Workshops + + + Explore the many workshops our Education Team has curated to become big brain. + + + */} { + allWorkshopsExercises.forEach((e) => { + if (e._raw.sourceFileDir.endsWith(o.slug)) { + paths.push({ params: { course_offering: o.slug, exercise: e.slug } }) + } + }) + }) + + return { + paths, + fallback: false + } +} + +export async function getStaticProps({ params }) { + const exercisesContent = allWorkshopsExercises.filter((e) => e._raw.sourceFileDir.endsWith(params.course_offering)).sort((a, b) => a.difficulty - b.difficulty) + const exerciseIdx = exercisesContent.findIndex((e) => e.slug === params.exercise) + return { + props: { + courseOfferingContent: allWorkshopsOfferings.find((c) => c.slug === params.course_offering), + exercisesContent, + exerciseIdx, + } + } +} + +const ExercisePage = ({ courseOfferingContent, exercisesContent, exerciseIdx }: PropTypes) => { + const MDXContent = useMDXComponent(exercisesContent[exerciseIdx].body.code) + + return ( + + + + {MDXContent && } + + + + ) +} + +export default ExercisePage diff --git a/pages/workshops/[course_offering]/index.tsx b/pages/workshops/[course_offering]/index.tsx new file mode 100644 index 0000000..c294862 --- /dev/null +++ b/pages/workshops/[course_offering]/index.tsx @@ -0,0 +1,69 @@ +import { Card } from '@modulz/design-system' +import { styled } from '@stitches/react' +import ArticleLayout from 'components/ArticleLayout' +import { Button } from 'components/Button' +import ContentContainer from 'components/course-revision/ContentContainer' +import CourseRevisionSidebar from 'components/course-revision/CourseRevisionSidebar' +import { ProblemCard } from 'components/ProblemCard' +import { Tag } from 'components/Tag' +import { Text } from 'components/Text' +import { allCourseRevisionOfferings, allCourseRevisionExercises, CourseRevisionOffering, WorkshopsOffering, allWorkshopsOfferings, allWorkshopsExercises } from 'contentlayer/generated' +import { InferGetStaticPropsType } from 'next' +import { useMDXComponent } from 'next-contentlayer/hooks' +import Link from 'next/link' +import Router, { useRouter } from 'next/router' +import { ArrowLeft } from 'phosphor-react' +import { useEffect, useState } from 'react' + +export async function getStaticPaths() { + const paths = allWorkshopsOfferings.map((o) => ({ params: { course_offering: o.slug } })) + return { + paths, + fallback: false + } +} + +export const getStaticProps = async ({ params }) => { + const courseOfferingContent = allWorkshopsOfferings.find( + (c) => c.slug === params.course_offering + ) + + return { + props: { + exercisesContent: allWorkshopsExercises.filter((e) => e._raw.sourceFileDir.endsWith(params.course_offering)).sort((a, b) => a.difficulty - b.difficulty), + courseOfferingContent + } + } +} + +const ExercisesPage = ({ + exercisesContent, + courseOfferingContent +}: InferGetStaticPropsType) => { + const MDXContent = useMDXComponent(courseOfferingContent.body.code) + + return ( +
+ + + + {MDXContent && } + {/* {exercisesContent?.map((exercise) => ( + + +

{exercise.title}

+ Pain score: {exercise.difficulty} 💀 +

{exercise.desc}

+
+ + ))} */} + +
+
+
+ ) +} + +export default ExercisesPage diff --git a/pages/workshops/index.tsx b/pages/workshops/index.tsx new file mode 100644 index 0000000..c8a2927 --- /dev/null +++ b/pages/workshops/index.tsx @@ -0,0 +1,58 @@ +import { Card } from '@modulz/design-system' +import { styled } from '@stitches/react' +import ArticleLayout from 'components/ArticleLayout' +import CourseRevisionCard from 'components/course-revision/CourseRevisionCard' +import { Tag } from 'components/Tag' +import { allWorkshopsOfferings } from 'contentlayer/generated' +import { InferGetStaticPropsType } from 'next' +import { useMDXComponent } from 'next-contentlayer/hooks' +import Link from 'next/link' +import Router, { useRouter } from 'next/router' + +export const getStaticProps = async ({ params }) => { + return { + props: { + allWorkshopsOfferings, + } + } +} + +const ExercisesPage = ({ + allWorkshopsOfferings, +}: InferGetStaticPropsType) => { + const router = useRouter() + return ( +
+ +

Workshops

+ {allWorkshopsOfferings?.map((offering) => ( + +
+ +

{offering.title}

+

{offering.desc}

+
+
+ + ))} +
+
+ ) +} + +export default ExercisesPage + +const H1 = styled('h1', { + fontSize: "1.4rem", + marginBottom: "0.2rem", + lineHeight: "1.6rem", + '@media (min-width: 640px)': { + fontSize: "1.8rem", + marginBottom: "0.2rem", + lineHeight: "2.2rem", + }, +}) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 995ab14..e7d2ce7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5398,4 +5398,4 @@ zod@^3.21.4: zwitch@^2.0.0, zwitch@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz" - integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== \ No newline at end of file