Files
resolutionflow/frontend/src/components/ai-chat/StaticStepsPreview.tsx
chihlasm f86e16661a feat: add procedural flow support to AI chat builder (Flow Assist)
- Add procedural-specific system prompts (schema, interview protocol, response format)
- Dispatch prompts by flow_type: procedural/maintenance use flat steps schema, troubleshooting uses decision tree schema
- Parse [STEPS_UPDATE] and [INTAKE_FORM] markers in AI responses
- Add validate_generated_procedural_steps() validator
- Handle intake form extraction in AI chat import endpoint
- Add StaticStepsPreview component for procedural flow preview
- Update store and page to render correct preview by flow type

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 02:20:14 -05:00

113 lines
4.2 KiB
TypeScript

import type { ProceduralStep } from '@/types'
import { Terminal, Info, CheckSquare, AlertTriangle, LayoutList } from 'lucide-react'
import { cn } from '@/lib/utils'
interface StaticStepsPreviewProps {
steps: ProceduralStep[]
name?: string
}
const CONTENT_TYPE_ICONS: Record<string, typeof Terminal> = {
action: Terminal,
informational: Info,
verification: CheckSquare,
warning: AlertTriangle,
}
export function StaticStepsPreview({ steps, name }: StaticStepsPreviewProps) {
let stepNumber = 0
return (
<div className="flex h-full flex-col">
<div className="border-b border-border px-4 py-2">
<h3 className="text-sm font-semibold text-foreground">
Preview: {name || 'Untitled Flow'}
</h3>
<p className="text-xs text-muted-foreground">
{steps.filter((s) => s.type === 'procedure_step').length} steps
</p>
</div>
<div className="flex-1 overflow-auto p-4">
<div className="space-y-1.5">
{steps.map((step) => {
if (step.type === 'section_header') {
return (
<div key={step.id} className="pt-3 pb-1 first:pt-0">
<div className="flex items-center gap-2">
<LayoutList className="h-3.5 w-3.5 text-primary" />
<span className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-primary">
{step.title}
</span>
</div>
</div>
)
}
if (step.type === 'procedure_end') {
return (
<div
key={step.id}
className="mt-2 rounded-lg border border-emerald-500/20 bg-emerald-500/5 px-3 py-2"
>
<span className="text-xs font-medium text-emerald-400">
{step.title || 'Procedure Complete'}
</span>
</div>
)
}
stepNumber++
const contentType = step.content_type || 'action'
const Icon = CONTENT_TYPE_ICONS[contentType] || Terminal
return (
<div
key={step.id}
className={cn(
'rounded-lg border px-3 py-2 text-xs',
contentType === 'warning'
? 'border-amber-500/20 bg-amber-500/5'
: 'border-border bg-card'
)}
>
<div className="flex items-start gap-2">
<span className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded bg-primary/10 font-label text-[0.5rem] text-primary">
{stepNumber}
</span>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-1.5">
<Icon className={cn(
'h-3 w-3 shrink-0',
contentType === 'warning' ? 'text-amber-400' : 'text-muted-foreground'
)} />
<span className={cn(
'font-medium truncate',
contentType === 'warning' ? 'text-amber-400' : 'text-foreground'
)}>
{step.title}
</span>
</div>
{step.commands && (
<div className="mt-1 flex items-center gap-1 text-muted-foreground">
<Terminal className="h-2.5 w-2.5" />
<span className="font-label text-[0.5rem]">
{Array.isArray(step.commands) ? step.commands.length : 1} command{(Array.isArray(step.commands) ? step.commands.length : 1) !== 1 ? 's' : ''}
</span>
</div>
)}
</div>
{step.estimated_minutes && (
<span className="shrink-0 font-label text-[0.5rem] text-muted-foreground">
~{step.estimated_minutes}m
</span>
)}
</div>
</div>
)
})}
</div>
</div>
</div>
)
}