@cellarnode/ui 0.12.15 → 0.14.1

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 (65) hide show
  1. package/dist/components/bottle-extract-dialog.d.ts +25 -0
  2. package/dist/components/bottle-extract-dialog.d.ts.map +1 -0
  3. package/dist/components/bottle-extract-dialog.js +87 -0
  4. package/dist/components/bottle-extract-dialog.js.map +1 -0
  5. package/dist/components/bottle-extract-dialog.stories.d.ts +17 -0
  6. package/dist/components/bottle-extract-dialog.stories.d.ts.map +1 -0
  7. package/dist/components/bottle-extract-dialog.stories.js +54 -0
  8. package/dist/components/bottle-extract-dialog.stories.js.map +1 -0
  9. package/dist/components/bottle-extraction-inline-progress.d.ts +14 -0
  10. package/dist/components/bottle-extraction-inline-progress.d.ts.map +1 -0
  11. package/dist/components/bottle-extraction-inline-progress.js +28 -0
  12. package/dist/components/bottle-extraction-inline-progress.js.map +1 -0
  13. package/dist/components/bottle-extraction-inline-progress.stories.d.ts +17 -0
  14. package/dist/components/bottle-extraction-inline-progress.stories.d.ts.map +1 -0
  15. package/dist/components/bottle-extraction-inline-progress.stories.js +35 -0
  16. package/dist/components/bottle-extraction-inline-progress.stories.js.map +1 -0
  17. package/dist/components/opportunity-brief.d.ts +23 -0
  18. package/dist/components/opportunity-brief.d.ts.map +1 -0
  19. package/dist/components/opportunity-brief.js +13 -0
  20. package/dist/components/opportunity-brief.js.map +1 -0
  21. package/dist/components/opportunity-brief.stories.d.ts +17 -0
  22. package/dist/components/opportunity-brief.stories.d.ts.map +1 -0
  23. package/dist/components/opportunity-brief.stories.js +38 -0
  24. package/dist/components/opportunity-brief.stories.js.map +1 -0
  25. package/dist/components/picker-empty-guidance.d.ts +20 -0
  26. package/dist/components/picker-empty-guidance.d.ts.map +1 -0
  27. package/dist/components/picker-empty-guidance.js +14 -0
  28. package/dist/components/picker-empty-guidance.js.map +1 -0
  29. package/dist/components/picker-empty-guidance.stories.d.ts +20 -0
  30. package/dist/components/picker-empty-guidance.stories.d.ts.map +1 -0
  31. package/dist/components/picker-empty-guidance.stories.js +33 -0
  32. package/dist/components/picker-empty-guidance.stories.js.map +1 -0
  33. package/dist/components/reason-pill.d.ts +17 -0
  34. package/dist/components/reason-pill.d.ts.map +1 -0
  35. package/dist/components/reason-pill.js +23 -0
  36. package/dist/components/reason-pill.js.map +1 -0
  37. package/dist/components/reason-pill.stories.d.ts +18 -0
  38. package/dist/components/reason-pill.stories.d.ts.map +1 -0
  39. package/dist/components/reason-pill.stories.js +18 -0
  40. package/dist/components/reason-pill.stories.js.map +1 -0
  41. package/dist/components/score-chip.d.ts +16 -0
  42. package/dist/components/score-chip.d.ts.map +1 -0
  43. package/dist/components/score-chip.js +26 -0
  44. package/dist/components/score-chip.js.map +1 -0
  45. package/dist/components/score-chip.stories.d.ts +18 -0
  46. package/dist/components/score-chip.stories.d.ts.map +1 -0
  47. package/dist/components/score-chip.stories.js +18 -0
  48. package/dist/components/score-chip.stories.js.map +1 -0
  49. package/dist/components/variant-picker-dialog.d.ts +66 -32
  50. package/dist/components/variant-picker-dialog.d.ts.map +1 -1
  51. package/dist/components/variant-picker-dialog.js +72 -81
  52. package/dist/components/variant-picker-dialog.js.map +1 -1
  53. package/dist/components/variant-picker-dialog.stories.d.ts +3 -3
  54. package/dist/components/variant-picker-dialog.stories.d.ts.map +1 -1
  55. package/dist/components/variant-picker-dialog.stories.js +133 -92
  56. package/dist/components/variant-picker-dialog.stories.js.map +1 -1
  57. package/dist/components/variant-picker-dialog.test.d.ts +2 -0
  58. package/dist/components/variant-picker-dialog.test.d.ts.map +1 -0
  59. package/dist/components/variant-picker-dialog.test.js +140 -0
  60. package/dist/components/variant-picker-dialog.test.js.map +1 -0
  61. package/dist/index.d.ts +7 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +9 -1
  64. package/dist/index.js.map +1 -1
  65. package/package.json +1 -1
@@ -0,0 +1,25 @@
1
+ export type BottleExtractDialogContext = {
2
+ kind: "beverage_gallery";
3
+ beverageId: string;
4
+ beverageName: string;
5
+ } | {
6
+ kind: "variant_carousel";
7
+ beverageId: string;
8
+ variantId: string;
9
+ variantName: string;
10
+ beverageName: string;
11
+ };
12
+ export interface BottleExtractDialogProps {
13
+ open: boolean;
14
+ context: BottleExtractDialogContext;
15
+ onClose: () => void;
16
+ onSubmit: (input: {
17
+ file: File;
18
+ aiExtract: boolean;
19
+ makePrimary: boolean;
20
+ }) => Promise<void>;
21
+ /** i18n lookup — producer-dashboard passes react-i18next's `t` */
22
+ t: (key: string, opts?: Record<string, unknown>) => string;
23
+ }
24
+ export declare function BottleExtractDialog({ open, context, onClose, onSubmit, t, }: BottleExtractDialogProps): import("react/jsx-runtime").JSX.Element;
25
+ //# sourceMappingURL=bottle-extract-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extract-dialog.d.ts","sourceRoot":"","sources":["../../src/components/bottle-extract-dialog.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,0BAA0B,GAClC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACtE;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,0BAA0B,CAAC;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7F,kEAAkE;IAClE,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;CAC5D;AAOD,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,OAAO,EACP,QAAQ,EACR,CAAC,GACF,EAAE,wBAAwB,2CAkK1B"}
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as Dialog from "@radix-ui/react-dialog";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+ export function BottleExtractDialog({ open, context, onClose, onSubmit, t, }) {
6
+ const [file, setFile] = React.useState(null);
7
+ const [mode, setMode] = React.useState("extract");
8
+ const [makePrimary, setMakePrimary] = React.useState(false);
9
+ const [state, setState] = React.useState({ kind: "idle" });
10
+ const fileInputRef = React.useRef(null);
11
+ const targetName = context.kind === "beverage_gallery" ? context.beverageName : context.variantName;
12
+ const titleId = React.useId();
13
+ const validate = (f) => {
14
+ if (f.name.toLowerCase().endsWith(".heic") ||
15
+ f.type === "image/heic" ||
16
+ f.type === "image/heif") {
17
+ return t("dialog.heic_error");
18
+ }
19
+ if (f.type === "image/webp")
20
+ return t("dialog.webp_error");
21
+ if (!["image/jpeg", "image/png"].includes(f.type)) {
22
+ return t("dialog.heic_error");
23
+ }
24
+ if (f.size > 10 * 1024 * 1024)
25
+ return "File is larger than 10MB.";
26
+ return null;
27
+ };
28
+ const handleFileChange = (files) => {
29
+ if (!files || files.length === 0)
30
+ return;
31
+ const first = files[0];
32
+ if (!first)
33
+ return;
34
+ const err = validate(first);
35
+ if (err) {
36
+ setState({ kind: "error", message: err });
37
+ return;
38
+ }
39
+ setFile(first);
40
+ setState({ kind: "idle" });
41
+ };
42
+ const handleSubmit = async (e) => {
43
+ e.preventDefault();
44
+ if (!file) {
45
+ setState({ kind: "error", message: "Please choose a file first." });
46
+ return;
47
+ }
48
+ setState({ kind: "submitting" });
49
+ try {
50
+ await onSubmit({
51
+ file,
52
+ aiExtract: mode === "extract",
53
+ makePrimary: mode === "extract" && makePrimary,
54
+ });
55
+ setFile(null);
56
+ setMakePrimary(false);
57
+ setMode("extract");
58
+ setState({ kind: "idle" });
59
+ onClose();
60
+ }
61
+ catch (err) {
62
+ setState({ kind: "error", message: err.message });
63
+ }
64
+ };
65
+ return (_jsx(Dialog.Root, { open: open, onOpenChange: (nextOpen) => !nextOpen && onClose(), children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-foreground/20 backdrop-blur-xs" }), _jsxs(Dialog.Content, { "aria-labelledby": titleId, className: cn("fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-lg -translate-x-1/2 -translate-y-1/2", "rounded-2xl border border-border bg-card p-6 shadow-xl"), children: [_jsx(Dialog.Title, { id: titleId, className: "text-base/6 font-semibold text-foreground", children: t("dialog.title") }), _jsxs("form", { onSubmit: handleSubmit, className: "mt-4 flex flex-col gap-4", children: [_jsx(DropZone, { ref: fileInputRef, file: file, onFileChange: handleFileChange, t: t }), _jsxs("fieldset", { className: "flex flex-col gap-2", children: [_jsx("legend", { className: "sr-only", children: "Upload mode" }), _jsxs("label", { className: "flex cursor-pointer items-start gap-3", children: [_jsx("input", { type: "radio", name: "mode", value: "as_is", checked: mode === "as_is", onChange: () => setMode("as_is"), className: "mt-1" }), _jsxs("span", { children: [_jsx("span", { className: "font-medium text-foreground", children: t("dialog.mode_as_is") }), _jsx("span", { className: "block text-sm text-muted-foreground", children: t("dialog.mode_as_is_help", { name: targetName }) })] })] }), _jsxs("label", { className: "flex cursor-pointer items-start gap-3", children: [_jsx("input", { type: "radio", name: "mode", value: "extract", checked: mode === "extract", onChange: () => setMode("extract"), className: "mt-1" }), _jsxs("span", { children: [_jsx("span", { className: "font-medium text-foreground", children: t("dialog.mode_extract") }), _jsx("span", { className: "block text-sm text-muted-foreground", children: t("dialog.mode_extract_help", { name: targetName }) })] })] })] }), mode === "extract" && (_jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-foreground", children: [_jsx("input", { type: "checkbox", checked: makePrimary, onChange: (e) => setMakePrimary(e.target.checked) }), _jsx("span", { children: t("dialog.make_primary") })] })), state.kind === "error" && (_jsx("div", { role: "alert", className: "rounded-lg border border-red-300 bg-red-50 p-2 text-sm text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-100", children: state.message })), _jsx("div", { "aria-live": "polite", className: "sr-only", children: state.kind === "submitting" ? t("dialog.uploading") : "" }), _jsxs("div", { className: "mt-2 flex justify-end gap-2", children: [_jsx("button", { type: "button", onClick: onClose, className: "rounded-md px-4 py-2 text-foreground hover:bg-muted/50 focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/50", children: t("dialog.cancel") }), _jsx("button", { type: "submit", disabled: !file || state.kind === "submitting", className: "rounded-md bg-emerald-600 px-4 py-2 text-white hover:bg-emerald-700 focus:outline-hidden focus-visible:ring-2 focus-visible:ring-emerald-500 disabled:opacity-50", children: state.kind === "submitting" ? t("dialog.uploading") : t("dialog.submit") })] })] }), _jsx(Dialog.Close, { className: "absolute right-4 top-4 text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/50", "aria-label": "Close", children: "\u00D7" })] })] }) }));
66
+ }
67
+ const DropZone = React.forwardRef(({ file, onFileChange, t }, ref) => {
68
+ const [hover, setHover] = React.useState(false);
69
+ const labelId = React.useId();
70
+ return (_jsxs("div", { role: "button", tabIndex: 0, "aria-labelledby": labelId, onClick: () => ref.current?.click(), onKeyDown: (e) => {
71
+ if (e.key === "Enter" || e.key === " ") {
72
+ e.preventDefault();
73
+ ref.current?.click();
74
+ }
75
+ }, onDragOver: (e) => {
76
+ e.preventDefault();
77
+ setHover(true);
78
+ }, onDragLeave: () => setHover(false), onDrop: (e) => {
79
+ e.preventDefault();
80
+ setHover(false);
81
+ onFileChange(e.dataTransfer.files);
82
+ }, className: cn("cursor-pointer rounded-lg border-2 border-dashed p-6 text-center focus:outline-hidden focus-visible:ring-2 focus-visible:ring-emerald-500", hover
83
+ ? "border-emerald-500 bg-emerald-50 dark:bg-emerald-950/30"
84
+ : "border-border bg-muted/20"), children: [_jsx("input", { ref: ref, type: "file", accept: "image/jpeg,image/png", className: "hidden", onChange: (e) => onFileChange(e.target.files) }), _jsx("p", { id: labelId, className: "text-sm text-foreground", children: file ? file.name : t("dialog.drop_zone") })] }));
85
+ });
86
+ DropZone.displayName = "DropZone";
87
+ //# sourceMappingURL=bottle-extract-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extract-dialog.js","sourceRoot":"","sources":["../../src/components/bottle-extract-dialog.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { BottleExtractDialog } from "./bottle-extract-dialog";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof BottleExtractDialog;
6
+ tags: string[];
7
+ parameters: {
8
+ a11y: {
9
+ test: string;
10
+ };
11
+ };
12
+ };
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+ export declare const BeverageGallery: Story;
16
+ export declare const VariantCarousel: Story;
17
+ //# sourceMappingURL=bottle-extract-dialog.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extract-dialog.stories.d.ts","sourceRoot":"","sources":["../../src/components/bottle-extract-dialog.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAsB9D,QAAA,MAAM,IAAI;;;;;;;;;CAKkC,CAAC;AAE7C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,eAAe,EAAE,KAY7B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAc7B,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { BottleExtractDialog } from "./bottle-extract-dialog";
2
+ const t = (key, opts) => {
3
+ const strings = {
4
+ "dialog.title": "Add bottle photo",
5
+ "dialog.drop_zone": "Drop an image here, or click to browse (JPEG or PNG, up to 10MB)",
6
+ "dialog.mode_as_is": "Upload as-is",
7
+ "dialog.mode_as_is_help": `Add this photo to ${opts?.name}'s gallery without AI processing.`,
8
+ "dialog.mode_extract": "Upload + AI extract bottle",
9
+ "dialog.mode_extract_help": `We'll send this image to our bottle-extraction AI to isolate the bottle. Takes ~10 seconds. Result will be added to ${opts?.name}'s gallery.`,
10
+ "dialog.make_primary": "Make the cutout the primary photo",
11
+ "dialog.submit": "Upload",
12
+ "dialog.cancel": "Cancel",
13
+ "dialog.uploading": "Uploading…",
14
+ "dialog.heic_error": "HEIC isn't supported yet. Please convert to JPEG.",
15
+ "dialog.webp_error": "WebP isn't supported yet. Please convert to JPEG or PNG.",
16
+ };
17
+ return strings[key] ?? key;
18
+ };
19
+ const meta = {
20
+ title: "Extraction/BottleExtractDialog",
21
+ component: BottleExtractDialog,
22
+ tags: ["autodocs"],
23
+ parameters: { a11y: { test: "error" } },
24
+ };
25
+ export default meta;
26
+ export const BeverageGallery = {
27
+ args: {
28
+ open: true,
29
+ context: {
30
+ kind: "beverage_gallery",
31
+ beverageId: "b1",
32
+ beverageName: "Tenuta Sample Reserve",
33
+ },
34
+ onClose: () => { },
35
+ onSubmit: async () => new Promise((resolve) => setTimeout(resolve, 600)),
36
+ t,
37
+ },
38
+ };
39
+ export const VariantCarousel = {
40
+ args: {
41
+ open: true,
42
+ context: {
43
+ kind: "variant_carousel",
44
+ beverageId: "b1",
45
+ variantId: "v1",
46
+ variantName: "Tenuta Sample Reserve 2021",
47
+ beverageName: "Tenuta Sample Reserve",
48
+ },
49
+ onClose: () => { },
50
+ onSubmit: async () => new Promise((resolve) => setTimeout(resolve, 600)),
51
+ t,
52
+ },
53
+ };
54
+ //# sourceMappingURL=bottle-extract-dialog.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extract-dialog.stories.js","sourceRoot":"","sources":["../../src/components/bottle-extract-dialog.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,IAA8B,EAAE,EAAE;IACxD,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,kBAAkB,EAChB,kEAAkE;QACpE,mBAAmB,EAAE,cAAc;QACnC,wBAAwB,EAAE,qBAAqB,IAAI,EAAE,IAAI,mCAAmC;QAC5F,qBAAqB,EAAE,4BAA4B;QACnD,0BAA0B,EAAE,uHAAuH,IAAI,EAAE,IAAI,aAAa;QAC1K,qBAAqB,EAAE,mCAAmC;QAC1D,eAAe,EAAE,QAAQ;QACzB,eAAe,EAAE,QAAQ;QACzB,kBAAkB,EAAE,YAAY;QAChC,mBAAmB,EAAE,mDAAmD;QACxE,mBAAmB,EACjB,0DAA0D;KAC7D,CAAC;IACF,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,gCAAgC;IACvC,SAAS,EAAE,mBAAmB;IAC9B,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;CACG,CAAC;AAE7C,eAAe,IAAI,CAAC;AAGpB,MAAM,CAAC,MAAM,eAAe,GAAU;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,IAAI,EAAE,kBAAkB;YACxB,UAAU,EAAE,IAAI;YAChB,YAAY,EAAE,uBAAuB;SACtC;QACD,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;QACjB,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACxE,CAAC;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAU;IACpC,IAAI,EAAE;QACJ,IAAI,EAAE,IAAI;QACV,OAAO,EAAE;YACP,IAAI,EAAE,kBAAkB;YACxB,UAAU,EAAE,IAAI;YAChB,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,4BAA4B;YACzC,YAAY,EAAE,uBAAuB;SACtC;QACD,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;QACjB,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACxE,CAAC;KACF;CACF,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface BottleExtractionInlineProgressProps {
2
+ jobId: string;
3
+ progress: {
4
+ processed: number;
5
+ total: number;
6
+ currentProductIndex?: number;
7
+ message?: string;
8
+ } | null;
9
+ status: "pending" | "processing" | "completed" | "failed" | "abandoned" | "awaiting_producer";
10
+ onCancel?: () => void;
11
+ t: (key: string, opts?: Record<string, unknown>) => string;
12
+ }
13
+ export declare function BottleExtractionInlineProgress(props: BottleExtractionInlineProgressProps): import("react/jsx-runtime").JSX.Element | null;
14
+ //# sourceMappingURL=bottle-extraction-inline-progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extraction-inline-progress.d.ts","sourceRoot":"","sources":["../../src/components/bottle-extraction-inline-progress.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,mCAAmC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI,CAAC;IACT,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,mBAAmB,CAAC;IAC9F,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;CAC5D;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,mCAAmC,kDAmDxF"}
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ export function BottleExtractionInlineProgress(props) {
4
+ const { progress, status, onCancel, t } = props;
5
+ const [startedAt] = React.useState(() => Date.now());
6
+ const [now, setNow] = React.useState(() => Date.now());
7
+ React.useEffect(() => {
8
+ if (status === "completed" || status === "failed" || status === "abandoned")
9
+ return;
10
+ const tick = setInterval(() => setNow(Date.now()), 1000);
11
+ return () => clearInterval(tick);
12
+ }, [status]);
13
+ const elapsedSeconds = Math.round((now - startedAt) / 1000);
14
+ const coldStartHint = elapsedSeconds >= 60 ? "slow_60s" : elapsedSeconds >= 20 ? "slow_20s" : null;
15
+ if (status === "completed")
16
+ return null;
17
+ const label = progress && progress.total > 1
18
+ ? t("progress.running_with_counts", {
19
+ processed: progress.processed,
20
+ total: progress.total,
21
+ })
22
+ : t("progress.running");
23
+ return (_jsxs("div", { role: "status", "aria-live": "polite", className: "flex items-center gap-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3 dark:border-emerald-800 dark:bg-emerald-950/30", children: [_jsx(Spinner, {}), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: label }), coldStartHint && (_jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t(`progress.${coldStartHint}`) }))] }), elapsedSeconds >= 60 && onCancel && (_jsx("button", { type: "button", onClick: onCancel, className: "rounded-md text-sm text-red-600 hover:underline focus:outline-hidden focus-visible:ring-2 focus-visible:ring-red-500", children: t("progress.cancel") }))] }));
24
+ }
25
+ function Spinner() {
26
+ return (_jsx("span", { "aria-hidden": "true", className: "inline-block size-4 animate-spin rounded-full border-2 border-emerald-600 border-t-transparent" }));
27
+ }
28
+ //# sourceMappingURL=bottle-extraction-inline-progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extraction-inline-progress.js","sourceRoot":"","sources":["../../src/components/bottle-extraction-inline-progress.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,MAAM,UAAU,8BAA8B,CAAC,KAA0C;IACvF,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC;IAChD,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACrD,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAEvD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,MAAM,KAAK,WAAW,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,WAAW;YAAE,OAAO;QACpF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACzD,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5D,MAAM,aAAa,GACjB,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IAE/E,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAExC,MAAM,KAAK,GACT,QAAQ,IAAI,QAAQ,CAAC,KAAK,GAAG,CAAC;QAC5B,CAAC,CAAC,CAAC,CAAC,8BAA8B,EAAE;YAChC,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,KAAK,EAAE,QAAQ,CAAC,KAAK;SACtB,CAAC;QACJ,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAE5B,OAAO,CACL,eACE,IAAI,EAAC,QAAQ,eACH,QAAQ,EAClB,SAAS,EAAC,+HAA+H,aAEzI,KAAC,OAAO,KAAG,EACX,eAAK,SAAS,EAAC,QAAQ,aACrB,YAAG,SAAS,EAAC,qCAAqC,YAAE,KAAK,GAAK,EAC7D,aAAa,IAAI,CAChB,YAAG,SAAS,EAAC,oCAAoC,YAC9C,CAAC,CAAC,YAAY,aAAa,EAAE,CAAC,GAC7B,CACL,IACG,EACL,cAAc,IAAI,EAAE,IAAI,QAAQ,IAAI,CACnC,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAC,sHAAsH,YAE/H,CAAC,CAAC,iBAAiB,CAAC,GACd,CACV,IACG,CACP,CAAC;AACJ,CAAC;AAED,SAAS,OAAO;IACd,OAAO,CACL,8BACc,MAAM,EAClB,SAAS,EAAC,gGAAgG,GAC1G,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { BottleExtractionInlineProgress } from "./bottle-extraction-inline-progress";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof BottleExtractionInlineProgress;
6
+ tags: string[];
7
+ parameters: {
8
+ a11y: {
9
+ test: string;
10
+ };
11
+ };
12
+ };
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+ export declare const SingleRunning: Story;
16
+ export declare const CatalogMidway: Story;
17
+ //# sourceMappingURL=bottle-extraction-inline-progress.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extraction-inline-progress.stories.d.ts","sourceRoot":"","sources":["../../src/components/bottle-extraction-inline-progress.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AAerF,QAAA,MAAM,IAAI;;;;;;;;;CAK6C,CAAC;AAExD,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,aAAa,EAAE,KAO3B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAO3B,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { BottleExtractionInlineProgress } from "./bottle-extraction-inline-progress";
2
+ const t = (key, opts) => {
3
+ const strings = {
4
+ "progress.running": "Extracting bottle photo…",
5
+ "progress.running_with_counts": `Extracting bottle photo… (${opts?.processed} of ${opts?.total} processed)`,
6
+ "progress.slow_20s": "This is taking a moment longer than usual — we're warming up the AI service.",
7
+ "progress.slow_60s": "Still working — bottle extraction can take 1–2 minutes on first run.",
8
+ "progress.cancel": "Cancel",
9
+ };
10
+ return strings[key] ?? key;
11
+ };
12
+ const meta = {
13
+ title: "Extraction/BottleExtractionInlineProgress",
14
+ component: BottleExtractionInlineProgress,
15
+ tags: ["autodocs"],
16
+ parameters: { a11y: { test: "error" } },
17
+ };
18
+ export default meta;
19
+ export const SingleRunning = {
20
+ args: {
21
+ jobId: "j1",
22
+ status: "processing",
23
+ progress: { processed: 0, total: 1 },
24
+ t,
25
+ },
26
+ };
27
+ export const CatalogMidway = {
28
+ args: {
29
+ jobId: "j2",
30
+ status: "processing",
31
+ progress: { processed: 4, total: 12 },
32
+ t,
33
+ },
34
+ };
35
+ //# sourceMappingURL=bottle-extraction-inline-progress.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottle-extraction-inline-progress.stories.js","sourceRoot":"","sources":["../../src/components/bottle-extraction-inline-progress.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AAErF,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,IAA8B,EAAE,EAAE;IACxD,MAAM,OAAO,GAA2B;QACtC,kBAAkB,EAAE,0BAA0B;QAC9C,8BAA8B,EAAE,6BAA6B,IAAI,EAAE,SAAS,OAAO,IAAI,EAAE,KAAK,aAAa;QAC3G,mBAAmB,EACjB,8EAA8E;QAChF,mBAAmB,EACjB,sEAAsE;QACxE,iBAAiB,EAAE,QAAQ;KAC5B,CAAC;IACF,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,2CAA2C;IAClD,SAAS,EAAE,8BAA8B;IACzC,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;CACc,CAAC;AAExD,eAAe,IAAI,CAAC;AAGpB,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,IAAI,EAAE;QACJ,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,YAAY;QACpB,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;QACpC,CAAC;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,IAAI,EAAE;QACJ,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,YAAY;QACpB,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;QACrC,CAAC;KACF;CACF,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface OpportunityBriefChip {
2
+ label: string;
3
+ }
4
+ export interface OpportunityBriefProps {
5
+ /** Title shown bold. */
6
+ name: string;
7
+ /** Optional buyer name shown muted under the title. */
8
+ buyerName?: string | null;
9
+ /** Pills (category, subtype, country, volume, deadline). Display order = props order. */
10
+ chips: OpportunityBriefChip[];
11
+ /** Eyebrow label above the name. @default "Looking for" */
12
+ eyebrow?: string;
13
+ className?: string;
14
+ }
15
+ /**
16
+ * Compact opportunity-context banner used at the top of the producer picker
17
+ * and anywhere an opportunity needs to be identified at a glance (importer
18
+ * opportunity list, match detail pane).
19
+ *
20
+ * Intentionally visual-only — all interaction happens on the host container.
21
+ */
22
+ export declare function OpportunityBrief({ name, buyerName, chips, eyebrow, className, }: OpportunityBriefProps): import("react/jsx-runtime").JSX.Element;
23
+ //# sourceMappingURL=opportunity-brief.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-brief.d.ts","sourceRoot":"","sources":["../../src/components/opportunity-brief.tsx"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yFAAyF;IACzF,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,SAAS,EACT,KAAK,EACL,OAAuB,EACvB,SAAS,GACV,EAAE,qBAAqB,2CA6BvB"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils";
3
+ /**
4
+ * Compact opportunity-context banner used at the top of the producer picker
5
+ * and anywhere an opportunity needs to be identified at a glance (importer
6
+ * opportunity list, match detail pane).
7
+ *
8
+ * Intentionally visual-only — all interaction happens on the host container.
9
+ */
10
+ export function OpportunityBrief({ name, buyerName, chips, eyebrow = "Looking for", className, }) {
11
+ return (_jsxs("div", { className: cn("rounded-lg border-l-[3px] border-primary bg-primary/5 px-3 py-2.5", className), children: [_jsx("p", { className: "text-[10px] font-bold uppercase tracking-[0.08em] text-primary", children: eyebrow }), _jsx("p", { className: "mt-0.5 text-sm font-semibold text-foreground", children: name }), buyerName && (_jsx("p", { className: "text-[11px] text-muted-foreground", children: buyerName })), chips.length > 0 && (_jsx("div", { className: "mt-1.5 flex flex-wrap gap-1", children: chips.map((c) => (_jsx("span", { className: "rounded-full bg-primary/15 px-2 py-0.5 text-[10px] font-semibold text-primary", children: c.label }, c.label))) }))] }));
12
+ }
13
+ //# sourceMappingURL=opportunity-brief.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-brief.js","sourceRoot":"","sources":["../../src/components/opportunity-brief.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAkBlC;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,IAAI,EACJ,SAAS,EACT,KAAK,EACL,OAAO,GAAG,aAAa,EACvB,SAAS,GACa;IACtB,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,mEAAmE,EACnE,SAAS,CACV,aAED,YAAG,SAAS,EAAC,gEAAgE,YAC1E,OAAO,GACN,EACJ,YAAG,SAAS,EAAC,8CAA8C,YAAE,IAAI,GAAK,EACrE,SAAS,IAAI,CACZ,YAAG,SAAS,EAAC,mCAAmC,YAAE,SAAS,GAAK,CACjE,EACA,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CACnB,cAAK,SAAS,EAAC,6BAA6B,YACzC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAChB,eAEE,SAAS,EAAC,+EAA+E,YAExF,CAAC,CAAC,KAAK,IAHH,CAAC,CAAC,KAAK,CAIP,CACR,CAAC,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { OpportunityBrief } from "./opportunity-brief";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof OpportunityBrief;
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 NoBuyer: Story;
15
+ export declare const NoChips: Story;
16
+ export declare const CustomEyebrow: Story;
17
+ //# sourceMappingURL=opportunity-brief.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-brief.stories.d.ts","sourceRoot":"","sources":["../../src/components/opportunity-brief.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,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,KAYrB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAKrB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAM3B,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { OpportunityBrief } from "./opportunity-brief";
2
+ const meta = {
3
+ title: "Picker/OpportunityBrief",
4
+ component: OpportunityBrief,
5
+ tags: ["autodocs"],
6
+ parameters: { layout: "centered" },
7
+ };
8
+ export default meta;
9
+ export const Default = {
10
+ args: {
11
+ name: "brandy spirits · Spain Selection",
12
+ buyerName: "Systembolaget",
13
+ chips: [
14
+ { label: "Spirits" },
15
+ { label: "Brandy" },
16
+ { label: "Spain" },
17
+ { label: "330 L" },
18
+ { label: "due Mar 17" },
19
+ ],
20
+ },
21
+ };
22
+ export const NoBuyer = {
23
+ args: {
24
+ name: "Riesling Selection 2026",
25
+ chips: [{ label: "Wine" }, { label: "White wine" }, { label: "Germany" }],
26
+ },
27
+ };
28
+ export const NoChips = {
29
+ args: { name: "Untyped opportunity", chips: [] },
30
+ };
31
+ export const CustomEyebrow = {
32
+ args: {
33
+ name: "Q2 discovery wishlist",
34
+ eyebrow: "Saved search",
35
+ chips: [{ label: "Organic" }, { label: "<15 EUR" }],
36
+ },
37
+ };
38
+ //# sourceMappingURL=opportunity-brief.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-brief.stories.js","sourceRoot":"","sources":["../../src/components/opportunity-brief.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,yBAAyB;IAChC,SAAS,EAAE,gBAAgB;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;CACK,CAAC;AAE1C,eAAe,IAAI,CAAC;AAGpB,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,kCAAkC;QACxC,SAAS,EAAE,eAAe;QAC1B,KAAK,EAAE;YACL,EAAE,KAAK,EAAE,SAAS,EAAE;YACpB,EAAE,KAAK,EAAE,QAAQ,EAAE;YACnB,EAAE,KAAK,EAAE,OAAO,EAAE;YAClB,EAAE,KAAK,EAAE,OAAO,EAAE;YAClB,EAAE,KAAK,EAAE,YAAY,EAAE;SACxB;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,IAAI,EAAE,yBAAyB;QAC/B,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;KAC1E;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,EAAE,EAAE;CACjD,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,IAAI,EAAE;QACJ,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;KACpD;CACF,CAAC"}
@@ -0,0 +1,20 @@
1
+ export interface PickerEmptyGuidanceProps {
2
+ /** Display label for the missing category, e.g. "Spirits · Brandy". */
3
+ categoryLabel: string;
4
+ /** CTA label for the create-new button, e.g. "Create a new brandy product". */
5
+ createLabel: string;
6
+ /** Skip CTA label. @default "Draft manually" */
7
+ skipLabel?: string;
8
+ /** Override the body copy (defaults to a category-aware message). */
9
+ body?: string;
10
+ onCreate: () => void;
11
+ onSkip: () => void;
12
+ className?: string;
13
+ }
14
+ /**
15
+ * Empty-state block shown inside the picker when the producer has no
16
+ * in-category products. Create-new CTA is elevated (primary); skip-to-manual
17
+ * is secondary. Host wires both handlers to its own navigation / mutation.
18
+ */
19
+ export declare function PickerEmptyGuidance({ categoryLabel, createLabel, skipLabel, body, onCreate, onSkip, className, }: PickerEmptyGuidanceProps): import("react/jsx-runtime").JSX.Element;
20
+ //# sourceMappingURL=picker-empty-guidance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker-empty-guidance.d.ts","sourceRoot":"","sources":["../../src/components/picker-empty-guidance.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,wBAAwB;IACvC,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,WAAW,EACX,SAA4B,EAC5B,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE,wBAAwB,2CA6B1B"}
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowRight, Plus } from "lucide-react";
3
+ import { cn } from "../lib/utils";
4
+ /**
5
+ * Empty-state block shown inside the picker when the producer has no
6
+ * in-category products. Create-new CTA is elevated (primary); skip-to-manual
7
+ * is secondary. Host wires both handlers to its own navigation / mutation.
8
+ */
9
+ export function PickerEmptyGuidance({ categoryLabel, createLabel, skipLabel = "Draft manually", body, onCreate, onSkip, className, }) {
10
+ const message = body
11
+ ?? `Your portfolio has no ${categoryLabel}. To respond to this opportunity you can either add a matching product or draft the offer manually.`;
12
+ return (_jsxs("div", { className: cn("flex flex-col gap-2", className), children: [_jsx("div", { className: "rounded-lg border border-dashed border-warning/60 bg-warning/10 px-3 py-2.5 text-[11px]/5 text-warning-foreground", children: message }), _jsxs("div", { className: "flex flex-col gap-2 sm:flex-row", children: [_jsxs("button", { type: "button", onClick: onCreate, className: "inline-flex flex-1 items-center justify-center gap-2 rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground transition-[filter] hover:brightness-110 focus:outline-none focus:ring-2 focus:ring-primary/40", children: [_jsx(Plus, { className: "size-3.5", "aria-hidden": true }), createLabel] }), _jsxs("button", { type: "button", onClick: onSkip, className: "inline-flex flex-1 items-center justify-center gap-2 rounded-lg border border-border bg-background px-4 py-2.5 text-sm font-semibold text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus:ring-2 focus:ring-primary/40", children: [_jsx(ArrowRight, { className: "size-3.5", "aria-hidden": true }), skipLabel] })] })] }));
13
+ }
14
+ //# sourceMappingURL=picker-empty-guidance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker-empty-guidance.js","sourceRoot":"","sources":["../../src/components/picker-empty-guidance.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAgBlC;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,aAAa,EACb,WAAW,EACX,SAAS,GAAG,gBAAgB,EAC5B,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,SAAS,GACgB;IACzB,MAAM,OAAO,GAAG,IAAI;WACf,yBAAyB,aAAa,qGAAqG,CAAC;IAEjJ,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC,aAClD,cAAK,SAAS,EAAC,mHAAmH,YAC/H,OAAO,GACJ,EACN,eAAK,SAAS,EAAC,iCAAiC,aAC9C,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAC,qOAAqO,aAE/O,KAAC,IAAI,IAAC,SAAS,EAAC,UAAU,wBAAe,EACxC,WAAW,IACL,EACT,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAC,0PAA0P,aAEpQ,KAAC,UAAU,IAAC,SAAS,EAAC,UAAU,wBAAe,EAC9C,SAAS,IACH,IACL,IACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { PickerEmptyGuidance } from "./picker-empty-guidance";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof PickerEmptyGuidance;
6
+ tags: string[];
7
+ parameters: {
8
+ layout: string;
9
+ };
10
+ args: {
11
+ onCreate: import("storybook/test").Mock<(...args: any[]) => any>;
12
+ onSkip: import("storybook/test").Mock<(...args: any[]) => any>;
13
+ };
14
+ };
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+ export declare const NoBrandy: Story;
18
+ export declare const NoWine: Story;
19
+ export declare const CustomBody: Story;
20
+ //# sourceMappingURL=picker-empty-guidance.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker-empty-guidance.stories.d.ts","sourceRoot":"","sources":["../../src/components/picker-empty-guidance.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,QAAA,MAAM,IAAI;;;;;;;;;;;CASkC,CAAC;AAE7C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,QAAQ,EAAE,KAKtB,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,KAKpB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { fn } from "storybook/test";
2
+ import { PickerEmptyGuidance } from "./picker-empty-guidance";
3
+ const meta = {
4
+ title: "Picker/PickerEmptyGuidance",
5
+ component: PickerEmptyGuidance,
6
+ tags: ["autodocs"],
7
+ parameters: { layout: "centered" },
8
+ args: {
9
+ onCreate: fn(),
10
+ onSkip: fn(),
11
+ },
12
+ };
13
+ export default meta;
14
+ export const NoBrandy = {
15
+ args: {
16
+ categoryLabel: "Spirits · Brandy",
17
+ createLabel: "Create a new brandy product",
18
+ },
19
+ };
20
+ export const NoWine = {
21
+ args: {
22
+ categoryLabel: "Wine",
23
+ createLabel: "Create a new wine product",
24
+ },
25
+ };
26
+ export const CustomBody = {
27
+ args: {
28
+ categoryLabel: "Cider · Perry",
29
+ createLabel: "Add a perry",
30
+ body: "No perry in your portfolio yet. Add one now so the opportunity's specifications pre-fill the new product page.",
31
+ },
32
+ };
33
+ //# sourceMappingURL=picker-empty-guidance.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"picker-empty-guidance.stories.js","sourceRoot":"","sources":["../../src/components/picker-empty-guidance.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,4BAA4B;IACnC,SAAS,EAAE,mBAAmB;IAC9B,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE;IAClC,IAAI,EAAE;QACJ,QAAQ,EAAE,EAAE,EAAE;QACd,MAAM,EAAE,EAAE,EAAE;KACb;CACyC,CAAC;AAE7C,eAAe,IAAI,CAAC;AAGpB,MAAM,CAAC,MAAM,QAAQ,GAAU;IAC7B,IAAI,EAAE;QACJ,aAAa,EAAE,kBAAkB;QACjC,WAAW,EAAE,6BAA6B;KAC3C;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAAU;IAC3B,IAAI,EAAE;QACJ,aAAa,EAAE,MAAM;QACrB,WAAW,EAAE,2BAA2B;KACzC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAU;IAC/B,IAAI,EAAE;QACJ,aAAa,EAAE,eAAe;QAC9B,WAAW,EAAE,aAAa;QAC1B,IAAI,EAAE,gHAAgH;KACvH;CACF,CAAC"}
@@ -0,0 +1,17 @@
1
+ export type ReasonPillKind = "ok" | "warn" | "miss";
2
+ export type ReasonPillSignal = "subtype" | "country" | "region" | "capacity" | "price";
3
+ export interface ReasonPillProps {
4
+ kind: ReasonPillKind;
5
+ signal: ReasonPillSignal;
6
+ label: string;
7
+ className?: string;
8
+ }
9
+ /**
10
+ * Tiny pill that communicates why a variant scored the way it did. Used
11
+ * inside the product picker, stacked under each ranked row.
12
+ *
13
+ * `ok` → check icon + brand tint. `warn` → `≈` glyph + amber tint (lucide
14
+ * has no almost-equal icon). `miss` → X icon + struck-through muted tint.
15
+ */
16
+ export declare function ReasonPill({ kind, signal, label, className }: ReasonPillProps): import("react/jsx-runtime").JSX.Element;
17
+ //# sourceMappingURL=reason-pill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reason-pill.d.ts","sourceRoot":"","sources":["../../src/components/reason-pill.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AACpD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,eAAe,2CAqB7E"}
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Check, X } from "lucide-react";
3
+ import { cn } from "../lib/utils";
4
+ const kindClasses = {
5
+ ok: "bg-primary/15 text-primary",
6
+ warn: "bg-amber-500/20 text-amber-900 dark:bg-amber-400/15 dark:text-amber-300",
7
+ // strike-through + muted so "miss" reads as a negative at a glance
8
+ miss: "bg-muted text-muted-foreground line-through",
9
+ };
10
+ /**
11
+ * Tiny pill that communicates why a variant scored the way it did. Used
12
+ * inside the product picker, stacked under each ranked row.
13
+ *
14
+ * `ok` → check icon + brand tint. `warn` → `≈` glyph + amber tint (lucide
15
+ * has no almost-equal icon). `miss` → X icon + struck-through muted tint.
16
+ */
17
+ export function ReasonPill({ kind, signal, label, className }) {
18
+ const ariaLabel = kind === "miss" ? `${signal} mismatch: ${label}` :
19
+ kind === "warn" ? `${signal} partial match: ${label}` :
20
+ `${signal} match: ${label}`;
21
+ return (_jsxs("span", { className: cn("inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[10px] font-semibold leading-none", kindClasses[kind], className), "aria-label": ariaLabel, children: [kind === "ok" && _jsx(Check, { className: "size-2.5", "aria-hidden": true }), kind === "miss" && _jsx(X, { className: "size-2.5", "aria-hidden": true }), kind === "warn" && _jsx("span", { "aria-hidden": true, children: "\u2248" }), _jsx("span", { "aria-hidden": true, children: label })] }));
22
+ }
23
+ //# sourceMappingURL=reason-pill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reason-pill.js","sourceRoot":"","sources":["../../src/components/reason-pill.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAYlC,MAAM,WAAW,GAAmC;IAClD,EAAE,EAAE,4BAA4B;IAChC,IAAI,EAAE,yEAAyE;IAC/E,mEAAmE;IACnE,IAAI,EAAE,6CAA6C;CACpD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAmB;IAC5E,MAAM,SAAS,GACb,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,cAAc,KAAK,EAAE,CAAC,CAAC;QAClD,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,mBAAmB,KAAK,EAAE,CAAC,CAAC;YACvD,GAAG,MAAM,WAAW,KAAK,EAAE,CAAC;IAE9B,OAAO,CACL,gBACE,SAAS,EAAE,EAAE,CACX,kGAAkG,EAClG,WAAW,CAAC,IAAI,CAAC,EACjB,SAAS,CACV,gBACW,SAAS,aAEpB,IAAI,KAAK,IAAI,IAAI,KAAC,KAAK,IAAC,SAAS,EAAC,UAAU,wBAAe,EAC3D,IAAI,KAAK,MAAM,IAAI,KAAC,CAAC,IAAC,SAAS,EAAC,UAAU,wBAAe,EACzD,IAAI,KAAK,MAAM,IAAI,yDAA0B,EAC9C,8CAAmB,KAAK,GAAQ,IAC3B,CACR,CAAC;AACJ,CAAC"}