diff --git a/src/app/[lang]/courses/[courseId]/page.tsx b/src/app/[lang]/courses/[courseId]/page.tsx index 8916c74e..d751a4ba 100644 --- a/src/app/[lang]/courses/[courseId]/page.tsx +++ b/src/app/[lang]/courses/[courseId]/page.tsx @@ -1,12 +1,19 @@ import Fade from "@/components/Animation/Fade"; -import supabase from "@/config/supabase"; import { getDictionary } from "@/dictionaries/dictionaries"; import {getCourse, getCoursePTTReview} from '@/lib/course'; import { LangProps } from "@/types/pages"; -import { Accordion, AccordionDetails, AccordionGroup, AccordionSummary, Alert, Chip, Divider } from "@mui/joy"; +import {Accordion, AccordionDetails, AccordionGroup, AccordionSummary, Alert, Chip, Divider, Button} from '@mui/joy'; import { format } from "date-fns"; -import { NextPage, ResolvingMetadata } from "next"; -import { AlertTriangle } from "react-feather"; +import { ResolvingMetadata } from "next"; +import {AlertTriangle, Minus, Plus} from 'react-feather'; +import { redirect } from 'next/navigation' +import CourseTagList from "@/components/Courses/CourseTagsList"; +import {useSettings} from '@/hooks/contexts/settings'; +import { useMemo } from "react"; +import SelectCourseButton from '@/components/Courses/SelectCourseButton'; +import { createTimetableFromCourses } from "@/helpers/timetable"; +import Timetable from "@/components/Timetable/Timetable"; +import { MinimalCourse } from "@/types/courses"; type PageProps = { params: { courseId? : string } @@ -27,25 +34,45 @@ const CourseDetailPage = async ({ params }: PageProps & LangProps) => { const reviews = await getCoursePTTReview(courseId); const dict = await getDictionary(params.lang); + if(!course) return redirect('/'); + + const timetableData = createTimetableFromCourses([course as MinimalCourse]); + return -
+
-

{`${course?.department} ${course?.course}-${course?.class}`}

-

{course!.name_zh} - {course?.teacher_zh?.join(',')?? ""}

-

{course!.name_en} - {course?.teacher_en?.join(',')?? ""}

- -

{dict.course.details.semesterid}: {course?.raw_id} • {dict.course.credits}: {course?.credits}

-

{dict.course.details.language}: {course?.language == '英' ? 'English' : 'Chinese'}

-

{dict.course.details.capacity}: {course?.capacity} • {dict.course.details.reserved}: {course?.reserve}

-

{dict.course.details.class}: {course?.class}

+
+
+
+

{course.semester} 學期

+

{`${course?.department} ${course?.course}-${course?.class}`}

+

{course!.name_zh} - {course?.teacher_zh?.join(',')?? ""}

+

{course!.name_en} - {course?.teacher_en?.join(',')?? ""}

+
+ +
+
+
+

時間地點

+ {course.venues? + course.venues.map((vn, i) =>

{vn} {course.times![i]}

) : +

No Venues

+ } +
+ +
+
-
+

{dict.course.details.description}

-

{course?.備註}

+

{course?.note}

+
+
+

時間表

+
- {reviews.length > 0 &&

{dict.course.details.ptt_title}

}> @@ -66,7 +93,7 @@ const CourseDetailPage = async ({ params }: PageProps & LangProps) => {

{dict.course.details.restrictions}

-

{course?.課程限制說明}

+

{course?.restrictions}

{dict.course.details.compulsory}

diff --git a/src/components/Courses/CourseListItem.tsx b/src/components/Courses/CourseListItem.tsx index 1aa61bbd..119261bc 100644 --- a/src/components/Courses/CourseListItem.tsx +++ b/src/components/Courses/CourseListItem.tsx @@ -1,29 +1,18 @@ +'use client'; import { CourseDefinition } from '@/config/supabase'; import useDictionary from '@/dictionaries/useDictionary'; import { useSettings } from '@/hooks/contexts/settings'; -import { Button, Chip, Tooltip } from '@mui/joy'; +import { Button, Tooltip } from '@mui/joy'; +import { FC, useMemo } from 'react'; +import { Minus, Plus } from 'react-feather'; import Link from 'next/link'; -import { DetailedHTMLProps, FC, HTMLAttributes, PropsWithChildren, useMemo } from 'react'; -import { Minus, Plus, Users } from 'react-feather'; -import {getGECType} from '@/helpers/courses'; +import CourseTagList from './CourseTagsList'; +import SelectCourseButton from './SelectCourseButton'; -const HighlightItem: FC, HTMLDivElement>>> = ({ - children, - className = "", - ...props -}) => { - return
- {children} -
-} const CourseListItem: FC<{ course: CourseDefinition }> = ({ course }) => { - const { courses, setCourses } = useSettings(); const dict = useDictionary(); - const isCourseSelected = useMemo(() => courses.includes(course.raw_id ?? ""), [courses, course]); + return
@@ -32,12 +21,12 @@ const CourseListItem: FC<{ course: CourseDefinition }> = ({ course }) => {

{course.name_en} - {(course.teacher_en ?? []).join(',')}

-

{course.課程限制說明}

-

{course.備註}

- {course.擋修說明 && +

{course.restrictions}

+

{course.note}

+ {course.prerequisites &&

} + title={

} >

有儅修

} @@ -51,67 +40,8 @@ const CourseListItem: FC<{ course: CourseDefinition }> = ({ course }) => {

No Venues

}
-
- - - {course.capacity ?? '-'} - {(course.reserve ?? 0) > 0 && <> - {` 保 ${course.reserve}`} - } - - - - - - {course.credits} - {dict.course.credits} - - {course.tags.includes('16周') && - 16 週 - } - {course.tags.includes('18周') && - 18 週 - } - {course.language == '英' ? - - English - : - - 國語 - - } - {/* bg-indigo-50 text-indigo-900 dark:bg-indigo-950 dark:text-indigo-100 */} - {course.tags.includes('X-Class') && - - X-Class - } - {(course.ge_target?.trim() || "").length > 0 && - - {course.ge_target} 通識 - } - {getGECType(course.ge_type || "") && - - 核通 {getGECType(course.ge_type!)} - } - - -
- {isCourseSelected ? - : - } + +
diff --git a/src/components/Courses/CourseTagsList.tsx b/src/components/Courses/CourseTagsList.tsx new file mode 100644 index 00000000..f05d8193 --- /dev/null +++ b/src/components/Courses/CourseTagsList.tsx @@ -0,0 +1,71 @@ +'use client'; +import { CourseDefinition } from '@/config/supabase'; +import useDictionary from '@/dictionaries/useDictionary'; +import { getGECType } from '@/helpers/courses'; +import { DetailedHTMLProps, FC, HTMLAttributes, PropsWithChildren } from 'react'; +import { Users } from 'react-feather'; + +const HighlightItem: FC, HTMLDivElement>>> = ({ + children, + className = "", + ...props +}) => { + return
+ {children} +
+} +const CourseTagList = ({ course }: { course: CourseDefinition }) => { + const dict = useDictionary(); + return ( +
+ + + {course.capacity ?? '-'} + {(course.reserve ?? 0) > 0 && <> + {` 保 ${course.reserve}`} + } + + + + + + {course.credits} + {dict.course.credits} + + {course.tags.includes('16周') && + 16 週 + } + {course.tags.includes('18周') && + 18 週 + } + {course.language == '英' ? + + English + : + + 國語 + + } + {/* bg-indigo-50 text-indigo-900 dark:bg-indigo-950 dark:text-indigo-100 */} + {course.tags.includes('X-Class') && + + X-Class + } + {(course.ge_target?.trim() || "").length > 0 && + + {course.ge_target} 通識 + } + {getGECType(course.ge_type || "") && + + 核通 {getGECType(course.ge_type!)} + } + + +
+ ) +} + +export default CourseTagList; \ No newline at end of file diff --git a/src/components/Courses/SelectCourseButton.tsx b/src/components/Courses/SelectCourseButton.tsx new file mode 100644 index 00000000..98b031a3 --- /dev/null +++ b/src/components/Courses/SelectCourseButton.tsx @@ -0,0 +1,32 @@ +'use client'; +import useDictionary from "@/dictionaries/useDictionary"; +import { useSettings } from "@/hooks/contexts/settings" +import { RawCourseID } from "@/types/courses"; +import { Button } from "@mui/joy"; +import { useMemo } from "react"; +import { Minus, Plus } from "react-feather"; + +const SelectCourseButton = ({ courseId }: { courseId: RawCourseID }) => { + const { courses, setCourses } = useSettings(); + const dict = useDictionary(); + + const isCourseSelected = useMemo(() => courses.includes(courseId), [courses, courseId]); + + if(isCourseSelected) return + else return +} + +export default SelectCourseButton; \ No newline at end of file diff --git a/src/components/FormComponents/SelectControl.tsx b/src/components/FormComponents/SelectControl.tsx index b1c598d1..dfa6b100 100644 --- a/src/components/FormComponents/SelectControl.tsx +++ b/src/components/FormComponents/SelectControl.tsx @@ -14,7 +14,7 @@ const SelectControl = ({ control, onChange={(e,v) => onChange(v)} {...rest} > - {options.map(option => )} + {options.map(option => )} )} /> diff --git a/src/types/supabase.ts b/src/types/supabase.ts index 241e8135..3f548a4c 100644 --- a/src/types/supabase.ts +++ b/src/types/supabase.ts @@ -610,11 +610,11 @@ export interface Database { teacher_zh: string[] times: string[] venues: string[] - 停開註記: string | null - 備註: string | null - 必選修說明: string | null - 擋修說明: string | null - 課程限制說明: string | null + closed_mark: string | null + note: string | null + raw_compulsory_elective: string | null + prerequisites: string | null + restrictions: string | null multilang_search: string | null time_slots: unknown | null } @@ -651,11 +651,11 @@ export interface Database { teacher_zh: string[] times: string[] venues: string[] - 停開註記?: string | null - 備註?: string | null - 必選修說明?: string | null - 擋修說明?: string | null - 課程限制說明?: string | null + closed_mark?: string | null + note?: string | null + raw_compulsory_elective?: string | null + prerequisites?: string | null + restrictions?: string | null } Update: { capacity?: number | null @@ -690,11 +690,11 @@ export interface Database { teacher_zh?: string[] times?: string[] venues?: string[] - 停開註記?: string | null - 備註?: string | null - 必選修說明?: string | null - 擋修說明?: string | null - 課程限制說明?: string | null + closed_mark?: string | null + note?: string | null + raw_compulsory_elective?: string | null + prerequisites?: string | null + restrictions?: string | null } Relationships: [] } @@ -1381,11 +1381,11 @@ export interface Database { teacher_zh: string[] times: string[] venues: string[] - 停開註記: string | null - 備註: string | null - 必選修說明: string | null - 擋修說明: string | null - 課程限制說明: string | null + closed_mark: string | null + note: string | null + raw_compulsory_elective: string | null + prerequisites: string | null + restrictions: string | null }[] } set_limit: {