feat: enhance textbook reader with anchor text support and improve knowledge point management
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
|
||||
import Link from "next/link"
|
||||
import { Calendar, ChevronRight, Clock, MapPin } from "lucide-react"
|
||||
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/shared/components/ui/hover-card"
|
||||
import type { ClassScheduleItem } from "@/modules/classes/types"
|
||||
|
||||
interface ClassScheduleWidgetProps {
|
||||
classId: string
|
||||
schedule: ClassScheduleItem[]
|
||||
}
|
||||
|
||||
const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||
const WEEKDAY_INDICES = [1, 2, 3, 4, 5, 6, 7] // 1=Mon, 7=Sun
|
||||
|
||||
export function ClassScheduleGrid({ schedule, compact = false }: { schedule: ClassScheduleItem[], compact?: boolean }) {
|
||||
// Group by weekday
|
||||
const groupedSchedule = schedule.reduce((acc, item) => {
|
||||
const day = item.weekday
|
||||
if (!acc[day]) acc[day] = []
|
||||
acc[day].push(item)
|
||||
return acc
|
||||
}, {} as Record<number, ClassScheduleItem[]>)
|
||||
|
||||
// Sort items within each day by start time
|
||||
Object.keys(groupedSchedule).forEach(key => {
|
||||
groupedSchedule[Number(key)].sort((a, b) => a.startTime.localeCompare(b.startTime))
|
||||
})
|
||||
|
||||
if (schedule.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center space-y-3 py-6 text-center">
|
||||
<div className="rounded-full bg-muted p-3">
|
||||
<Calendar className="h-6 w-6 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">No sessions scheduled.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-5 gap-1 text-center h-full grid-rows-[auto_1fr]">
|
||||
{WEEKDAYS.slice(0, 5).map((day, i) => (
|
||||
<div key={day} className="text-[10px] font-medium text-muted-foreground uppercase py-0.5 border-b bg-muted/20 h-fit">
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{WEEKDAY_INDICES.slice(0, 5).map((dayNum) => {
|
||||
const items = groupedSchedule[dayNum] || []
|
||||
return (
|
||||
<div key={dayNum} className={`flex flex-col gap-1 py-1 border-r last:border-r-0 border-muted/30 ${compact ? 'max-h-[140px]' : 'min-h-[100px]'}`}>
|
||||
{items.length === 0 ? (
|
||||
<div className="flex-1" />
|
||||
) : (
|
||||
items.map(item => (
|
||||
<HoverCard key={item.id}>
|
||||
<HoverCardTrigger asChild>
|
||||
<div className="bg-primary/5 text-primary rounded-[2px] p-1 text-[10px] text-left relative hover:bg-primary/10 transition-colors cursor-default leading-tight shrink-0">
|
||||
<div className="font-semibold truncate">{item.course}</div>
|
||||
<div className="opacity-70 scale-90 origin-left mt-0.5 whitespace-nowrap">{item.startTime}-{item.endTime}</div>
|
||||
</div>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="w-48 p-3" align="start" side="top">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="font-semibold text-sm border-b pb-1 mb-1">{item.course}</div>
|
||||
<div className="flex items-center gap-2 text-muted-foreground text-xs">
|
||||
<Clock className="h-3.5 w-3.5 shrink-0" />
|
||||
<span>{item.startTime} - {item.endTime}</span>
|
||||
</div>
|
||||
{item.location && (
|
||||
<div className="flex items-center gap-2 text-muted-foreground text-xs">
|
||||
<MapPin className="h-3.5 w-3.5 shrink-0" />
|
||||
<span>{item.location}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ClassScheduleWidget({ classId, schedule }: ClassScheduleWidgetProps) {
|
||||
return (
|
||||
<Card className="h-fit">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-base font-semibold">Weekly Schedule</CardTitle>
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
<Link href={`/teacher/classes/schedule?classId=${encodeURIComponent(classId)}`}>
|
||||
Manage
|
||||
<ChevronRight className="ml-2 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-4">
|
||||
<ClassScheduleGrid schedule={schedule} />
|
||||
<div className="mt-2 text-[10px] text-muted-foreground text-center">
|
||||
* Showing Mon-Fri schedule
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user