/** * NoTemplateDialog — three-option dialog when a Suggested Fix has no matching * Script Library template (per FLOWPILOT-MIGRATION.md mockup 03 + Section 3.3). * * The AI has drafted a session-specific script (`fix.ai_drafted_script`); the * engineer picks one of: * * 1. Run as one-off — no template created, script captured in session * 2. Run now, templatize after — RECOMMENDED; draft_templates row queued for * the post-resolve TemplatizePrompt (Phase 6) * 3. Build as template now — redirect to /scripts/builder pre-loaded * * The drafted script is shown above the option cards with AI-proposed * parameter values highlighted in amber via ParameterizationPreview. * * Inline-edit on the script body is supported so the engineer can tweak * before deciding — the edited body is sent to the decision endpoint. */ import { useState } from 'react' import { Loader2, Pencil, Check, X, Terminal, FileText, Hammer } from 'lucide-react' import { cn } from '@/lib/utils' import type { SessionSuggestedFix, UserDecision } from '@/api/sessionSuggestedFixes' import { ParameterizationPreview } from './ParameterizationPreview' interface NoTemplateDialogProps { fix: SessionSuggestedFix onClose: () => void // Returns the rendered script (or null if the engineer chose build_template // and is being redirected away). onDecide: ( decision: UserDecision, options: { editedScript: string; parametersUsed: Record }, ) => Promise busy: boolean } interface OptionCardProps { label: string description: string tradeoffs: string recommended?: boolean tone: 'neutral' | 'cyan' | 'purple' icon: typeof Terminal onClick: () => void disabled: boolean } function OptionCard({ label, description, tradeoffs, recommended, tone, icon: Icon, onClick, disabled, }: OptionCardProps) { const toneClasses = { neutral: 'border-default hover:border-hover bg-card', cyan: 'border-accent/40 hover:border-accent/70 bg-accent-dim/15', purple: 'border-purple/40 hover:border-purple/70 bg-purple/5', }[tone] const accentText = { neutral: 'text-heading', cyan: 'text-accent-text', purple: 'text-purple', }[tone] return ( ) } export function NoTemplateDialog({ fix, onClose, onDecide, busy }: NoTemplateDialogProps) { const [editing, setEditing] = useState(false) const [draftBody, setDraftBody] = useState(fix.ai_drafted_script ?? '') // Surface the AI's proposed parameters as highlight values in the preview, // and pass them along to the decision endpoint as parameters_used so the // draft_templates row records what the first run used. const proposedParams: Record = {} const params = fix.ai_drafted_parameters ?? {} for (const [k, v] of Object.entries(params)) { if (typeof v === 'string') proposedParams[k] = v } const decide = (decision: UserDecision) => { onDecide(decision, { editedScript: draftBody.trim(), parametersUsed: proposedParams, }) } return (
No matching template — draft script below
{fix.description}
Drafted script {!editing && ( )} {editing && ( )}
{editing ? (