sync-docs-and-fixes
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
FormMessage,
|
||||
} from "@/shared/components/ui/form"
|
||||
import { Input } from "@/shared/components/ui/input"
|
||||
import { ScrollArea } from "@/shared/components/ui/scroll-area"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -36,9 +37,9 @@ import {
|
||||
} from "@/shared/components/ui/select"
|
||||
import { Textarea } from "@/shared/components/ui/textarea"
|
||||
import { BaseQuestionSchema } from "../schema"
|
||||
import { createNestedQuestion, updateQuestionAction } from "../actions"
|
||||
import { createNestedQuestion, getKnowledgePointOptionsAction, updateQuestionAction } from "../actions"
|
||||
import { toast } from "sonner"
|
||||
import { Question } from "../types"
|
||||
import { KnowledgePointOption, Question } from "../types"
|
||||
|
||||
const QuestionFormSchema = BaseQuestionSchema.extend({
|
||||
difficulty: z.number().min(1).max(5),
|
||||
@@ -111,6 +112,10 @@ export function CreateQuestionDialog({
|
||||
const router = useRouter()
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
const isEdit = !!initialData
|
||||
const [knowledgePointOptions, setKnowledgePointOptions] = useState<KnowledgePointOption[]>([])
|
||||
const [knowledgePointQuery, setKnowledgePointQuery] = useState("")
|
||||
const [selectedKnowledgePointIds, setSelectedKnowledgePointIds] = useState<string[]>([])
|
||||
const [isLoadingKnowledgePoints, setIsLoadingKnowledgePoints] = useState(false)
|
||||
|
||||
const form = useForm<QuestionFormValues>({
|
||||
resolver: zodResolver(QuestionFormSchema),
|
||||
@@ -151,7 +156,60 @@ export function CreateQuestionDialog({
|
||||
}
|
||||
}, [initialData, form, open, defaultContent, defaultType])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
setIsLoadingKnowledgePoints(true)
|
||||
getKnowledgePointOptionsAction()
|
||||
.then((rows) => {
|
||||
setKnowledgePointOptions(rows)
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Failed to load knowledge points")
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoadingKnowledgePoints(false)
|
||||
})
|
||||
}, [open])
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return
|
||||
if (initialData) {
|
||||
const nextIds = initialData.knowledgePoints.map((kp) => kp.id)
|
||||
setSelectedKnowledgePointIds((prev) => {
|
||||
if (prev.length === nextIds.length && prev.every((id, idx) => id === nextIds[idx])) {
|
||||
return prev
|
||||
}
|
||||
return nextIds
|
||||
})
|
||||
return
|
||||
}
|
||||
setSelectedKnowledgePointIds((prev) => {
|
||||
if (
|
||||
prev.length === defaultKnowledgePointIds.length &&
|
||||
prev.every((id, idx) => id === defaultKnowledgePointIds[idx])
|
||||
) {
|
||||
return prev
|
||||
}
|
||||
return defaultKnowledgePointIds
|
||||
})
|
||||
}, [open, initialData, defaultKnowledgePointIds])
|
||||
|
||||
const questionType = form.watch("type")
|
||||
const filteredKnowledgePoints = knowledgePointOptions.filter((kp) => {
|
||||
const query = knowledgePointQuery.trim().toLowerCase()
|
||||
if (!query) return true
|
||||
const fullLabel = [
|
||||
kp.textbookTitle,
|
||||
kp.chapterTitle,
|
||||
kp.name,
|
||||
kp.subject,
|
||||
kp.grade,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ")
|
||||
.toLowerCase()
|
||||
return fullLabel.includes(query)
|
||||
})
|
||||
|
||||
const buildContent = (data: QuestionFormValues) => {
|
||||
const text = data.content.trim()
|
||||
@@ -194,7 +252,7 @@ export function CreateQuestionDialog({
|
||||
type: data.type,
|
||||
difficulty: data.difficulty,
|
||||
content: buildContent(data),
|
||||
knowledgePointIds: isEdit ? [] : defaultKnowledgePointIds,
|
||||
knowledgePointIds: selectedKnowledgePointIds,
|
||||
}
|
||||
const fd = new FormData()
|
||||
fd.set("json", JSON.stringify(payload))
|
||||
@@ -306,6 +364,58 @@ export function CreateQuestionDialog({
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<FormLabel>Knowledge Points</FormLabel>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{selectedKnowledgePointIds.length > 0 ? `${selectedKnowledgePointIds.length} selected` : "Optional"}
|
||||
</span>
|
||||
</div>
|
||||
<Input
|
||||
placeholder="Search knowledge points..."
|
||||
value={knowledgePointQuery}
|
||||
onChange={(e) => setKnowledgePointQuery(e.target.value)}
|
||||
/>
|
||||
<div className="rounded-md border">
|
||||
<ScrollArea className="h-48">
|
||||
{isLoadingKnowledgePoints ? (
|
||||
<div className="p-3 text-sm text-muted-foreground">Loading...</div>
|
||||
) : filteredKnowledgePoints.length === 0 ? (
|
||||
<div className="p-3 text-sm text-muted-foreground">No knowledge points found.</div>
|
||||
) : (
|
||||
<div className="space-y-1 p-2">
|
||||
{filteredKnowledgePoints.map((kp) => {
|
||||
const labelParts = [
|
||||
kp.textbookTitle,
|
||||
kp.chapterTitle,
|
||||
kp.name,
|
||||
].filter(Boolean)
|
||||
const label = labelParts.join(" · ")
|
||||
return (
|
||||
<label key={kp.id} className="flex items-center gap-2 rounded-md px-2 py-1 hover:bg-muted/50">
|
||||
<Checkbox
|
||||
checked={selectedKnowledgePointIds.includes(kp.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
const isChecked = checked === true
|
||||
setSelectedKnowledgePointIds((prev) => {
|
||||
if (isChecked) {
|
||||
if (prev.includes(kp.id)) return prev
|
||||
return [...prev, kp.id]
|
||||
}
|
||||
return prev.filter((id) => id !== kp.id)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<span className="text-sm">{label}</span>
|
||||
</label>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(questionType === "single_choice" || questionType === "multiple_choice") && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user