@cellarnode/ui 0.8.19 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/branch-overview-panel.d.ts +51 -0
- package/dist/components/branch-overview-panel.d.ts.map +1 -0
- package/dist/components/branch-overview-panel.js +48 -0
- package/dist/components/branch-overview-panel.js.map +1 -0
- package/dist/components/branch-overview-panel.stories.d.ts +16 -0
- package/dist/components/branch-overview-panel.stories.d.ts.map +1 -0
- package/dist/components/branch-overview-panel.stories.js +96 -0
- package/dist/components/branch-overview-panel.stories.js.map +1 -0
- package/dist/components/branch-resolution-modal.d.ts +31 -0
- package/dist/components/branch-resolution-modal.d.ts.map +1 -0
- package/dist/components/branch-resolution-modal.js +35 -0
- package/dist/components/branch-resolution-modal.js.map +1 -0
- package/dist/components/branch-resolution-modal.stories.d.ts +16 -0
- package/dist/components/branch-resolution-modal.stories.d.ts.map +1 -0
- package/dist/components/branch-resolution-modal.stories.js +66 -0
- package/dist/components/branch-resolution-modal.stories.js.map +1 -0
- package/dist/components/branch-resolution-modal.test.d.ts +2 -0
- package/dist/components/branch-resolution-modal.test.d.ts.map +1 -0
- package/dist/components/branch-resolution-modal.test.js +52 -0
- package/dist/components/branch-resolution-modal.test.js.map +1 -0
- package/dist/components/celebration-pulse.d.ts +21 -0
- package/dist/components/celebration-pulse.d.ts.map +1 -0
- package/dist/components/celebration-pulse.js +16 -0
- package/dist/components/celebration-pulse.js.map +1 -0
- package/dist/components/celebration-pulse.stories.d.ts +16 -0
- package/dist/components/celebration-pulse.stories.d.ts.map +1 -0
- package/dist/components/celebration-pulse.stories.js +29 -0
- package/dist/components/celebration-pulse.stories.js.map +1 -0
- package/dist/components/completed-offer-card.d.ts +34 -0
- package/dist/components/completed-offer-card.d.ts.map +1 -0
- package/dist/components/completed-offer-card.js +20 -0
- package/dist/components/completed-offer-card.js.map +1 -0
- package/dist/components/completed-offer-card.stories.d.ts +17 -0
- package/dist/components/completed-offer-card.stories.d.ts.map +1 -0
- package/dist/components/completed-offer-card.stories.js +75 -0
- package/dist/components/completed-offer-card.stories.js.map +1 -0
- package/dist/components/importer-picker-modal.d.ts +50 -0
- package/dist/components/importer-picker-modal.d.ts.map +1 -0
- package/dist/components/importer-picker-modal.js +82 -0
- package/dist/components/importer-picker-modal.js.map +1 -0
- package/dist/components/importer-picker-modal.stories.d.ts +17 -0
- package/dist/components/importer-picker-modal.stories.d.ts.map +1 -0
- package/dist/components/importer-picker-modal.stories.js +62 -0
- package/dist/components/importer-picker-modal.stories.js.map +1 -0
- package/dist/components/importer-picker-modal.test.d.ts +2 -0
- package/dist/components/importer-picker-modal.test.d.ts.map +1 -0
- package/dist/components/importer-picker-modal.test.js +51 -0
- package/dist/components/importer-picker-modal.test.js.map +1 -0
- package/dist/components/kanban-board.d.ts +19 -0
- package/dist/components/kanban-board.d.ts.map +1 -1
- package/dist/components/kanban-board.js.map +1 -1
- package/dist/components/kanban-dnd-board.d.ts +1 -1
- package/dist/components/kanban-dnd-board.d.ts.map +1 -1
- package/dist/components/kanban-dnd-board.js +52 -7
- package/dist/components/kanban-dnd-board.js.map +1 -1
- package/dist/components/kanban-dnd-board.stories.d.ts +1 -0
- package/dist/components/kanban-dnd-board.stories.d.ts.map +1 -1
- package/dist/components/kanban-dnd-board.stories.js +66 -0
- package/dist/components/kanban-dnd-board.stories.js.map +1 -1
- package/dist/components/kanban-dnd-board.test.d.ts +2 -0
- package/dist/components/kanban-dnd-board.test.d.ts.map +1 -0
- package/dist/components/kanban-dnd-board.test.js +34 -0
- package/dist/components/kanban-dnd-board.test.js.map +1 -0
- package/dist/components/offer-status-dots.d.ts +23 -0
- package/dist/components/offer-status-dots.d.ts.map +1 -0
- package/dist/components/offer-status-dots.js +34 -0
- package/dist/components/offer-status-dots.js.map +1 -0
- package/dist/components/offer-status-dots.stories.d.ts +19 -0
- package/dist/components/offer-status-dots.stories.d.ts.map +1 -0
- package/dist/components/offer-status-dots.stories.js +49 -0
- package/dist/components/offer-status-dots.stories.js.map +1 -0
- package/dist/components/sent-offer-card.d.ts +31 -0
- package/dist/components/sent-offer-card.d.ts.map +1 -0
- package/dist/components/sent-offer-card.js +23 -0
- package/dist/components/sent-offer-card.js.map +1 -0
- package/dist/components/sent-offer-card.stories.d.ts +17 -0
- package/dist/components/sent-offer-card.stories.d.ts.map +1 -0
- package/dist/components/sent-offer-card.stories.js +51 -0
- package/dist/components/sent-offer-card.stories.js.map +1 -0
- package/dist/hooks/use-celebration-pulse.d.ts +12 -0
- package/dist/hooks/use-celebration-pulse.d.ts.map +1 -0
- package/dist/hooks/use-celebration-pulse.js +28 -0
- package/dist/hooks/use-celebration-pulse.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/kanban-transitions.d.ts +62 -0
- package/dist/lib/kanban-transitions.d.ts.map +1 -1
- package/dist/lib/kanban-transitions.js +32 -0
- package/dist/lib/kanban-transitions.js.map +1 -1
- package/dist/lib/kanban-transitions.test.d.ts +2 -0
- package/dist/lib/kanban-transitions.test.d.ts.map +1 -0
- package/dist/lib/kanban-transitions.test.js +75 -0
- package/dist/lib/kanban-transitions.test.js.map +1 -0
- package/dist/offer/index.d.ts +3 -0
- package/dist/offer/index.d.ts.map +1 -1
- package/dist/offer/index.js +7 -0
- package/dist/offer/index.js.map +1 -1
- package/dist/offer/sensory-profile.d.ts.map +1 -1
- package/dist/offer/sensory-profile.js +1 -1
- package/dist/offer/sensory-profile.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type OfferStatusDotState } from "./offer-status-dots";
|
|
2
|
+
/**
|
|
3
|
+
* One offer sent to one importer (a child offer in backend terms).
|
|
4
|
+
* The "branch" word lives only in the type name and internal file name —
|
|
5
|
+
* user-visible copy uses "offer" exclusively.
|
|
6
|
+
*/
|
|
7
|
+
export interface OfferBranch {
|
|
8
|
+
childOfferId: string;
|
|
9
|
+
importerOrgId: string;
|
|
10
|
+
importerName: string;
|
|
11
|
+
status: OfferStatusDotState;
|
|
12
|
+
/** Current price label (e.g. "€48 / unit"). */
|
|
13
|
+
currentPrice: string;
|
|
14
|
+
/** Direction of the latest counter relative to the original offer price. */
|
|
15
|
+
priceDelta: "up" | "down" | "flat";
|
|
16
|
+
/** Human-readable last activity, e.g. "Counter-offered · 6h ago". */
|
|
17
|
+
lastActivityLabel: string;
|
|
18
|
+
/** Whether the producer can click "Accept" on this branch. */
|
|
19
|
+
canAccept: boolean;
|
|
20
|
+
/** Importer logo URL — optional. */
|
|
21
|
+
logoUrl?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface OffersSentPanelProps {
|
|
24
|
+
open: boolean;
|
|
25
|
+
onOpenChange: (open: boolean) => void;
|
|
26
|
+
productName: string;
|
|
27
|
+
/** Subtitle for the header — typically "Sent {time} · N offers · Draft #{id}". */
|
|
28
|
+
opportunityName: string;
|
|
29
|
+
branches: OfferBranch[];
|
|
30
|
+
/** Called when the producer clicks "Accept" on a branch. */
|
|
31
|
+
onAccept: (childOfferId: string) => void;
|
|
32
|
+
/** Called when the producer clicks "View offer →" on a branch. */
|
|
33
|
+
onViewBranch: (childOfferId: string) => void;
|
|
34
|
+
/** Called when the producer clicks the header "Open parent offer" link. */
|
|
35
|
+
onOpenParent?: () => void;
|
|
36
|
+
/** Show the "Send to another importer" footer button (open mode only). */
|
|
37
|
+
showAddImporter?: boolean;
|
|
38
|
+
onAddImporter?: () => void;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Right slide-over panel listing all offers sent for a parent offer.
|
|
43
|
+
* Shows a sorted list of branches (by activity priority), a best-offer
|
|
44
|
+
* banner if any negotiation branches exist, and per-branch Accept and
|
|
45
|
+
* View actions.
|
|
46
|
+
*
|
|
47
|
+
* The panel is responsive: max-w-md desktop, full-screen sheet on mobile
|
|
48
|
+
* (handled by Tailwind classes; no JS).
|
|
49
|
+
*/
|
|
50
|
+
export declare function OffersSentPanel({ open, onOpenChange, productName, opportunityName, branches, onAccept, onViewBranch, onOpenParent, showAddImporter, onAddImporter, className, }: OffersSentPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
//# sourceMappingURL=branch-overview-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-overview-panel.d.ts","sourceRoot":"","sources":["../../src/components/branch-overview-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEhF;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,+CAA+C;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,UAAU,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACnC,qEAAqE;IACrE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,SAAS,EAAE,OAAO,CAAC;IACnB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,4DAA4D;IAC5D,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,kEAAkE;IAClE,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,0EAA0E;IAC1E,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4BD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAuB,EACvB,aAAa,EACb,SAAS,GACV,EAAE,oBAAoB,2CAyItB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as Dialog from "@radix-ui/react-dialog";
|
|
3
|
+
import { ArrowDown, ArrowRight, ArrowUp, CheckCircle2, Minus, Trophy, X } from "lucide-react";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
import { OfferStatusDots } from "./offer-status-dots";
|
|
6
|
+
const STATUS_BORDER_CLASSES = {
|
|
7
|
+
pending: "border-l-slate-300 dark:border-l-slate-600",
|
|
8
|
+
viewed: "border-l-blue-400 dark:border-l-blue-500",
|
|
9
|
+
in_negotiation: "border-l-amber-400 dark:border-l-amber-500",
|
|
10
|
+
declined: "border-l-red-400 dark:border-l-red-500",
|
|
11
|
+
rejected: "border-l-red-400 dark:border-l-red-500",
|
|
12
|
+
accepted: "border-l-emerald-400 dark:border-l-emerald-500",
|
|
13
|
+
finalized: "border-l-amber-300 dark:border-l-amber-400",
|
|
14
|
+
};
|
|
15
|
+
const STATUS_SORT_ORDER = {
|
|
16
|
+
finalized: 0,
|
|
17
|
+
accepted: 1,
|
|
18
|
+
in_negotiation: 2,
|
|
19
|
+
viewed: 3,
|
|
20
|
+
pending: 4,
|
|
21
|
+
declined: 5,
|
|
22
|
+
rejected: 6,
|
|
23
|
+
};
|
|
24
|
+
function PriceDeltaIcon({ delta }) {
|
|
25
|
+
if (delta === "up")
|
|
26
|
+
return _jsx(ArrowUp, { className: "size-3 text-emerald-500", "aria-label": "Counter higher" });
|
|
27
|
+
if (delta === "down")
|
|
28
|
+
return _jsx(ArrowDown, { className: "size-3 text-red-500", "aria-label": "Counter lower" });
|
|
29
|
+
return _jsx(Minus, { className: "size-3 text-muted-foreground", "aria-label": "Same price" });
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Right slide-over panel listing all offers sent for a parent offer.
|
|
33
|
+
* Shows a sorted list of branches (by activity priority), a best-offer
|
|
34
|
+
* banner if any negotiation branches exist, and per-branch Accept and
|
|
35
|
+
* View actions.
|
|
36
|
+
*
|
|
37
|
+
* The panel is responsive: max-w-md desktop, full-screen sheet on mobile
|
|
38
|
+
* (handled by Tailwind classes; no JS).
|
|
39
|
+
*/
|
|
40
|
+
export function OffersSentPanel({ open, onOpenChange, productName, opportunityName, branches, onAccept, onViewBranch, onOpenParent, showAddImporter = false, onAddImporter, className, }) {
|
|
41
|
+
// Sort branches: most active first
|
|
42
|
+
const sortedBranches = [...branches].sort((a, b) => STATUS_SORT_ORDER[a.status] - STATUS_SORT_ORDER[b.status]);
|
|
43
|
+
// Pick the first in_negotiation branch as the "best active offer" banner.
|
|
44
|
+
// Price comparison is the caller's responsibility — they pass priceDelta.
|
|
45
|
+
const bestActive = branches.find((b) => b.status === "in_negotiation") ?? null;
|
|
46
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: onOpenChange, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/30" }), _jsxs(Dialog.Content, { className: cn("fixed inset-y-0 right-0 z-50 flex w-full max-w-md flex-col border-l border-border bg-card shadow-2xl md:max-w-[420px]", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-border p-4", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(Dialog.Title, { className: "text-base font-semibold text-foreground", children: productName }), _jsx(Dialog.Description, { className: "mt-1 text-xs text-muted-foreground", children: opportunityName }), onOpenParent && (_jsxs("button", { type: "button", onClick: onOpenParent, className: "mt-2 inline-flex items-center gap-1 text-xs font-medium text-primary hover:text-primary/80", children: ["Open parent offer", _jsx(ArrowRight, { className: "size-3", "aria-hidden": true })] }))] }), _jsx(Dialog.Close, { className: "rounded-md p-1 text-muted-foreground hover:bg-muted/50 hover:text-foreground", "aria-label": "Close offers panel", children: _jsx(X, { className: "size-4" }) })] }), bestActive && (_jsxs("div", { className: "flex items-center gap-2 border-b border-border bg-emerald-500/5 px-4 py-3", children: [_jsx(Trophy, { className: "size-4 shrink-0 text-emerald-600 dark:text-emerald-400", "aria-hidden": true }), _jsxs("p", { className: "text-xs font-medium text-foreground", children: ["Best current offer: ", _jsx("span", { className: "font-semibold", children: bestActive.currentPrice }), " \u2014", " ", bestActive.importerName] })] })), _jsx("div", { className: "flex-1 overflow-y-auto p-4", children: sortedBranches.length === 0 ? (_jsx("p", { className: "py-8 text-center text-xs text-muted-foreground", children: "No offers sent yet." })) : (_jsx("div", { className: "flex flex-col gap-3", children: sortedBranches.map((branch) => (_jsxs("div", { className: cn("rounded-md border border-l-4 border-border bg-background p-3", STATUS_BORDER_CLASSES[branch.status], (branch.status === "declined" || branch.status === "rejected") && "opacity-60"), children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [branch.logoUrl ? (_jsx("img", { src: branch.logoUrl, alt: "", className: "size-6 shrink-0 rounded-md object-cover" })) : (_jsx("div", { className: "size-6 shrink-0 rounded-md bg-muted", "aria-hidden": true })), _jsx("p", { className: "truncate text-sm font-medium text-foreground", children: branch.importerName })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1 text-xs", children: [_jsx("span", { className: "font-semibold text-foreground", children: branch.currentPrice }), _jsx(PriceDeltaIcon, { delta: branch.priceDelta })] })] }), _jsxs("div", { className: "mt-2 flex items-center gap-1.5", children: [_jsx(OfferStatusDots, { states: [branch.status] }), _jsx("span", { className: "text-xs text-muted-foreground", children: branch.lastActivityLabel })] }), _jsxs("div", { className: "mt-2 flex items-center gap-2", children: [branch.canAccept && (_jsxs("button", { type: "button", onClick: () => onAccept(branch.childOfferId), className: "inline-flex items-center gap-1 rounded-md bg-emerald-100 px-2 py-1 text-xs font-semibold text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-300 dark:hover:bg-emerald-500/20", children: [_jsx(CheckCircle2, { className: "size-3", "aria-hidden": true }), "Accept"] })), _jsxs("button", { type: "button", onClick: () => onViewBranch(branch.childOfferId), className: "inline-flex items-center gap-1 text-xs font-medium text-primary hover:text-primary/80", children: ["View offer", _jsx(ArrowRight, { className: "size-3", "aria-hidden": true })] })] })] }, branch.childOfferId))) })) }), showAddImporter && onAddImporter && (_jsx("div", { className: "border-t border-border p-4", children: _jsx("button", { type: "button", onClick: onAddImporter, className: "w-full rounded-md border border-dashed border-border bg-background px-3 py-2 text-xs font-medium text-foreground hover:bg-muted/30", children: "Send to another importer" }) }))] })] }) }));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=branch-overview-panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-overview-panel.js","sourceRoot":"","sources":["../../src/components/branch-overview-panel.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AAC9F,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,eAAe,EAA4B,MAAM,qBAAqB,CAAC;AA2ChF,MAAM,qBAAqB,GAAwC;IACjE,OAAO,EAAE,4CAA4C;IACrD,MAAM,EAAE,0CAA0C;IAClD,cAAc,EAAE,4CAA4C;IAC5D,QAAQ,EAAE,wCAAwC;IAClD,QAAQ,EAAE,wCAAwC;IAClD,QAAQ,EAAE,gDAAgD;IAC1D,SAAS,EAAE,4CAA4C;CACxD,CAAC;AAEF,MAAM,iBAAiB,GAAwC;IAC7D,SAAS,EAAE,CAAC;IACZ,QAAQ,EAAE,CAAC;IACX,cAAc,EAAE,CAAC;IACjB,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IACV,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF,SAAS,cAAc,CAAC,EAAE,KAAK,EAAwC;IACrE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAC,OAAO,IAAC,SAAS,EAAC,yBAAyB,gBAAY,gBAAgB,GAAG,CAAC;IACvG,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,KAAC,SAAS,IAAC,SAAS,EAAC,qBAAqB,gBAAY,eAAe,GAAG,CAAC;IACtG,OAAO,KAAC,KAAK,IAAC,SAAS,EAAC,8BAA8B,gBAAY,YAAY,GAAG,CAAC;AACpF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,eAAe,EACf,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,GAAG,KAAK,EACvB,aAAa,EACb,SAAS,GACY;IACrB,mCAAmC;IACnC,MAAM,cAAc,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CACvC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,CACpE,CAAC;IAEF,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,gBAAgB,CAAC,IAAI,IAAI,CAAC;IAE/E,OAAO,CACL,KAAC,MAAM,CAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,YACjD,MAAC,MAAM,CAAC,MAAM,eACZ,KAAC,MAAM,CAAC,OAAO,IAAC,SAAS,EAAC,gCAAgC,GAAG,EAC7D,MAAC,MAAM,CAAC,OAAO,IACb,SAAS,EAAE,EAAE,CACX,uHAAuH,EACvH,SAAS,CACV,aAGD,eAAK,SAAS,EAAC,mEAAmE,aAChF,eAAK,SAAS,EAAC,gBAAgB,aAC7B,KAAC,MAAM,CAAC,KAAK,IAAC,SAAS,EAAC,yCAAyC,YAAE,WAAW,GAAgB,EAC9F,KAAC,MAAM,CAAC,WAAW,IAAC,SAAS,EAAC,oCAAoC,YAC/D,eAAe,GACG,EACpB,YAAY,IAAI,CACf,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,YAAY,EACrB,SAAS,EAAC,4FAA4F,kCAGtG,KAAC,UAAU,IAAC,SAAS,EAAC,QAAQ,wBAAe,IACtC,CACV,IACG,EACN,KAAC,MAAM,CAAC,KAAK,IACX,SAAS,EAAC,8EAA8E,gBAC7E,oBAAoB,YAE/B,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,GACX,IACX,EAGL,UAAU,IAAI,CACb,eAAK,SAAS,EAAC,2EAA2E,aACxF,KAAC,MAAM,IAAC,SAAS,EAAC,wDAAwD,wBAAe,EACzF,aAAG,SAAS,EAAC,qCAAqC,qCAC5B,eAAM,SAAS,EAAC,eAAe,YAAE,UAAU,CAAC,YAAY,GAAQ,aAAG,GAAG,EACzF,UAAU,CAAC,YAAY,IACtB,IACA,CACP,EAGD,cAAK,SAAS,EAAC,4BAA4B,YACxC,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAC7B,YAAG,SAAS,EAAC,gDAAgD,oCAAwB,CACtF,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,qBAAqB,YACjC,cAAc,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC9B,eAEE,SAAS,EAAE,EAAE,CACX,8DAA8D,EAC9D,qBAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,EACpC,CAAC,MAAM,CAAC,MAAM,KAAK,UAAU,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI,YAAY,CAC/E,aAGD,eAAK,SAAS,EAAC,yCAAyC,aACtD,eAAK,SAAS,EAAC,iCAAiC,aAC7C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAChB,cAAK,GAAG,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,EAAC,EAAE,EAAC,SAAS,EAAC,yCAAyC,GAAG,CACxF,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,qCAAqC,wBAAe,CACpE,EACD,YAAG,SAAS,EAAC,8CAA8C,YAAE,MAAM,CAAC,YAAY,GAAK,IACjF,EACN,eAAK,SAAS,EAAC,0CAA0C,aACvD,eAAM,SAAS,EAAC,+BAA+B,YAAE,MAAM,CAAC,YAAY,GAAQ,EAC5E,KAAC,cAAc,IAAC,KAAK,EAAE,MAAM,CAAC,UAAU,GAAI,IACxC,IACF,EAGN,eAAK,SAAS,EAAC,gCAAgC,aAC7C,KAAC,eAAe,IAAC,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAI,EAC5C,eAAM,SAAS,EAAC,+BAA+B,YAAE,MAAM,CAAC,iBAAiB,GAAQ,IAC7E,EAGN,eAAK,SAAS,EAAC,8BAA8B,aAC1C,MAAM,CAAC,SAAS,IAAI,CACnB,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,EAC5C,SAAS,EAAC,0MAA0M,aAEpN,KAAC,YAAY,IAAC,SAAS,EAAC,QAAQ,wBAAe,cAExC,CACV,EACD,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,YAAY,CAAC,EAChD,SAAS,EAAC,uFAAuF,2BAGjG,KAAC,UAAU,IAAC,SAAS,EAAC,QAAQ,wBAAe,IACtC,IACL,KAjDD,MAAM,CAAC,YAAY,CAkDpB,CACP,CAAC,GACE,CACP,GACG,EAGL,eAAe,IAAI,aAAa,IAAI,CACnC,cAAK,SAAS,EAAC,4BAA4B,YACzC,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,aAAa,EACtB,SAAS,EAAC,oIAAoI,yCAGvI,GACL,CACP,IACc,IACH,GACJ,CACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { OffersSentPanel } from "./branch-overview-panel";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof OffersSentPanel;
|
|
6
|
+
tags: string[];
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
export declare const Default: Story;
|
|
14
|
+
export declare const NoCounterOffers: Story;
|
|
15
|
+
export declare const WithFinalized: Story;
|
|
16
|
+
//# sourceMappingURL=branch-overview-panel.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-overview-panel.stories.d.ts","sourceRoot":"","sources":["../../src/components/branch-overview-panel.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,eAAe,EAAoB,MAAM,yBAAyB,CAAC;AAE5E,QAAA,MAAM,IAAI;;;;;;;CAK8B,CAAC;AAEzC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAmEnC,eAAO,MAAM,OAAO,EAAE,KAWrB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAoB7B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAmB3B,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { OffersSentPanel } from "./branch-overview-panel";
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/OffersSentPanel",
|
|
6
|
+
component: OffersSentPanel,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
parameters: { layout: "fullscreen" },
|
|
9
|
+
};
|
|
10
|
+
export default meta;
|
|
11
|
+
const sampleBranches = [
|
|
12
|
+
{
|
|
13
|
+
childOfferId: "child-1",
|
|
14
|
+
importerOrgId: "imp-1",
|
|
15
|
+
importerName: "Berlin Spirits GmbH",
|
|
16
|
+
status: "in_negotiation",
|
|
17
|
+
currentPrice: "€48 / unit",
|
|
18
|
+
priceDelta: "up",
|
|
19
|
+
lastActivityLabel: "Counter-offered · 6h ago",
|
|
20
|
+
canAccept: true,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
childOfferId: "child-2",
|
|
24
|
+
importerOrgId: "imp-2",
|
|
25
|
+
importerName: "Nordic Wine Partners",
|
|
26
|
+
status: "pending",
|
|
27
|
+
currentPrice: "€45 / unit",
|
|
28
|
+
priceDelta: "flat",
|
|
29
|
+
lastActivityLabel: "Awaiting response · sent 2 days ago",
|
|
30
|
+
canAccept: false,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
childOfferId: "child-3",
|
|
34
|
+
importerOrgId: "imp-3",
|
|
35
|
+
importerName: "Iberian Imports Co",
|
|
36
|
+
status: "declined",
|
|
37
|
+
currentPrice: "€45 / unit",
|
|
38
|
+
priceDelta: "flat",
|
|
39
|
+
lastActivityLabel: "Declined · 1 day ago",
|
|
40
|
+
canAccept: false,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
function PanelStory({ branches }) {
|
|
44
|
+
const [open, setOpen] = useState(true);
|
|
45
|
+
const [accepted, setAccepted] = useState(null);
|
|
46
|
+
return (_jsxs("div", { className: "relative h-screen bg-muted/20 p-6", children: [_jsx("button", { type: "button", onClick: () => setOpen(true), className: "rounded-md border border-border bg-background px-3 py-1.5 text-xs", children: "Open offers panel" }), accepted && (_jsxs("pre", { className: "mt-4 rounded-md bg-card p-3 text-xs", children: ["Accepted: ", accepted] })), _jsx(OffersSentPanel, { open: open, onOpenChange: setOpen, productName: "Cabernet Sauvignon 2023", opportunityName: "Sent 2 days ago \u00B7 3 offers \u00B7 Draft #abc123", branches: branches, onAccept: (branchId) => {
|
|
47
|
+
setAccepted(branchId);
|
|
48
|
+
setOpen(false);
|
|
49
|
+
}, onViewBranch: (branchId) => alert(`View branch: ${branchId}`) })] }));
|
|
50
|
+
}
|
|
51
|
+
export const Default = {
|
|
52
|
+
args: {
|
|
53
|
+
open: true,
|
|
54
|
+
onOpenChange: () => undefined,
|
|
55
|
+
productName: "",
|
|
56
|
+
opportunityName: "",
|
|
57
|
+
branches: [],
|
|
58
|
+
onAccept: () => undefined,
|
|
59
|
+
onViewBranch: () => undefined,
|
|
60
|
+
},
|
|
61
|
+
render: () => _jsx(PanelStory, { branches: sampleBranches }),
|
|
62
|
+
};
|
|
63
|
+
export const NoCounterOffers = {
|
|
64
|
+
args: {
|
|
65
|
+
open: true,
|
|
66
|
+
onOpenChange: () => undefined,
|
|
67
|
+
productName: "",
|
|
68
|
+
opportunityName: "",
|
|
69
|
+
branches: [],
|
|
70
|
+
onAccept: () => undefined,
|
|
71
|
+
onViewBranch: () => undefined,
|
|
72
|
+
},
|
|
73
|
+
render: () => (_jsx(PanelStory, { branches: sampleBranches.map((b) => ({
|
|
74
|
+
...b,
|
|
75
|
+
status: "pending",
|
|
76
|
+
canAccept: false,
|
|
77
|
+
lastActivityLabel: "Awaiting response",
|
|
78
|
+
})) })),
|
|
79
|
+
};
|
|
80
|
+
export const WithFinalized = {
|
|
81
|
+
args: {
|
|
82
|
+
open: true,
|
|
83
|
+
onOpenChange: () => undefined,
|
|
84
|
+
productName: "",
|
|
85
|
+
opportunityName: "",
|
|
86
|
+
branches: [],
|
|
87
|
+
onAccept: () => undefined,
|
|
88
|
+
onViewBranch: () => undefined,
|
|
89
|
+
},
|
|
90
|
+
render: () => (_jsx(PanelStory, { branches: [
|
|
91
|
+
{ ...sampleBranches[0], status: "accepted", canAccept: false },
|
|
92
|
+
sampleBranches[1],
|
|
93
|
+
sampleBranches[2],
|
|
94
|
+
] })),
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=branch-overview-panel.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-overview-panel.stories.js","sourceRoot":"","sources":["../../src/components/branch-overview-panel.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,eAAe,EAAoB,MAAM,yBAAyB,CAAC;AAE5E,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,4BAA4B;IACnC,SAAS,EAAE,eAAe;IAC1B,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE;CACE,CAAC;AAEzC,eAAe,IAAI,CAAC;AAGpB,MAAM,cAAc,GAAkB;IACpC;QACE,YAAY,EAAE,SAAS;QACvB,aAAa,EAAE,OAAO;QACtB,YAAY,EAAE,qBAAqB;QACnC,MAAM,EAAE,gBAAgB;QACxB,YAAY,EAAE,YAAY;QAC1B,UAAU,EAAE,IAAI;QAChB,iBAAiB,EAAE,0BAA0B;QAC7C,SAAS,EAAE,IAAI;KAChB;IACD;QACE,YAAY,EAAE,SAAS;QACvB,aAAa,EAAE,OAAO;QACtB,YAAY,EAAE,sBAAsB;QACpC,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,YAAY;QAC1B,UAAU,EAAE,MAAM;QAClB,iBAAiB,EAAE,qCAAqC;QACxD,SAAS,EAAE,KAAK;KACjB;IACD;QACE,YAAY,EAAE,SAAS;QACvB,aAAa,EAAE,OAAO;QACtB,YAAY,EAAE,oBAAoB;QAClC,MAAM,EAAE,UAAU;QAClB,YAAY,EAAE,YAAY;QAC1B,UAAU,EAAE,MAAM;QAClB,iBAAiB,EAAE,sBAAsB;QACzC,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,SAAS,UAAU,CAAC,EAAE,QAAQ,EAA+B;IAC3D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAE9D,OAAO,CACL,eAAK,SAAS,EAAC,mCAAmC,aAChD,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAC5B,SAAS,EAAC,mEAAmE,kCAGtE,EACR,QAAQ,IAAI,CACX,eAAK,SAAS,EAAC,qCAAqC,2BAAY,QAAQ,IAAO,CAChF,EACD,KAAC,eAAe,IACd,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,OAAO,EACrB,WAAW,EAAC,yBAAyB,EACrC,eAAe,EAAC,sDAA4C,EAC5D,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE;oBACrB,WAAW,CAAC,QAAQ,CAAC,CAAC;oBACtB,OAAO,CAAC,KAAK,CAAC,CAAC;gBACjB,CAAC,EACD,YAAY,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,CAAC,gBAAgB,QAAQ,EAAE,CAAC,GAC7D,IACE,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAC7B,WAAW,EAAE,EAAE;QACf,eAAe,EAAE,EAAE;QACnB,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;QACzB,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;KAC9B;IACD,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,UAAU,IAAC,QAAQ,EAAE,cAAc,GAAI;CACvD,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAU;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAC7B,WAAW,EAAE,EAAE;QACf,eAAe,EAAE,EAAE;QACnB,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;QACzB,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;KAC9B;IACD,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,KAAC,UAAU,IACT,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,GAAG,CAAC;YACJ,MAAM,EAAE,SAAkB;YAC1B,SAAS,EAAE,KAAK;YAChB,iBAAiB,EAAE,mBAAmB;SACvC,CAAC,CAAC,GACH,CACH;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,IAAI,EAAE;QACJ,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;QAC7B,WAAW,EAAE,EAAE;QACf,eAAe,EAAE,EAAE;QACnB,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;QACzB,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;KAC9B;IACD,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,KAAC,UAAU,IACT,QAAQ,EAAE;YACR,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAmB,EAAE,SAAS,EAAE,KAAK,EAAE;YACvE,cAAc,CAAC,CAAC,CAAC;YACjB,cAAc,CAAC,CAAC,CAAC;SAClB,GACD,CACH;CACF,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface FinalizeDealTerm {
|
|
2
|
+
label: string;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export interface FinalizeDealClosedBranch {
|
|
6
|
+
importerName: string;
|
|
7
|
+
/** Human-readable status, e.g. "Awaiting response" or "Counter-offered". */
|
|
8
|
+
status: string;
|
|
9
|
+
}
|
|
10
|
+
export interface FinalizeDealModalProps {
|
|
11
|
+
open: boolean;
|
|
12
|
+
onOpenChange: (open: boolean) => void;
|
|
13
|
+
winnerName: string;
|
|
14
|
+
finalTerms: FinalizeDealTerm[];
|
|
15
|
+
/** Other offers that will be closed by this finalization. Empty for solo deals. */
|
|
16
|
+
closedBranches: FinalizeDealClosedBranch[];
|
|
17
|
+
/**
|
|
18
|
+
* Called when the user confirms. May be async — the modal shows a
|
|
19
|
+
* loading spinner while the promise resolves, then closes itself.
|
|
20
|
+
*/
|
|
21
|
+
onConfirm: () => Promise<void> | void;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Confirmation modal shown when the producer clicks "Accept" on a branch
|
|
26
|
+
* in the OffersSentPanel or on the offer detail page. Lists the final
|
|
27
|
+
* terms of the winning offer and warns about the other offers that will
|
|
28
|
+
* be closed.
|
|
29
|
+
*/
|
|
30
|
+
export declare function FinalizeDealModal({ open, onOpenChange, winnerName, finalTerms, closedBranches, onConfirm, className, }: FinalizeDealModalProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=branch-resolution-modal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-resolution-modal.d.ts","sourceRoot":"","sources":["../../src/components/branch-resolution-modal.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,mFAAmF;IACnF,cAAc,EAAE,wBAAwB,EAAE,CAAC;IAC3C;;;OAGG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,SAAS,GACV,EAAE,sBAAsB,2CAoHxB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as Dialog from "@radix-ui/react-dialog";
|
|
3
|
+
import { AlertTriangle, CheckCircle2, Loader2, X } from "lucide-react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
/**
|
|
7
|
+
* Confirmation modal shown when the producer clicks "Accept" on a branch
|
|
8
|
+
* in the OffersSentPanel or on the offer detail page. Lists the final
|
|
9
|
+
* terms of the winning offer and warns about the other offers that will
|
|
10
|
+
* be closed.
|
|
11
|
+
*/
|
|
12
|
+
export function FinalizeDealModal({ open, onOpenChange, winnerName, finalTerms, closedBranches, onConfirm, className, }) {
|
|
13
|
+
const [submitting, setSubmitting] = useState(false);
|
|
14
|
+
const [done, setDone] = useState(false);
|
|
15
|
+
const handleConfirm = async () => {
|
|
16
|
+
if (submitting || done)
|
|
17
|
+
return;
|
|
18
|
+
setSubmitting(true);
|
|
19
|
+
try {
|
|
20
|
+
await onConfirm();
|
|
21
|
+
setDone(true);
|
|
22
|
+
// Auto-close after a brief success indication
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
onOpenChange(false);
|
|
25
|
+
setSubmitting(false);
|
|
26
|
+
setDone(false);
|
|
27
|
+
}, 600);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
setSubmitting(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: (next) => !submitting && onOpenChange(next), children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/40 backdrop-blur-sm" }), _jsxs(Dialog.Content, { className: cn("fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-card p-6 shadow-xl", className), children: [_jsxs("div", { className: "mb-4 flex items-start justify-between", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx(CheckCircle2, { className: "size-5 shrink-0 text-emerald-500", "aria-hidden": true }), _jsx("div", { children: _jsxs(Dialog.Title, { className: "text-base font-semibold text-foreground", children: ["Finalize deal with ", winnerName, "?"] }) })] }), !submitting && (_jsx(Dialog.Close, { className: "rounded-md p-1 text-muted-foreground hover:bg-muted/50 hover:text-foreground", "aria-label": "Close", children: _jsx(X, { className: "size-4" }) }))] }), _jsxs("div", { className: "mb-4", children: [_jsx("h4", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Final terms" }), _jsx("dl", { className: "space-y-1.5", children: finalTerms.map((term) => (_jsxs("div", { className: "flex items-baseline justify-between gap-3 text-sm", children: [_jsx("dt", { className: "text-muted-foreground", children: term.label }), _jsx("dd", { className: "font-medium text-foreground", children: term.value })] }, term.label))) })] }), closedBranches.length > 0 && (_jsx("div", { className: "mb-4 rounded-md border border-amber-200 bg-amber-50 p-3 dark:border-amber-500/30 dark:bg-amber-500/5", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0 text-amber-600 dark:text-amber-400", "aria-hidden": true }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-xs font-semibold text-foreground", children: "Other offers" }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: ["Accepting this offer will close the ", closedBranches.length, " other active", " ", closedBranches.length === 1 ? "offer" : "offers", ". Each importer will receive a polite notification that the offer has been finalized with another partner."] }), _jsx("ul", { className: "mt-2 space-y-1", children: closedBranches.map((b) => (_jsxs("li", { className: "flex justify-between text-xs", children: [_jsx("span", { className: "text-foreground", children: b.importerName }), _jsx("span", { className: "text-muted-foreground", children: b.status })] }, b.importerName))) })] })] }) })), _jsxs("div", { className: "mt-4 flex items-center justify-end gap-2 border-t border-border pt-4", children: [_jsx("button", { type: "button", onClick: () => onOpenChange(false), disabled: submitting, className: "rounded-md px-3 py-1.5 text-xs font-medium text-foreground hover:bg-muted/50 disabled:opacity-50", children: "Cancel" }), _jsxs("button", { type: "button", onClick: handleConfirm, disabled: submitting || done, className: "inline-flex items-center gap-1.5 rounded-md bg-emerald-600 px-3 py-1.5 text-xs font-semibold text-white hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-50", children: [submitting && !done && _jsx(Loader2, { className: "size-3 animate-spin", "aria-hidden": true }), done && _jsx(CheckCircle2, { className: "size-3", "aria-hidden": true }), done ? "Finalized" : submitting ? "Finalizing..." : "Accept and finalize"] })] })] })] }) }));
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=branch-resolution-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-resolution-modal.js","sourceRoot":"","sources":["../../src/components/branch-resolution-modal.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AA4BlC;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,SAAS,GACc;IACvB,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,KAAK,IAAI,EAAE;QAC/B,IAAI,UAAU,IAAI,IAAI;YAAE,OAAO;QAC/B,aAAa,CAAC,IAAI,CAAC,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,SAAS,EAAE,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,8CAA8C;YAC9C,UAAU,CAAC,GAAG,EAAE;gBACd,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,aAAa,CAAC,KAAK,CAAC,CAAC;gBACrB,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC,EAAE,GAAG,CAAC,CAAC;QACV,CAAC;QAAC,MAAM,CAAC;YACP,aAAa,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,MAAM,CAAC,IAAI,IAAC,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,IAAI,YAAY,CAAC,IAAI,CAAC,YAChF,MAAC,MAAM,CAAC,MAAM,eACZ,KAAC,MAAM,CAAC,OAAO,IAAC,SAAS,EAAC,iDAAiD,GAAG,EAC9E,MAAC,MAAM,CAAC,OAAO,IACb,SAAS,EAAE,EAAE,CACX,qIAAqI,EACrI,SAAS,CACV,aAGD,eAAK,SAAS,EAAC,uCAAuC,aACpD,eAAK,SAAS,EAAC,wBAAwB,aACrC,KAAC,YAAY,IAAC,SAAS,EAAC,kCAAkC,wBAAe,EACzE,wBACE,MAAC,MAAM,CAAC,KAAK,IAAC,SAAS,EAAC,yCAAyC,oCAC3C,UAAU,SACjB,GACX,IACF,EACL,CAAC,UAAU,IAAI,CACd,KAAC,MAAM,CAAC,KAAK,IACX,SAAS,EAAC,8EAA8E,gBAC7E,OAAO,YAElB,KAAC,CAAC,IAAC,SAAS,EAAC,QAAQ,GAAG,GACX,CAChB,IACG,EAGN,eAAK,SAAS,EAAC,MAAM,aACnB,aAAI,SAAS,EAAC,0EAA0E,4BAEnF,EACL,aAAI,SAAS,EAAC,aAAa,YACxB,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACxB,eAAsB,SAAS,EAAC,mDAAmD,aACjF,aAAI,SAAS,EAAC,uBAAuB,YAAE,IAAI,CAAC,KAAK,GAAM,EACvD,aAAI,SAAS,EAAC,6BAA6B,YAAE,IAAI,CAAC,KAAK,GAAM,KAFrD,IAAI,CAAC,KAAK,CAGd,CACP,CAAC,GACC,IACD,EAGL,cAAc,CAAC,MAAM,GAAG,CAAC,IAAI,CAC5B,cAAK,SAAS,EAAC,sGAAsG,YACnH,eAAK,SAAS,EAAC,wBAAwB,aACrC,KAAC,aAAa,IAAC,SAAS,EAAC,oDAAoD,wBAAe,EAC5F,eAAK,SAAS,EAAC,QAAQ,aACrB,YAAG,SAAS,EAAC,uCAAuC,6BAAiB,EACrE,aAAG,SAAS,EAAC,oCAAoC,qDACV,cAAc,CAAC,MAAM,mBAAe,GAAG,EAC3E,cAAc,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,kHAE/C,EACJ,aAAI,SAAS,EAAC,gBAAgB,YAC3B,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACzB,cAAyB,SAAS,EAAC,8BAA8B,aAC/D,eAAM,SAAS,EAAC,iBAAiB,YAAE,CAAC,CAAC,YAAY,GAAQ,EACzD,eAAM,SAAS,EAAC,uBAAuB,YAAE,CAAC,CAAC,MAAM,GAAQ,KAFlD,CAAC,CAAC,YAAY,CAGlB,CACN,CAAC,GACC,IACD,IACF,GACF,CACP,EAGD,eAAK,SAAS,EAAC,sEAAsE,aACnF,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,EAClC,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAC,kGAAkG,uBAGrG,EACT,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,UAAU,IAAI,IAAI,EAC5B,SAAS,EAAC,8KAA8K,aAEvL,UAAU,IAAI,CAAC,IAAI,IAAI,KAAC,OAAO,IAAC,SAAS,EAAC,qBAAqB,wBAAe,EAC9E,IAAI,IAAI,KAAC,YAAY,IAAC,SAAS,EAAC,QAAQ,wBAAe,EACvD,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,qBAAqB,IACnE,IACL,IACS,IACH,GACJ,CACf,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { FinalizeDealModal } from "./branch-resolution-modal";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof FinalizeDealModal;
|
|
6
|
+
tags: string[];
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
export declare const Default: Story;
|
|
14
|
+
export declare const NoOtherBranches: Story;
|
|
15
|
+
export declare const ManyBranches: Story;
|
|
16
|
+
//# sourceMappingURL=branch-resolution-modal.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-resolution-modal.stories.d.ts","sourceRoot":"","sources":["../../src/components/branch-resolution-modal.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAiC,MAAM,2BAA2B,CAAC;AAE7F,QAAA,MAAM,IAAI;;;;;;;CAKgC,CAAC;AAE3C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAuCnC,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAU7B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAmB1B,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { FinalizeDealModal } from "./branch-resolution-modal";
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/FinalizeDealModal",
|
|
6
|
+
component: FinalizeDealModal,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
parameters: { layout: "fullscreen" },
|
|
9
|
+
};
|
|
10
|
+
export default meta;
|
|
11
|
+
const closedBranches = [
|
|
12
|
+
{ importerName: "Nordic Wine Partners", status: "Awaiting response" },
|
|
13
|
+
{ importerName: "Iberian Imports Co", status: "Declined" },
|
|
14
|
+
];
|
|
15
|
+
function ModalStory({ branches }) {
|
|
16
|
+
const [open, setOpen] = useState(true);
|
|
17
|
+
return (_jsxs("div", { className: "p-6", children: [_jsx("button", { type: "button", onClick: () => setOpen(true), className: "rounded-md border border-border bg-background px-3 py-1.5 text-xs", children: "Open modal" }), _jsx(FinalizeDealModal, { open: open, onOpenChange: setOpen, winnerName: "Berlin Spirits GmbH", finalTerms: [
|
|
18
|
+
{ label: "Price", value: "€48 / unit" },
|
|
19
|
+
{ label: "Volume", value: "5,000 L" },
|
|
20
|
+
{ label: "Payment", value: "Net 30" },
|
|
21
|
+
{ label: "Delivery", value: "DDP Hamburg" },
|
|
22
|
+
{ label: "Valid until", value: "May 15, 2026" },
|
|
23
|
+
], closedBranches: branches, onConfirm: async () => {
|
|
24
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
25
|
+
setOpen(false);
|
|
26
|
+
} })] }));
|
|
27
|
+
}
|
|
28
|
+
export const Default = {
|
|
29
|
+
args: {
|
|
30
|
+
open: true,
|
|
31
|
+
onOpenChange: () => undefined,
|
|
32
|
+
winnerName: "",
|
|
33
|
+
finalTerms: [],
|
|
34
|
+
closedBranches: [],
|
|
35
|
+
onConfirm: () => undefined,
|
|
36
|
+
},
|
|
37
|
+
render: () => _jsx(ModalStory, { branches: closedBranches }),
|
|
38
|
+
};
|
|
39
|
+
export const NoOtherBranches = {
|
|
40
|
+
args: {
|
|
41
|
+
open: true,
|
|
42
|
+
onOpenChange: () => undefined,
|
|
43
|
+
winnerName: "",
|
|
44
|
+
finalTerms: [],
|
|
45
|
+
closedBranches: [],
|
|
46
|
+
onConfirm: () => undefined,
|
|
47
|
+
},
|
|
48
|
+
render: () => _jsx(ModalStory, { branches: [] }),
|
|
49
|
+
};
|
|
50
|
+
export const ManyBranches = {
|
|
51
|
+
args: {
|
|
52
|
+
open: true,
|
|
53
|
+
onOpenChange: () => undefined,
|
|
54
|
+
winnerName: "",
|
|
55
|
+
finalTerms: [],
|
|
56
|
+
closedBranches: [],
|
|
57
|
+
onConfirm: () => undefined,
|
|
58
|
+
},
|
|
59
|
+
render: () => (_jsx(ModalStory, { branches: [
|
|
60
|
+
{ importerName: "Nordic Wine Partners", status: "Awaiting response" },
|
|
61
|
+
{ importerName: "Iberian Imports Co", status: "Declined" },
|
|
62
|
+
{ importerName: "Tokyo Beverage Corp", status: "Counter-offered" },
|
|
63
|
+
{ importerName: "Sao Paulo Distributors", status: "Awaiting response" },
|
|
64
|
+
] })),
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=branch-resolution-modal.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-resolution-modal.stories.js","sourceRoot":"","sources":["../../src/components/branch-resolution-modal.stories.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-resolution-modal.test.d.ts","sourceRoot":"","sources":["../../src/components/branch-resolution-modal.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { describe, expect, test, vi } from "vitest";
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { FinalizeDealModal } from "./branch-resolution-modal";
|
|
5
|
+
const baseProps = {
|
|
6
|
+
winnerName: "Test Winner",
|
|
7
|
+
finalTerms: [{ label: "Price", value: "€48" }],
|
|
8
|
+
closedBranches: [{ importerName: "Other A", status: "Awaiting" }],
|
|
9
|
+
};
|
|
10
|
+
describe("FinalizeDealModal", () => {
|
|
11
|
+
test("calls onConfirm when the user clicks Accept", async () => {
|
|
12
|
+
const onConfirm = vi.fn().mockResolvedValue(undefined);
|
|
13
|
+
const onOpenChange = vi.fn();
|
|
14
|
+
render(_jsx(FinalizeDealModal, { open: true, onOpenChange: onOpenChange, ...baseProps, onConfirm: onConfirm }));
|
|
15
|
+
fireEvent.click(screen.getByRole("button", { name: /accept and finalize/i }));
|
|
16
|
+
await waitFor(() => expect(onConfirm).toHaveBeenCalledTimes(1));
|
|
17
|
+
});
|
|
18
|
+
test("disables the confirm button while submitting and shows the success state", async () => {
|
|
19
|
+
let resolve = () => undefined;
|
|
20
|
+
const promise = new Promise((r) => {
|
|
21
|
+
resolve = r;
|
|
22
|
+
});
|
|
23
|
+
const onConfirm = vi.fn().mockReturnValue(promise);
|
|
24
|
+
render(_jsx(FinalizeDealModal, { open: true, onOpenChange: () => undefined, ...baseProps, onConfirm: onConfirm }));
|
|
25
|
+
fireEvent.click(screen.getByRole("button", { name: /accept and finalize/i }));
|
|
26
|
+
await waitFor(() => {
|
|
27
|
+
expect(screen.getByRole("button", { name: /finalizing/i })).toBeDisabled();
|
|
28
|
+
});
|
|
29
|
+
resolve();
|
|
30
|
+
await waitFor(() => {
|
|
31
|
+
expect(screen.getByRole("button", { name: /finalized/i })).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
test("does not show the closed-branches warning when none provided", () => {
|
|
35
|
+
render(_jsx(FinalizeDealModal, { open: true, onOpenChange: () => undefined, winnerName: "Solo Winner", finalTerms: [{ label: "Price", value: "€48" }], closedBranches: [], onConfirm: () => undefined }));
|
|
36
|
+
expect(screen.queryByText(/other offers/i)).not.toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
test("renders all final terms in the dl", () => {
|
|
39
|
+
render(_jsx(FinalizeDealModal, { open: true, onOpenChange: () => undefined, ...baseProps, finalTerms: [
|
|
40
|
+
{ label: "Price", value: "€48" },
|
|
41
|
+
{ label: "Volume", value: "5,000 L" },
|
|
42
|
+
{ label: "Payment", value: "Net 30" },
|
|
43
|
+
], onConfirm: () => undefined }));
|
|
44
|
+
expect(screen.getByText("Price")).toBeInTheDocument();
|
|
45
|
+
expect(screen.getByText("€48")).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByText("Volume")).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText("5,000 L")).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByText("Payment")).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByText("Net 30")).toBeInTheDocument();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=branch-resolution-modal.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-resolution-modal.test.js","sourceRoot":"","sources":["../../src/components/branch-resolution-modal.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface CelebrationPulseProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
/** Pulse duration in ms. Default: 500. */
|
|
5
|
+
duration?: number;
|
|
6
|
+
/** Pulse ring color (Tailwind ring class). Default: amber. */
|
|
7
|
+
ringClassName?: string;
|
|
8
|
+
/** Glow color after the pulse (Tailwind shadow class). Default: green. */
|
|
9
|
+
glowClassName?: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Wraps a child element with a one-shot entrance pulse: a brief gold ring
|
|
14
|
+
* expansion followed by a soft green glow that fades out. Used to mark the
|
|
15
|
+
* arrival of a finalized deal in the Completed column.
|
|
16
|
+
*
|
|
17
|
+
* Respects `prefers-reduced-motion`: in reduced-motion mode the wrapper
|
|
18
|
+
* renders the child without any animation classes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function CelebrationPulse({ children, duration, ringClassName, glowClassName, className, }: CelebrationPulseProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
//# sourceMappingURL=celebration-pulse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"celebration-pulse.d.ts","sourceRoot":"","sources":["../../src/components/celebration-pulse.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,SAAS,CAAC;IACpB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,QAAc,EACd,aAAmC,EACnC,aAAwD,EACxD,SAAS,GACV,EAAE,qBAAqB,2CAevB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
import { useCelebrationPulse } from "../hooks/use-celebration-pulse";
|
|
4
|
+
/**
|
|
5
|
+
* Wraps a child element with a one-shot entrance pulse: a brief gold ring
|
|
6
|
+
* expansion followed by a soft green glow that fades out. Used to mark the
|
|
7
|
+
* arrival of a finalized deal in the Completed column.
|
|
8
|
+
*
|
|
9
|
+
* Respects `prefers-reduced-motion`: in reduced-motion mode the wrapper
|
|
10
|
+
* renders the child without any animation classes.
|
|
11
|
+
*/
|
|
12
|
+
export function CelebrationPulse({ children, duration = 500, ringClassName = "ring-amber-300/50", glowClassName = "shadow-[0_0_24px_rgba(34,197,94,0.25)]", className, }) {
|
|
13
|
+
const isPulsing = useCelebrationPulse(duration);
|
|
14
|
+
return (_jsx("div", { className: cn("rounded-lg transition-all duration-500 ease-out", isPulsing && `ring-4 ${ringClassName}`, isPulsing && glowClassName, className), children: children }));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=celebration-pulse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"celebration-pulse.js","sourceRoot":"","sources":["../../src/components/celebration-pulse.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAarE;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,QAAQ,EACR,QAAQ,GAAG,GAAG,EACd,aAAa,GAAG,mBAAmB,EACnC,aAAa,GAAG,wCAAwC,EACxD,SAAS,GACa;IACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAEhD,OAAO,CACL,cACE,SAAS,EAAE,EAAE,CACX,iDAAiD,EACjD,SAAS,IAAI,UAAU,aAAa,EAAE,EACtC,SAAS,IAAI,aAAa,EAC1B,SAAS,CACV,YAEA,QAAQ,GACL,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { CelebrationPulse } from "./celebration-pulse";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof CelebrationPulse;
|
|
6
|
+
tags: string[];
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
type Story = StoryObj<typeof meta>;
|
|
13
|
+
export declare const Default: Story;
|
|
14
|
+
export declare const Replayable: Story;
|
|
15
|
+
export declare const WithLongContent: Story;
|
|
16
|
+
//# sourceMappingURL=celebration-pulse.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"celebration-pulse.stories.d.ts","sourceRoot":"","sources":["../../src/components/celebration-pulse.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,QAAA,MAAM,IAAI;;;;;;;CAK+B,CAAC;AAE1C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAQrB,CAAC;AAsBF,eAAO,MAAM,UAAU,EAAE,KAGxB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAY7B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { CelebrationPulse } from "./celebration-pulse";
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/CelebrationPulse",
|
|
6
|
+
component: CelebrationPulse,
|
|
7
|
+
tags: ["autodocs"],
|
|
8
|
+
parameters: { layout: "padded" },
|
|
9
|
+
};
|
|
10
|
+
export default meta;
|
|
11
|
+
export const Default = {
|
|
12
|
+
args: {
|
|
13
|
+
children: (_jsx("div", { className: "rounded-lg border border-border bg-card p-6 text-sm", children: "Card content. Reload the story to see the entrance pulse." })),
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
function ReplayableStory() {
|
|
17
|
+
const [key, setKey] = useState(0);
|
|
18
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx("button", { type: "button", className: "rounded-md border border-border bg-background px-3 py-1.5 text-xs hover:bg-muted/50", onClick: () => setKey((k) => k + 1), children: "Replay pulse" }), _jsx(CelebrationPulse, { children: _jsx("div", { className: "rounded-lg border border-border bg-card p-6 text-sm", children: "Click the button above to replay the celebration pulse." }) }, key)] }));
|
|
19
|
+
}
|
|
20
|
+
export const Replayable = {
|
|
21
|
+
args: { children: null },
|
|
22
|
+
render: () => _jsx(ReplayableStory, {}),
|
|
23
|
+
};
|
|
24
|
+
export const WithLongContent = {
|
|
25
|
+
args: {
|
|
26
|
+
children: (_jsxs("div", { className: "rounded-lg border border-border bg-card p-6", children: [_jsx("h3", { className: "mb-2 text-base font-semibold", children: "Deal finalized" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "The accepted offer is highlighted with a brief glow on first render. The animation respects prefers-reduced-motion." })] })),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=celebration-pulse.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"celebration-pulse.stories.js","sourceRoot":"","sources":["../../src/components/celebration-pulse.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,6BAA6B;IACpC,SAAS,EAAE,gBAAgB;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;CACO,CAAC;AAE1C,eAAe,IAAI,CAAC;AAGpB,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,QAAQ,EAAE,CACR,cAAK,SAAS,EAAC,qDAAqD,0EAE9D,CACP;KACF;CACF,CAAC;AAEF,SAAS,eAAe;IACtB,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClC,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,aACxB,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,qFAAqF,EAC/F,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,6BAG5B,EACT,KAAC,gBAAgB,cACf,cAAK,SAAS,EAAC,qDAAqD,wEAE9D,IAHe,GAAG,CAIP,IACf,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAU;IAC/B,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;IACxB,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,eAAe,KAAG;CAClC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAU;IACpC,IAAI,EAAE;QACJ,QAAQ,EAAE,CACR,eAAK,SAAS,EAAC,6CAA6C,aAC1D,aAAI,SAAS,EAAC,8BAA8B,+BAAoB,EAChE,YAAG,SAAS,EAAC,+BAA+B,oIAGxC,IACA,CACP;KACF;CACF,CAAC"}
|