@cellarnode/ui 0.8.19 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/branch-overview-panel.d.ts +51 -0
- package/dist/components/branch-overview-panel.d.ts.map +1 -0
- package/dist/components/branch-overview-panel.js +48 -0
- package/dist/components/branch-overview-panel.js.map +1 -0
- package/dist/components/branch-overview-panel.stories.d.ts +16 -0
- package/dist/components/branch-overview-panel.stories.d.ts.map +1 -0
- package/dist/components/branch-overview-panel.stories.js +96 -0
- package/dist/components/branch-overview-panel.stories.js.map +1 -0
- package/dist/components/branch-resolution-modal.d.ts +31 -0
- package/dist/components/branch-resolution-modal.d.ts.map +1 -0
- package/dist/components/branch-resolution-modal.js +35 -0
- package/dist/components/branch-resolution-modal.js.map +1 -0
- package/dist/components/branch-resolution-modal.stories.d.ts +16 -0
- package/dist/components/branch-resolution-modal.stories.d.ts.map +1 -0
- package/dist/components/branch-resolution-modal.stories.js +66 -0
- package/dist/components/branch-resolution-modal.stories.js.map +1 -0
- package/dist/components/branch-resolution-modal.test.d.ts +2 -0
- package/dist/components/branch-resolution-modal.test.d.ts.map +1 -0
- package/dist/components/branch-resolution-modal.test.js +52 -0
- package/dist/components/branch-resolution-modal.test.js.map +1 -0
- package/dist/components/celebration-pulse.d.ts +21 -0
- package/dist/components/celebration-pulse.d.ts.map +1 -0
- package/dist/components/celebration-pulse.js +16 -0
- package/dist/components/celebration-pulse.js.map +1 -0
- package/dist/components/celebration-pulse.stories.d.ts +16 -0
- package/dist/components/celebration-pulse.stories.d.ts.map +1 -0
- package/dist/components/celebration-pulse.stories.js +29 -0
- package/dist/components/celebration-pulse.stories.js.map +1 -0
- package/dist/components/completed-offer-card.d.ts +34 -0
- package/dist/components/completed-offer-card.d.ts.map +1 -0
- package/dist/components/completed-offer-card.js +20 -0
- package/dist/components/completed-offer-card.js.map +1 -0
- package/dist/components/completed-offer-card.stories.d.ts +17 -0
- package/dist/components/completed-offer-card.stories.d.ts.map +1 -0
- package/dist/components/completed-offer-card.stories.js +75 -0
- package/dist/components/completed-offer-card.stories.js.map +1 -0
- package/dist/components/importer-picker-modal.d.ts +50 -0
- package/dist/components/importer-picker-modal.d.ts.map +1 -0
- package/dist/components/importer-picker-modal.js +82 -0
- package/dist/components/importer-picker-modal.js.map +1 -0
- package/dist/components/importer-picker-modal.stories.d.ts +17 -0
- package/dist/components/importer-picker-modal.stories.d.ts.map +1 -0
- package/dist/components/importer-picker-modal.stories.js +62 -0
- package/dist/components/importer-picker-modal.stories.js.map +1 -0
- package/dist/components/importer-picker-modal.test.d.ts +2 -0
- package/dist/components/importer-picker-modal.test.d.ts.map +1 -0
- package/dist/components/importer-picker-modal.test.js +51 -0
- package/dist/components/importer-picker-modal.test.js.map +1 -0
- package/dist/components/kanban-board.d.ts +19 -0
- package/dist/components/kanban-board.d.ts.map +1 -1
- package/dist/components/kanban-board.js.map +1 -1
- package/dist/components/kanban-dnd-board.d.ts +1 -1
- package/dist/components/kanban-dnd-board.d.ts.map +1 -1
- package/dist/components/kanban-dnd-board.js +52 -7
- package/dist/components/kanban-dnd-board.js.map +1 -1
- package/dist/components/kanban-dnd-board.stories.d.ts +1 -0
- package/dist/components/kanban-dnd-board.stories.d.ts.map +1 -1
- package/dist/components/kanban-dnd-board.stories.js +66 -0
- package/dist/components/kanban-dnd-board.stories.js.map +1 -1
- package/dist/components/kanban-dnd-board.test.d.ts +2 -0
- package/dist/components/kanban-dnd-board.test.d.ts.map +1 -0
- package/dist/components/kanban-dnd-board.test.js +34 -0
- package/dist/components/kanban-dnd-board.test.js.map +1 -0
- package/dist/components/offer-status-dots.d.ts +23 -0
- package/dist/components/offer-status-dots.d.ts.map +1 -0
- package/dist/components/offer-status-dots.js +34 -0
- package/dist/components/offer-status-dots.js.map +1 -0
- package/dist/components/offer-status-dots.stories.d.ts +19 -0
- package/dist/components/offer-status-dots.stories.d.ts.map +1 -0
- package/dist/components/offer-status-dots.stories.js +49 -0
- package/dist/components/offer-status-dots.stories.js.map +1 -0
- package/dist/components/sent-offer-card.d.ts +31 -0
- package/dist/components/sent-offer-card.d.ts.map +1 -0
- package/dist/components/sent-offer-card.js +23 -0
- package/dist/components/sent-offer-card.js.map +1 -0
- package/dist/components/sent-offer-card.stories.d.ts +17 -0
- package/dist/components/sent-offer-card.stories.d.ts.map +1 -0
- package/dist/components/sent-offer-card.stories.js +51 -0
- package/dist/components/sent-offer-card.stories.js.map +1 -0
- package/dist/hooks/use-celebration-pulse.d.ts +12 -0
- package/dist/hooks/use-celebration-pulse.d.ts.map +1 -0
- package/dist/hooks/use-celebration-pulse.js +28 -0
- package/dist/hooks/use-celebration-pulse.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/kanban-transitions.d.ts +62 -0
- package/dist/lib/kanban-transitions.d.ts.map +1 -1
- package/dist/lib/kanban-transitions.js +32 -0
- package/dist/lib/kanban-transitions.js.map +1 -1
- package/dist/lib/kanban-transitions.test.d.ts +2 -0
- package/dist/lib/kanban-transitions.test.d.ts.map +1 -0
- package/dist/lib/kanban-transitions.test.js +75 -0
- package/dist/lib/kanban-transitions.test.js.map +1 -0
- package/dist/offer/index.d.ts +3 -0
- package/dist/offer/index.d.ts.map +1 -1
- package/dist/offer/index.js +7 -0
- package/dist/offer/index.js.map +1 -1
- package/dist/offer/sensory-profile.d.ts.map +1 -1
- package/dist/offer/sensory-profile.js +1 -1
- package/dist/offer/sensory-profile.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import { DragDropProvider, DragOverlay, useDragOperation, useDroppable, } from "
|
|
|
8
8
|
import { cn } from "../lib/utils";
|
|
9
9
|
import { KanbanColumnShell } from "./kanban-board";
|
|
10
10
|
// ─── DnD Board ───────────────────────────────────────────────────────────────
|
|
11
|
-
function KanbanDndBoard({ columns, renderColumnCards, className, items, getItemId, getItemColumn, onDragEnd, onReorder, onDragStartRaw, onDragEndRaw, isValidTransition, renderDragOverlay, renderColumn, columnClassName, columnBodyClassName, }) {
|
|
11
|
+
function KanbanDndBoard({ columns, renderColumnCards, className, items, getItemId, getItemColumn, onDragEnd, onReorder, onDragStartRaw, onDragEndRaw, isValidTransition, renderDragOverlay, renderColumn, columnClassName, columnBodyClassName, gateRules, onGateResult, }) {
|
|
12
12
|
const [dragSourceColumn, setDragSourceColumn] = useState(null);
|
|
13
13
|
const handleDragStart = useCallback((event) => {
|
|
14
14
|
onDragStartRaw?.(event);
|
|
@@ -64,16 +64,61 @@ function KanbanDndBoard({ columns, renderColumnCards, className, items, getItemI
|
|
|
64
64
|
}
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
|
+
// ── NEW: Gate dispatch path ─────────────────────────────────────
|
|
68
|
+
if (gateRules) {
|
|
69
|
+
const fromRow = gateRules[sourceColumnId];
|
|
70
|
+
const result = fromRow?.[targetColumnId] ?? { kind: "blocked" };
|
|
71
|
+
if (onGateResult) {
|
|
72
|
+
onGateResult(result, sourceColumnId, targetColumnId, sourceItemId);
|
|
73
|
+
}
|
|
74
|
+
// The consumer is responsible for calling the move handler from
|
|
75
|
+
// their onGateResult dispatcher. We do NOT call onDragEnd from
|
|
76
|
+
// here when gateRules is provided.
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// ── Existing path: boolean isValidTransition ────────────────────
|
|
67
80
|
if (isValidTransition && !isValidTransition(sourceColumnId, targetColumnId))
|
|
68
81
|
return;
|
|
69
82
|
onDragEnd?.(sourceItemId, sourceColumnId, targetColumnId);
|
|
70
|
-
}, [
|
|
83
|
+
}, [
|
|
84
|
+
dragSourceColumn,
|
|
85
|
+
items,
|
|
86
|
+
getItemId,
|
|
87
|
+
getItemColumn,
|
|
88
|
+
isValidTransition,
|
|
89
|
+
onDragEnd,
|
|
90
|
+
onReorder,
|
|
91
|
+
onDragEndRaw,
|
|
92
|
+
gateRules,
|
|
93
|
+
onGateResult,
|
|
94
|
+
]);
|
|
71
95
|
return (_jsxs(DragDropProvider, { onDragStart: handleDragStart, onDragEnd: handleDragEnd, children: [_jsx("div", { className: cn("grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4", className), children: columns.map((col) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
let isValid;
|
|
97
|
+
let isInvalid;
|
|
98
|
+
if (gateRules) {
|
|
99
|
+
// Use gateRules for visual feedback during drag
|
|
100
|
+
if (dragSourceColumn === null) {
|
|
101
|
+
isValid = false;
|
|
102
|
+
isInvalid = false;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const fromRow = gateRules[dragSourceColumn];
|
|
106
|
+
const result = fromRow?.[col.id];
|
|
107
|
+
// "allowed", "hard-gate", "modal-gate" are all valid drop targets visually
|
|
108
|
+
// "blocked" or missing is invalid
|
|
109
|
+
isValid = result !== undefined && result.kind !== "blocked";
|
|
110
|
+
isInvalid = !isValid && dragSourceColumn !== col.id;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Existing path
|
|
115
|
+
isValid =
|
|
116
|
+
dragSourceColumn !== null &&
|
|
117
|
+
(isValidTransition
|
|
118
|
+
? isValidTransition(dragSourceColumn, col.id)
|
|
119
|
+
: dragSourceColumn !== col.id);
|
|
120
|
+
isInvalid = dragSourceColumn !== null && !isValid;
|
|
121
|
+
}
|
|
77
122
|
return (_jsx(DroppableColumn, { column: col, disabled: isInvalid, isValidTarget: isValid, isInvalidTarget: isInvalid, isDragging: dragSourceColumn !== null, renderColumn: renderColumn, columnClassName: columnClassName, bodyClassName: columnBodyClassName, children: renderColumnCards(col.id) }, col.id));
|
|
78
123
|
}) }), renderDragOverlay && items && getItemId && (_jsx(DndOverlay, { items: items, getItemId: getItemId, renderDragOverlay: renderDragOverlay }))] }));
|
|
79
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kanban-dnd-board.js","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.tsx"],"names":[],"mappings":";AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAkB,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"kanban-dnd-board.js","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kanban-dnd-board.stories.d.ts","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,cAAc,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"kanban-dnd-board.stories.d.ts","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAKhD,QAAA,MAAM,IAAI;;;;;;;CAK6B,CAAC;AAExC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAkHnC,eAAO,MAAM,OAAO,EAAE,KAGrB,CAAC;AAsDF,eAAO,MAAM,mBAAmB,EAAE,KAGjC,CAAC;AAsGF,eAAO,MAAM,aAAa,EAAE,KAG3B,CAAC"}
|
|
@@ -109,4 +109,70 @@ export const WithTransitionRules = {
|
|
|
109
109
|
args: stubArgs,
|
|
110
110
|
render: () => _jsx(WithTransitionRulesRender, {}),
|
|
111
111
|
};
|
|
112
|
+
function WithGateRulesRender() {
|
|
113
|
+
const [items, setItems] = useState([
|
|
114
|
+
{ id: "i1", column: "drafting", title: "Draft 1 (complete)", missingFields: 0 },
|
|
115
|
+
{ id: "i2", column: "drafting", title: "Draft 2 (incomplete)", missingFields: 5 },
|
|
116
|
+
{ id: "i3", column: "ready", title: "Ready item", missingFields: 0 },
|
|
117
|
+
]);
|
|
118
|
+
const [lastDispatch, setLastDispatch] = useState("none");
|
|
119
|
+
const gateRules = {
|
|
120
|
+
drafting: {
|
|
121
|
+
ready: {
|
|
122
|
+
kind: "hard-gate",
|
|
123
|
+
check: (item) => item.missingFields === 0,
|
|
124
|
+
failMessage: "Missing fields",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
ready: {
|
|
128
|
+
sent: { kind: "modal-gate", openModalKey: "importer-picker" },
|
|
129
|
+
drafting: { kind: "allowed" },
|
|
130
|
+
},
|
|
131
|
+
sent: {
|
|
132
|
+
completed: { kind: "blocked" },
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
const gateColumns = [
|
|
136
|
+
{ id: "drafting", title: "Drafting", count: items.filter((i) => i.column === "drafting").length },
|
|
137
|
+
{ id: "ready", title: "Ready", count: items.filter((i) => i.column === "ready").length },
|
|
138
|
+
{ id: "sent", title: "Sent", count: items.filter((i) => i.column === "sent").length },
|
|
139
|
+
{ id: "completed", title: "Completed", count: items.filter((i) => i.column === "completed").length },
|
|
140
|
+
];
|
|
141
|
+
const handleGateResult = (result, from, to, itemId) => {
|
|
142
|
+
const item = items.find((i) => i.id === itemId);
|
|
143
|
+
if (!item)
|
|
144
|
+
return;
|
|
145
|
+
switch (result.kind) {
|
|
146
|
+
case "allowed":
|
|
147
|
+
setItems((prev) => prev.map((i) => (i.id === itemId ? { ...i, column: to } : i)));
|
|
148
|
+
setLastDispatch(`Allowed: ${from} → ${to}`);
|
|
149
|
+
break;
|
|
150
|
+
case "blocked":
|
|
151
|
+
setLastDispatch(`Blocked: ${from} → ${to}${result.reason ? ` (${result.reason})` : ""}`);
|
|
152
|
+
break;
|
|
153
|
+
case "hard-gate":
|
|
154
|
+
if (result.check(item)) {
|
|
155
|
+
setItems((prev) => prev.map((i) => (i.id === itemId ? { ...i, column: to } : i)));
|
|
156
|
+
setLastDispatch(`Hard gate passed: ${from} → ${to}`);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
setLastDispatch(`Hard gate failed: ${result.failMessage}`);
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
case "modal-gate":
|
|
163
|
+
// In a real consumer, you'd open the modal and call setItems on confirm.
|
|
164
|
+
// For this story, we simulate auto-confirm:
|
|
165
|
+
setItems((prev) => prev.map((i) => (i.id === itemId ? { ...i, column: to } : i)));
|
|
166
|
+
setLastDispatch(`Modal opened: ${result.openModalKey} (auto-confirmed)`);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "rounded-md bg-muted/50 px-3 py-2 text-xs", children: ["Last dispatch: ", _jsx("span", { className: "font-mono", children: lastDispatch })] }), _jsx(KanbanDndBoard, { columns: gateColumns, renderColumnCards: (colId) => items
|
|
171
|
+
.filter((i) => i.column === colId)
|
|
172
|
+
.map((item) => (_jsxs(KanbanCard, { children: [item.title, item.missingFields > 0 && (_jsxs("div", { className: "mt-1 text-xs text-amber-600", children: [item.missingFields, " missing fields"] }))] }, item.id))), items: items, getItemId: (i) => i.id, getItemColumn: (i) => i.column, gateRules: gateRules, onGateResult: handleGateResult })] }));
|
|
173
|
+
}
|
|
174
|
+
export const WithGateRules = {
|
|
175
|
+
args: stubArgs,
|
|
176
|
+
render: () => _jsx(WithGateRulesRender, {}),
|
|
177
|
+
};
|
|
112
178
|
//# sourceMappingURL=kanban-dnd-board.stories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kanban-dnd-board.stories.js","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"kanban-dnd-board.stories.js","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,cAAc,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,2BAA2B;IAClC,SAAS,EAAE,cAAc;IACzB,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;CACK,CAAC;AAExC,eAAe,IAAI,CAAC;AAapB,MAAM,OAAO,GAAmB;IAC9B;QACE,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,CAAC;QACR,WAAW,EAAE,YAAY;QACzB,eAAe,EAAE,6BAA6B;QAC9C,qBAAqB,EAAE,sDAAsD;QAC7E,cAAc,EACZ,uGAAuG;KAC1G;IACD;QACE,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,aAAa;QACpB,QAAQ,EAAE,eAAe;QACzB,KAAK,EAAE,CAAC;QACR,WAAW,EAAE,cAAc;QAC3B,eAAe,EAAE,iCAAiC;QAClD,qBAAqB,EAAE,0DAA0D;QACjF,cAAc,EACZ,mHAAmH;KACtH;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,mBAAmB;QAC7B,KAAK,EAAE,CAAC;QACR,WAAW,EAAE,gBAAgB;QAC7B,eAAe,EAAE,qCAAqC;QACtD,qBAAqB,EAAE,8DAA8D;QACrF,cAAc,EACZ,+HAA+H;KAClI;IACD;QACE,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,QAAQ;QACf,QAAQ,EAAE,uBAAuB;QACjC,KAAK,EAAE,CAAC;QACR,WAAW,EAAE,cAAc;QAC3B,eAAe,EAAE,iCAAiC;QAClD,qBAAqB,EAAE,0DAA0D;QACjF,cAAc,EACZ,mHAAmH;KACtH;CACF,CAAC;AAEF,MAAM,YAAY,GAAgB;IAChC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE;IAC5G,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE;IACnG,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,QAAQ,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE;IAC1H,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,wBAAwB,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE;IAC7H,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,kBAAkB,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE;IAC5G,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;CACzG,CAAC;AAEF,0EAA0E;AAC1E,MAAM,QAAQ,GAAG;IACf,OAAO;IACP,iBAAiB,EAAE,GAAG,EAAE,CAAC,IAAI;CAC9B,CAAC;AAEF,iFAAiF;AAEjF,SAAS,aAAa;IACpB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc,YAAY,CAAC,CAAC;IAE9D,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1C,GAAG,GAAG;QACN,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM;KACvD,CAAC,CAAC,CAAC;IAEJ,SAAS,iBAAiB,CAAC,QAAgB;QACzC,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;aAC1C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACb,KAAC,UAAU,cACT,eAAK,SAAS,EAAC,uBAAuB,aACpC,YAAG,SAAS,EAAC,uCAAuC,YAAE,IAAI,CAAC,KAAK,GAAK,EACrE,YAAG,SAAS,EAAC,+BAA+B,YAAE,IAAI,CAAC,QAAQ,GAAK,EAChE,eAAM,SAAS,EAAC,8CAA8C,YAAE,IAAI,CAAC,KAAK,GAAQ,IAC9E,IALS,IAAI,CAAC,EAAE,CAMX,CACd,CAAC,CAAC;IACP,CAAC;IAED,OAAO,CACL,KAAC,cAAc,IACb,OAAO,EAAE,aAAa,EACtB,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EACpC,SAAS,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;YAC/B,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAChB,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAC1E,CAAC;QACJ,CAAC,GACD,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,aAAa,KAAG;CAChC,CAAC;AAEF,iFAAiF;AAEjF,SAAS,yBAAyB;IAChC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc,YAAY,CAAC,CAAC;IAE9D,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1C,GAAG,GAAG;QACN,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM;KACvD,CAAC,CAAC,CAAC;IAEJ,+DAA+D;IAC/D,MAAM,iBAAiB,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,EAAE;QACrD,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC3D,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,CAAC,CAAC;IAEF,SAAS,iBAAiB,CAAC,QAAgB;QACzC,OAAO,KAAK;aACT,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;aAC1C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACb,KAAC,UAAU,cACT,eAAK,SAAS,EAAC,uBAAuB,aACpC,YAAG,SAAS,EAAC,uCAAuC,YAAE,IAAI,CAAC,KAAK,GAAK,EACrE,YAAG,SAAS,EAAC,+BAA+B,YAAE,IAAI,CAAC,QAAQ,GAAK,EAChE,eAAM,SAAS,EAAC,8CAA8C,YAAE,IAAI,CAAC,KAAK,GAAQ,IAC9E,IALS,IAAI,CAAC,EAAE,CAMX,CACd,CAAC,CAAC;IACP,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,qBAAqB,aAClC,YAAG,SAAS,EAAC,+BAA+B,kHAExC,EACJ,KAAC,cAAc,IACb,OAAO,EAAE,aAAa,EACtB,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAC5B,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EACpC,iBAAiB,EAAE,iBAAiB,EACpC,SAAS,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;oBAC/B,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAChB,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAC1E,CAAC;gBACJ,CAAC,GACD,IACE,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAU;IACxC,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,yBAAyB,KAAG;CAC5C,CAAC;AAMF,SAAS,mBAAmB;IAC1B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAa;QAC7C,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC,EAAE;QAC/E,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,CAAC,EAAE;QACjF,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,EAAE;KACrE,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAS,MAAM,CAAC,CAAC;IAEjE,MAAM,SAAS,GAAwB;QACrC,QAAQ,EAAE;YACR,KAAK,EAAE;gBACL,IAAI,EAAE,WAAW;gBACjB,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC;gBACzC,WAAW,EAAE,gBAAgB;aAC9B;SACF;QACD,KAAK,EAAE;YACL,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE;YAC7D,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAC9B;QACD,IAAI,EAAE;YACJ,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAC/B;KACF,CAAC;IAEF,MAAM,WAAW,GAAmB;QAClC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,MAAM,EAAE;QACjG,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,MAAM,EAAE;QACxF,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,EAAE;QACrF,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM,EAAE;KACrG,CAAC;IAEF,MAAM,gBAAgB,GAAG,CACvB,MAA4B,EAC5B,IAAY,EACZ,EAAU,EACV,MAAc,EACd,EAAE;QACF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI;YAAE,OAAO;QAElB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,SAAS;gBACZ,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAClF,eAAe,CAAC,YAAY,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC5C,MAAM;YACR,KAAK,SAAS;gBACZ,eAAe,CAAC,YAAY,IAAI,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACzF,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;oBACvB,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBAClF,eAAe,CAAC,qBAAqB,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;gBACvD,CAAC;qBAAM,CAAC;oBACN,eAAe,CAAC,qBAAqB,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;gBAC7D,CAAC;gBACD,MAAM;YACR,KAAK,YAAY;gBACf,yEAAyE;gBACzE,4CAA4C;gBAC5C,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAClF,eAAe,CAAC,iBAAiB,MAAM,CAAC,YAAY,mBAAmB,CAAC,CAAC;gBACzE,MAAM;QACV,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,aACxB,eAAK,SAAS,EAAC,0CAA0C,gCACxC,eAAM,SAAS,EAAC,WAAW,YAAE,YAAY,GAAQ,IAC5D,EACN,KAAC,cAAc,IACb,OAAO,EAAE,WAAW,EACpB,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAC3B,KAAK;qBACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC;qBACjC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACb,MAAC,UAAU,eACR,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,aAAa,GAAG,CAAC,IAAI,CACzB,eAAK,SAAS,EAAC,6BAA6B,aAAE,IAAI,CAAC,aAAa,uBAAsB,CACvF,KAJc,IAAI,CAAC,EAAE,CAKX,CACd,CAAC,EAEN,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EACtB,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EAC9B,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,gBAAgB,GAC9B,IACE,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,GAAG,EAAE,CAAC,KAAC,mBAAmB,KAAG;CACtC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-dnd-board.test.d.ts","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { createGatedTransitionValidator } from "../lib/kanban-transitions";
|
|
3
|
+
// We're not testing the DnD interaction itself (that's covered by Storybook
|
|
4
|
+
// browser tests). We're testing the gate-rules data structure that the
|
|
5
|
+
// component dispatches against, which is the highest-leverage testable
|
|
6
|
+
// surface for this change.
|
|
7
|
+
describe("KanbanDndBoard gate dispatch (logic)", () => {
|
|
8
|
+
const rules = {
|
|
9
|
+
drafting: {
|
|
10
|
+
ready: { kind: "hard-gate", check: (i) => i.missingFields === 0, failMessage: "Missing fields" },
|
|
11
|
+
interested: { kind: "allowed" },
|
|
12
|
+
},
|
|
13
|
+
ready: {
|
|
14
|
+
sent: { kind: "modal-gate", openModalKey: "importer-picker" },
|
|
15
|
+
drafting: { kind: "allowed" },
|
|
16
|
+
},
|
|
17
|
+
sent: {
|
|
18
|
+
completed: { kind: "blocked", reason: "automatic-only" },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
test("validator returns the correct gate kind for each transition", () => {
|
|
22
|
+
const v = createGatedTransitionValidator(rules);
|
|
23
|
+
const completeItem = { id: "1", missingFields: 0 };
|
|
24
|
+
expect(v.check("drafting", "ready", completeItem).kind).toBe("hard-gate");
|
|
25
|
+
expect(v.check("ready", "sent", completeItem).kind).toBe("modal-gate");
|
|
26
|
+
expect(v.check("sent", "completed", completeItem).kind).toBe("blocked");
|
|
27
|
+
expect(v.check("ready", "drafting", completeItem).kind).toBe("allowed");
|
|
28
|
+
});
|
|
29
|
+
test("missing transition defaults to blocked", () => {
|
|
30
|
+
const v = createGatedTransitionValidator(rules);
|
|
31
|
+
expect(v.check("drafting", "completed", { id: "1", missingFields: 0 }).kind).toBe("blocked");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=kanban-dnd-board.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-dnd-board.test.js","sourceRoot":"","sources":["../../src/components/kanban-dnd-board.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,8BAA8B,EAAkB,MAAM,2BAA2B,CAAC;AAE3F,4EAA4E;AAC5E,uEAAuE;AACvE,uEAAuE;AACvE,2BAA2B;AAE3B,QAAQ,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAGpD,MAAM,KAAK,GAAoB;QAC7B,QAAQ,EAAE;YACR,KAAK,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE;YAChG,UAAU,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAChC;QACD,KAAK,EAAE;YACL,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE;YAC7D,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAC9B;QACD,IAAI,EAAE;YACJ,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE;SACzD;KACF,CAAC;IAEF,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,GAAG,8BAA8B,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,YAAY,GAAS,EAAE,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QAEzD,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1E,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACvE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,GAAG,8BAA8B,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One of the recipient statuses a child offer can be in. Mirrors
|
|
3
|
+
* `offer_recipient_status` enum on the backend, plus the synthetic
|
|
4
|
+
* "finalized" state used briefly during the post-acceptance pulse.
|
|
5
|
+
*/
|
|
6
|
+
export type OfferStatusDotState = "pending" | "viewed" | "in_negotiation" | "declined" | "rejected" | "accepted" | "finalized";
|
|
7
|
+
export interface OfferStatusDotsProps {
|
|
8
|
+
/** One state per offer sent — order matches the importer order. */
|
|
9
|
+
states: OfferStatusDotState[];
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Show a count after the dots. */
|
|
12
|
+
showCount?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Compact row of colored dots representing the status of each offer
|
|
16
|
+
* sent to importers. Used on the SentOfferCard auto-summary line.
|
|
17
|
+
*
|
|
18
|
+
* Each dot has an aria-label describing its state. The component
|
|
19
|
+
* gracefully handles 0 states (renders nothing) and large counts
|
|
20
|
+
* (caller decides how many to show).
|
|
21
|
+
*/
|
|
22
|
+
export declare function OfferStatusDots({ states, className, showCount, }: OfferStatusDotsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
23
|
+
//# sourceMappingURL=offer-status-dots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-status-dots.d.ts","sourceRoot":"","sources":["../../src/components/offer-status-dots.tsx"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,QAAQ,GACR,gBAAgB,GAChB,UAAU,GACV,UAAU,GACV,UAAU,GACV,WAAW,CAAC;AAsBhB,MAAM,WAAW,oBAAoB;IACnC,mEAAmE;IACnE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,SAAS,EACT,SAAiB,GAClB,EAAE,oBAAoB,kDA0BtB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
const STATE_CLASSES = {
|
|
4
|
+
pending: "bg-slate-300 dark:bg-slate-600",
|
|
5
|
+
viewed: "bg-blue-400 dark:bg-blue-500",
|
|
6
|
+
in_negotiation: "bg-amber-400 dark:bg-amber-500",
|
|
7
|
+
declined: "bg-red-400 dark:bg-red-500",
|
|
8
|
+
rejected: "bg-red-400 dark:bg-red-500",
|
|
9
|
+
accepted: "bg-emerald-400 dark:bg-emerald-500",
|
|
10
|
+
finalized: "bg-amber-300 dark:bg-amber-400 animate-pulse",
|
|
11
|
+
};
|
|
12
|
+
const STATE_LABELS = {
|
|
13
|
+
pending: "Awaiting response",
|
|
14
|
+
viewed: "Viewed",
|
|
15
|
+
in_negotiation: "In negotiation",
|
|
16
|
+
declined: "Declined",
|
|
17
|
+
rejected: "Rejected",
|
|
18
|
+
accepted: "Accepted",
|
|
19
|
+
finalized: "Finalized",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Compact row of colored dots representing the status of each offer
|
|
23
|
+
* sent to importers. Used on the SentOfferCard auto-summary line.
|
|
24
|
+
*
|
|
25
|
+
* Each dot has an aria-label describing its state. The component
|
|
26
|
+
* gracefully handles 0 states (renders nothing) and large counts
|
|
27
|
+
* (caller decides how many to show).
|
|
28
|
+
*/
|
|
29
|
+
export function OfferStatusDots({ states, className, showCount = false, }) {
|
|
30
|
+
if (states.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
return (_jsxs("div", { className: cn("inline-flex items-center gap-1", className), role: "group", "aria-label": "Offer statuses", children: [states.map((state, index) => (_jsx("span", { className: cn("inline-block size-2 shrink-0 rounded-full transition-colors duration-200", STATE_CLASSES[state]), "aria-label": STATE_LABELS[state], title: STATE_LABELS[state] }, `${state}-${index}`))), showCount && (_jsx("span", { className: "ml-1 text-xs text-muted-foreground", children: states.length }))] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=offer-status-dots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-status-dots.js","sourceRoot":"","sources":["../../src/components/offer-status-dots.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAgBlC,MAAM,aAAa,GAAwC;IACzD,OAAO,EAAE,gCAAgC;IACzC,MAAM,EAAE,8BAA8B;IACtC,cAAc,EAAE,gCAAgC;IAChD,QAAQ,EAAE,4BAA4B;IACtC,QAAQ,EAAE,4BAA4B;IACtC,QAAQ,EAAE,oCAAoC;IAC9C,SAAS,EAAE,8CAA8C;CAC1D,CAAC;AAEF,MAAM,YAAY,GAAwC;IACxD,OAAO,EAAE,mBAAmB;IAC5B,MAAM,EAAE,QAAQ;IAChB,cAAc,EAAE,gBAAgB;IAChC,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;CACvB,CAAC;AAUF;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,MAAM,EACN,SAAS,EACT,SAAS,GAAG,KAAK,GACI;IACrB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CAAC,gCAAgC,EAAE,SAAS,CAAC,EAC1D,IAAI,EAAC,OAAO,gBACD,gBAAgB,aAE1B,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAC5B,eAGE,SAAS,EAAE,EAAE,CACX,0EAA0E,EAC1E,aAAa,CAAC,KAAK,CAAC,CACrB,gBACW,YAAY,CAAC,KAAK,CAAC,EAC/B,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,IANrB,GAAG,KAAK,IAAI,KAAK,EAAE,CAOxB,CACH,CAAC,EACD,SAAS,IAAI,CACZ,eAAM,SAAS,EAAC,oCAAoC,YAAE,MAAM,CAAC,MAAM,GAAQ,CAC5E,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { OfferStatusDots } from "./offer-status-dots";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof OfferStatusDots;
|
|
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 SingleDot: Story;
|
|
15
|
+
export declare const ManyDots: Story;
|
|
16
|
+
export declare const AllStatesShowcase: Story;
|
|
17
|
+
export declare const Empty: Story;
|
|
18
|
+
export declare const WithCount: Story;
|
|
19
|
+
//# sourceMappingURL=offer-status-dots.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-status-dots.stories.d.ts","sourceRoot":"","sources":["../../src/components/offer-status-dots.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAA4B,MAAM,qBAAqB,CAAC;AAEhF,QAAA,MAAM,IAAI;;;;;;;CAK8B,CAAC;AAEzC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAYnC,eAAO,MAAM,OAAO,EAAE,KAIrB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAIvB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAItB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAY/B,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAInB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAKvB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { OfferStatusDots } from "./offer-status-dots";
|
|
3
|
+
const meta = {
|
|
4
|
+
title: "Components/OfferStatusDots",
|
|
5
|
+
component: OfferStatusDots,
|
|
6
|
+
tags: ["autodocs"],
|
|
7
|
+
parameters: { layout: "padded" },
|
|
8
|
+
};
|
|
9
|
+
export default meta;
|
|
10
|
+
const allStates = [
|
|
11
|
+
"pending",
|
|
12
|
+
"viewed",
|
|
13
|
+
"in_negotiation",
|
|
14
|
+
"declined",
|
|
15
|
+
"rejected",
|
|
16
|
+
"accepted",
|
|
17
|
+
"finalized",
|
|
18
|
+
];
|
|
19
|
+
export const Default = {
|
|
20
|
+
args: {
|
|
21
|
+
states: ["pending", "in_negotiation", "declined"],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export const SingleDot = {
|
|
25
|
+
args: {
|
|
26
|
+
states: ["in_negotiation"],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export const ManyDots = {
|
|
30
|
+
args: {
|
|
31
|
+
states: ["pending", "pending", "viewed", "in_negotiation", "in_negotiation", "declined"],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export const AllStatesShowcase = {
|
|
35
|
+
args: { states: allStates },
|
|
36
|
+
render: () => (_jsx("div", { className: "flex flex-col gap-3", children: allStates.map((state) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(OfferStatusDots, { states: [state] }), _jsx("span", { className: "text-xs text-muted-foreground", children: state })] }, state))) })),
|
|
37
|
+
};
|
|
38
|
+
export const Empty = {
|
|
39
|
+
args: {
|
|
40
|
+
states: [],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export const WithCount = {
|
|
44
|
+
args: {
|
|
45
|
+
states: ["pending", "in_negotiation", "accepted"],
|
|
46
|
+
showCount: true,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=offer-status-dots.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-status-dots.stories.js","sourceRoot":"","sources":["../../src/components/offer-status-dots.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAA4B,MAAM,qBAAqB,CAAC;AAEhF,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,4BAA4B;IACnC,SAAS,EAAE,eAAe;IAC1B,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;CACM,CAAC;AAEzC,eAAe,IAAI,CAAC;AAGpB,MAAM,SAAS,GAA0B;IACvC,SAAS;IACT,QAAQ;IACR,gBAAgB;IAChB,UAAU;IACV,UAAU;IACV,UAAU;IACV,WAAW;CACZ,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,UAAU,CAAC;KAClD;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,gBAAgB,CAAC;KAC3B;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAU;IAC7B,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,CAAC;KACzF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAU;IACtC,IAAI,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;IAC3B,MAAM,EAAE,GAAG,EAAE,CAAC,CACZ,cAAK,SAAS,EAAC,qBAAqB,YACjC,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACxB,eAAiB,SAAS,EAAC,yBAAyB,aAClD,KAAC,eAAe,IAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAI,EACpC,eAAM,SAAS,EAAC,+BAA+B,YAAE,KAAK,GAAQ,KAFtD,KAAK,CAGT,CACP,CAAC,GACE,CACP;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAU;IAC1B,IAAI,EAAE;QACJ,MAAM,EAAE,EAAE;KACX;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,IAAI,EAAE;QACJ,MAAM,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,UAAU,CAAC;QACjD,SAAS,EAAE,IAAI;KAChB;CACF,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type OfferStatusDotState } from "./offer-status-dots";
|
|
3
|
+
export interface SentOfferCardProps {
|
|
4
|
+
title: string;
|
|
5
|
+
subtitle?: string;
|
|
6
|
+
/** Icon shown in the meta row (typically lucide-react). 12px recommended. */
|
|
7
|
+
sourceIcon?: ReactNode;
|
|
8
|
+
/** One state per offer sent. */
|
|
9
|
+
statuses: OfferStatusDotState[];
|
|
10
|
+
/**
|
|
11
|
+
* Auto-summary line shown beneath the dots, e.g.
|
|
12
|
+
* "Sent to 3 importers · 1 counter-offer · best €48".
|
|
13
|
+
* The caller is responsible for computing this string.
|
|
14
|
+
*/
|
|
15
|
+
summaryLine: string;
|
|
16
|
+
/** Action label shown at the bottom. Default: "View offers". */
|
|
17
|
+
actionLabel?: string;
|
|
18
|
+
/** Click handler — fires when the card or the action link is activated. */
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compact card representing a parent offer that has been sent to one or
|
|
24
|
+
* more importers. Designed for the producer journey board's "Sent" column.
|
|
25
|
+
*
|
|
26
|
+
* The card shows: meta row (icon + subtitle), title, status dots row,
|
|
27
|
+
* auto-summary line, and an action link. Click handler fires from the
|
|
28
|
+
* whole card surface for keyboard accessibility.
|
|
29
|
+
*/
|
|
30
|
+
export declare function SentOfferCard({ title, subtitle, sourceIcon, statuses, summaryLine, actionLabel, onClick, className, }: SentOfferCardProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=sent-offer-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sent-offer-card.d.ts","sourceRoot":"","sources":["../../src/components/sent-offer-card.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,gCAAgC;IAChC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,WAAW,EACX,WAA2B,EAC3B,OAAO,EACP,SAAS,GACV,EAAE,kBAAkB,2CAkDpB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowRight } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { OfferStatusDots } from "./offer-status-dots";
|
|
5
|
+
/**
|
|
6
|
+
* Compact card representing a parent offer that has been sent to one or
|
|
7
|
+
* more importers. Designed for the producer journey board's "Sent" column.
|
|
8
|
+
*
|
|
9
|
+
* The card shows: meta row (icon + subtitle), title, status dots row,
|
|
10
|
+
* auto-summary line, and an action link. Click handler fires from the
|
|
11
|
+
* whole card surface for keyboard accessibility.
|
|
12
|
+
*/
|
|
13
|
+
export function SentOfferCard({ title, subtitle, sourceIcon, statuses, summaryLine, actionLabel = "View offers", onClick, className, }) {
|
|
14
|
+
return (_jsxs("div", { role: onClick ? "button" : undefined, tabIndex: onClick ? 0 : undefined, onClick: onClick, onKeyDown: onClick
|
|
15
|
+
? (e) => {
|
|
16
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
onClick();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
: undefined, className: cn("group flex flex-col gap-2 rounded-lg border border-border bg-card p-3 shadow-xs transition-colors", onClick && "cursor-pointer hover:border-border/80 hover:bg-muted/20", className), children: [(sourceIcon || subtitle) && (_jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [sourceIcon, subtitle && _jsx("span", { children: subtitle })] })), _jsx("h4", { className: "text-sm/5 font-semibold text-foreground", children: title }), statuses.length > 0 && (_jsx("div", { className: "mt-1", children: _jsx(OfferStatusDots, { states: statuses }) })), _jsx("p", { className: "text-xs text-muted-foreground", children: summaryLine }), _jsxs("div", { className: "mt-1 flex items-center gap-1 text-xs font-medium text-primary group-hover:text-primary/80", children: [actionLabel, _jsx(ArrowRight, { className: "size-3", "aria-hidden": true })] })] }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=sent-offer-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sent-offer-card.js","sourceRoot":"","sources":["../../src/components/sent-offer-card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,eAAe,EAA4B,MAAM,qBAAqB,CAAC;AAsBhF;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,WAAW,EACX,WAAW,GAAG,aAAa,EAC3B,OAAO,EACP,SAAS,GACU;IACnB,OAAO,CACL,eACE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EACpC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EACjC,OAAO,EAAE,OAAO,EAChB,SAAS,EACP,OAAO;YACL,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;gBACJ,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;oBACvC,CAAC,CAAC,cAAc,EAAE,CAAC;oBACnB,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;YACH,CAAC,CAAC,SAAS,EAEf,SAAS,EAAE,EAAE,CACX,mGAAmG,EACnG,OAAO,IAAI,yDAAyD,EACpE,SAAS,CACV,aAGA,CAAC,UAAU,IAAI,QAAQ,CAAC,IAAI,CAC3B,eAAK,SAAS,EAAC,yDAAyD,aACrE,UAAU,EACV,QAAQ,IAAI,yBAAO,QAAQ,GAAQ,IAChC,CACP,EAGD,aAAI,SAAS,EAAC,yCAAyC,YAAE,KAAK,GAAM,EAGnE,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,cAAK,SAAS,EAAC,MAAM,YACnB,KAAC,eAAe,IAAC,MAAM,EAAE,QAAQ,GAAI,GACjC,CACP,EAGD,YAAG,SAAS,EAAC,+BAA+B,YAAE,WAAW,GAAK,EAG9D,eAAK,SAAS,EAAC,2FAA2F,aACvG,WAAW,EACZ,KAAC,UAAU,IAAC,SAAS,EAAC,QAAQ,wBAAe,IACzC,IACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { SentOfferCard } from "./sent-offer-card";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof SentOfferCard;
|
|
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 NoResponses: Story;
|
|
15
|
+
export declare const SingleImporter: Story;
|
|
16
|
+
export declare const ManyImporters: Story;
|
|
17
|
+
//# sourceMappingURL=sent-offer-card.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sent-offer-card.stories.d.ts","sourceRoot":"","sources":["../../src/components/sent-offer-card.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,QAAA,MAAM,IAAI;;;;;;;CAK4B,CAAC;AAEvC,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KASrB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAS5B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Sparkles } from "lucide-react";
|
|
3
|
+
import { SentOfferCard } from "./sent-offer-card";
|
|
4
|
+
const meta = {
|
|
5
|
+
title: "Components/SentOfferCard",
|
|
6
|
+
component: SentOfferCard,
|
|
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 · 2 days ago",
|
|
15
|
+
sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
|
|
16
|
+
statuses: ["pending", "in_negotiation", "declined"],
|
|
17
|
+
summaryLine: "Sent to 3 importers · 1 counter-offer · best €48",
|
|
18
|
+
onClick: () => alert("Card clicked"),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export const NoResponses = {
|
|
22
|
+
args: {
|
|
23
|
+
title: "Pinot Noir 2022",
|
|
24
|
+
subtitle: "Match · 5h ago",
|
|
25
|
+
sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
|
|
26
|
+
statuses: ["pending", "pending"],
|
|
27
|
+
summaryLine: "Sent to 2 importers · no responses yet",
|
|
28
|
+
onClick: () => alert("Card clicked"),
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export const SingleImporter = {
|
|
32
|
+
args: {
|
|
33
|
+
title: "Riesling 2021",
|
|
34
|
+
subtitle: "Importer-direct · 1d ago",
|
|
35
|
+
sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
|
|
36
|
+
statuses: ["accepted"],
|
|
37
|
+
summaryLine: "Sent to 1 importer · accepted (review pending)",
|
|
38
|
+
onClick: () => alert("Card clicked"),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
export const ManyImporters = {
|
|
42
|
+
args: {
|
|
43
|
+
title: "Merlot 2024",
|
|
44
|
+
subtitle: "Match · 3 days ago",
|
|
45
|
+
sourceIcon: _jsx(Sparkles, { className: "size-3", "aria-hidden": true }),
|
|
46
|
+
statuses: ["pending", "viewed", "in_negotiation", "declined", "in_negotiation"],
|
|
47
|
+
summaryLine: "Sent to 5 importers · 2 counter-offers · best €52",
|
|
48
|
+
onClick: () => alert("Card clicked"),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=sent-offer-card.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sent-offer-card.stories.js","sourceRoot":"","sources":["../../src/components/sent-offer-card.stories.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;CACI,CAAC;AAEvC,eAAe,IAAI,CAAC;AAGpB,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,KAAK,EAAE,yBAAyB;QAChC,QAAQ,EAAE,oBAAoB;QAC9B,UAAU,EAAE,KAAC,QAAQ,IAAC,SAAS,EAAC,QAAQ,wBAAe;QACvD,QAAQ,EAAE,CAAC,SAAS,EAAE,gBAAgB,EAAE,UAAU,CAAC;QACnD,WAAW,EAAE,kDAAkD;QAC/D,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC;KACrC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAU;IAChC,IAAI,EAAE;QACJ,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,gBAAgB;QAC1B,UAAU,EAAE,KAAC,QAAQ,IAAC,SAAS,EAAC,QAAQ,wBAAe;QACvD,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC;QAChC,WAAW,EAAE,wCAAwC;QACrD,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC;KACrC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAU;IACnC,IAAI,EAAE;QACJ,KAAK,EAAE,eAAe;QACtB,QAAQ,EAAE,0BAA0B;QACpC,UAAU,EAAE,KAAC,QAAQ,IAAC,SAAS,EAAC,QAAQ,wBAAe;QACvD,QAAQ,EAAE,CAAC,UAAU,CAAC;QACtB,WAAW,EAAE,gDAAgD;QAC7D,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC;KACrC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAU;IAClC,IAAI,EAAE;QACJ,KAAK,EAAE,aAAa;QACpB,QAAQ,EAAE,oBAAoB;QAC9B,UAAU,EAAE,KAAC,QAAQ,IAAC,SAAS,EAAC,QAAQ,wBAAe;QACvD,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,gBAAgB,CAAC;QAC/E,WAAW,EAAE,mDAAmD;QAChE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC;KACrC;CACF,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks whether the celebration pulse animation should fire on mount.
|
|
3
|
+
* Returns `true` for the first ~500ms, then `false`.
|
|
4
|
+
*
|
|
5
|
+
* Honors the `prefers-reduced-motion` media query — when reduced motion
|
|
6
|
+
* is preferred, the hook returns `false` immediately so the wrapper can
|
|
7
|
+
* skip the animation entirely (or substitute a single fade).
|
|
8
|
+
*
|
|
9
|
+
* @param duration milliseconds the pulse stays "active" — default 500
|
|
10
|
+
*/
|
|
11
|
+
export declare function useCelebrationPulse(duration?: number): boolean;
|
|
12
|
+
//# sourceMappingURL=use-celebration-pulse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-celebration-pulse.d.ts","sourceRoot":"","sources":["../../src/hooks/use-celebration-pulse.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,SAAM,GAAG,OAAO,CAe3D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Tracks whether the celebration pulse animation should fire on mount.
|
|
4
|
+
* Returns `true` for the first ~500ms, then `false`.
|
|
5
|
+
*
|
|
6
|
+
* Honors the `prefers-reduced-motion` media query — when reduced motion
|
|
7
|
+
* is preferred, the hook returns `false` immediately so the wrapper can
|
|
8
|
+
* skip the animation entirely (or substitute a single fade).
|
|
9
|
+
*
|
|
10
|
+
* @param duration milliseconds the pulse stays "active" — default 500
|
|
11
|
+
*/
|
|
12
|
+
export function useCelebrationPulse(duration = 500) {
|
|
13
|
+
// Lazy-init avoids calling setState synchronously inside the effect:
|
|
14
|
+
// SSR → never animate; reduced motion → never animate; otherwise start active.
|
|
15
|
+
const [isActive, setIsActive] = useState(() => {
|
|
16
|
+
if (typeof window === "undefined")
|
|
17
|
+
return false;
|
|
18
|
+
return !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
19
|
+
});
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!isActive)
|
|
22
|
+
return;
|
|
23
|
+
const timer = window.setTimeout(() => setIsActive(false), duration);
|
|
24
|
+
return () => window.clearTimeout(timer);
|
|
25
|
+
}, [duration, isActive]);
|
|
26
|
+
return isActive;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=use-celebration-pulse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-celebration-pulse.js","sourceRoot":"","sources":["../../src/hooks/use-celebration-pulse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAQ,GAAG,GAAG;IAChD,qEAAqE;IACrE,+EAA+E;IAC/E,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE;QAC5C,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO,KAAK,CAAC;QAChD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAC;QACpE,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEzB,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|