Compare commits
No commits in common. "6910fb55a4f40d4f9c954685a5d2cf3824cee33b" and "527d36c6e3342d0af460a83ec7ae647b56d19da9" have entirely different histories.
6910fb55a4
...
527d36c6e3
|
|
@ -513,46 +513,6 @@ export function SpeakingPage() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const trimmedValue = rawValue.trim()
|
|
||||||
const isURL = /^https?:\/\//i.test(trimmedValue)
|
|
||||||
|
|
||||||
if (isURL) {
|
|
||||||
updateDraft(draftIndex, (draft) =>
|
|
||||||
field === "voice_prompt"
|
|
||||||
? { ...draft, uploadingVoicePrompt: true }
|
|
||||||
: { ...draft, uploadingSamplePrompt: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
const res = await uploadAudioFile(trimmedValue)
|
|
||||||
const objectKey = res.data?.data?.object_key?.trim()
|
|
||||||
const immediateUrl = res.data?.data?.url?.trim() ?? ""
|
|
||||||
if (!objectKey) throw new Error("Missing uploaded audio object key")
|
|
||||||
|
|
||||||
if (field === "voice_prompt") {
|
|
||||||
updateDraft(draftIndex, (draft) => ({
|
|
||||||
...draft,
|
|
||||||
voicePrompt: objectKey,
|
|
||||||
voicePromptPreviewUrl: immediateUrl || draft.voicePromptPreviewUrl,
|
|
||||||
}))
|
|
||||||
if (!immediateUrl) {
|
|
||||||
const resolvedUrl = await resolvePreviewUrl(objectKey)
|
|
||||||
updateDraft(draftIndex, (draft) => ({ ...draft, voicePromptPreviewUrl: resolvedUrl }))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateDraft(draftIndex, (draft) => ({
|
|
||||||
...draft,
|
|
||||||
sampleAnswerVoicePrompt: objectKey,
|
|
||||||
samplePromptPreviewUrl: immediateUrl || draft.samplePromptPreviewUrl,
|
|
||||||
}))
|
|
||||||
if (!immediateUrl) {
|
|
||||||
const resolvedUrl = await resolvePreviewUrl(objectKey)
|
|
||||||
updateDraft(draftIndex, (draft) => ({ ...draft, samplePromptPreviewUrl: resolvedUrl }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toast.success("Audio URL imported successfully")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = await resolvePreviewUrl(rawValue)
|
const url = await resolvePreviewUrl(rawValue)
|
||||||
updateDraft(draftIndex, (draft) =>
|
updateDraft(draftIndex, (draft) =>
|
||||||
field === "voice_prompt"
|
field === "voice_prompt"
|
||||||
|
|
@ -561,13 +521,7 @@ export function SpeakingPage() {
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to resolve audio preview URL:", error)
|
console.error("Failed to resolve audio preview URL:", error)
|
||||||
toast.error("Could not import/resolve audio URL")
|
toast.error("Could not resolve audio preview URL")
|
||||||
} finally {
|
|
||||||
updateDraft(draftIndex, (draft) =>
|
|
||||||
field === "voice_prompt"
|
|
||||||
? { ...draft, uploadingVoicePrompt: false }
|
|
||||||
: { ...draft, uploadingSamplePrompt: false },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1568,25 +1522,33 @@ export function SpeakingPage() {
|
||||||
{currentStep === 2 && (
|
{currentStep === 2 && (
|
||||||
<Card className="w-full overflow-hidden border-grayScale-200/80 shadow-sm">
|
<Card className="w-full overflow-hidden border-grayScale-200/80 shadow-sm">
|
||||||
<div className="border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-5 py-5 sm:px-8 sm:py-6">
|
<div className="border-b border-grayScale-100 bg-gradient-to-r from-grayScale-50/80 to-white px-5 py-5 sm:px-8 sm:py-6">
|
||||||
<div>
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<h2 className="text-lg font-semibold tracking-tight text-grayScale-900 sm:text-xl">Step 2: AUDIO questions</h2>
|
<div>
|
||||||
<p className="mt-1 max-w-3xl text-sm text-grayScale-500">
|
<h2 className="text-lg font-semibold tracking-tight text-grayScale-900 sm:text-xl">Step 2: AUDIO questions</h2>
|
||||||
Build each question with voice prompts, optional media, and answer guidance.
|
<p className="mt-1 max-w-3xl text-sm text-grayScale-500">
|
||||||
</p>
|
Upload or record prompts, add reference image, and set optional tips.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="shrink-0"
|
||||||
|
onClick={() => setQuestionDrafts((prev) => [...prev, createEmptyDraft()])}
|
||||||
|
disabled={saving}
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Add question
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-5 sm:p-8">
|
<div className="p-5 sm:p-8">
|
||||||
<div className="rounded-xl border border-grayScale-200 bg-grayScale-50/30 p-4 sm:p-5">
|
<div className="rounded-xl border border-grayScale-200 bg-grayScale-50/30 p-4 sm:p-5">
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
{questionDrafts.map((draft, draftIndex) => (
|
{questionDrafts.map((draft, draftIndex) => (
|
||||||
<div key={draftIndex} className="rounded-2xl border border-grayScale-200 border-l-4 border-l-brand-500 bg-white p-4 shadow-sm transition-shadow hover:shadow-md sm:p-6">
|
<div key={draftIndex} className="rounded-xl border border-grayScale-200 border-l-4 border-l-brand-500 bg-white p-4 shadow-sm sm:p-6">
|
||||||
<div className="mb-4 flex items-center justify-between gap-2">
|
<div className="mb-4 flex items-center justify-between gap-2">
|
||||||
<div className="flex items-center gap-2">
|
<p className="text-sm font-semibold text-grayScale-900">Question {draftIndex + 1}</p>
|
||||||
<span className="rounded-md bg-brand-100 px-2 py-1 text-xs font-semibold tracking-wide text-brand-700">
|
|
||||||
AUDIO
|
|
||||||
</span>
|
|
||||||
<p className="text-sm font-semibold text-grayScale-900">Question {draftIndex + 1}</p>
|
|
||||||
</div>
|
|
||||||
{questionDrafts.length > 1 ? (
|
{questionDrafts.length > 1 ? (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -1828,24 +1790,6 @@ export function SpeakingPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t border-grayScale-100 bg-white px-5 py-4 sm:px-8">
|
|
||||||
<div className="flex flex-col gap-3 rounded-xl border border-dashed border-grayScale-200 bg-grayScale-50/40 p-3 sm:flex-row sm:items-center sm:justify-between sm:p-4">
|
|
||||||
<p className="text-xs text-grayScale-500 sm:text-sm">
|
|
||||||
Need another item? Add a new AUDIO question to the end of the list.
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className="shrink-0 border-brand-200 text-brand-700 hover:bg-brand-50 hover:text-brand-800"
|
|
||||||
onClick={() => setQuestionDrafts((prev) => [...prev, createEmptyDraft()])}
|
|
||||||
disabled={saving}
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4" />
|
|
||||||
Add question
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col-reverse gap-3 border-t border-grayScale-100 bg-grayScale-50/30 px-5 py-4 sm:flex-row sm:justify-end sm:px-8 sm:py-5">
|
<div className="flex flex-col-reverse gap-3 border-t border-grayScale-100 bg-grayScale-50/30 px-5 py-4 sm:flex-row sm:justify-end sm:px-8 sm:py-5">
|
||||||
<Button variant="outline" onClick={() => setCurrentStep(1)} disabled={saving} className="sm:w-auto">
|
<Button variant="outline" onClick={() => setCurrentStep(1)} disabled={saving} className="sm:w-auto">
|
||||||
Back
|
Back
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user