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: () => {},
},
};