"use client"; /** * 题目详情对话框(ARCHITECTURE.md §5.4 / §9.4 / §10 P5) * * 数据契约: * - adminQuestion(id):❌ schema 无 → MSW 兜底(@contract-pending) * * 适配 portal-shell: * - 用原生轻量模态(fixed inset-0 + bg-black/50 + 卡片)替代 shadcn Dialog * - 数据通过 useAdminQuestion hook 拉取 * - 错误处理走 notify.error() * * 关联:ARCHITECTURE.md §5.3 契约纪律 / §9.4 / §11.3 DoD */ import { useEffect } from "react"; import { useTranslations } from "next-intl"; import { BookOpen, Calendar, FileText, Hash, HelpCircle, Lightbulb, RefreshCw, Target, User, } from "lucide-react"; import { useAdminQuestion } from "@/lib/api"; import { notify } from "@/shared/lib/notify"; import { Badge } from "@/shared/components/ui/badge"; import { Button } from "@/shared/components/ui/button"; import { Separator } from "@/shared/components/ui/separator"; import { difficultyToColorClass, formatDifficulty, formatQuestionDate, formatQuestionStatus, formatQuestionType, questionStatusToBadgeClass, } from "@/features/admin/questions/transformations"; export interface QuestionDetailDialogProps { /** 当前选中的题目 id,为空时关闭对话框 */ questionId: string | null; onOpenChange: (open: boolean) => void; } /** * 题目详情对话框。questionId 非空时打开,按 id 拉取详情。 */ export function QuestionDetailDialog({ questionId, onOpenChange, }: QuestionDetailDialogProps): React.ReactElement { const t = useTranslations("admin.questions.detailDialog"); const tCommon = useTranslations("common"); const open = questionId !== null && questionId.length > 0; const { data, loading, error, refetch } = useAdminQuestion(questionId ?? "", { enabled: open, }); // 查询失败时通知用户(§11.3 DoD #8:catch 块必须包含 notify.error()) useEffect(() => { if (error) { notify.error(tCommon("error.loadFailed", { message: String(error) })); } }, [error, tCommon]); // ESC 键关闭 useEffect(() => { if (!open) return; const handleKeyDown = (e: KeyboardEvent): void => { if (e.key === "Escape") onOpenChange(false); }; window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [open, onOpenChange]); if (!open) return <>>; return (
{tCommon("error.loadFailed", { message: String(error) })}
{data.content || t("noContent")}
{data.answer}
{data.explanation}
{data.source}
{label}
{value || "--"}