add quick-create path on empty human language view
Allow creating the initial subcategory/course path directly from the Human Language page empty state so module and sub-module creation can start immediately. Made-with: Cursor
This commit is contained in:
parent
ee2dbc5792
commit
e6adf2850e
|
|
@ -4,7 +4,7 @@ import { BookOpen, ChevronDown, ChevronRight, Languages, Loader2, Plus } from "l
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card"
|
||||||
import { Button } from "../../components/ui/button"
|
import { Button } from "../../components/ui/button"
|
||||||
import { SpinnerIcon } from "../../components/ui/spinner-icon"
|
import { SpinnerIcon } from "../../components/ui/spinner-icon"
|
||||||
import { createHumanLanguageLesson, getHumanLanguageHierarchy } from "../../api/courses.api"
|
import { createCourse, createHumanLanguageLesson, getHumanLanguageHierarchy } from "../../api/courses.api"
|
||||||
import type { HumanLanguageCourseTree, HumanLanguageSubCategoryTree } from "../../types/course.types"
|
import type { HumanLanguageCourseTree, HumanLanguageSubCategoryTree } from "../../types/course.types"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
|
|
||||||
|
|
@ -20,6 +20,9 @@ export function HumanLanguagePage() {
|
||||||
const [selectedLevel, setSelectedLevel] = useState<CefrLevel | "ALL">("ALL")
|
const [selectedLevel, setSelectedLevel] = useState<CefrLevel | "ALL">("ALL")
|
||||||
const [collapsedLevels, setCollapsedLevels] = useState<CefrLevel[]>([])
|
const [collapsedLevels, setCollapsedLevels] = useState<CefrLevel[]>([])
|
||||||
const [creatingKey, setCreatingKey] = useState<string | null>(null)
|
const [creatingKey, setCreatingKey] = useState<string | null>(null)
|
||||||
|
const [quickSubCategoryName, setQuickSubCategoryName] = useState("")
|
||||||
|
const [quickCourseName, setQuickCourseName] = useState("")
|
||||||
|
const [quickCreating, setQuickCreating] = useState(false)
|
||||||
|
|
||||||
const loadHierarchy = async () => {
|
const loadHierarchy = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
@ -147,6 +150,35 @@ export function HumanLanguagePage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleQuickCreatePath = async () => {
|
||||||
|
if (!categoryId) {
|
||||||
|
toast.error("Human Language category is not available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!quickSubCategoryName.trim() || !quickCourseName.trim()) {
|
||||||
|
toast.error("Subcategory and course names are required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setQuickCreating(true)
|
||||||
|
try {
|
||||||
|
const title = `${quickSubCategoryName.trim()} - ${quickCourseName.trim()}`
|
||||||
|
await createCourse({
|
||||||
|
category_id: categoryId,
|
||||||
|
title,
|
||||||
|
description: `${quickSubCategoryName.trim()} / ${quickCourseName.trim()}`,
|
||||||
|
})
|
||||||
|
toast.success("Subcategory/course path created")
|
||||||
|
setQuickSubCategoryName("")
|
||||||
|
setQuickCourseName("")
|
||||||
|
await loadHierarchy()
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to quick-create language path:", error)
|
||||||
|
toast.error("Failed to create subcategory/course path")
|
||||||
|
} finally {
|
||||||
|
setQuickCreating(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="rounded-2xl border border-grayScale-200 bg-gradient-to-r from-white to-brand-50/30 p-5 shadow-sm">
|
<div className="rounded-2xl border border-grayScale-200 bg-gradient-to-r from-white to-brand-50/30 p-5 shadow-sm">
|
||||||
|
|
@ -235,6 +267,42 @@ export function HumanLanguagePage() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
{availableCourses.length === 0 ? (
|
||||||
|
<Card className="border-grayScale-200/80">
|
||||||
|
<CardContent className="flex flex-col items-start gap-3 p-4">
|
||||||
|
<p className="text-sm text-grayScale-600">
|
||||||
|
No Human Language subcategory/course is available yet. Create the language course path first, then you can add incremental modules and sub-modules per level.
|
||||||
|
</p>
|
||||||
|
<div className="grid w-full grid-cols-1 gap-2 md:grid-cols-3">
|
||||||
|
<input
|
||||||
|
className="h-10 rounded-md border border-grayScale-200 bg-white px-3 text-sm"
|
||||||
|
placeholder="Subcategory (e.g., English)"
|
||||||
|
value={quickSubCategoryName}
|
||||||
|
onChange={(e) => setQuickSubCategoryName(e.target.value)}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="h-10 rounded-md border border-grayScale-200 bg-white px-3 text-sm"
|
||||||
|
placeholder="Course (e.g., Speaking)"
|
||||||
|
value={quickCourseName}
|
||||||
|
onChange={(e) => setQuickCourseName(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button onClick={handleQuickCreatePath} disabled={quickCreating || !categoryId}>
|
||||||
|
{quickCreating ? "Creating..." : "Quick Create Path"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<Link to="/content/courses">
|
||||||
|
<Button variant="outline">Open Courses</Button>
|
||||||
|
</Link>
|
||||||
|
{categoryId ? (
|
||||||
|
<Link to={`/content/category/${categoryId}/courses`}>
|
||||||
|
<Button>Open Human Language Courses</Button>
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
{CEFR_LEVELS.filter((l) => selectedLevel === "ALL" || l === selectedLevel).map((level) => {
|
{CEFR_LEVELS.filter((l) => selectedLevel === "ALL" || l === selectedLevel).map((level) => {
|
||||||
const modulesByCourse = selectedCourses
|
const modulesByCourse = selectedCourses
|
||||||
.map((course: HumanLanguageCourseTree) => {
|
.map((course: HumanLanguageCourseTree) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user