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
This commit is contained in:
@@ -9,6 +9,7 @@ import { Badge } from "@/shared/components/ui/badge"
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/components/ui/tooltip"
|
||||
import { EmptyState } from "@/shared/components/ui/empty-state"
|
||||
import { WidgetBoundary } from "@/shared/components/widget-boundary"
|
||||
import { formatDate } from "@/shared/lib/utils"
|
||||
import { MasteryRadarChart } from "./mastery-radar-chart"
|
||||
import {
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
confidenceBadgeVariant,
|
||||
type ConfidenceLevel,
|
||||
} from "./confidence-utils"
|
||||
import { getDiagnosticRoleConfig, type DiagnosticRole } from "../role-config"
|
||||
import type { DiagnosticReportWithDetails, MasteryRadarPoint, StudentMasterySummary } from "../types"
|
||||
|
||||
interface StudentDiagnosticViewProps {
|
||||
@@ -23,11 +25,13 @@ interface StudentDiagnosticViewProps {
|
||||
reports: DiagnosticReportWithDetails[]
|
||||
classAverageMastery?: MasteryRadarPoint[]
|
||||
/**
|
||||
* v3-P2-6: "练习"按钮的跳转基础路径。
|
||||
* - 学生视角:默认 `/student/learning/assignments`
|
||||
* - 教师视角:传入 `/teacher/questions`(题目库支持 kp 查询参数筛选)
|
||||
* - 家长视角:传入 `null` 隐藏练习按钮(家长无练习入口)
|
||||
* 最终链接会附加 `?kp={knowledgePointId}` 实现个性化练习推荐。
|
||||
* v4-P2-2: 角色配置驱动。
|
||||
* 组件内部根据 role 查找 DIAGNOSTIC_ROLE_CONFIG 获取 practiceHrefBase 等角色差异配置。
|
||||
* 新增角色只需在 role-config.ts 中添加配置项,无需修改组件 props。
|
||||
*/
|
||||
role?: DiagnosticRole
|
||||
/**
|
||||
* @deprecated v4-P2-2: 请改用 `role` prop。保留向后兼容,若同时传入则 role 优先。
|
||||
*/
|
||||
practiceHrefBase?: string | null
|
||||
}
|
||||
@@ -36,9 +40,12 @@ export function StudentDiagnosticView({
|
||||
summary,
|
||||
reports,
|
||||
classAverageMastery,
|
||||
practiceHrefBase = "/student/learning/assignments",
|
||||
role = "student",
|
||||
practiceHrefBase,
|
||||
}: StudentDiagnosticViewProps) {
|
||||
const t = useTranslations("diagnostic")
|
||||
// v4-P2-2: 角色配置驱动,role prop 优先于 deprecated practiceHrefBase
|
||||
const resolvedPracticeHrefBase = practiceHrefBase ?? getDiagnosticRoleConfig(role).practiceHrefBase
|
||||
|
||||
if (!summary) {
|
||||
return (
|
||||
@@ -132,11 +139,14 @@ export function StudentDiagnosticView({
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 雷达图 */}
|
||||
<MasteryRadarChart data={radarData} />
|
||||
{/* v2-P1-6: 雷达图区块独立 Error Boundary */}
|
||||
<WidgetBoundary title={t("chart.radarTitle")} skeletonHeight={384}>
|
||||
<MasteryRadarChart data={radarData} />
|
||||
</WidgetBoundary>
|
||||
|
||||
{/* 强项 / 弱项 */}
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
{/* v2-P1-6: 强项 / 弱项区块独立 Error Boundary */}
|
||||
<WidgetBoundary title={t("strengths.title")} skeletonHeight={300}>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
@@ -179,9 +189,9 @@ export function StudentDiagnosticView({
|
||||
<span className="text-sm truncate">{m.knowledgePointName}</span>
|
||||
<Badge variant="destructive" className="shrink-0">{m.masteryLevel.toFixed(1)}%</Badge>
|
||||
</div>
|
||||
{practiceHrefBase ? (
|
||||
{resolvedPracticeHrefBase ? (
|
||||
<Button asChild variant="ghost" size="sm" className="h-7 shrink-0 text-xs" aria-label={t("studentDiagnostic.practiceAriaLabel", { name: m.knowledgePointName })}>
|
||||
<Link href={`${practiceHrefBase}?kp=${m.knowledgePointId}`}>
|
||||
<Link href={`${resolvedPracticeHrefBase}?kp=${m.knowledgePointId}`}>
|
||||
{t("weaknesses.practice")}
|
||||
<ArrowRight className="ml-1 h-3 w-3" />
|
||||
</Link>
|
||||
@@ -194,9 +204,11 @@ export function StudentDiagnosticView({
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</WidgetBoundary>
|
||||
|
||||
{/* 最新报告 / 建议 */}
|
||||
{/* v2-P1-6: 最新报告区块独立 Error Boundary */}
|
||||
{latestReport ? (
|
||||
<WidgetBoundary title={t("studentDiagnostic.diagnosticReportTitle")} skeletonHeight={200}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
@@ -245,10 +257,12 @@ export function StudentDiagnosticView({
|
||||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</WidgetBoundary>
|
||||
) : null}
|
||||
|
||||
{/* 历史报告列表 */}
|
||||
{/* v2-P1-6: 历史报告区块独立 Error Boundary */}
|
||||
{publishedReports.length > 1 ? (
|
||||
<WidgetBoundary title={t("report.history")} skeletonHeight={200}>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
@@ -287,6 +301,7 @@ export function StudentDiagnosticView({
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</WidgetBoundary>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user