feat: StepDetail accepts RuntimeStep, renders Custom Step badge for custom steps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { AlertTriangle, CheckCircle2, Info, Zap, Copy, Check, ExternalLink } from 'lucide-react'
|
||||
import type { ProceduralStep, StepContentType, CommandBlock } from '@/types'
|
||||
import type { RuntimeStep, StepContentType, CommandBlock } from '@/types'
|
||||
import { resolveVariables } from '@/lib/variableResolver'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
@@ -12,7 +12,7 @@ const contentTypeConfig: Record<StepContentType, { icon: typeof Zap; color: stri
|
||||
}
|
||||
|
||||
interface StepDetailProps {
|
||||
step: ProceduralStep
|
||||
step: RuntimeStep
|
||||
stepNumber: number
|
||||
totalSteps: number
|
||||
variables: Record<string, string>
|
||||
@@ -39,13 +39,18 @@ export function StepDetail({
|
||||
isLast,
|
||||
}: StepDetailProps) {
|
||||
const [copiedIndex, setCopiedIndex] = useState<number | null>(null)
|
||||
const isCustom = 'isCustom' in step && step.isCustom
|
||||
const contentType = step.content_type || 'action'
|
||||
const config = contentTypeConfig[contentType]
|
||||
const Icon = config.icon
|
||||
|
||||
// Derive verification from either flat fields or nested object
|
||||
const verificationPrompt = step.verification_prompt || step.verification?.prompt
|
||||
const verificationType = step.verification_type || step.verification?.type
|
||||
const verificationPrompt = !isCustom && 'verification_prompt' in step
|
||||
? step.verification_prompt || step.verification?.prompt
|
||||
: undefined
|
||||
const verificationType = !isCustom && 'verification_type' in step
|
||||
? step.verification_type || step.verification?.type
|
||||
: undefined
|
||||
|
||||
const resolve = (text: string | undefined) => {
|
||||
if (!text) return ''
|
||||
@@ -87,14 +92,20 @@ export function StepDetail({
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="text-lg font-semibold text-foreground">{step.title}</h2>
|
||||
<div className="mt-1 flex items-center gap-2">
|
||||
<span className={cn('inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs', config.bg, config.color)}>
|
||||
<Icon className="h-3 w-3" />
|
||||
{config.label}
|
||||
</span>
|
||||
{isCustom ? (
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-amber-400/15 px-2 py-0.5 text-xs text-amber-400">
|
||||
✦ Custom Step
|
||||
</span>
|
||||
) : (
|
||||
<span className={cn('inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs', config.bg, config.color)}>
|
||||
<Icon className="h-3 w-3" />
|
||||
{config.label}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Step {stepNumber} of {totalSteps}
|
||||
</span>
|
||||
{step.estimated_minutes && (
|
||||
{'estimated_minutes' in step && step.estimated_minutes && (
|
||||
<span className="text-xs text-muted-foreground">~{step.estimated_minutes} min</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -102,7 +113,7 @@ export function StepDetail({
|
||||
</div>
|
||||
|
||||
{/* Warning banner */}
|
||||
{step.warning_text && (
|
||||
{'warning_text' in step && step.warning_text && (
|
||||
<div className="flex items-start gap-2 rounded-lg border border-yellow-400/20 bg-yellow-400/5 px-3 py-2.5">
|
||||
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-400" />
|
||||
<p className="text-sm text-yellow-200">{resolve(step.warning_text)}</p>
|
||||
@@ -142,7 +153,7 @@ export function StepDetail({
|
||||
)}
|
||||
|
||||
{/* Expected outcome */}
|
||||
{step.expected_outcome && (
|
||||
{'expected_outcome' in step && step.expected_outcome && (
|
||||
<div className="rounded-lg border border-border bg-white/[0.02] p-3">
|
||||
<h4 className="mb-1 text-xs font-medium text-muted-foreground">Expected Outcome</h4>
|
||||
<p className="text-sm text-muted-foreground">{resolve(step.expected_outcome)}</p>
|
||||
@@ -181,7 +192,7 @@ export function StepDetail({
|
||||
)}
|
||||
|
||||
{/* Notes */}
|
||||
{step.notes_enabled !== false && (
|
||||
{(!('notes_enabled' in step) || step.notes_enabled !== false) && (
|
||||
<div>
|
||||
<label className="mb-1 block text-xs font-medium text-muted-foreground">Notes</label>
|
||||
<textarea
|
||||
@@ -195,7 +206,7 @@ export function StepDetail({
|
||||
)}
|
||||
|
||||
{/* Reference link */}
|
||||
{step.reference_url && (
|
||||
{'reference_url' in step && step.reference_url && (
|
||||
<a
|
||||
href={resolve(step.reference_url)}
|
||||
target="_blank"
|
||||
|
||||
Reference in New Issue
Block a user