Files
floci-ui/packages/frontend/src/components/WorkflowCreatePanel.tsx
T

52 lines
3.8 KiB
TypeScript

import {useState} from 'react'
import {Braces, PanelsTopLeft} from 'lucide-react'
import {validateAslDefinition, workflowCreateSubmission, type WorkflowCreateSubmission} from '@/lib/workflowCreate'
type CreationMode = 'studio' | 'asl'
export function WorkflowCreatePanel({isSubmitting, submitError, onCancel, onSubmit}: {isSubmitting: boolean; submitError: string | null; onCancel: () => void; onSubmit: (submission: WorkflowCreateSubmission) => void}) {
const [mode, setMode] = useState<CreationMode>('studio')
const [name, setName] = useState('')
const [definition, setDefinition] = useState('')
const [definitionError, setDefinitionError] = useState<string | null>(null)
const validName = /^[a-zA-Z0-9_-]{1,80}$/.test(name.trim())
return <form className="dynamic-form" onSubmit={(event) => {
event.preventDefault()
if (!validName) return
if (mode === 'asl') {
const error = validateAslDefinition(definition)
setDefinitionError(error)
if (error) return
onSubmit(workflowCreateSubmission(name, 'explorer', definition))
return
}
onSubmit(workflowCreateSubmission(name, 'studio'))
}}>
<div className="workflow-create-modes" role="group" aria-label="Creation mode">
<button className={`workflow-create-mode${mode === 'studio' ? ' active' : ''}`} type="button" aria-pressed={mode === 'studio'} disabled={isSubmitting} onClick={() => { setMode('studio'); setDefinitionError(null) }}>
<PanelsTopLeft size={18}/><span><strong>Workflow Studio</strong><small>Create a valid starter workflow, then design it visually.</small></span>
</button>
<button className={`workflow-create-mode${mode === 'asl' ? ' active' : ''}`} type="button" aria-pressed={mode === 'asl'} disabled={isSubmitting} onClick={() => setMode('asl')}>
<Braces size={18}/><span><strong>ASL definition</strong><small>Create directly from a complete Amazon States Language document.</small></span>
</button>
</div>
<label className="dynamic-field dynamic-field--span">
<span>State Machine Name<em className="field-required">*</em></span>
<input className="input" autoFocus value={name} disabled={isSubmitting} onChange={(event) => setName(event.target.value)} maxLength={80} required pattern="[a-zA-Z0-9_-]+" aria-describedby="workflow-name-help"/>
<small id="workflow-name-help">Up to 80 characters: letters, digits, hyphens, and underscores.</small>
</label>
{mode === 'asl' && <label className="dynamic-field dynamic-field--span">
<span>ASL Definition<em className="field-required">*</em></span>
<textarea className={`textarea workflow-definition-input${definitionError ? ' invalid' : ''}`} value={definition} disabled={isSubmitting} onChange={(event) => { setDefinition(event.target.value); setDefinitionError(null) }} rows={14} spellCheck={false} aria-describedby="workflow-definition-help workflow-definition-error"/>
<small id="workflow-definition-help">Enter the complete JSON definition. It must contain a string <code>StartAt</code> and an object <code>States</code>.</small>
{definitionError && <small className="field-error" id="workflow-definition-error" role="alert">{definitionError}</small>}
</label>}
{submitError && <p className="error-text" role="alert">{submitError}</p>}
<div className="form-actions">
<button className="button" type="button" disabled={isSubmitting} onClick={onCancel}>Cancel</button>
<button className="button primary" type="submit" disabled={!validName || isSubmitting}>{isSubmitting ? 'Creating' : mode === 'studio' ? 'Create and open Studio' : 'Create state machine'}</button>
</div>
</form>
}