media preview fix
This commit is contained in:
parent
4210a05ba9
commit
d1842579e9
File diff suppressed because it is too large
Load Diff
21
src/lib/practiceMedia.ts
Normal file
21
src/lib/practiceMedia.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { resolveFileUrl } from "../api/files.api"
|
||||
|
||||
export function normalizeObjectKey(value: string): string {
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed) return ""
|
||||
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) return trimmed
|
||||
const protocolMatch = trimmed.match(/^[a-z]+:\/\//i)
|
||||
if (protocolMatch) {
|
||||
return trimmed.replace(/^[a-z]+:\/\//i, "")
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
|
||||
export async function resolveMediaPreviewUrl(value: string): Promise<string> {
|
||||
if (!value.trim()) return ""
|
||||
if (value.startsWith("http://") || value.startsWith("https://")) return value
|
||||
const key = normalizeObjectKey(value)
|
||||
if (!key) return ""
|
||||
const res = await resolveFileUrl(key)
|
||||
return res.data?.data?.url ?? ""
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ interface Question {
|
|||
sampleAnswerVoicePrompt: string
|
||||
audioCorrectAnswerText: string
|
||||
shortAnswers: string[]
|
||||
imageUrl: string
|
||||
}
|
||||
|
||||
const PERSONAS: Persona[] = [
|
||||
|
|
@ -91,6 +92,7 @@ function createEmptyQuestion(id: string): Question {
|
|||
sampleAnswerVoicePrompt: "",
|
||||
audioCorrectAnswerText: "",
|
||||
shortAnswers: [],
|
||||
imageUrl: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -232,6 +234,7 @@ export function AddNewPracticePage() {
|
|||
voice_prompt: q.voicePrompt || undefined,
|
||||
sample_answer_voice_prompt: q.sampleAnswerVoicePrompt || undefined,
|
||||
audio_correct_answer_text: q.audioCorrectAnswerText || undefined,
|
||||
image_url: q.imageUrl.trim() || undefined,
|
||||
short_answers: q.shortAnswers.length > 0 ? q.shortAnswers : undefined,
|
||||
})
|
||||
|
||||
|
|
@ -606,6 +609,7 @@ export function AddNewPracticePage() {
|
|||
sampleAnswerVoicePrompt: question.sampleAnswerVoicePrompt,
|
||||
audioCorrectAnswerText: question.audioCorrectAnswerText,
|
||||
shortAnswer: question.shortAnswers[0] ?? "",
|
||||
imageUrl: question.imageUrl,
|
||||
}}
|
||||
onChange={(next) => {
|
||||
updateQuestion(question.id, {
|
||||
|
|
@ -620,8 +624,10 @@ export function AddNewPracticePage() {
|
|||
sampleAnswerVoicePrompt: next.sampleAnswerVoicePrompt,
|
||||
audioCorrectAnswerText: next.audioCorrectAnswerText,
|
||||
shortAnswers: next.shortAnswer.trim() ? [next.shortAnswer.trim()] : [],
|
||||
imageUrl: next.imageUrl,
|
||||
})
|
||||
}}
|
||||
mediaBusy={saving}
|
||||
/>
|
||||
</Card>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -210,7 +210,6 @@ export function HumanLanguagePage() {
|
|||
const [questionDialog, setQuestionDialog] = useState<QuestionDialogState>({ open: false })
|
||||
const [practiceForm, setPracticeForm] = useState({ title: "", description: "", persona: "" })
|
||||
const [questionDraft, setQuestionDraft] = useState<PracticeQuestionEditorValue>(() => createEmptyPracticeQuestionDraft())
|
||||
const [questionImageUrl, setQuestionImageUrl] = useState("")
|
||||
const [questionDetailById, setQuestionDetailById] = useState<Record<number, QuestionDetail>>({})
|
||||
const [practiceTargetDelete, setPracticeTargetDelete] = useState<{ id: number; title: string } | null>(null)
|
||||
const [questionTargetDelete, setQuestionTargetDelete] = useState<{ id: number; practiceId: number; text: string } | null>(null)
|
||||
|
|
@ -597,7 +596,6 @@ export function HumanLanguagePage() {
|
|||
const resetPracticeForm = () => setPracticeForm({ title: "", description: "", persona: "" })
|
||||
const resetQuestionForm = () => {
|
||||
setQuestionDraft(createEmptyPracticeQuestionDraft())
|
||||
setQuestionImageUrl("")
|
||||
}
|
||||
|
||||
const openCreatePracticeDialog = (subModuleId: number) => {
|
||||
|
|
@ -680,7 +678,6 @@ export function HumanLanguagePage() {
|
|||
return
|
||||
}
|
||||
setQuestionDetailById((prev) => ({ ...prev, [qid]: detail }))
|
||||
setQuestionImageUrl(detail.image_url ?? "")
|
||||
const sortedOpts = (detail.options ?? []).slice().sort((a, b) => a.option_order - b.option_order)
|
||||
const shortAnswer =
|
||||
Array.isArray(detail.short_answers) && detail.short_answers.length > 0
|
||||
|
|
@ -738,6 +735,7 @@ export function HumanLanguagePage() {
|
|||
sampleAnswerVoicePrompt: detail.sample_answer_voice_prompt ?? "",
|
||||
audioCorrectAnswerText: detail.audio_correct_answer_text ?? "",
|
||||
shortAnswer,
|
||||
imageUrl: detail.image_url ?? "",
|
||||
})
|
||||
// Open only after the same form shape as create is fully populated (no empty-state flash).
|
||||
setQuestionDialog({ open: true, mode: "edit", practiceId, questionId: qid })
|
||||
|
|
@ -758,7 +756,7 @@ export function HumanLanguagePage() {
|
|||
points: Number(d.points) || 1,
|
||||
tips: d.tips.trim() || undefined,
|
||||
explanation: d.explanation.trim() || undefined,
|
||||
image_url: questionImageUrl.trim() || undefined,
|
||||
image_url: d.imageUrl.trim() || undefined,
|
||||
voice_prompt: d.voicePrompt.trim() || undefined,
|
||||
sample_answer_voice_prompt: d.sampleAnswerVoicePrompt.trim() || undefined,
|
||||
audio_correct_answer_text: d.audioCorrectAnswerText.trim() || undefined,
|
||||
|
|
@ -1917,21 +1915,8 @@ export function HumanLanguagePage() {
|
|||
}}
|
||||
fieldErrors={questionFieldErrors}
|
||||
showFieldErrors={questionSubmitAttempted || questionFormTouched}
|
||||
mediaBusy={savingQuestion}
|
||||
/>
|
||||
|
||||
<div className="mt-5 space-y-2 border-t border-grayScale-100 pt-5">
|
||||
<label className="text-xs font-medium uppercase tracking-wider text-grayScale-500">Image URL (Optional)</label>
|
||||
<Input
|
||||
value={questionImageUrl}
|
||||
onChange={(e) => {
|
||||
setQuestionFormTouched(true)
|
||||
setQuestionImageUrl(e.target.value)
|
||||
}}
|
||||
placeholder="https://…"
|
||||
type="url"
|
||||
className="h-11 font-mono text-[13px]"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user