AdvisoryBuilderWorkshop / frontend /src /components /PersonaPromptBuildHost.tsx
NeonClary's picture
Deploy tutorial feature and recent fixes
d8808e7 verified
Raw
History Blame Contribute Delete
4.15 kB
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { PERSONA_PROMPT_LOW_CONFIDENCE_COPY } from '../lib/personaPromptBuildCopy'
import {
PERSONA_PROMPT_BUILD_COMPLETE,
PERSONA_PROMPT_BUILD_END,
PERSONA_PROMPT_BUILD_ERROR,
PERSONA_PROMPT_BUILD_START,
type PersonaPromptBuildCompleteDetail,
} from '../lib/personaPromptBuildEvents'
/**
* Global UI for persona prompt build: bottom banner while running, modal when done (any route).
*/
export function PersonaPromptBuildHost() {
const navigate = useNavigate()
const [banner, setBanner] = useState(false)
const [modal, setModal] = useState<PersonaPromptBuildCompleteDetail | null>(null)
const [errorMsg, setErrorMsg] = useState<string | null>(null)
useEffect(() => {
const onStart = () => {
setErrorMsg(null)
setBanner(true)
}
const onEnd = () => setBanner(false)
const onComplete = (e: Event) => {
setBanner(false)
const d = (e as CustomEvent<PersonaPromptBuildCompleteDetail>).detail
if (d?.personaId) setModal(d)
}
const onError = (e: Event) => {
setBanner(false)
const m = (e as CustomEvent<{ message?: string }>).detail?.message
setErrorMsg(m || 'Could not build persona prompt.')
}
window.addEventListener(PERSONA_PROMPT_BUILD_START, onStart)
window.addEventListener(PERSONA_PROMPT_BUILD_END, onEnd)
window.addEventListener(PERSONA_PROMPT_BUILD_COMPLETE, onComplete)
window.addEventListener(PERSONA_PROMPT_BUILD_ERROR, onError)
return () => {
window.removeEventListener(PERSONA_PROMPT_BUILD_START, onStart)
window.removeEventListener(PERSONA_PROMPT_BUILD_END, onEnd)
window.removeEventListener(PERSONA_PROMPT_BUILD_COMPLETE, onComplete)
window.removeEventListener(PERSONA_PROMPT_BUILD_ERROR, onError)
}
}, [])
const goExperiment = () => {
if (!modal) return
const id = modal.personaId
setModal(null)
navigate(`/experiment?persona=${encodeURIComponent(id)}`)
}
return (
<>
{banner && (
<div className="persona-prompt-build-banner" role="status" aria-live="polite">
Building, feel free to do other things while you wait
</div>
)}
{errorMsg && (
<div className="modal-overlay" role="alertdialog" aria-modal>
<div className="modal-box">
<h2>Build failed</h2>
<p>{errorMsg}</p>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '1rem' }}>
<button type="button" className="btn btn-primary" onClick={() => setErrorMsg(null)}>
OK
</button>
</div>
</div>
</div>
)}
{modal && (
<div className="modal-overlay" role="dialog" aria-modal aria-labelledby="persona-build-done-title">
<div className="modal-box">
<h2 id="persona-build-done-title">Persona prompt ready</h2>
<p>
Finished building <strong>{modal.personaName}</strong>. Would you like to see it?
</p>
<p style={{ fontSize: '0.88rem', color: 'var(--text-secondary)', marginBottom: 0 }}>
Opens <strong>Persona Prompt Testing</strong> (Experiment with Personas workshop) with this persona
loaded.
</p>
{modal.confidence === 'low' && (
<p className="persona-prompt-build-warn">
{modal.qualityMessage?.trim() || PERSONA_PROMPT_LOW_CONFIDENCE_COPY}
</p>
)}
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', justifyContent: 'flex-end', marginTop: '1rem' }}>
<button type="button" className="btn btn-secondary" onClick={() => setModal(null)}>
Not now
</button>
<button type="button" className="btn btn-primary" onClick={goExperiment}>
Yes
</button>
</div>
</div>
</div>
)}
</>
)
}