import type { Meta, StoryObj } from '@storybook/nextjs-vite'; import type React from 'react'; import { ApprovalBatchMessage } from '../components/chat/approval-batch-message'; import type { ApprovalBatchData, ApprovalBatchExecutionState, PendingToolCallData } from '../components/chat/types'; /** * The "Command Block" from Figma (open-design-system, node 1092-2807). * * A single command/tool call the AI wants to run, rendered as an approvable * card: a command title line, an expandable body with the call arguments * (RunAsUser, timeoutSeconds, …) and — once executed — a Result block. The * footer flips between Approve/Reject buttons (pending) and a resolved status * tag ("Approved / Rejected / Cancelled by {name}"). * * Click a row to expand/collapse its body. */ const LONG_COMMAND = `Get-CimInstance Win32_Process | Select-Object ProcessId, Name, Path, CommandLine | Where-Object { $_.Path -like 'C:\\Users\\Public\\*' }`; const RESULT = `pid | name | path | cmdline 4821 | suspicious.exe | C:\\Users\\Public\\suspicious.exe | suspicious.exe --hidden --connect 185.220.101.45`; const REQUEST_ID = 'req-1'; const toolCall: PendingToolCallData = { toolExecutionRequestId: REQUEST_ID, toolName: 'run_script', toolTitle: 'Run Script', toolExplanation: 'Runs a diagnostic script on the endpoint to inspect running processes.', toolType: 'OPENFRAME_RMM', requiresApproval: true, approvalType: 'ADMIN', toolCallArguments: { command: LONG_COMMAND, RunAsUser: 'False', timeoutSeconds: 60, }, }; const makeData = (executions?: Record): ApprovalBatchData => ({ approvalRequestId: 'batch-1', approvalType: 'ADMIN', toolCalls: [toolCall], executions, }); const executingData = makeData({ [REQUEST_ID]: { status: 'executing' } }); const doneData = makeData({ [REQUEST_ID]: { status: 'done', result: RESULT, success: true }, }); const constrainedDecorator = (width?: number) => // Named function expression: Storybook renders decorators as components, so // the name is what shows up in the React tree instead of `Anonymous`. function ConstrainedDecorator(Story: React.ComponentType) { return (
); }; const meta = { title: 'Chat/Admin/ApprovalBatchMessage', component: ApprovalBatchMessage, tags: ['autodocs'], parameters: { docs: { description: { component: 'Command Block (Figma node 1092-2807). Approvable tool-call card with pending / approved / rejected / cancelled states, an expandable body of call arguments, and a Result block after execution.', }, }, }, decorators: [constrainedDecorator()], } satisfies Meta; export default meta; type Story = StoryObj; /** Pending — Approve / Reject buttons shown. Click the row to expand args. */ export const Pending: Story = { args: { data: makeData(), status: 'pending', onApprove: () => {}, onReject: () => {}, }, }; /** Approved and still executing — grey loader on the row, no result yet. */ export const ApprovedExecuting: Story = { args: { data: executingData, status: 'approved', resolvedByName: 'Michael Johnson', }, }; /** Approved and finished — green check + expandable Result block. */ export const ApprovedDone: Story = { args: { data: doneData, status: 'approved', resolvedByName: 'Michael Johnson', }, }; /** Rejected by a user — red status tag, no execution. */ export const Rejected: Story = { args: { data: makeData(), status: 'rejected', resolvedByName: 'Michael Johnson', }, }; /** Cancelled — grey status tag, no execution. */ export const Cancelled: Story = { args: { data: makeData(), status: 'cancelled', resolvedByName: 'Michael Johnson', }, }; /** * All states side by side — mirrors the Figma "Command Blocks" board so the * full set can be reviewed at a glance. */ export const AllStates: Story = { decorators: [constrainedDecorator(420)], render: () => (
{}} onReject={() => {}} />
), };