import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import type React from 'react';
import { ApprovalBatchMessage } from '../components/chat/approval-batch-message';
import type { ApprovalBatchData, PendingToolCallData } from '../components/chat/types';
/**
* CLIENT (end-user Fae desktop app) variant of the approval block — Figma
* node 203-11947 ("fae-approval-block").
*
* Same component and props as the Admin card, driven by `variant="client"`.
* The end client sees ONLY the BE-generated title plus the Approve/Reject
* buttons, or the full-text resolved pill ("Approved by Michael Johnson").
* Commands, scripts, expandable args/results and execution icons are never
* rendered in this variant.
*/
const LONG_COMMAND = `Get-CimInstance Win32_Process |
Select-Object ProcessId, Name, Path, CommandLine |
Where-Object { $_.Path -like 'C:\\Users\\Public\\*' }`;
const REQUEST_ID = 'req-1';
const toolCall: PendingToolCallData = {
toolExecutionRequestId: REQUEST_ID,
toolName: 'run_script',
toolTitle: 'Run Script',
// BE-generated human-readable title — the ONLY content the client sees.
toolExplanation: "Collects today's system, application, and security event logs and exports them to a CSV file.",
toolType: 'OPENFRAME_RMM',
requiresApproval: true,
approvalType: 'CLIENT',
toolCallArguments: {
command: LONG_COMMAND,
RunAsUser: 'False',
timeoutSeconds: 60,
},
};
const makeData = (): ApprovalBatchData => ({
approvalRequestId: 'batch-1',
approvalType: 'CLIENT',
toolCalls: [toolCall],
});
const plainDecorator = (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 PlainDecorator(Story: React.ComponentType) {
return (
);
};
const meta = {
title: 'Chat/Client/ApprovalBatchMessage',
component: ApprovalBatchMessage,
tags: ['autodocs'],
args: { variant: 'client' },
parameters: {
docs: {
description: {
component:
"CLIENT (Fae end-user) approval block — title-only card with Approve/Reject or a full-text status pill. Commands/scripts are never shown. Gated by variant='client' (Figma 203-11947).",
},
},
},
decorators: [plainDecorator()],
} satisfies Meta;
export default meta;
type Story = StoryObj;
/** Pending — title + Approve / Reject buttons. No expandable command. */
export const Pending: Story = {
args: {
data: makeData(),
status: 'pending',
onApprove: () => {},
onReject: () => {},
},
};
/** Approved — full-text green status pill. */
export const Approved: Story = {
args: {
data: makeData(),
status: 'approved',
resolvedByName: 'Michael Johnson',
},
};
/** Rejected by a user — full-text red status pill. */
export const Rejected: Story = {
args: {
data: makeData(),
status: 'rejected',
resolvedByName: 'Michael Johnson',
},
};
/** Cancelled — full-text grey status pill. */
export const Cancelled: Story = {
args: {
data: makeData(),
status: 'cancelled',
resolvedByName: 'Michael Johnson',
},
};
/**
* All states side by side — mirrors the Figma CLIENT approval-block frames
* (1-6575 / 1-6621 / 1-6637 / 1-6653).
*/
export const AllStates: Story = {
decorators: [plainDecorator(420)],
render: () => {
const states = [
{ data: makeData(), status: 'pending' as const, onApprove: () => {}, onReject: () => {} },
{ data: makeData(), status: 'approved' as const, resolvedByName: 'Michael Johnson' },
{ data: makeData(), status: 'rejected' as const, resolvedByName: 'Michael Johnson' },
{ data: makeData(), status: 'cancelled' as const, resolvedByName: 'Michael Johnson' },
];
return (
{states.map((s, i) => (
))}
);
},
};