feat: integrate FallbackSteps into editor and session runner (Task 18)
Wire FallbackSteps edit mode into StepEditor for procedure_step type with add/remove/update handlers using crypto.randomUUID(). Add execute mode rendering in ProceduralNavigationPage with fallbackDecisions state tracking per parent step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { useState } from 'react'
|
||||
import { ChevronUp, ChevronDown, AlertTriangle, Clock, ExternalLink, CheckSquare, Terminal, Settings2 } from 'lucide-react'
|
||||
import type { ProceduralStep, StepContentType, IntakeFormField } from '@/types'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { FallbackSteps } from '@/components/procedural/FallbackSteps'
|
||||
|
||||
const CONTENT_TYPE_OPTIONS: { value: StepContentType; label: string; color: string }[] = [
|
||||
{ value: 'action', label: 'Action', color: 'text-blue-400' },
|
||||
@@ -278,6 +279,32 @@ export function StepEditor({ step, stepNumber, onUpdate, onCollapse, availableVa
|
||||
</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Fallback Steps — procedure_step only */}
|
||||
{step.type === 'procedure_step' && (
|
||||
<FallbackSteps
|
||||
fallbackSteps={step.fallback_steps ?? []}
|
||||
mode="edit"
|
||||
onAdd={() => {
|
||||
const newFallback: ProceduralStep = {
|
||||
id: crypto.randomUUID(),
|
||||
type: 'procedure_step',
|
||||
title: '',
|
||||
}
|
||||
onUpdate({ fallback_steps: [...(step.fallback_steps ?? []), newFallback] })
|
||||
}}
|
||||
onRemove={(index) => {
|
||||
const updated = (step.fallback_steps ?? []).filter((_, i) => i !== index)
|
||||
onUpdate({ fallback_steps: updated.length > 0 ? updated : undefined })
|
||||
}}
|
||||
onUpdate={(index, updates) => {
|
||||
const updated = (step.fallback_steps ?? []).map((fb, i) =>
|
||||
i === index ? { ...fb, ...updates } : fb
|
||||
)
|
||||
onUpdate({ fallback_steps: updated })
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -5,7 +5,8 @@ import { treesApi } from '@/api/trees'
|
||||
import { sessionsApi } from '@/api/sessions'
|
||||
import { stepsApi } from '@/api/steps'
|
||||
import type { Tree, Session, ProceduralStep, DecisionRecord, RuntimeStep, CustomProceduralStep, IntakeFormField } from '@/types'
|
||||
import type { CustomStep } from '@/types/session'
|
||||
import type { CustomStep, FallbackStepRecord } from '@/types/session'
|
||||
import { FallbackSteps } from '@/components/procedural/FallbackSteps'
|
||||
import type { Step } from '@/types/step'
|
||||
import { StepChecklist } from '@/components/procedural/StepChecklist'
|
||||
import { StepDetail } from '@/components/procedural/StepDetail'
|
||||
@@ -84,6 +85,9 @@ export function ProceduralNavigationPage() {
|
||||
const [batchProgress, setBatchProgress] = useState<{ completed: number; total: number } | null>(null)
|
||||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
// Fallback step decisions
|
||||
const [fallbackDecisions, setFallbackDecisions] = useState<FallbackStepRecord[]>([])
|
||||
|
||||
// Custom step state
|
||||
const [runtimeSteps, setRuntimeSteps] = useState<RuntimeStep[]>([])
|
||||
const [sessionCustomSteps, setSessionCustomSteps] = useState<CustomStep[]>([])
|
||||
@@ -434,6 +438,22 @@ export function ProceduralNavigationPage() {
|
||||
setShowCsatModal(false)
|
||||
}
|
||||
|
||||
const handleFallbackComplete = (
|
||||
parentStepId: string,
|
||||
fallbackStepId: string,
|
||||
notes: string | null,
|
||||
outcome: 'resolved' | 'not_resolved' | 'skipped'
|
||||
) => {
|
||||
const record: FallbackStepRecord = {
|
||||
parent_step_id: parentStepId,
|
||||
fallback_step_id: fallbackStepId,
|
||||
completed_at: new Date().toISOString(),
|
||||
notes,
|
||||
outcome,
|
||||
}
|
||||
setFallbackDecisions((prev) => [...prev, record])
|
||||
}
|
||||
|
||||
const handleStepCreated = (step: Step | CustomStepDraft, isFromLibrary: boolean) => {
|
||||
setPendingCustomStep(step)
|
||||
setPendingIsFromLibrary(isFromLibrary)
|
||||
@@ -734,6 +754,22 @@ export function ProceduralNavigationPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Fallback steps — shown when step has fallback alternatives */}
|
||||
{currentStep && !('isCustom' in currentStep && currentStep.isCustom) && 'fallback_steps' in currentStep && (
|
||||
<FallbackSteps
|
||||
fallbackSteps={(currentStep as ProceduralStep).fallback_steps ?? []}
|
||||
mode="execute"
|
||||
completedIds={new Set(
|
||||
fallbackDecisions
|
||||
.filter((d) => d.parent_step_id === currentStep.id && d.outcome === 'resolved')
|
||||
.map((d) => d.fallback_step_id)
|
||||
)}
|
||||
onComplete={(fallbackStepId, notes, outcome) =>
|
||||
handleFallbackComplete(currentStep.id, fallbackStepId, notes, outcome)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Add custom step — only on current active incomplete non-custom step */}
|
||||
{currentStep && !completedStepIds.has(currentStep.id) && !('isCustom' in currentStep && currentStep.isCustom) && (
|
||||
<div className="mt-4">
|
||||
|
||||
Reference in New Issue
Block a user