"use client"; import { useMemo, type ReactNode } from "react"; import { motion } from "framer-motion"; import { ArrowRight, Check, Copy, Sparkles, ChevronRight } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { SearchableSelect } from "@/components/ui/searchable-select"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { defaultAgentWorkflowUiConfig, flattenAgentWorkflowSteps, flattenVisibleAgentWorkflowSteps, getAgentWorkflowBrandStoreEntries, getAgentWorkflowSelectedOptionPath, getAgentWorkflowStoreOptionsByBrand, getEnabledAgentWorkflowOptionTree, getEnabledAgentWorkflowStepTree, serializeAgentWorkflowSelectionPath, } from "@/lib/agents/workflow-ui-config"; import { AgentCanvasTopNavDark } from "@/components/agents/AgentCanvasTopNavDark"; import { AgentWorkflowShowcaseBanner } from "@/components/agents/AgentWorkflowShowcaseBanner"; import { buildAgentWorkflowComposerContext, getAgentWorkflowProgress, } from "@/lib/agents/workflow-composer"; import type { AgentWorkflowComposerSelection, AgentWorkflowMaterialSelections, AgentWorkflowShowcaseConfig, AgentWorkflowStepKey, AgentWorkflowUiConfig, } from "@/types/agent"; export interface AgentWorkflowInputs { primarySku: string; secondarySku: string; brand: string; store: string; } interface AgentWorkflowPanelDarkV2Props { config?: AgentWorkflowUiConfig | null; inputs: AgentWorkflowInputs; onInputsChange: (next: AgentWorkflowInputs) => void; materials: AgentWorkflowMaterialSelections; onMaterialsChange: (next: AgentWorkflowMaterialSelections) => void; selections: Partial>; onSelectionsChange: (next: Partial>) => void; onReset?: () => void; onNextStep?: () => void; onSkipStep?: () => void; siteName?: string | null; siteLogo?: string | null; siteLogoDark?: string | null; showcaseConfig?: AgentWorkflowShowcaseConfig | null; topNavRightSlot?: ReactNode; } function buildSelectionSummary(params: { config: AgentWorkflowUiConfig; selections: Partial>; }) { const result: AgentWorkflowComposerSelection[] = []; for (const { step } of flattenVisibleAgentWorkflowSteps(params.config.steps, params.selections)) { const selectedOptions = getAgentWorkflowSelectedOptionPath({ step, selectionValue: params.selections[step.key], }); selectedOptions.forEach((option) => { result.push({ stepKey: step.key, stepTitle: step.title, optionKey: option.key, optionName: option.buttonName, promptFragment: option.promptFragment || "", }); }); } return result; } function StepOptionCard({ option, step, selected, onClick, index, }: { option: { key: string; buttonName: string; description?: string | null; previewImageUrl?: string | null }; step: { title: string }; selected: boolean; onClick: () => void; index: number; }) { return ( {/* 背景 */}
{option.previewImageUrl ? ( <> {option.buttonName}
) : (
)}
{/* 选中标记 */} {selected && ( )} {/* 步骤标签 */}
{step.title}
{/* 底部信息 */}

{option.buttonName}

{option.description && (

{option.description}

)}
{/* 选中边框 */} {selected && (
)} ); } export function AgentWorkflowPanelDarkV2({ config, inputs, onInputsChange, materials, onMaterialsChange, selections, onSelectionsChange, onNextStep, onSkipStep, siteName, siteLogo, siteLogoDark, showcaseConfig, topNavRightSlot, }: AgentWorkflowPanelDarkV2Props) { const brandOptions = useMemo( () => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [], [config?.inputOptions?.brands] ); const brandStoreEntries = useMemo( () => getAgentWorkflowBrandStoreEntries(config), [config] ); const storeOptions = useMemo( () => getAgentWorkflowStoreOptionsByBrand({ config, brand: inputs.brand }), [config, inputs.brand] ); const resolvedConfig = useMemo(() => { const source = config || defaultAgentWorkflowUiConfig; return { steps: getEnabledAgentWorkflowStepTree(source.steps || []), }; }, [config]); const flattenedResolvedSteps = useMemo( () => flattenVisibleAgentWorkflowSteps(resolvedConfig.steps, selections).filter( ({ step }) => Array.isArray(step.options) && step.options.length > 0 ), [resolvedConfig.steps, selections] ); const summary = useMemo( () => buildSelectionSummary({ config: resolvedConfig, selections }), [resolvedConfig, selections] ); const composerContext = useMemo( () => buildAgentWorkflowComposerContext({ config: resolvedConfig, inputs, materials, selections, }), [inputs, materials, resolvedConfig, selections] ); const progress = useMemo( () => getAgentWorkflowProgress({ config: resolvedConfig, selections }), [resolvedConfig, selections] ); const renderOptionLevels = ( step: AgentWorkflowUiConfig["steps"][number], options: AgentWorkflowUiConfig["steps"][number]["options"], depth = 1, parentPath: string[] = [] ): ReactNode => { const selectedPathKeys = getAgentWorkflowSelectedOptionPath({ step, selectionValue: selections[step.key], }).map((option) => option.key); const currentSelectedKey = selectedPathKeys[depth - 1] || ""; const currentSelectedOption = options.find((option) => option.key === currentSelectedKey) || null; const visibleChildren = getEnabledAgentWorkflowOptionTree( currentSelectedOption?.children || [] ); const levelLabel = depth === 1 ? "主卡片" : depth === 2 ? "二级卡片" : "三级卡片"; return (
{levelLabel} {depth > 1 ? ( 该层卡片从上一级已选卡片中继续展开 ) : null}
{options.map((option, optionIndex) => ( { const prefix = parentPath.slice(0, depth - 1); const nextKeys = currentSelectedKey === option.key ? prefix : [...prefix, option.key]; onSelectionsChange({ ...selections, [step.key]: nextKeys.length > 0 ? serializeAgentWorkflowSelectionPath(nextKeys) : undefined, }); }} index={optionIndex} /> ))}
{currentSelectedOption && visibleChildren.length > 0 ? renderOptionLevels(step, visibleChildren, depth + 1, [ ...parentPath.slice(0, depth - 1), currentSelectedOption.key, ]) : null}
); }; const handleCopyPrompt = async () => { if (!composerContext?.promptText) { toast.error("当前还没有可复制的 Prompt"); return; } try { await navigator.clipboard.writeText(composerContext.promptText); toast.success("Prompt 已复制"); } catch { toast.error("复制失败"); } }; return (
{/* Hero Section */}
{ const target = document.getElementById("agent-workflow-content"); if (!target) return; target.scrollIntoView({ behavior: "smooth", block: "start" }); }} onSecondaryActionClick={onSkipStep} />
{/* Main Content */}
{/* 基础配置 */}

1 基础配置

onInputsChange({ ...inputs, brand: value, store: value.trim().toLowerCase() === inputs.brand.trim().toLowerCase() ? inputs.store : "", }) } /> 0 && storeOptions.length === 0)} onValueChange={(value) => onInputsChange({ ...inputs, store: value })} />
{/* 工作流步骤 */} {flattenedResolvedSteps.map(({ step, depth }, stepIndex) => ( 1 && "ml-3 border-l-4 border-l-emerald-500/30 sm:ml-6" )} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 + stepIndex * 0.05 }} >

{stepIndex + 2} {step.title} {depth > 1 ? ( {depth === 2 ? "二级模块" : "三级模块"} ) : null}

{step.description && (

{step.description}

)} {renderOptionLevels(step, getEnabledAgentWorkflowOptionTree(step.options || []))}
))} {/* 当前选择摘要 */} {summary.length > 0 && (

当前选择

{summary.map((item) => (
{item.stepTitle} {item.optionName}
))}
)} {/* Prompt 预览 */}

Prompt 预览