Files
NextEdu/src/modules/dashboard/components/teacher-dashboard/teacher-schedule.tsx
SpecialX 138b6f1b00 feat(dashboard,diagnostic,elective): add widgets, layout, parent dashboard, role-config, services, elective components
dashboard:

- Add comparison-badge, dashboard-notification-widget, dashboard-responsive-layout, dashboard-time-range-filter

- Add parent-dashboard components directory

- Add config, hooks, and services directories

diagnostic:

- Add role-config and services directory

elective:

- Add elective-course-detail, elective-stats-cards, parent-selection-view components

- Add data-access-settings and data-access-stats
2026-07-03 10:25:46 +08:00

126 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from "next/link"
import { getTranslations } from "next-intl/server"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { Badge } from "@/shared/components/ui/badge"
import { CalendarDays, CalendarX, MapPin } from "lucide-react"
import { EmptyState } from "@/shared/components/ui/empty-state"
import { cn } from "@/shared/lib/utils"
import { ScrollArea } from "@/shared/components/ui/scroll-area"
import { getScheduleStatus } from "@/modules/dashboard/lib/dashboard-utils"
import type { TeacherTodayScheduleItem } from "@/modules/dashboard/types"
export async function TeacherSchedule({ items }: { items: TeacherTodayScheduleItem[] }) {
const t = await getTranslations("dashboard")
const hasSchedule = items.length > 0
return (
<Card>
<CardHeader className="pb-3">
<CardTitle className="flex items-center gap-2 text-base">
<CalendarDays className="h-4 w-4 text-muted-foreground" />
{t("sections.todaySchedule")}
</CardTitle>
</CardHeader>
<CardContent className="p-0">
{!hasSchedule ? (
<EmptyState
icon={CalendarX}
title={t("empty.noClassesToday")}
description={t("empty.noClassesTodayDesc")}
action={{ label: t("quickActions.viewSchedule"), href: "/teacher/classes/schedule" }}
className="border-none h-[200px]"
/>
) : (
<ScrollArea className="h-[240px] px-6 py-2">
<div className="relative space-y-0 ml-1">
<div className="absolute left-[11px] -top-2 -bottom-2 w-px bg-border/50" />
<div className="absolute left-[11px] -top-3 h-3 w-px bg-gradient-to-t from-border/50 to-transparent" />
{items.map((item, index) => {
const status = getScheduleStatus(item.startTime, item.endTime, new Date())
const isLive = status === "live"
const isPast = status === "past"
const isLast = index === items.length - 1
return (
<div key={item.id} className="relative pl-8 py-2 first:pt-0 last:pb-0 group">
<div className={cn(
"absolute left-[7px] top-[14px] h-2.5 w-2.5 rounded-full border-2 ring-4 ring-background transition-colors z-10",
isLive ? "bg-primary border-primary" :
isPast ? "bg-muted border-muted-foreground/30" :
"bg-background border-primary"
)} />
<Link
href={`/teacher/classes/my/${encodeURIComponent(item.classId)}`}
className={cn(
"block rounded-md border p-2.5 transition-all hover:bg-muted/50",
isLive ? "bg-primary/5 border-primary/50 shadow-sm" :
isPast ? "opacity-60 grayscale bg-muted/20 border-transparent" :
"bg-card"
)}
>
<div className="flex items-start justify-between gap-3">
<div className="space-y-0.5 min-w-0">
<div className="flex items-center gap-2">
<span className={cn(
"font-medium text-sm truncate",
isLive ? "text-primary" : "text-foreground"
)}>
{item.course}
</span>
{isLive && (
<Badge variant="default" className="h-4 px-1 text-[9px] animate-pulse">
{t("badge.live")}
</Badge>
)}
</div>
<div className="flex items-center gap-1.5 text-xs text-muted-foreground truncate">
<span>{item.className}</span>
{item.location && (
<>
<span>·</span>
<span className="flex items-center">
<MapPin className="mr-0.5 h-2.5 w-2.5" />
{item.location}
</span>
</>
)}
</div>
</div>
<div className={cn(
"text-right text-xs font-medium tabular-nums whitespace-nowrap",
isLive ? "text-primary" : "text-muted-foreground"
)}>
{item.startTime}
<span className="text-[10px] opacity-70 ml-0.5"> {item.endTime}</span>
</div>
</div>
</Link>
{!isLast && (
<div className="absolute left-[11px] top-[24px] bottom-[-8px] w-px bg-border" />
)}
</div>
)
})}
{items.length > 3 ? (
<div className="text-[10px] text-center text-muted-foreground pt-2 pb-1 opacity-50">
{t("schedule.scrollForMore")}
</div>
) : (
<div className="text-[10px] text-center text-muted-foreground pt-4 pb-1 opacity-50 italic">
{t("schedule.noMoreClasses")}
</div>
)}
</div>
</ScrollArea>
)}
</CardContent>
</Card>
)
}