fix human language navigation and practice fetch guards
Route published lesson success back to the Human Language page so prior scroll position is restored, and explicitly guard practice question rendering by fetch status. Made-with: Cursor
This commit is contained in:
parent
814a6a54e8
commit
d33bacf628
|
|
@ -122,6 +122,7 @@ export function AddNewLessonPage() {
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [resultStatus, setResultStatus] = useState<ResultStatus | null>(null)
|
const [resultStatus, setResultStatus] = useState<ResultStatus | null>(null)
|
||||||
const [resultMessage, setResultMessage] = useState("")
|
const [resultMessage, setResultMessage] = useState("")
|
||||||
|
const [lastSavedStatus, setLastSavedStatus] = useState<"DRAFT" | "PUBLISHED" | null>(null)
|
||||||
|
|
||||||
const [lessonTitle, setLessonTitle] = useState("")
|
const [lessonTitle, setLessonTitle] = useState("")
|
||||||
const [lessonDescription, setLessonDescription] = useState("")
|
const [lessonDescription, setLessonDescription] = useState("")
|
||||||
|
|
@ -255,11 +256,13 @@ export function AddNewLessonPage() {
|
||||||
|
|
||||||
setResultStatus("success")
|
setResultStatus("success")
|
||||||
setResultMessage(status === "PUBLISHED" ? "Lesson published successfully." : "Lesson saved as draft.")
|
setResultMessage(status === "PUBLISHED" ? "Lesson published successfully." : "Lesson saved as draft.")
|
||||||
|
setLastSavedStatus(status)
|
||||||
setCurrentStep(4)
|
setCurrentStep(4)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to save lesson:", error)
|
console.error("Failed to save lesson:", error)
|
||||||
setResultStatus("error")
|
setResultStatus("error")
|
||||||
setResultMessage(error instanceof Error ? error.message : "Failed to save lesson")
|
setResultMessage(error instanceof Error ? error.message : "Failed to save lesson")
|
||||||
|
setLastSavedStatus(null)
|
||||||
setCurrentStep(4)
|
setCurrentStep(4)
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
|
|
@ -620,7 +623,12 @@ export function AddNewLessonPage() {
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-3 text-sm text-grayScale-500">{resultStatus === "success" ? "Your lesson is now active." : resultMessage}</p>
|
<p className="mt-3 text-sm text-grayScale-500">{resultStatus === "success" ? "Your lesson is now active." : resultMessage}</p>
|
||||||
<div className="mt-8 w-full space-y-3">
|
<div className="mt-8 w-full space-y-3">
|
||||||
<Button className="h-11 w-full text-base" onClick={() => navigate(backTo)}>
|
<Button
|
||||||
|
className="h-11 w-full text-base"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(lastSavedStatus === "PUBLISHED" ? "/content/human-language" : backTo)
|
||||||
|
}
|
||||||
|
>
|
||||||
Go back to Course
|
Go back to Course
|
||||||
</Button>
|
</Button>
|
||||||
{resultStatus === "success" ? (
|
{resultStatus === "success" ? (
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, useMemo, useRef, useState, type ChangeEvent } from "react"
|
import { useEffect, useMemo, useRef, useState, type ChangeEvent } from "react"
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom"
|
import { useLocation, useNavigate } from "react-router-dom"
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
|
|
@ -119,13 +119,6 @@ type QuestionDialogState =
|
||||||
questionId?: number
|
questionId?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDurationSeconds(total: number): string {
|
|
||||||
const s = Math.max(0, Math.floor(total))
|
|
||||||
const m = Math.floor(s / 60)
|
|
||||||
const r = s % 60
|
|
||||||
return `${m}:${r.toString().padStart(2, "0")}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function practiceStatusStyle(status: string): string {
|
function practiceStatusStyle(status: string): string {
|
||||||
const u = status.toUpperCase()
|
const u = status.toUpperCase()
|
||||||
if (u === "PUBLISHED") return "bg-green-50 text-green-700 ring-1 ring-inset ring-green-200"
|
if (u === "PUBLISHED") return "bg-green-50 text-green-700 ring-1 ring-inset ring-green-200"
|
||||||
|
|
@ -1774,13 +1767,6 @@ export function HumanLanguagePage() {
|
||||||
</button>
|
</button>
|
||||||
{categoryId ? (
|
{categoryId ? (
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<Link
|
|
||||||
to={`/content/human-language/${categoryId}/${course.course_id}/sub-module/${subModule.id}`}
|
|
||||||
>
|
|
||||||
<Button type="button" variant="outline" size="sm" className="h-8 border-grayScale-200 bg-white text-xs hover:border-brand-200 hover:bg-brand-50/40">
|
|
||||||
Open editor
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -2169,7 +2155,8 @@ export function HumanLanguagePage() {
|
||||||
<Loader2 className="h-5 w-5 animate-spin text-brand-500" aria-hidden />
|
<Loader2 className="h-5 w-5 animate-spin text-brand-500" aria-hidden />
|
||||||
Loading questions…
|
Loading questions…
|
||||||
</div>
|
</div>
|
||||||
) : practiceFetch.status === "error" ? (
|
) : null}
|
||||||
|
{practiceFetch?.status === "error" ? (
|
||||||
<div className="rounded-lg border border-red-100 bg-red-50/50 px-4 py-3">
|
<div className="rounded-lg border border-red-100 bg-red-50/50 px-4 py-3">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<HelpCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-600" aria-hidden />
|
<HelpCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-600" aria-hidden />
|
||||||
|
|
@ -2187,7 +2174,8 @@ export function HumanLanguagePage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : practiceFetch.questions.length === 0 ? (
|
) : null}
|
||||||
|
{practiceFetch?.status === "ok" && practiceFetch.questions.length === 0 ? (
|
||||||
<div className="rounded-lg border border-dashed border-grayScale-200 bg-white px-4 py-10 text-center">
|
<div className="rounded-lg border border-dashed border-grayScale-200 bg-white px-4 py-10 text-center">
|
||||||
<ClipboardList className="mx-auto mb-2 h-8 w-8 text-grayScale-300" aria-hidden />
|
<ClipboardList className="mx-auto mb-2 h-8 w-8 text-grayScale-300" aria-hidden />
|
||||||
<p className="text-sm text-grayScale-600">
|
<p className="text-sm text-grayScale-600">
|
||||||
|
|
@ -2197,7 +2185,8 @@ export function HumanLanguagePage() {
|
||||||
Add them via <span className="font-medium text-grayScale-700">Open editor</span>.
|
Add them via <span className="font-medium text-grayScale-700">Open editor</span>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : null}
|
||||||
|
{practiceFetch?.status === "ok" && practiceFetch.questions.length > 0 ? (
|
||||||
<ul className="max-h-[min(28rem,calc(100vh-16rem))] space-y-3 overflow-y-auto pr-1 [scrollbar-gutter:stable]">
|
<ul className="max-h-[min(28rem,calc(100vh-16rem))] space-y-3 overflow-y-auto pr-1 [scrollbar-gutter:stable]">
|
||||||
{practiceFetch.questions.map((q, qIdx) => {
|
{practiceFetch.questions.map((q, qIdx) => {
|
||||||
const qType = String(q.question_type ?? "—")
|
const qType = String(q.question_type ?? "—")
|
||||||
|
|
@ -2350,7 +2339,7 @@ export function HumanLanguagePage() {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
) : null}
|
||||||
{practiceFetch?.status === "ok" &&
|
{practiceFetch?.status === "ok" &&
|
||||||
practiceFetch.totalCount > practiceFetch.questions.length ? (
|
practiceFetch.totalCount > practiceFetch.questions.length ? (
|
||||||
<div className="mt-4 rounded-lg border border-grayScale-100 bg-white/80 px-3 py-2 text-center text-xs text-grayScale-600">
|
<div className="mt-4 rounded-lg border border-grayScale-100 bg-white/80 px-3 py-2 text-center text-xs text-grayScale-600">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user