import { useNavigate } from "react-router-dom"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { StepWizard, type WizardStepDef } from "@/components/wizard/StepWizard"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { lmsService } from "@/services/lms.service"; import { describeApiError } from "@/lib/api-client"; import type { CourseCreateRequest } from "@/types"; /** * Course creation wizard (3 steps). * * 1. Basics — title, code (auto-derived from title by default), description * 2. Level & capacity — difficulty, CEFR level, max capacity * 3. Review → Finish posts to lmsService.createCourse * * Mirrors the fields of the existing AdminCourses "New course" dialog but * surfaced as a guided flow. Fields the admin normally leaves blank * (topic_ids, tag_ids, subject_id) are omitted here; they can be refined * on the full course page after the course is created. */ interface CourseWizardState { title: string; code: string; description: string; difficulty_level: "beginner" | "intermediate" | "advanced" | ""; cefr_level: string; max_capacity: number; } const INITIAL: CourseWizardState = { title: "", code: "", description: "", difficulty_level: "", cefr_level: "", max_capacity: 30, }; /** * Odoo `op.course.cefr_level` is a Selection field with lowercase keys * (`pre_a1`, `a1`, `a2`, …). Sending `"A2"` raises * `Wrong value for op.course.cefr_level` (HTTP 500). We display the * uppercase label but always submit the lowercase key. */ const CEFR_LEVELS: { value: string; label: string }[] = [ { value: "pre_a1", label: "Pre-A1" }, { value: "a1", label: "A1" }, { value: "a2", label: "A2" }, { value: "b1", label: "B1" }, { value: "b2", label: "B2" }, { value: "c1", label: "C1" }, { value: "c2", label: "C2" }, ]; function deriveCode(title: string): string { return title.trim().toUpperCase().replace(/\s+/g, "-").slice(0, 16); } export default function CourseWizard() { const { t } = useTranslation(); const navigate = useNavigate(); const qc = useQueryClient(); const createMut = useMutation({ mutationFn: (data: CourseCreateRequest) => lmsService.createCourse(data), onSuccess: () => { qc.invalidateQueries({ queryKey: ["lms", "courses"] }); toast.success(t("wizard.course.toastSuccess")); navigate("/admin/courses"); }, onError: (err: unknown) => { toast.error(describeApiError(err, t("wizard.course.toastError"))); }, }); const steps: WizardStepDef[] = [ { id: "basics", titleKey: "wizard.course.step1.title", descriptionKey: "wizard.course.step1.description", validate: (s) => { if (!s.title.trim()) return t("wizard.course.errors.titleRequired"); return null; }, render: ({ state, update }) => (
update({ title: e.target.value })} placeholder={t("wizard.course.placeholders.title")} />
update({ code: e.target.value })} placeholder={deriveCode(state.title) || t("wizard.course.placeholders.code")} />

{t("wizard.course.codeHint")}