import type { Meta, StoryObj } from '@storybook/nextjs' import { useEffect } from 'react' import MessageLogModal from '.' import type { IChatItem } from '@/app/components/base/chat/chat/type' import { useStore } from '@/app/components/app/store' import type { WorkflowRunDetailResponse } from '@/models/log' import type { NodeTracing, NodeTracingListResponse } from '@/types/workflow' import { BlockEnum } from '@/app/components/workflow/types' import { WorkflowContextProvider } from '@/app/components/workflow/context' const SAMPLE_APP_DETAIL = { id: 'app-demo-1', name: 'Support Assistant', mode: 'chat', } as any const mockRunDetail: WorkflowRunDetailResponse = { id: 'run-demo-1', version: 'v1.0.0', graph: { nodes: [], edges: [], }, inputs: JSON.stringify({ question: 'How do I reset my password?' }, null, 2), inputs_truncated: false, status: 'succeeded', outputs: JSON.stringify({ answer: 'Follow the reset link we just emailed you.' }, null, 2), outputs_truncated: false, total_steps: 3, created_by_role: 'account', created_by_account: { id: 'account-1', name: 'Demo Admin', email: 'demo@example.com', }, created_at: 1700000000, finished_at: 1700000006, elapsed_time: 5.2, total_tokens: 864, } const buildNode = (override: Partial): NodeTracing => ({ id: 'node-start', index: 0, predecessor_node_id: '', node_id: 'node-start', node_type: BlockEnum.Start, title: 'Start', inputs: {}, inputs_truncated: false, process_data: {}, process_data_truncated: false, outputs: {}, outputs_truncated: false, status: 'succeeded', metadata: { iterator_length: 1, iterator_index: 0, loop_length: 1, loop_index: 0, }, created_at: 1700000000, created_by: { id: 'account-1', name: 'Demo Admin', email: 'demo@example.com', }, finished_at: 1700000001, elapsed_time: 1.1, extras: {}, ...override, }) const mockTracingList: NodeTracingListResponse = { data: [ buildNode({}), buildNode({ id: 'node-answer', node_id: 'node-answer', node_type: BlockEnum.Answer, title: 'Answer', inputs: { prompt: 'How do I reset my password?' }, outputs: { output: 'Follow the reset link we just emailed you.' }, finished_at: 1700000005, elapsed_time: 2.6, }), ], } const mockCurrentLogItem: IChatItem = { id: 'message-1', content: 'Follow the reset link we just emailed you.', isAnswer: true, workflow_run_id: 'run-demo-1', } const useMessageLogMocks = () => { useEffect(() => { const store = useStore.getState() store.setAppDetail(SAMPLE_APP_DETAIL) const originalFetch = globalThis.fetch?.bind(globalThis) ?? null const handle = async (input: RequestInfo | URL, init?: RequestInit) => { const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url if (url.includes('/workflow-runs/run-demo-1/') && url.endsWith('/node-executions')) { return new Response( JSON.stringify(mockTracingList), { headers: { 'Content-Type': 'application/json' }, status: 200 }, ) } if (url.endsWith('/workflow-runs/run-demo-1')) { return new Response( JSON.stringify(mockRunDetail), { headers: { 'Content-Type': 'application/json' }, status: 200 }, ) } if (originalFetch) return originalFetch(input, init) throw new Error(`Unmocked fetch call for ${url}`) } globalThis.fetch = handle as typeof globalThis.fetch return () => { globalThis.fetch = originalFetch || globalThis.fetch useStore.getState().setAppDetail(undefined) } }, []) } type MessageLogModalProps = React.ComponentProps const MessageLogPreview = (props: MessageLogModalProps) => { useMessageLogMocks() return (
) } const meta = { title: 'Base/Feedback/MessageLogModal', component: MessageLogPreview, parameters: { layout: 'fullscreen', docs: { description: { component: 'Workflow run inspector presented alongside chat transcripts. This Storybook mock provides canned run details and tracing metadata.', }, }, }, args: { defaultTab: 'DETAIL', width: 960, fixedWidth: true, onCancel: () => { console.log('Modal closed') }, }, tags: ['autodocs'], } satisfies Meta export default meta type Story = StoryObj export const FixedPanel: Story = {} export const FloatingPanel: Story = { args: { fixedWidth: false, }, }