diff --git a/src/app/lectureInformation/page.tsx b/src/app/lectureInformation/page.tsx new file mode 100644 index 0000000..bd6ac37 --- /dev/null +++ b/src/app/lectureInformation/page.tsx @@ -0,0 +1,256 @@ +import type { Metadata } from "next"; +import { api } from "@/lib/api"; + +export const metadata: Metadata = { + title: "休講・補講・教室変更", + description: "休講・補講・教室変更情報の一覧", +}; + +type LectureInfoType = "Cancelled" | "Makeup" | "RoomChanged"; + +type LectureInfoItem = { + id: string; + type: LectureInfoType; + date: string; + period: string; + subjectName: string; + detail: string; +}; + +function formatDate(iso: string): { year: string; month: string; day: string } { + const d = new Date(`${iso}T00:00:00`); + return { + year: String(d.getFullYear()), + month: String(d.getMonth() + 1).padStart(2, "0"), + day: String(d.getDate()).padStart(2, "0"), + }; +} + +function periodLabel(period: string): string { + return period.replace("Period", "") + "限"; +} + +function typeLabel(type: LectureInfoType): string { + if (type === "Cancelled") return "休講"; + if (type === "Makeup") return "補講"; + return "教室変更"; +} + +function typeBadgeClass(type: LectureInfoType): string { + if (type === "Cancelled") { + return "border-accent-error/30 text-accent-error bg-accent-error/5"; + } + if (type === "Makeup") { + return "border-accent-info/30 text-accent-info bg-accent-info/5"; + } + return "border-border-primary text-label-secondary bg-background-secondary"; +} + +function sortByDateDesc(a: LectureInfoItem, b: LectureInfoItem): number { + const aTime = new Date(`${a.date}T00:00:00`).getTime(); + const bTime = new Date(`${b.date}T00:00:00`).getTime(); + return bTime - aTime; +} + +function sectionLabel(type: LectureInfoType): string { + return typeLabel(type); +} + +function sectionId(type: LectureInfoType): string { + if (type === "Cancelled") return "cancelled"; + if (type === "Makeup") return "makeup"; + return "room-changed"; +} + +export default async function Page() { + const [cancelledRes, makeupRes, roomChangeRes] = await Promise.all([ + api.GET("/v1/cancelledClasses"), + api.GET("/v1/makeupClasses"), + api.GET("/v1/roomChanges"), + ]); + + const hasError = !!( + cancelledRes.error || + makeupRes.error || + roomChangeRes.error || + !cancelledRes.data || + !makeupRes.data || + !roomChangeRes.data + ); + + const cancelledItems: LectureInfoItem[] = hasError + ? [] + : cancelledRes.data.cancelledClasses + .map((item) => ({ + id: item.id, + type: "Cancelled" as const, + date: item.date, + period: item.period, + subjectName: item.subject.name, + detail: item.comment, + })) + .sort(sortByDateDesc); + + const makeupItems: LectureInfoItem[] = hasError + ? [] + : makeupRes.data.makeupClasses + .map((item) => ({ + id: item.id, + type: "Makeup" as const, + date: item.date, + period: item.period, + subjectName: item.subject.name, + detail: item.comment, + })) + .sort(sortByDateDesc); + + const roomChangedItems: LectureInfoItem[] = hasError + ? [] + : roomChangeRes.data.roomChanges + .map((item) => ({ + id: item.id, + type: "RoomChanged" as const, + date: item.date, + period: item.period, + subjectName: item.subject.name, + detail: `${item.originalRoom.name} → ${item.newRoom.name}`, + })) + .sort(sortByDateDesc); + + const sections: { type: LectureInfoType; items: LectureInfoItem[] }[] = [ + { type: "Cancelled", items: cancelledItems }, + { type: "Makeup", items: makeupItems }, + { type: "RoomChanged", items: roomChangedItems }, + ]; + + const totalCount = + cancelledItems.length + makeupItems.length + roomChangedItems.length; + + return ( +
+ Lecture / Information +
++ {totalCount} 件 +
+ )} ++ 情報の取得に失敗しました。 +
+現在、情報はありません
++ 該当する情報はありません +
++ {item.subjectName} +
++ {item.detail} +
+