@cellarnode/ui 0.9.1 → 0.9.3

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.
Files changed (159) hide show
  1. package/dist/components/asset-list.d.ts +34 -0
  2. package/dist/components/asset-list.d.ts.map +1 -0
  3. package/dist/components/asset-list.js +34 -0
  4. package/dist/components/asset-list.js.map +1 -0
  5. package/dist/components/asset-list.stories.d.ts +20 -0
  6. package/dist/components/asset-list.stories.d.ts.map +1 -0
  7. package/dist/components/asset-list.stories.js +50 -0
  8. package/dist/components/asset-list.stories.js.map +1 -0
  9. package/dist/components/branch-overview-panel.d.ts +51 -0
  10. package/dist/components/branch-overview-panel.d.ts.map +1 -0
  11. package/dist/components/branch-overview-panel.js +48 -0
  12. package/dist/components/branch-overview-panel.js.map +1 -0
  13. package/dist/components/branch-overview-panel.stories.d.ts +16 -0
  14. package/dist/components/branch-overview-panel.stories.d.ts.map +1 -0
  15. package/dist/components/branch-overview-panel.stories.js +96 -0
  16. package/dist/components/branch-overview-panel.stories.js.map +1 -0
  17. package/dist/components/branch-resolution-modal.d.ts +31 -0
  18. package/dist/components/branch-resolution-modal.d.ts.map +1 -0
  19. package/dist/components/branch-resolution-modal.js +35 -0
  20. package/dist/components/branch-resolution-modal.js.map +1 -0
  21. package/dist/components/branch-resolution-modal.stories.d.ts +16 -0
  22. package/dist/components/branch-resolution-modal.stories.d.ts.map +1 -0
  23. package/dist/components/branch-resolution-modal.stories.js +66 -0
  24. package/dist/components/branch-resolution-modal.stories.js.map +1 -0
  25. package/dist/components/branch-resolution-modal.test.d.ts +2 -0
  26. package/dist/components/branch-resolution-modal.test.d.ts.map +1 -0
  27. package/dist/components/branch-resolution-modal.test.js +52 -0
  28. package/dist/components/branch-resolution-modal.test.js.map +1 -0
  29. package/dist/components/card-stack.d.ts +25 -0
  30. package/dist/components/card-stack.d.ts.map +1 -0
  31. package/dist/components/card-stack.js +26 -0
  32. package/dist/components/card-stack.js.map +1 -0
  33. package/dist/components/card-stack.stories.d.ts +21 -0
  34. package/dist/components/card-stack.stories.d.ts.map +1 -0
  35. package/dist/components/card-stack.stories.js +59 -0
  36. package/dist/components/card-stack.stories.js.map +1 -0
  37. package/dist/components/celebration-pulse.d.ts +21 -0
  38. package/dist/components/celebration-pulse.d.ts.map +1 -0
  39. package/dist/components/celebration-pulse.js +16 -0
  40. package/dist/components/celebration-pulse.js.map +1 -0
  41. package/dist/components/celebration-pulse.stories.d.ts +16 -0
  42. package/dist/components/celebration-pulse.stories.d.ts.map +1 -0
  43. package/dist/components/celebration-pulse.stories.js +29 -0
  44. package/dist/components/celebration-pulse.stories.js.map +1 -0
  45. package/dist/components/completed-offer-card.d.ts +34 -0
  46. package/dist/components/completed-offer-card.d.ts.map +1 -0
  47. package/dist/components/completed-offer-card.js +20 -0
  48. package/dist/components/completed-offer-card.js.map +1 -0
  49. package/dist/components/completed-offer-card.stories.d.ts +17 -0
  50. package/dist/components/completed-offer-card.stories.d.ts.map +1 -0
  51. package/dist/components/completed-offer-card.stories.js +75 -0
  52. package/dist/components/completed-offer-card.stories.js.map +1 -0
  53. package/dist/components/file-dropzone.d.ts +26 -0
  54. package/dist/components/file-dropzone.d.ts.map +1 -0
  55. package/dist/components/file-dropzone.js +49 -0
  56. package/dist/components/file-dropzone.js.map +1 -0
  57. package/dist/components/file-dropzone.stories.d.ts +21 -0
  58. package/dist/components/file-dropzone.stories.d.ts.map +1 -0
  59. package/dist/components/file-dropzone.stories.js +55 -0
  60. package/dist/components/file-dropzone.stories.js.map +1 -0
  61. package/dist/components/image-carousel.d.ts +41 -0
  62. package/dist/components/image-carousel.d.ts.map +1 -0
  63. package/dist/components/image-carousel.js +77 -0
  64. package/dist/components/image-carousel.js.map +1 -0
  65. package/dist/components/image-carousel.stories.d.ts +20 -0
  66. package/dist/components/image-carousel.stories.d.ts.map +1 -0
  67. package/dist/components/image-carousel.stories.js +62 -0
  68. package/dist/components/image-carousel.stories.js.map +1 -0
  69. package/dist/components/importer-picker-modal.d.ts +50 -0
  70. package/dist/components/importer-picker-modal.d.ts.map +1 -0
  71. package/dist/components/importer-picker-modal.js +82 -0
  72. package/dist/components/importer-picker-modal.js.map +1 -0
  73. package/dist/components/importer-picker-modal.stories.d.ts +17 -0
  74. package/dist/components/importer-picker-modal.stories.d.ts.map +1 -0
  75. package/dist/components/importer-picker-modal.stories.js +62 -0
  76. package/dist/components/importer-picker-modal.stories.js.map +1 -0
  77. package/dist/components/importer-picker-modal.test.d.ts +2 -0
  78. package/dist/components/importer-picker-modal.test.d.ts.map +1 -0
  79. package/dist/components/importer-picker-modal.test.js +51 -0
  80. package/dist/components/importer-picker-modal.test.js.map +1 -0
  81. package/dist/components/kanban-board.d.ts +19 -0
  82. package/dist/components/kanban-board.d.ts.map +1 -1
  83. package/dist/components/kanban-board.js.map +1 -1
  84. package/dist/components/kanban-dnd-board.d.ts +1 -1
  85. package/dist/components/kanban-dnd-board.d.ts.map +1 -1
  86. package/dist/components/kanban-dnd-board.js +52 -7
  87. package/dist/components/kanban-dnd-board.js.map +1 -1
  88. package/dist/components/kanban-dnd-board.stories.d.ts +1 -0
  89. package/dist/components/kanban-dnd-board.stories.d.ts.map +1 -1
  90. package/dist/components/kanban-dnd-board.stories.js +66 -0
  91. package/dist/components/kanban-dnd-board.stories.js.map +1 -1
  92. package/dist/components/kanban-dnd-board.test.d.ts +2 -0
  93. package/dist/components/kanban-dnd-board.test.d.ts.map +1 -0
  94. package/dist/components/kanban-dnd-board.test.js +34 -0
  95. package/dist/components/kanban-dnd-board.test.js.map +1 -0
  96. package/dist/components/offer-status-dots.d.ts +23 -0
  97. package/dist/components/offer-status-dots.d.ts.map +1 -0
  98. package/dist/components/offer-status-dots.js +34 -0
  99. package/dist/components/offer-status-dots.js.map +1 -0
  100. package/dist/components/offer-status-dots.stories.d.ts +19 -0
  101. package/dist/components/offer-status-dots.stories.d.ts.map +1 -0
  102. package/dist/components/offer-status-dots.stories.js +49 -0
  103. package/dist/components/offer-status-dots.stories.js.map +1 -0
  104. package/dist/components/product-card.d.ts +42 -0
  105. package/dist/components/product-card.d.ts.map +1 -0
  106. package/dist/components/product-card.js +20 -0
  107. package/dist/components/product-card.js.map +1 -0
  108. package/dist/components/product-card.stories.d.ts +20 -0
  109. package/dist/components/product-card.stories.d.ts.map +1 -0
  110. package/dist/components/product-card.stories.js +59 -0
  111. package/dist/components/product-card.stories.js.map +1 -0
  112. package/dist/components/product-list-row.d.ts +29 -0
  113. package/dist/components/product-list-row.d.ts.map +1 -0
  114. package/dist/components/product-list-row.js +13 -0
  115. package/dist/components/product-list-row.js.map +1 -0
  116. package/dist/components/product-list-row.stories.d.ts +19 -0
  117. package/dist/components/product-list-row.stories.d.ts.map +1 -0
  118. package/dist/components/product-list-row.stories.js +37 -0
  119. package/dist/components/product-list-row.stories.js.map +1 -0
  120. package/dist/components/sent-offer-card.d.ts +31 -0
  121. package/dist/components/sent-offer-card.d.ts.map +1 -0
  122. package/dist/components/sent-offer-card.js +23 -0
  123. package/dist/components/sent-offer-card.js.map +1 -0
  124. package/dist/components/sent-offer-card.stories.d.ts +17 -0
  125. package/dist/components/sent-offer-card.stories.d.ts.map +1 -0
  126. package/dist/components/sent-offer-card.stories.js +51 -0
  127. package/dist/components/sent-offer-card.stories.js.map +1 -0
  128. package/dist/components/view-toggle.d.ts +14 -0
  129. package/dist/components/view-toggle.d.ts.map +1 -0
  130. package/dist/components/view-toggle.js +14 -0
  131. package/dist/components/view-toggle.js.map +1 -0
  132. package/dist/components/view-toggle.stories.d.ts +20 -0
  133. package/dist/components/view-toggle.stories.d.ts.map +1 -0
  134. package/dist/components/view-toggle.stories.js +41 -0
  135. package/dist/components/view-toggle.stories.js.map +1 -0
  136. package/dist/hooks/use-celebration-pulse.d.ts +12 -0
  137. package/dist/hooks/use-celebration-pulse.d.ts.map +1 -0
  138. package/dist/hooks/use-celebration-pulse.js +28 -0
  139. package/dist/hooks/use-celebration-pulse.js.map +1 -0
  140. package/dist/index.d.ts +13 -1
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +15 -1
  143. package/dist/index.js.map +1 -1
  144. package/dist/lib/kanban-transitions.d.ts +62 -0
  145. package/dist/lib/kanban-transitions.d.ts.map +1 -1
  146. package/dist/lib/kanban-transitions.js +32 -0
  147. package/dist/lib/kanban-transitions.js.map +1 -1
  148. package/dist/lib/kanban-transitions.test.d.ts +2 -0
  149. package/dist/lib/kanban-transitions.test.d.ts.map +1 -0
  150. package/dist/lib/kanban-transitions.test.js +75 -0
  151. package/dist/lib/kanban-transitions.test.js.map +1 -0
  152. package/dist/offer/index.d.ts +3 -0
  153. package/dist/offer/index.d.ts.map +1 -1
  154. package/dist/offer/index.js +7 -0
  155. package/dist/offer/index.js.map +1 -1
  156. package/dist/offer/sensory-profile.d.ts.map +1 -1
  157. package/dist/offer/sensory-profile.js +1 -1
  158. package/dist/offer/sensory-profile.js.map +1 -1
  159. package/package.json +1 -1
@@ -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,25 @@
1
+ import * as React from "react";
2
+ export interface CardStackProps {
3
+ /** Total number of items the stack represents (primary + siblings). */
4
+ count: number;
5
+ /** The primary child — typically a card component (e.g. ProductCard). */
6
+ children: React.ReactNode;
7
+ /** Optional className forwarded to the outer wrapper. */
8
+ className?: string;
9
+ /** Click handler for the stack badge (separate from child body clicks). */
10
+ onStackClick?: () => void;
11
+ /** Custom badge content — defaults to `+N` where N = count - 1. */
12
+ stackBadge?: React.ReactNode;
13
+ }
14
+ /**
15
+ * Decorative stack-of-cards primitive. Wraps a single child and renders up to
16
+ * 2 ghost cards offset behind it, plus an optional click-to-expand badge.
17
+ *
18
+ * When `count <= 1` the component is a pass-through (no stack rendered) so
19
+ * consumers can safely wrap every card uniformly.
20
+ *
21
+ * The stack badge click target is separate from the child body — consumers
22
+ * stop propagation in their child's own click handlers if needed.
23
+ */
24
+ export declare function CardStack({ count, children, className, onStackClick, stackBadge, }: CardStackProps): import("react/jsx-runtime").JSX.Element;
25
+ //# sourceMappingURL=card-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-stack.d.ts","sourceRoot":"","sources":["../../src/components/card-stack.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mEAAmE;IACnE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,GACX,EAAE,cAAc,2CAsChB"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils";
3
+ /**
4
+ * Decorative stack-of-cards primitive. Wraps a single child and renders up to
5
+ * 2 ghost cards offset behind it, plus an optional click-to-expand badge.
6
+ *
7
+ * When `count <= 1` the component is a pass-through (no stack rendered) so
8
+ * consumers can safely wrap every card uniformly.
9
+ *
10
+ * The stack badge click target is separate from the child body — consumers
11
+ * stop propagation in their child's own click handlers if needed.
12
+ */
13
+ export function CardStack({ count, children, className, onStackClick, stackBadge, }) {
14
+ if (count <= 1) {
15
+ return _jsx("div", { className: className, children: children });
16
+ }
17
+ const ghostCount = Math.min(count - 1, 2);
18
+ return (_jsxs("div", { className: cn("relative", className), children: [Array.from({ length: ghostCount }, (_, i) => i + 1).map((depth) => (_jsx("div", { "aria-hidden": true, className: cn("pointer-events-none absolute inset-0 rounded-[28px] border border-border/40 bg-card/60 shadow-sm backdrop-blur-sm transition-transform"), style: {
19
+ transform: `translate(${depth * 6}px, ${depth * 6}px) scale(${1 - depth * 0.015})`,
20
+ zIndex: -depth,
21
+ } }, depth))), _jsx("div", { className: "relative z-0", children: children }), onStackClick && (_jsx("button", { type: "button", onClick: (e) => {
22
+ e.stopPropagation();
23
+ onStackClick();
24
+ }, className: "absolute right-3 top-3 z-20 rounded-lg border border-border/60 bg-background/90 px-3 py-1.5 text-xs font-semibold text-foreground shadow-sm backdrop-blur-md transition-all hover:bg-background hover:shadow-md", "aria-label": `Expand ${count - 1} more items`, children: stackBadge ?? `+${count - 1}` }))] }));
25
+ }
26
+ //# sourceMappingURL=card-stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-stack.js","sourceRoot":"","sources":["../../src/components/card-stack.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAelC;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,UAAU,GACK;IACf,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,OAAO,cAAK,SAAS,EAAE,SAAS,YAAG,QAAQ,GAAO,CAAC;IACrD,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE1C,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,aACtC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAClE,mCAGE,SAAS,EAAE,EAAE,CACX,wIAAwI,CACzI,EACD,KAAK,EAAE;oBACL,SAAS,EAAE,aAAa,KAAK,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,aAAa,CAAC,GAAG,KAAK,GAAG,KAAK,GAAG;oBAClF,MAAM,EAAE,CAAC,KAAK;iBACf,IARI,KAAK,CASV,CACH,CAAC,EACF,cAAK,SAAS,EAAC,cAAc,YAAE,QAAQ,GAAO,EAC7C,YAAY,IAAI,CACf,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;oBACb,CAAC,CAAC,eAAe,EAAE,CAAC;oBACpB,YAAY,EAAE,CAAC;gBACjB,CAAC,EACD,SAAS,EAAC,iNAAiN,gBAC/M,UAAU,KAAK,GAAG,CAAC,aAAa,YAE3C,UAAU,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,GACvB,CACV,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { CardStack } from "./card-stack";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof CardStack;
6
+ tags: string[];
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: string;
11
+ };
12
+ };
13
+ };
14
+ };
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+ export declare const SingleCard: Story;
18
+ export declare const TwoCards: Story;
19
+ export declare const FiveCards: Story;
20
+ export declare const CustomBadge: Story;
21
+ //# sourceMappingURL=card-stack.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-stack.stories.d.ts","sourceRoot":"","sources":["../../src/components/card-stack.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,QAAA,MAAM,IAAI;;;;;;;;;;;CAYwB,CAAC;AAEnC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAYnC,eAAO,MAAM,UAAU,EAAE,KAYxB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAatB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAavB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAczB,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CardStack } from "./card-stack";
3
+ const meta = {
4
+ title: "Components/CardStack",
5
+ component: CardStack,
6
+ tags: ["autodocs"],
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: "Decorative stack-of-cards primitive. Renders up to 2 ghost cards offset behind the primary child, plus an optional click-to-expand badge. Used for sibling-product stacking in the producer portfolio.",
11
+ },
12
+ },
13
+ },
14
+ };
15
+ export default meta;
16
+ function DemoCard({ label, subtitle }) {
17
+ return (_jsxs("div", { className: "rounded-[28px] border border-border bg-card p-6 shadow-sm", children: [_jsx("div", { className: "aspect-[16/10] rounded-xl bg-muted" }), _jsx("h3", { className: "mt-4 text-lg font-bold", children: label }), subtitle && _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: subtitle })] }));
18
+ }
19
+ export const SingleCard = {
20
+ args: {
21
+ count: 1,
22
+ children: _jsx(DemoCard, { label: "Just one", subtitle: "No stack rendered when count = 1" }),
23
+ },
24
+ decorators: [
25
+ (Story) => (_jsx("div", { className: "w-80", children: _jsx(Story, {}) })),
26
+ ],
27
+ };
28
+ export const TwoCards = {
29
+ args: {
30
+ count: 2,
31
+ children: _jsx(DemoCard, { label: "Ch\u00E2teau Margaux 2018", subtitle: "Red Wine \u00B7 Bordeaux" }),
32
+ onStackClick: () => window.alert("Expand stack"),
33
+ },
34
+ decorators: [
35
+ (Story) => (_jsx("div", { className: "w-80 p-8", children: _jsx(Story, {}) })),
36
+ ],
37
+ };
38
+ export const FiveCards = {
39
+ args: {
40
+ count: 5,
41
+ children: _jsx(DemoCard, { label: "Ch\u00E2teau Margaux", subtitle: "+4 vintages" }),
42
+ onStackClick: () => window.alert("Expand stack"),
43
+ },
44
+ decorators: [
45
+ (Story) => (_jsx("div", { className: "w-80 p-8", children: _jsx(Story, {}) })),
46
+ ],
47
+ };
48
+ export const CustomBadge = {
49
+ args: {
50
+ count: 4,
51
+ children: _jsx(DemoCard, { label: "Sourdough Boule", subtitle: "Bread \u00B7 72-hour fermented" }),
52
+ stackBadge: "+3 sizes",
53
+ onStackClick: () => window.alert("Expand stack"),
54
+ },
55
+ decorators: [
56
+ (Story) => (_jsx("div", { className: "w-80 p-8", children: _jsx(Story, {}) })),
57
+ ],
58
+ };
59
+ //# sourceMappingURL=card-stack.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"card-stack.stories.js","sourceRoot":"","sources":["../../src/components/card-stack.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,sBAAsB;IAC7B,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE;QACV,IAAI,EAAE;YACJ,WAAW,EAAE;gBACX,SAAS,EACP,wMAAwM;aAC3M;SACF;KACF;CAC+B,CAAC;AAEnC,eAAe,IAAI,CAAC;AAGpB,SAAS,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAwC;IACzE,OAAO,CACL,eAAK,SAAS,EAAC,2DAA2D,aACxE,cAAK,SAAS,EAAC,oCAAoC,GAAG,EACtD,aAAI,SAAS,EAAC,wBAAwB,YAAE,KAAK,GAAM,EAClD,QAAQ,IAAI,YAAG,SAAS,EAAC,oCAAoC,YAAE,QAAQ,GAAK,IACzE,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAU;IAC/B,IAAI,EAAE;QACJ,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,KAAC,QAAQ,IAAC,KAAK,EAAC,UAAU,EAAC,QAAQ,EAAC,kCAAkC,GAAG;KACpF;IACD,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,cAAK,SAAS,EAAC,MAAM,YACnB,KAAC,KAAK,KAAG,GACL,CACP;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAU;IAC7B,IAAI,EAAE;QACJ,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,KAAC,QAAQ,IAAC,KAAK,EAAC,2BAAsB,EAAC,QAAQ,EAAC,0BAAqB,GAAG;QAClF,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC;KACjD;IACD,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,cAAK,SAAS,EAAC,UAAU,YACvB,KAAC,KAAK,KAAG,GACL,CACP;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,IAAI,EAAE;QACJ,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,KAAC,QAAQ,IAAC,KAAK,EAAC,sBAAiB,EAAC,QAAQ,EAAC,aAAa,GAAG;QACrE,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC;KACjD;IACD,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,cAAK,SAAS,EAAC,UAAU,YACvB,KAAC,KAAK,KAAG,GACL,CACP;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAU;IAChC,IAAI,EAAE;QACJ,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,KAAC,QAAQ,IAAC,KAAK,EAAC,iBAAiB,EAAC,QAAQ,EAAC,gCAA2B,GAAG;QACnF,UAAU,EAAE,UAAU;QACtB,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC;KACjD;IACD,UAAU,EAAE;QACV,CAAC,KAAK,EAAE,EAAE,CAAC,CACT,cAAK,SAAS,EAAC,UAAU,YACvB,KAAC,KAAK,KAAG,GACL,CACP;KACF;CACF,CAAC"}
@@ -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"}
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from "react";
2
+ export interface CompletedOfferStat {
3
+ label: string;
4
+ value: string;
5
+ }
6
+ export interface CompletedOfferCardProps {
7
+ title: string;
8
+ subtitle?: string;
9
+ sourceIcon?: ReactNode;
10
+ winnerName: string;
11
+ winnerPrice: string;
12
+ /** Winner volume — optional, omitted from layout when undefined. */
13
+ winnerVolume?: string;
14
+ /** 1–3 stats shown in the bottom row. */
15
+ stats: CompletedOfferStat[];
16
+ /** Click handler for the "View offer" link. */
17
+ onView?: () => void;
18
+ /** Click handler for the "Archive" link. */
19
+ onArchive?: () => void;
20
+ /** Disable the entrance pulse (e.g. when re-rendering an existing card). */
21
+ noPulse?: boolean;
22
+ className?: string;
23
+ }
24
+ /**
25
+ * Card representing a finalized deal in the producer Completed column.
26
+ * Shows: meta row, title, "Finalized" pill badge, winner sub-card with
27
+ * trophy icon and final terms, and a stat row.
28
+ *
29
+ * On first mount, the card emits a brief celebration pulse via
30
+ * `CelebrationPulse`. Pass `noPulse` to suppress it (e.g. when an existing
31
+ * card is being re-rendered after a state refresh, not a new arrival).
32
+ */
33
+ export declare function CompletedOfferCard({ title, subtitle, sourceIcon, winnerName, winnerPrice, winnerVolume, stats, onView, onArchive, noPulse, className, }: CompletedOfferCardProps): import("react/jsx-runtime").JSX.Element;
34
+ //# sourceMappingURL=completed-offer-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completed-offer-card.d.ts","sourceRoot":"","sources":["../../src/components/completed-offer-card.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,EACR,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,KAAK,EACL,MAAM,EACN,SAAS,EACT,OAAe,EACf,SAAS,GACV,EAAE,uBAAuB,2CAgFzB"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Archive, ArrowRight, CheckCircle2, Trophy } from "lucide-react";
3
+ import { cn } from "../lib/utils";
4
+ import { CelebrationPulse } from "./celebration-pulse";
5
+ /**
6
+ * Card representing a finalized deal in the producer Completed column.
7
+ * Shows: meta row, title, "Finalized" pill badge, winner sub-card with
8
+ * trophy icon and final terms, and a stat row.
9
+ *
10
+ * On first mount, the card emits a brief celebration pulse via
11
+ * `CelebrationPulse`. Pass `noPulse` to suppress it (e.g. when an existing
12
+ * card is being re-rendered after a state refresh, not a new arrival).
13
+ */
14
+ export function CompletedOfferCard({ title, subtitle, sourceIcon, winnerName, winnerPrice, winnerVolume, stats, onView, onArchive, noPulse = false, className, }) {
15
+ const card = (_jsxs("div", { className: cn("flex flex-col gap-3 rounded-lg border border-l-4 border-border border-l-emerald-500 bg-card p-3 shadow-xs", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [(sourceIcon || subtitle) && (_jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [sourceIcon, subtitle && _jsx("span", { children: subtitle })] })), _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-emerald-100 px-2 py-0.5 text-[10px] font-semibold text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-300", children: [_jsx(CheckCircle2, { className: "size-3", "aria-hidden": true }), "Finalized"] })] }), _jsx("h4", { className: "text-sm/5 font-semibold text-foreground", children: title }), _jsxs("div", { className: "flex items-center gap-2 rounded-md border border-emerald-200/40 bg-emerald-500/5 p-2 dark:border-emerald-400/20", children: [_jsx(Trophy, { className: "size-4 shrink-0 text-emerald-600 dark:text-emerald-400", "aria-hidden": true }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-xs font-semibold text-foreground", children: winnerName }), _jsxs("p", { className: "text-xs text-muted-foreground", children: [winnerPrice, winnerVolume && _jsxs("span", { children: [" \u00B7 ", winnerVolume] })] })] })] }), stats.length > 0 && (_jsx("div", { className: "flex items-center justify-around border-t border-border pt-2", children: stats.map((stat) => (_jsxs("div", { className: "text-center", children: [_jsx("div", { className: "text-sm font-bold text-foreground", children: stat.value }), _jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: stat.label })] }, stat.label))) })), _jsxs("div", { className: "flex items-center justify-between text-xs", children: [onView && (_jsxs("button", { type: "button", onClick: onView, className: "inline-flex items-center gap-1 font-medium text-primary hover:text-primary/80", children: ["View offer", _jsx(ArrowRight, { className: "size-3", "aria-hidden": true })] })), onArchive && (_jsxs("button", { type: "button", onClick: onArchive, className: "inline-flex items-center gap-1 font-medium text-muted-foreground hover:text-foreground", children: [_jsx(Archive, { className: "size-3", "aria-hidden": true }), "Archive"] }))] })] }));
16
+ if (noPulse)
17
+ return card;
18
+ return _jsx(CelebrationPulse, { children: card });
19
+ }
20
+ //# sourceMappingURL=completed-offer-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completed-offer-card.js","sourceRoot":"","sources":["../../src/components/completed-offer-card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA0BvD;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,EACR,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,KAAK,EACL,MAAM,EACN,SAAS,EACT,OAAO,GAAG,KAAK,EACf,SAAS,GACe;IACxB,MAAM,IAAI,GAAG,CACX,eACE,SAAS,EAAE,EAAE,CACX,2GAA2G,EAC3G,SAAS,CACV,aAGD,eAAK,SAAS,EAAC,wCAAwC,aACpD,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,CAC3B,eAAK,SAAS,EAAC,yDAAyD,aACrE,UAAU,EACV,QAAQ,IAAI,yBAAO,QAAQ,GAAQ,IAChC,CACP,EACD,gBAAM,SAAS,EAAC,gKAAgK,aAC9K,KAAC,YAAY,IAAC,SAAS,EAAC,QAAQ,wBAAe,iBAE1C,IACH,EAGN,aAAI,SAAS,EAAC,yCAAyC,YAAE,KAAK,GAAM,EAGpE,eAAK,SAAS,EAAC,iHAAiH,aAC9H,KAAC,MAAM,IAAC,SAAS,EAAC,wDAAwD,wBAAe,EACzF,eAAK,SAAS,EAAC,gBAAgB,aAC7B,YAAG,SAAS,EAAC,gDAAgD,YAAE,UAAU,GAAK,EAC9E,aAAG,SAAS,EAAC,+BAA+B,aACzC,WAAW,EACX,YAAY,IAAI,uCAAU,YAAY,IAAQ,IAC7C,IACA,IACF,EAGL,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CACnB,cAAK,SAAS,EAAC,8DAA8D,YAC1E,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,eAAsB,SAAS,EAAC,aAAa,aAC3C,cAAK,SAAS,EAAC,mCAAmC,YAAE,IAAI,CAAC,KAAK,GAAO,EACrE,cAAK,SAAS,EAAC,2DAA2D,YACvE,IAAI,CAAC,KAAK,GACP,KAJE,IAAI,CAAC,KAAK,CAKd,CACP,CAAC,GACE,CACP,EAGD,eAAK,SAAS,EAAC,2CAA2C,aACvD,MAAM,IAAI,CACT,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAC,+EAA+E,2BAGzF,KAAC,UAAU,IAAC,SAAS,EAAC,QAAQ,wBAAe,IACtC,CACV,EACA,SAAS,IAAI,CACZ,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,SAAS,EAClB,SAAS,EAAC,wFAAwF,aAElG,KAAC,OAAO,IAAC,SAAS,EAAC,QAAQ,wBAAe,eAEnC,CACV,IACG,IACF,CACP,CAAC;IAEF,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IAEzB,OAAO,KAAC,gBAAgB,cAAE,IAAI,GAAoB,CAAC;AACrD,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { CompletedOfferCard } from "./completed-offer-card";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof CompletedOfferCard;
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 SingleStat: Story;
15
+ export declare const NoVolume: Story;
16
+ export declare const NoPulse: Story;
17
+ //# sourceMappingURL=completed-offer-card.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completed-offer-card.stories.d.ts","sourceRoot":"","sources":["../../src/components/completed-offer-card.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,QAAA,MAAM,IAAI;;;;;;;CAKiC,CAAC;AAE5C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAgBrB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAYxB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAetB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAiBrB,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Sparkles } from "lucide-react";
3
+ import { CompletedOfferCard } from "./completed-offer-card";
4
+ const meta = {
5
+ title: "Components/CompletedOfferCard",
6
+ component: CompletedOfferCard,
7
+ tags: ["autodocs"],
8
+ parameters: { layout: "padded" },
9
+ };
10
+ export default meta;
11
+ export const Default = {
12
+ args: {
13
+ title: "Cabernet Sauvignon 2023",
14
+ subtitle: "Match · 5 days ago",
15
+ sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
16
+ winnerName: "Berlin Spirits GmbH",
17
+ winnerPrice: "€48 / unit",
18
+ winnerVolume: "5,000 L",
19
+ stats: [
20
+ { label: "Offers sent", value: "3" },
21
+ { label: "Responded", value: "2" },
22
+ { label: "To close", value: "4d" },
23
+ ],
24
+ onView: () => alert("View offer"),
25
+ onArchive: () => alert("Archive"),
26
+ },
27
+ };
28
+ export const SingleStat = {
29
+ args: {
30
+ title: "Pinot Noir 2022",
31
+ subtitle: "Match · 1d ago",
32
+ sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
33
+ winnerName: "Nordic Wine Partners",
34
+ winnerPrice: "€42 / unit",
35
+ winnerVolume: "2,000 L",
36
+ stats: [{ label: "Closed in", value: "6h" }],
37
+ onView: () => alert("View offer"),
38
+ onArchive: () => alert("Archive"),
39
+ },
40
+ };
41
+ export const NoVolume = {
42
+ args: {
43
+ title: "Riesling 2021",
44
+ subtitle: "Importer-direct · 2d ago",
45
+ sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
46
+ winnerName: "Iberian Imports Co",
47
+ winnerPrice: "€35 / unit",
48
+ stats: [
49
+ { label: "Offers sent", value: "1" },
50
+ { label: "Responded", value: "1" },
51
+ { label: "To close", value: "18m" },
52
+ ],
53
+ onView: () => alert("View offer"),
54
+ onArchive: () => alert("Archive"),
55
+ },
56
+ };
57
+ export const NoPulse = {
58
+ args: {
59
+ title: "Gewürztraminer 2023",
60
+ subtitle: "Match · 1 week ago",
61
+ sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
62
+ winnerName: "Tokyo Beverage Corp",
63
+ winnerPrice: "€55 / unit",
64
+ winnerVolume: "1,200 L",
65
+ stats: [
66
+ { label: "Offers sent", value: "2" },
67
+ { label: "Responded", value: "2" },
68
+ { label: "To close", value: "1d" },
69
+ ],
70
+ noPulse: true,
71
+ onView: () => alert("View offer"),
72
+ onArchive: () => alert("Archive"),
73
+ },
74
+ };
75
+ //# sourceMappingURL=completed-offer-card.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completed-offer-card.stories.js","sourceRoot":"","sources":["../../src/components/completed-offer-card.stories.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import * as React from "react";
2
+ export interface FileDropzoneProps {
3
+ /** Called with the accepted files. Validation runs before this fires. */
4
+ onFiles: (files: File[]) => void;
5
+ /** Comma-separated MIME types — e.g. "image/jpeg,image/png,image/webp". */
6
+ accept?: string;
7
+ /** Max file size in bytes. */
8
+ maxSize?: number;
9
+ /** Whether to allow selecting multiple files at once. Default true. */
10
+ multiple?: boolean;
11
+ /** Disable the dropzone (no click, no drop). */
12
+ disabled?: boolean;
13
+ /** className forwarded to the outer wrapper. */
14
+ className?: string;
15
+ /** Primary label text / node. Default: "Drag files here or click to browse". */
16
+ label?: React.ReactNode;
17
+ /** Secondary hint text shown below the label. */
18
+ hint?: React.ReactNode;
19
+ }
20
+ /**
21
+ * Multi-file drag-and-drop zone with click-to-browse fallback, MIME +
22
+ * size validation, and keyboard activation via Enter/Space. Headless
23
+ * callback API — consumers own the upload state and progress UI.
24
+ */
25
+ export declare function FileDropzone({ onFiles, accept, maxSize, multiple, disabled, className, label, hint, }: FileDropzoneProps): import("react/jsx-runtime").JSX.Element;
26
+ //# sourceMappingURL=file-dropzone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-dropzone.d.ts","sourceRoot":"","sources":["../../src/components/file-dropzone.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,iBAAiB;IAChC,yEAAyE;IACzE,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACjC,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,iDAAiD;IACjD,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EACN,OAAO,EACP,QAAe,EACf,QAAgB,EAChB,SAAS,EACT,KAAK,EACL,IAAI,GACL,EAAE,iBAAiB,2CA4EnB"}
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowUpTrayIcon } from "@heroicons/react/20/solid";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+ /**
6
+ * Multi-file drag-and-drop zone with click-to-browse fallback, MIME +
7
+ * size validation, and keyboard activation via Enter/Space. Headless
8
+ * callback API — consumers own the upload state and progress UI.
9
+ */
10
+ export function FileDropzone({ onFiles, accept, maxSize, multiple = true, disabled = false, className, label, hint, }) {
11
+ const inputRef = React.useRef(null);
12
+ const [isDragging, setIsDragging] = React.useState(false);
13
+ const [error, setError] = React.useState(null);
14
+ const handleFiles = React.useCallback((files) => {
15
+ if (!files || files.length === 0)
16
+ return;
17
+ setError(null);
18
+ const arr = Array.from(files);
19
+ const acceptList = accept?.split(",").map((s) => s.trim()) ?? [];
20
+ for (const file of arr) {
21
+ if (acceptList.length > 0 && !acceptList.includes(file.type)) {
22
+ setError(`"${file.name}": unsupported file type`);
23
+ return;
24
+ }
25
+ if (maxSize && file.size > maxSize) {
26
+ const mb = (maxSize / 1024 / 1024).toFixed(0);
27
+ setError(`"${file.name}": exceeds ${mb} MB`);
28
+ return;
29
+ }
30
+ }
31
+ onFiles(arr);
32
+ }, [accept, maxSize, onFiles]);
33
+ return (_jsxs("div", { onClick: () => !disabled && inputRef.current?.click(), onKeyDown: (e) => {
34
+ if ((e.key === "Enter" || e.key === " ") && !disabled) {
35
+ e.preventDefault();
36
+ inputRef.current?.click();
37
+ }
38
+ }, onDragOver: (e) => {
39
+ e.preventDefault();
40
+ if (!disabled)
41
+ setIsDragging(true);
42
+ }, onDragLeave: () => setIsDragging(false), onDrop: (e) => {
43
+ e.preventDefault();
44
+ setIsDragging(false);
45
+ if (!disabled)
46
+ handleFiles(e.dataTransfer.files);
47
+ }, role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled, className: cn("flex cursor-pointer flex-col items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-border/60 bg-muted/20 p-8 text-center transition-all", "hover:border-primary/40 hover:bg-primary/5", isDragging && "scale-[1.01] border-primary bg-primary/10", disabled && "cursor-not-allowed opacity-50", className), children: [_jsx("input", { ref: inputRef, type: "file", accept: accept, multiple: multiple, disabled: disabled, className: "sr-only", onChange: (e) => handleFiles(e.target.files) }), _jsx(ArrowUpTrayIcon, { className: "size-8 text-muted-foreground", "aria-hidden": true }), _jsx("div", { className: "text-sm font-semibold text-foreground", children: label ?? "Drag files here or click to browse" }), hint && _jsx("div", { className: "text-xs text-muted-foreground", children: hint }), error && _jsx("div", { className: "mt-1 text-xs font-medium text-destructive", children: error })] }));
48
+ }
49
+ //# sourceMappingURL=file-dropzone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-dropzone.js","sourceRoot":"","sources":["../../src/components/file-dropzone.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAqBlC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EACN,OAAO,EACP,QAAQ,GAAG,IAAI,EACf,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,KAAK,EACL,IAAI,GACc;IAClB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAmB,IAAI,CAAC,CAAC;IACtD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAE9D,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CACnC,CAAC,KAAsB,EAAE,EAAE;QACzB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACzC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE9B,MAAM,UAAU,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QACjE,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;YACvB,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7D,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,0BAA0B,CAAC,CAAC;gBAClD,OAAO;YACT,CAAC;YACD,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,CAAC;gBACnC,MAAM,EAAE,GAAG,CAAC,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC9C,QAAQ,CAAC,IAAI,IAAI,CAAC,IAAI,cAAc,EAAE,KAAK,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,CAAC;IACf,CAAC,EACD,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAC3B,CAAC;IAEF,OAAO,CACL,eACE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,EACrD,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;YACf,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACtD,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC,EACD,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;YAChB,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,QAAQ;gBAAE,aAAa,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC,EACD,WAAW,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EACvC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,CAAC,QAAQ;gBAAE,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACnD,CAAC,EACD,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,mBACZ,QAAQ,EACvB,SAAS,EAAE,EAAE,CACX,+JAA+J,EAC/J,4CAA4C,EAC5C,UAAU,IAAI,2CAA2C,EACzD,QAAQ,IAAI,+BAA+B,EAC3C,SAAS,CACV,aAED,gBACE,GAAG,EAAE,QAAQ,EACb,IAAI,EAAC,MAAM,EACX,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAC,SAAS,EACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAC5C,EACF,KAAC,eAAe,IAAC,SAAS,EAAC,8BAA8B,wBAAe,EACxE,cAAK,SAAS,EAAC,uCAAuC,YACnD,KAAK,IAAI,oCAAoC,GAC1C,EACL,IAAI,IAAI,cAAK,SAAS,EAAC,+BAA+B,YAAE,IAAI,GAAO,EACnE,KAAK,IAAI,cAAK,SAAS,EAAC,2CAA2C,YAAE,KAAK,GAAO,IAC9E,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { FileDropzone } from "./file-dropzone";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof FileDropzone;
6
+ tags: string[];
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: string;
11
+ };
12
+ };
13
+ };
14
+ };
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+ export declare const Default: Story;
18
+ export declare const ImagesOnly: Story;
19
+ export declare const PdfsAndImages: Story;
20
+ export declare const Disabled: Story;
21
+ //# sourceMappingURL=file-dropzone.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-dropzone.stories.d.ts","sourceRoot":"","sources":["../../src/components/file-dropzone.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,QAAA,MAAM,IAAI;;;;;;;;;;;CAY2B,CAAC;AAEtC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAWrB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAcxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAc3B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAYtB,CAAC"}