## P1 功能(20 项) - 站内消息系统、家长仪表盘、学生考勤管理 - Excel 导入导出、用户批量导入、成绩导出 - 排课规则+自动排课+课表调整 - 成绩趋势+对比分析、密码安全策略、速率限制 - 数据变更日志、文件预览+存储策略、全文检索 - 依赖审计集成 CI、数据库定时备份、E2E 测试完善 - 通知偏好管理 ## 基础设施修复 - src/proxy.ts: 将 middleware 导出重命名为 proxy(Next.js 16 要求) - .env: MySQL 端口从 13002 切换至 14013 - scripts/create-db.ts: 新增数据库初始化脚本 ## 架构文档同步 - 004_architecture_impact_map.md 和 005_architecture_data.json 完整记录所有新增表、模块、路由、权限、依赖关系
95 lines
3.5 KiB
TypeScript
95 lines
3.5 KiB
TypeScript
"use client"
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
|
import { Badge } from "@/shared/components/ui/badge"
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
|
|
import { AlertTriangle, CheckCircle2 } from "lucide-react"
|
|
|
|
import type { AutoScheduleResult } from "../types"
|
|
|
|
const WEEKDAY_LABELS = ["", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
|
|
|
export function AutoScheduleResultView({ result }: { result: AutoScheduleResult }) {
|
|
const sortedSchedules = [...result.schedules].sort(
|
|
(a, b) => a.weekday - b.weekday || a.startTime.localeCompare(b.startTime)
|
|
)
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center justify-between">
|
|
<span>Generated Schedule</span>
|
|
<div className="flex items-center gap-2 text-sm font-normal">
|
|
<Badge variant="secondary">{result.scheduledCount} sessions</Badge>
|
|
<Badge variant={result.conflictCount > 0 ? "destructive" : "default"}>
|
|
{result.conflictCount} conflicts
|
|
</Badge>
|
|
</div>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{sortedSchedules.length === 0 ? (
|
|
<p className="text-muted-foreground text-sm">No sessions generated.</p>
|
|
) : (
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Day</TableHead>
|
|
<TableHead>Start</TableHead>
|
|
<TableHead>End</TableHead>
|
|
<TableHead>Course</TableHead>
|
|
<TableHead>Location</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{sortedSchedules.map((s, idx) => (
|
|
<TableRow key={`${s.weekday}-${s.startTime}-${idx}`}>
|
|
<TableCell>{WEEKDAY_LABELS[s.weekday] ?? `Day ${s.weekday}`}</TableCell>
|
|
<TableCell>{s.startTime}</TableCell>
|
|
<TableCell>{s.endTime}</TableCell>
|
|
<TableCell className="font-medium">{s.course}</TableCell>
|
|
<TableCell>{s.location ?? "-"}</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{result.conflicts.length > 0 && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<AlertTriangle className="text-destructive h-5 w-5" />
|
|
<span>Conflicts & Warnings</span>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ul className="space-y-2 text-sm">
|
|
{result.conflicts.map((c, idx) => (
|
|
<li key={idx} className="text-muted-foreground">
|
|
<Badge variant="outline" className="mr-2">
|
|
{c.type}
|
|
</Badge>
|
|
{c.description}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{result.conflicts.length === 0 && result.scheduledCount > 0 && (
|
|
<Card>
|
|
<CardContent className="flex items-center gap-2 py-4 text-sm">
|
|
<CheckCircle2 className="text-primary h-5 w-5" />
|
|
<span>No conflicts detected. The schedule is ready to apply.</span>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|