完整性更新
现在已经实现了大部分基础功能
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import type { HomeworkAssignmentQuestionAnalytics } from "@/modules/homework/types"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
|
||||
|
||||
function ErrorRateChart({
|
||||
questions,
|
||||
gradedSampleCount,
|
||||
}: {
|
||||
questions: HomeworkAssignmentQuestionAnalytics[]
|
||||
gradedSampleCount: number
|
||||
}) {
|
||||
const w = 100
|
||||
const h = 60
|
||||
const padL = 10
|
||||
const padR = 3
|
||||
const padT = 4
|
||||
const padB = 10
|
||||
const plotW = w - padL - padR
|
||||
const plotH = h - padT - padB
|
||||
const n = questions.length
|
||||
|
||||
const clamp01 = (v: number) => Math.max(0, Math.min(1, v))
|
||||
const xFor = (i: number) => padL + (n <= 1 ? 0 : (i / (n - 1)) * plotW)
|
||||
const yFor = (rate: number) => padT + (1 - clamp01(rate)) * plotH
|
||||
|
||||
const points = questions.map((q, i) => `${xFor(i)},${yFor(q.errorRate)}`).join(" ")
|
||||
const areaD =
|
||||
n === 0
|
||||
? ""
|
||||
: `M ${padL} ${padT + plotH} L ${points.split(" ").join(" L ")} L ${padL + plotW} ${padT + plotH} Z`
|
||||
|
||||
const gridYs = [
|
||||
{ v: 1, label: "100%" },
|
||||
{ v: 0.5, label: "50%" },
|
||||
{ v: 0, label: "0%" },
|
||||
]
|
||||
|
||||
return (
|
||||
<svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" className="h-full w-full">
|
||||
{gridYs.map((g) => {
|
||||
const y = yFor(g.v)
|
||||
return (
|
||||
<g key={g.label}>
|
||||
<line x1={padL} y1={y} x2={padL + plotW} y2={y} className="stroke-border" strokeWidth={0.5} />
|
||||
<text x={2} y={y + 1.2} className="fill-muted-foreground text-[3px]">
|
||||
{g.label}
|
||||
</text>
|
||||
</g>
|
||||
)
|
||||
})}
|
||||
|
||||
<line x1={padL} y1={padT} x2={padL} y2={padT + plotH} className="stroke-border" strokeWidth={0.7} />
|
||||
<line
|
||||
x1={padL}
|
||||
y1={padT + plotH}
|
||||
x2={padL + plotW}
|
||||
y2={padT + plotH}
|
||||
className="stroke-border"
|
||||
strokeWidth={0.7}
|
||||
/>
|
||||
|
||||
{n >= 2 ? <path d={areaD} className="fill-primary/10" /> : null}
|
||||
<polyline
|
||||
points={points}
|
||||
fill="none"
|
||||
className="stroke-primary"
|
||||
strokeWidth={1.2}
|
||||
strokeLinejoin="round"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
|
||||
{questions.map((q, i) => {
|
||||
const cx = xFor(i)
|
||||
const cy = yFor(q.errorRate)
|
||||
const label = `Q${i + 1}`
|
||||
return (
|
||||
<g key={q.questionId}>
|
||||
<circle cx={cx} cy={cy} r={1.2} className="fill-primary" />
|
||||
<title>{`${label}: ${(q.errorRate * 100).toFixed(1)}% (${q.errorCount} / ${gradedSampleCount})`}</title>
|
||||
</g>
|
||||
)
|
||||
})}
|
||||
|
||||
{questions.map((q, i) => {
|
||||
if (n > 12 && i % 2 === 1) return null
|
||||
const x = xFor(i)
|
||||
return (
|
||||
<text
|
||||
key={`x-${q.questionId}`}
|
||||
x={x}
|
||||
y={h - 2}
|
||||
textAnchor="middle"
|
||||
className="fill-muted-foreground text-[3px]"
|
||||
>
|
||||
{i + 1}
|
||||
</text>
|
||||
)
|
||||
})}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function HomeworkAssignmentQuestionErrorOverviewCard({
|
||||
questions,
|
||||
gradedSampleCount,
|
||||
}: {
|
||||
questions: HomeworkAssignmentQuestionAnalytics[]
|
||||
gradedSampleCount: number
|
||||
}) {
|
||||
return (
|
||||
<Card className="md:col-span-1">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm font-medium text-muted-foreground">Question Error Overview</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{questions.length === 0 || gradedSampleCount === 0 ? (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
No graded submissions yet. Error analytics will appear here after grading.
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>Graded students</span>
|
||||
<span className="font-medium text-foreground">{gradedSampleCount}</span>
|
||||
</div>
|
||||
<div className="h-56 rounded-md border bg-muted/40 px-3 py-2">
|
||||
<ErrorRateChart questions={questions} gradedSampleCount={gradedSampleCount} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user