- Update classes data-access (invitations, main) for invitation management - Update course-plans actions, data-access, and types - Update diagnostic data-access for report queries - Update questions data-access for question bank queries - Update settings actions, ai-provider-settings-card, data-access, and types - Update student course-filters, student-courses-view, student-schedule-filters, student-schedule-view - Update layout app-sidebar, site-header, and navigation config
98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
|
import { EmptyState } from "@/shared/components/ui/empty-state"
|
|
import { CalendarX } from "lucide-react"
|
|
import { ScheduleList } from "@/shared/components/schedule/schedule-list"
|
|
import { cn } from "@/shared/lib/utils"
|
|
import { useTranslations } from "next-intl"
|
|
|
|
import type { StudentScheduleItem } from "@/modules/classes/types"
|
|
|
|
const WEEKDAY_KEYS: Array<{ key: 1 | 2 | 3 | 4 | 5 | 6 | 7; label: string }> = [
|
|
{ key: 1, label: "mon" },
|
|
{ key: 2, label: "tue" },
|
|
{ key: 3, label: "wed" },
|
|
{ key: 4, label: "thu" },
|
|
{ key: 5, label: "fri" },
|
|
{ key: 6, label: "sat" },
|
|
{ key: 7, label: "sun" },
|
|
]
|
|
|
|
const getTodayWeekday = (): 1 | 2 | 3 | 4 | 5 | 6 | 7 => {
|
|
// getDay() returns 0 (Sun) - 6 (Sat); convert to 1 (Mon) - 7 (Sun)
|
|
const WEEKDAY_MAP = [7, 1, 2, 3, 4, 5, 6] as const
|
|
const day = new Date().getDay()
|
|
if (day < 0 || day > 6) {
|
|
throw new Error(`Invalid day from getDay(): ${day}`)
|
|
}
|
|
return WEEKDAY_MAP[day]
|
|
}
|
|
|
|
export function StudentScheduleView({ items }: { items: StudentScheduleItem[] }) {
|
|
const t = useTranslations("student")
|
|
|
|
if (items.length === 0) {
|
|
return (
|
|
<EmptyState
|
|
icon={CalendarX}
|
|
title={t("scheduleView.noSchedule")}
|
|
description={t("scheduleView.noScheduleDesc")}
|
|
className="h-80"
|
|
/>
|
|
)
|
|
}
|
|
|
|
const todayKey = getTodayWeekday()
|
|
|
|
const itemsByDay = new Map<number, StudentScheduleItem[]>()
|
|
for (const item of items) {
|
|
const list = itemsByDay.get(item.weekday) ?? []
|
|
list.push(item)
|
|
itemsByDay.set(item.weekday, list)
|
|
}
|
|
for (const list of itemsByDay.values()) {
|
|
list.sort((a, b) => a.startTime.localeCompare(b.startTime))
|
|
}
|
|
|
|
return (
|
|
<div className="grid gap-4 lg:grid-cols-2">
|
|
{WEEKDAY_KEYS.map((d) => {
|
|
const dayItems = itemsByDay.get(d.key) ?? []
|
|
const isToday = d.key === todayKey
|
|
return (
|
|
<Card
|
|
key={d.key}
|
|
className={cn(
|
|
isToday && "border-primary ring-1 ring-primary/30 shadow-sm"
|
|
)}
|
|
>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="flex items-center gap-2 text-sm font-medium">
|
|
<span>{t(`weekdays.${d.label}`)}</span>
|
|
{isToday && (
|
|
<span className="rounded-full bg-primary px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-primary-foreground">
|
|
{t("scheduleView.today")}
|
|
</span>
|
|
)}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{dayItems.length === 0 ? (
|
|
<div className="text-sm text-muted-foreground">{t("scheduleView.noClasses")}</div>
|
|
) : (
|
|
<ScheduleList
|
|
items={dayItems.map((item) => ({
|
|
...item,
|
|
href: `/student/learning/courses/${encodeURIComponent(item.classId)}`,
|
|
}))}
|
|
variant="card"
|
|
spacingClassName="space-y-3"
|
|
/>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|