import type { Meta, StoryObj } from '@storybook/nextjs-vite';
import type React from 'react';
import { EscalationOfferMessage } from '../components/chat/escalation-offer-message';
/**
* `escalation_offer` message segment — Fae asking the client to confirm a
* handoff to a human technician (Figma: fae chat › "escalation to admin").
*
* Renders THROUGH the client approval card rather than reimplementing it, so
* it is pixel-identical to `Chat/Client/ApprovalRequestMessage` down to the
* Approve/Reject labels and the resolved pill. What differs is the wiring:
* approve/reject call the ticket-escalation GraphQL mutations, never the
* tool-approval endpoint — which is why it is a distinct segment type.
*
* The body copy is backend-fixed; the client never composes it. `origin`
* (TOOL / MANUAL / a trigger reason) is decoded for telemetry only — every
* origin renders the same card.
*/
const OFFER_TEXT =
'Having issues with the AI assistant? This ticket can be handed off to a technician. Fae will no longer respond in this conversation. A technician will review the ticket and reply when available.';
const baseData = { offerId: 'offer-1', text: OFFER_TEXT, origin: 'MANUAL' };
const plainDecorator = (Story: React.ComponentType) => (
);
const meta = {
title: 'Chat/Client/EscalationOfferMessage',
component: EscalationOfferMessage,
tags: ['autodocs'],
parameters: {
docs: {
description: {
component:
'Renders an `escalation_offer` segment: the backend-fixed handoff prompt with Approve/Reject, or a resolved status pill. Approve performs the handoff (ticket → Tech Required, Fae goes silent); Reject just records the choice.',
},
},
},
decorators: [plainDecorator],
} satisfies Meta;
export default meta;
type Story = StoryObj;
/** Pending — the client has not decided yet. */
export const Pending: Story = {
args: {
data: baseData,
status: 'pending',
onApprove: () => {},
onReject: () => {},
},
};
/** Approved — the handoff ran. Paired in the thread with `TicketEscalatedMessage`. */
export const ApprovedByUser: Story = {
args: { data: baseData, status: 'approved', resolvedByName: 'John Smith' },
};
/** Declined — the client chose to keep working with Fae. */
export const DeclinedByUser: Story = {
args: { data: baseData, status: 'rejected', resolvedByName: 'John Smith' },
};
/**
* Superseded — the client typed a new message over the pending offer, which
* the backend treats as declining it. Maps onto the shared `cancelled` status.
*/
export const Superseded: Story = {
args: { data: baseData, status: 'cancelled', resolvedByName: 'John Smith' },
};
/** Raised by Fae's own `escalateToHuman` tool call rather than the header action. */
export const FromToolCall: Story = {
args: {
data: { ...baseData, origin: 'TOOL' },
status: 'pending',
onApprove: () => {},
onReject: () => {},
},
};