/** * CreateWorkflowModal * ------------------- * * Centered modal that scaffolds a new workflow via `openhuman.workflows_create`. * Mirrors CreateSkillModal — backdrop, Escape-to-close, focus capture, 520px * max-width, clean white background. */ import debug from 'debug'; import { useEffect, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { useT } from '../../lib/i18n/I18nContext'; import { type Workflow, workflowsApi } from '../../services/api/workflowsApi'; const log = debug('workflows:create-modal'); interface Props { onClose: () => void; onCreated: (workflow: Workflow) => void; } export default function CreateWorkflowModal({ onClose, onCreated }: Props) { const { t } = useT(); const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [whenToUse, setWhenToUse] = useState(''); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const nameInputRef = useRef(null); const previousFocusRef = useRef(null); const isValid = name.trim().length > 0; useEffect(() => { previousFocusRef.current = document.activeElement as HTMLElement | null; const raf = window.requestAnimationFrame(() => { nameInputRef.current?.focus(); }); log('mount'); return () => { window.cancelAnimationFrame(raf); previousFocusRef.current?.focus?.(); log('unmount'); }; }, []); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === 'Escape' && !submitting) { log('escape-key close'); onClose(); } }; document.addEventListener('keydown', handler); return () => document.removeEventListener('keydown', handler); }, [onClose, submitting]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!isValid || submitting) return; setSubmitting(true); setError(null); log('submit name=%s', name.trim()); try { const workflow = await workflowsApi.createWorkflow({ name: name.trim(), description: description.trim() || undefined, when_to_use: whenToUse.trim() || undefined, }); log('created name=%s', workflow.name); onCreated(workflow); } catch (err) { const msg = err instanceof Error ? err.message : String(err); log('create error %s', msg); setError(msg); setSubmitting(false); } }; return createPortal(
{ if (e.target === e.currentTarget && !submitting) { log('backdrop-click close'); onClose(); } }}>