- 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>
113 lines
4.2 KiB
TypeScript
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>
|
|
)
|
|
}
|