@cellarnode/ui 0.14.1 → 0.16.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.
- package/dist/components/confirmation/beverage-comparison-modal.d.ts +66 -0
- package/dist/components/confirmation/beverage-comparison-modal.d.ts.map +1 -0
- package/dist/components/confirmation/beverage-comparison-modal.js +64 -0
- package/dist/components/confirmation/beverage-comparison-modal.js.map +1 -0
- package/dist/components/confirmation/beverage-comparison-modal.stories.d.ts +32 -0
- package/dist/components/confirmation/beverage-comparison-modal.stories.d.ts.map +1 -0
- package/dist/components/confirmation/beverage-comparison-modal.stories.js +160 -0
- package/dist/components/confirmation/beverage-comparison-modal.stories.js.map +1 -0
- package/dist/components/confirmation/confirmation-card.d.ts +77 -0
- package/dist/components/confirmation/confirmation-card.d.ts.map +1 -0
- package/dist/components/confirmation/confirmation-card.js +78 -0
- package/dist/components/confirmation/confirmation-card.js.map +1 -0
- package/dist/components/confirmation/confirmation-card.stories.d.ts +32 -0
- package/dist/components/confirmation/confirmation-card.stories.d.ts.map +1 -0
- package/dist/components/confirmation/confirmation-card.stories.js +138 -0
- package/dist/components/confirmation/confirmation-card.stories.js.map +1 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.d.ts +51 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.d.ts.map +1 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.js +59 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.js.map +1 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.stories.d.ts +29 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.stories.d.ts.map +1 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.stories.js +99 -0
- package/dist/components/confirmation/cross-portfolio-nudge-sheet.stories.js.map +1 -0
- package/dist/components/extraction-phase-stepper.d.ts +19 -0
- package/dist/components/extraction-phase-stepper.d.ts.map +1 -0
- package/dist/components/extraction-phase-stepper.js +26 -0
- package/dist/components/extraction-phase-stepper.js.map +1 -0
- package/dist/components/extraction-phase-stepper.stories.d.ts +18 -0
- package/dist/components/extraction-phase-stepper.stories.d.ts.map +1 -0
- package/dist/components/extraction-phase-stepper.stories.js +31 -0
- package/dist/components/extraction-phase-stepper.stories.js.map +1 -0
- package/dist/components/flagged-field-editor.d.ts +23 -0
- package/dist/components/flagged-field-editor.d.ts.map +1 -0
- package/dist/components/flagged-field-editor.js +16 -0
- package/dist/components/flagged-field-editor.js.map +1 -0
- package/dist/components/flagged-field-editor.stories.d.ts +27 -0
- package/dist/components/flagged-field-editor.stories.d.ts.map +1 -0
- package/dist/components/flagged-field-editor.stories.js +60 -0
- package/dist/components/flagged-field-editor.stories.js.map +1 -0
- package/dist/components/preflight-verdict-sheet.d.ts +20 -0
- package/dist/components/preflight-verdict-sheet.d.ts.map +1 -0
- package/dist/components/preflight-verdict-sheet.js +23 -0
- package/dist/components/preflight-verdict-sheet.js.map +1 -0
- package/dist/components/preflight-verdict-sheet.stories.d.ts +29 -0
- package/dist/components/preflight-verdict-sheet.stories.d.ts.map +1 -0
- package/dist/components/preflight-verdict-sheet.stories.js +48 -0
- package/dist/components/preflight-verdict-sheet.stories.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ConfirmationCard } from "./confirmation-card";
|
|
2
|
+
const noop = () => { };
|
|
3
|
+
/**
|
|
4
|
+
* Minimal i18n shim that mirrors the producer-dashboard keys used by the
|
|
5
|
+
* component. In real usage react-i18next's `t` fills the placeholders from
|
|
6
|
+
* `public/locales/en/imports.json`.
|
|
7
|
+
*/
|
|
8
|
+
const t = (key, opts) => {
|
|
9
|
+
switch (key) {
|
|
10
|
+
case "confirmation.card.headline_same_vintage":
|
|
11
|
+
return `We found a near-match: ${opts?.name_a} and ${opts?.name_b}.`;
|
|
12
|
+
case "confirmation.card.headline_vintages":
|
|
13
|
+
return `We found ${opts?.name_base} in two vintages — ${opts?.vintage_a} and ${opts?.vintage_b}.`;
|
|
14
|
+
case "confirmation.card.subhead":
|
|
15
|
+
return "Are they the same product?";
|
|
16
|
+
case "confirmation.card.field_count_hint":
|
|
17
|
+
return `${opts?.name_a} has ${opts?.count_a} fields · ${opts?.name_b} has ${opts?.count_b} fields`;
|
|
18
|
+
case "confirmation.card.direction_announce":
|
|
19
|
+
return `Keep details from ${opts?.name}`;
|
|
20
|
+
case "confirmation.card.direction_label":
|
|
21
|
+
return "Keep details from";
|
|
22
|
+
case "confirmation.card.confirm_prefix":
|
|
23
|
+
return `Yes · keep details from ${opts?.name}`;
|
|
24
|
+
case "confirmation.card.confirm_loading":
|
|
25
|
+
return "Joining records…";
|
|
26
|
+
case "confirmation.card.dismiss":
|
|
27
|
+
return "No, keep separate";
|
|
28
|
+
case "confirmation.card.show_comparison":
|
|
29
|
+
return "Show comparison";
|
|
30
|
+
default:
|
|
31
|
+
return key;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const meta = {
|
|
35
|
+
title: "Confirmation/ConfirmationCard",
|
|
36
|
+
component: ConfirmationCard,
|
|
37
|
+
tags: ["autodocs"],
|
|
38
|
+
parameters: { a11y: { test: "error" } },
|
|
39
|
+
argTypes: {
|
|
40
|
+
onConfirm: { action: "confirm" },
|
|
41
|
+
onDismiss: { action: "dismiss" },
|
|
42
|
+
onShowComparison: { action: "show-comparison" },
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export default meta;
|
|
46
|
+
export const SameVintageMatch = {
|
|
47
|
+
args: {
|
|
48
|
+
pair: {
|
|
49
|
+
id: "pair-1",
|
|
50
|
+
a: { id: "bev-a", name: "Château Margaux 2018", fieldCount: 12 },
|
|
51
|
+
b: { id: "bev-b", name: "Chateau Margaux 2018", fieldCount: 8 },
|
|
52
|
+
baseName: null,
|
|
53
|
+
llmMergeDirection: "a_to_b",
|
|
54
|
+
},
|
|
55
|
+
onConfirm: noop,
|
|
56
|
+
onDismiss: noop,
|
|
57
|
+
onShowComparison: noop,
|
|
58
|
+
t,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
export const VintageVariants = {
|
|
62
|
+
args: {
|
|
63
|
+
pair: {
|
|
64
|
+
id: "pair-2",
|
|
65
|
+
a: { id: "bev-a", name: "Opus One 2018", vintage: "2018", fieldCount: 10 },
|
|
66
|
+
b: { id: "bev-b", name: "Opus One 2019", vintage: "2019", fieldCount: 10 },
|
|
67
|
+
baseName: "Opus One",
|
|
68
|
+
llmMergeDirection: "a_to_b",
|
|
69
|
+
},
|
|
70
|
+
onConfirm: noop,
|
|
71
|
+
onDismiss: noop,
|
|
72
|
+
onShowComparison: noop,
|
|
73
|
+
t,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
export const LLMDefaultDirection = {
|
|
77
|
+
args: {
|
|
78
|
+
pair: {
|
|
79
|
+
id: "pair-3",
|
|
80
|
+
a: { id: "bev-a", name: "Penfolds Grange 2017", fieldCount: 6 },
|
|
81
|
+
b: { id: "bev-b", name: "Penfolds Grange 2017 Bin 95", fieldCount: 14 },
|
|
82
|
+
baseName: null,
|
|
83
|
+
llmMergeDirection: "b_to_a",
|
|
84
|
+
},
|
|
85
|
+
onConfirm: noop,
|
|
86
|
+
onDismiss: noop,
|
|
87
|
+
onShowComparison: noop,
|
|
88
|
+
t,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
export const NullLLMFallbackByFieldCount = {
|
|
92
|
+
args: {
|
|
93
|
+
pair: {
|
|
94
|
+
id: "pair-4",
|
|
95
|
+
a: { id: "bev-a", name: "Cloudy Bay Sauvignon Blanc", fieldCount: 14 },
|
|
96
|
+
b: { id: "bev-b", name: "Cloudy Bay Sauv Blanc", fieldCount: 4 },
|
|
97
|
+
baseName: null,
|
|
98
|
+
llmMergeDirection: null,
|
|
99
|
+
},
|
|
100
|
+
onConfirm: noop,
|
|
101
|
+
onDismiss: noop,
|
|
102
|
+
onShowComparison: noop,
|
|
103
|
+
t,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
export const Loading = {
|
|
107
|
+
args: {
|
|
108
|
+
pair: {
|
|
109
|
+
id: "pair-5",
|
|
110
|
+
a: { id: "bev-a", name: "Sassicaia 2016", fieldCount: 12 },
|
|
111
|
+
b: { id: "bev-b", name: "Sassicaia '16", fieldCount: 7 },
|
|
112
|
+
baseName: null,
|
|
113
|
+
llmMergeDirection: "a_to_b",
|
|
114
|
+
},
|
|
115
|
+
loading: true,
|
|
116
|
+
onConfirm: noop,
|
|
117
|
+
onDismiss: noop,
|
|
118
|
+
onShowComparison: noop,
|
|
119
|
+
t,
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
export const InlineConflictError = {
|
|
123
|
+
args: {
|
|
124
|
+
pair: {
|
|
125
|
+
id: "pair-6",
|
|
126
|
+
a: { id: "bev-a", name: "Krug Grande Cuvée", fieldCount: 11 },
|
|
127
|
+
b: { id: "bev-b", name: "Krug Grande Cuvee 170th", fieldCount: 11 },
|
|
128
|
+
baseName: null,
|
|
129
|
+
llmMergeDirection: "a_to_b",
|
|
130
|
+
},
|
|
131
|
+
inlineError: "Can't auto-confirm — some fields don't line up. Show comparison to resolve by hand.",
|
|
132
|
+
onConfirm: noop,
|
|
133
|
+
onDismiss: noop,
|
|
134
|
+
onShowComparison: noop,
|
|
135
|
+
t,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
//# sourceMappingURL=confirmation-card.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmation-card.stories.js","sourceRoot":"","sources":["../../../src/components/confirmation/confirmation-card.stories.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface PortfolioCounter {
|
|
2
|
+
portfolioId: string;
|
|
3
|
+
portfolioName: string;
|
|
4
|
+
count: number;
|
|
5
|
+
}
|
|
6
|
+
export interface CrossPortfolioOverlap {
|
|
7
|
+
overlapPct: number;
|
|
8
|
+
totalProducts: number;
|
|
9
|
+
nonTargetMatchedProductCount: number;
|
|
10
|
+
}
|
|
11
|
+
export interface PortfolioRef {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CrossPortfolioNudgeSheetProps {
|
|
16
|
+
/** Visibility — controlled by the caller. */
|
|
17
|
+
open: boolean;
|
|
18
|
+
/** Detected overlap metrics. */
|
|
19
|
+
overlap: CrossPortfolioOverlap;
|
|
20
|
+
/** Sorted desc by `count`. When `length > 1` the sheet exposes an expandable list. */
|
|
21
|
+
countersByPortfolio: PortfolioCounter[];
|
|
22
|
+
/** The portfolio the producer picked during upload. */
|
|
23
|
+
targetPortfolio: PortfolioRef;
|
|
24
|
+
/** The portfolio with the highest overlap; may be null when countersByPortfolio is empty. */
|
|
25
|
+
suggestedPortfolio: PortfolioRef | null;
|
|
26
|
+
/** Producer clicked "Continue to {{target}}". */
|
|
27
|
+
onProceed: () => void;
|
|
28
|
+
/** Producer clicked "Switch to {{suggested}}". */
|
|
29
|
+
onSwitchTarget: () => void;
|
|
30
|
+
/** Producer cancelled (Escape, backdrop-click-to-cancel, or explicit button). */
|
|
31
|
+
onCancel: () => void;
|
|
32
|
+
/** Disables all buttons while a resume-nudge mutation is in flight. */
|
|
33
|
+
loading?: boolean;
|
|
34
|
+
/** i18n lookup — producer-dashboard passes react-i18next's `t`. */
|
|
35
|
+
t: (key: string, opts?: Record<string, unknown>) => string;
|
|
36
|
+
className?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Cross-portfolio nudge modal shown after the classify phase of the beverage
|
|
40
|
+
* extraction workflow when ≥70% of detected products already live in another
|
|
41
|
+
* portfolio.
|
|
42
|
+
*
|
|
43
|
+
* Spec (SP2 §3):
|
|
44
|
+
* - Radix Dialog with `role="dialog"` + `aria-modal="true"` + focus trap
|
|
45
|
+
* - Focus starts on "Continue to {{target}}" (safe default — preserves producer intent)
|
|
46
|
+
* - Escape → `onCancel`
|
|
47
|
+
* - When `countersByPortfolio.length > 1` the sheet includes an expandable
|
|
48
|
+
* "View all portfolios" list.
|
|
49
|
+
*/
|
|
50
|
+
export declare function CrossPortfolioNudgeSheet(props: CrossPortfolioNudgeSheetProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
//# sourceMappingURL=cross-portfolio-nudge-sheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-portfolio-nudge-sheet.d.ts","sourceRoot":"","sources":["../../../src/components/confirmation/cross-portfolio-nudge-sheet.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,4BAA4B,EAAE,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,6BAA6B;IAC5C,6CAA6C;IAC7C,IAAI,EAAE,OAAO,CAAC;IACd,gCAAgC;IAChC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,sFAAsF;IACtF,mBAAmB,EAAE,gBAAgB,EAAE,CAAC;IACxC,uDAAuD;IACvD,eAAe,EAAE,YAAY,CAAC;IAC9B,6FAA6F;IAC7F,kBAAkB,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC,iDAAiD;IACjD,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kDAAkD;IAClD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,iFAAiF;IACjF,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mEAAmE;IACnE,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,2CA4K5E"}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
/**
|
|
6
|
+
* Cross-portfolio nudge modal shown after the classify phase of the beverage
|
|
7
|
+
* extraction workflow when ≥70% of detected products already live in another
|
|
8
|
+
* portfolio.
|
|
9
|
+
*
|
|
10
|
+
* Spec (SP2 §3):
|
|
11
|
+
* - Radix Dialog with `role="dialog"` + `aria-modal="true"` + focus trap
|
|
12
|
+
* - Focus starts on "Continue to {{target}}" (safe default — preserves producer intent)
|
|
13
|
+
* - Escape → `onCancel`
|
|
14
|
+
* - When `countersByPortfolio.length > 1` the sheet includes an expandable
|
|
15
|
+
* "View all portfolios" list.
|
|
16
|
+
*/
|
|
17
|
+
export function CrossPortfolioNudgeSheet(props) {
|
|
18
|
+
const { open, overlap, countersByPortfolio, targetPortfolio, suggestedPortfolio, onProceed, onSwitchTarget, onCancel, loading = false, t, className, } = props;
|
|
19
|
+
const titleId = React.useId();
|
|
20
|
+
const descriptionId = React.useId();
|
|
21
|
+
const proceedButtonRef = React.useRef(null);
|
|
22
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
23
|
+
const hasMultiple = countersByPortfolio.length > 1;
|
|
24
|
+
const topSuggested = suggestedPortfolio ?? countersByPortfolio[0]
|
|
25
|
+
? {
|
|
26
|
+
id: suggestedPortfolio?.id ?? countersByPortfolio[0].portfolioId,
|
|
27
|
+
name: suggestedPortfolio?.name ?? countersByPortfolio[0].portfolioName,
|
|
28
|
+
}
|
|
29
|
+
: null;
|
|
30
|
+
const suggestedCount = countersByPortfolio.find((c) => c.portfolioId === topSuggested?.id)?.count ??
|
|
31
|
+
countersByPortfolio[0]?.count ??
|
|
32
|
+
0;
|
|
33
|
+
const headline = hasMultiple
|
|
34
|
+
? t("confirmation.nudge.headline_multi", {
|
|
35
|
+
nonTargetMatched: overlap.nonTargetMatchedProductCount,
|
|
36
|
+
total: overlap.totalProducts,
|
|
37
|
+
suggestedPortfolioName: topSuggested?.name ?? "",
|
|
38
|
+
suggestedCount,
|
|
39
|
+
})
|
|
40
|
+
: t("confirmation.nudge.headline_single", {
|
|
41
|
+
nonTargetMatched: overlap.nonTargetMatchedProductCount,
|
|
42
|
+
total: overlap.totalProducts,
|
|
43
|
+
suggestedPortfolioName: topSuggested?.name ?? "",
|
|
44
|
+
});
|
|
45
|
+
return (_jsx(Dialog.Root, { open: open, onOpenChange: (nextOpen) => {
|
|
46
|
+
if (!nextOpen && !loading)
|
|
47
|
+
onCancel();
|
|
48
|
+
}, 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, "aria-describedby": descriptionId, onOpenAutoFocus: (e) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
proceedButtonRef.current?.focus();
|
|
51
|
+
}, className: cn("fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-xl -translate-x-1/2 -translate-y-1/2", "rounded-2xl border border-border bg-card p-6 text-card-foreground shadow-xl", className), children: [_jsx(Dialog.Title, { id: titleId, className: "text-base/6 font-semibold text-foreground", children: t("confirmation.nudge.title") }), _jsx(Dialog.Description, { id: descriptionId, className: "mt-2 text-sm text-muted-foreground", children: headline }), hasMultiple && (_jsxs("div", { className: "mt-4 rounded-lg border border-border bg-background/50", children: [_jsxs("button", { type: "button", "aria-expanded": expanded, onClick: () => setExpanded((prev) => !prev), className: cn("flex w-full items-center justify-between px-4 py-3 text-sm font-medium text-foreground", "hover:bg-muted/40", "focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/50"), children: [_jsx("span", { children: t("confirmation.nudge.view_all_portfolios") }), _jsx("span", { "aria-hidden": true, className: "text-muted-foreground", children: expanded ? "−" : "+" })] }), expanded && (_jsx("ul", { className: "divide-y divide-border border-t border-border", children: countersByPortfolio.map((counter) => (_jsxs("li", { className: "flex items-center justify-between px-4 py-2 text-sm", children: [_jsx("span", { className: "text-foreground", children: counter.portfolioName }), _jsx("span", { className: "text-muted-foreground", children: t("confirmation.nudge.counter", { count: counter.count }) })] }, counter.portfolioId))) }))] })), _jsxs("div", { className: "mt-6 flex flex-wrap justify-end gap-2", children: [_jsx("button", { type: "button", disabled: loading, onClick: onCancel, className: cn("rounded-md px-4 py-2 text-sm font-medium text-muted-foreground", "hover:bg-muted/50 hover:text-foreground", "focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/50", "disabled:cursor-not-allowed disabled:opacity-60"), children: t("confirmation.nudge.cancel") }), topSuggested && (_jsx("button", { type: "button", disabled: loading || !topSuggested, onClick: onSwitchTarget, className: cn("rounded-md border border-border bg-background px-4 py-2 text-sm font-semibold text-foreground", "hover:bg-muted/50", "focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/50", "disabled:cursor-not-allowed disabled:opacity-60"), children: t("confirmation.nudge.switch_target", {
|
|
52
|
+
suggestedPortfolioName: topSuggested.name,
|
|
53
|
+
}) })), _jsx("button", { ref: proceedButtonRef, type: "button", disabled: loading, onClick: onProceed, className: cn("rounded-md bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground", "hover:bg-primary/90", "focus:outline-hidden focus-visible:ring-2 focus-visible:ring-primary/50", "disabled:cursor-not-allowed disabled:opacity-60"), children: loading
|
|
54
|
+
? t("confirmation.nudge.proceed_loading")
|
|
55
|
+
: t("confirmation.nudge.proceed", {
|
|
56
|
+
targetPortfolioName: targetPortfolio.name,
|
|
57
|
+
}) })] })] })] }) }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=cross-portfolio-nudge-sheet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-portfolio-nudge-sheet.js","sourceRoot":"","sources":["../../../src/components/confirmation/cross-portfolio-nudge-sheet.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { CrossPortfolioNudgeSheet } from "./cross-portfolio-nudge-sheet";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof CrossPortfolioNudgeSheet;
|
|
6
|
+
tags: string[];
|
|
7
|
+
parameters: {
|
|
8
|
+
a11y: {
|
|
9
|
+
test: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
argTypes: {
|
|
13
|
+
onProceed: {
|
|
14
|
+
action: string;
|
|
15
|
+
};
|
|
16
|
+
onSwitchTarget: {
|
|
17
|
+
action: string;
|
|
18
|
+
};
|
|
19
|
+
onCancel: {
|
|
20
|
+
action: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
export default meta;
|
|
25
|
+
type Story = StoryObj<typeof meta>;
|
|
26
|
+
export declare const SingleSuggested: Story;
|
|
27
|
+
export declare const MultiPortfolio: Story;
|
|
28
|
+
export declare const LoadingAfterAction: Story;
|
|
29
|
+
//# sourceMappingURL=cross-portfolio-nudge-sheet.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-portfolio-nudge-sheet.stories.d.ts","sourceRoot":"","sources":["../../../src/components/confirmation/cross-portfolio-nudge-sheet.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AA6BzE,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;CAUuC,CAAC;AAElD,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,eAAe,EAAE,KAkB7B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAoB5B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAmBhC,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { CrossPortfolioNudgeSheet } from "./cross-portfolio-nudge-sheet";
|
|
2
|
+
const noop = () => { };
|
|
3
|
+
const t = (key, opts) => {
|
|
4
|
+
switch (key) {
|
|
5
|
+
case "confirmation.nudge.title":
|
|
6
|
+
return "Heads up";
|
|
7
|
+
case "confirmation.nudge.headline_single":
|
|
8
|
+
return `${opts?.nonTargetMatched} of the ${opts?.total} products you uploaded already live in ${opts?.suggestedPortfolioName}.`;
|
|
9
|
+
case "confirmation.nudge.headline_multi":
|
|
10
|
+
return `${opts?.nonTargetMatched} of the ${opts?.total} products you uploaded already live in other portfolios — mostly ${opts?.suggestedPortfolioName} (${opts?.suggestedCount}).`;
|
|
11
|
+
case "confirmation.nudge.view_all_portfolios":
|
|
12
|
+
return "View all portfolios";
|
|
13
|
+
case "confirmation.nudge.counter":
|
|
14
|
+
return `${opts?.count} products`;
|
|
15
|
+
case "confirmation.nudge.proceed":
|
|
16
|
+
return `Continue to ${opts?.targetPortfolioName}`;
|
|
17
|
+
case "confirmation.nudge.proceed_loading":
|
|
18
|
+
return "Continuing…";
|
|
19
|
+
case "confirmation.nudge.switch_target":
|
|
20
|
+
return `Switch to ${opts?.suggestedPortfolioName}`;
|
|
21
|
+
case "confirmation.nudge.cancel":
|
|
22
|
+
return "Cancel";
|
|
23
|
+
default:
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const meta = {
|
|
28
|
+
title: "Confirmation/CrossPortfolioNudgeSheet",
|
|
29
|
+
component: CrossPortfolioNudgeSheet,
|
|
30
|
+
tags: ["autodocs"],
|
|
31
|
+
parameters: { a11y: { test: "error" } },
|
|
32
|
+
argTypes: {
|
|
33
|
+
onProceed: { action: "proceed" },
|
|
34
|
+
onSwitchTarget: { action: "switch-target" },
|
|
35
|
+
onCancel: { action: "cancel" },
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
export default meta;
|
|
39
|
+
export const SingleSuggested = {
|
|
40
|
+
args: {
|
|
41
|
+
open: true,
|
|
42
|
+
overlap: {
|
|
43
|
+
overlapPct: 0.8,
|
|
44
|
+
totalProducts: 10,
|
|
45
|
+
nonTargetMatchedProductCount: 8,
|
|
46
|
+
},
|
|
47
|
+
countersByPortfolio: [
|
|
48
|
+
{ portfolioId: "p-master", portfolioName: "Master Catalog", count: 8 },
|
|
49
|
+
],
|
|
50
|
+
targetPortfolio: { id: "p-2024", name: "2024 Summer Collection" },
|
|
51
|
+
suggestedPortfolio: { id: "p-master", name: "Master Catalog" },
|
|
52
|
+
onProceed: noop,
|
|
53
|
+
onSwitchTarget: noop,
|
|
54
|
+
onCancel: noop,
|
|
55
|
+
t,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export const MultiPortfolio = {
|
|
59
|
+
args: {
|
|
60
|
+
open: true,
|
|
61
|
+
overlap: {
|
|
62
|
+
overlapPct: 0.75,
|
|
63
|
+
totalProducts: 12,
|
|
64
|
+
nonTargetMatchedProductCount: 9,
|
|
65
|
+
},
|
|
66
|
+
countersByPortfolio: [
|
|
67
|
+
{ portfolioId: "p-master", portfolioName: "Master Catalog", count: 5 },
|
|
68
|
+
{ portfolioId: "p-2023", portfolioName: "2023 Collection", count: 3 },
|
|
69
|
+
{ portfolioId: "p-sparkling", portfolioName: "Sparkling Only", count: 1 },
|
|
70
|
+
],
|
|
71
|
+
targetPortfolio: { id: "p-2024", name: "2024 Summer Collection" },
|
|
72
|
+
suggestedPortfolio: { id: "p-master", name: "Master Catalog" },
|
|
73
|
+
onProceed: noop,
|
|
74
|
+
onSwitchTarget: noop,
|
|
75
|
+
onCancel: noop,
|
|
76
|
+
t,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
export const LoadingAfterAction = {
|
|
80
|
+
args: {
|
|
81
|
+
open: true,
|
|
82
|
+
overlap: {
|
|
83
|
+
overlapPct: 0.8,
|
|
84
|
+
totalProducts: 10,
|
|
85
|
+
nonTargetMatchedProductCount: 8,
|
|
86
|
+
},
|
|
87
|
+
countersByPortfolio: [
|
|
88
|
+
{ portfolioId: "p-master", portfolioName: "Master Catalog", count: 8 },
|
|
89
|
+
],
|
|
90
|
+
targetPortfolio: { id: "p-2024", name: "2024 Summer Collection" },
|
|
91
|
+
suggestedPortfolio: { id: "p-master", name: "Master Catalog" },
|
|
92
|
+
loading: true,
|
|
93
|
+
onProceed: noop,
|
|
94
|
+
onSwitchTarget: noop,
|
|
95
|
+
onCancel: noop,
|
|
96
|
+
t,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=cross-portfolio-nudge-sheet.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-portfolio-nudge-sheet.stories.js","sourceRoot":"","sources":["../../../src/components/confirmation/cross-portfolio-nudge-sheet.stories.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface StepperStep {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
conditional?: boolean;
|
|
5
|
+
visible?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ExtractionPhaseStepperProps {
|
|
8
|
+
steps: StepperStep[];
|
|
9
|
+
currentPhaseKey: string;
|
|
10
|
+
reviewCount?: number;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Horizontal phase stepper for the multi-phase extraction workflow.
|
|
15
|
+
* Hides conditional steps (e.g. "Needs review") unless `visible` is true.
|
|
16
|
+
* Announces review count changes via an `aria-live="polite"` region.
|
|
17
|
+
*/
|
|
18
|
+
export declare function ExtractionPhaseStepper(props: ExtractionPhaseStepperProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=extraction-phase-stepper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-phase-stepper.d.ts","sourceRoot":"","sources":["../../src/components/extraction-phase-stepper.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,2CA+CxE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
/**
|
|
5
|
+
* Horizontal phase stepper for the multi-phase extraction workflow.
|
|
6
|
+
* Hides conditional steps (e.g. "Needs review") unless `visible` is true.
|
|
7
|
+
* Announces review count changes via an `aria-live="polite"` region.
|
|
8
|
+
*/
|
|
9
|
+
export function ExtractionPhaseStepper(props) {
|
|
10
|
+
const { steps, currentPhaseKey, reviewCount, className } = props;
|
|
11
|
+
const visible = steps.filter((s) => !s.conditional || s.visible);
|
|
12
|
+
const currentIdx = visible.findIndex((s) => s.key === currentPhaseKey);
|
|
13
|
+
const [lastReviewCount, setLastReviewCount] = React.useState(reviewCount ?? 0);
|
|
14
|
+
const announceRef = React.useRef(null);
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
if ((reviewCount ?? 0) > lastReviewCount && announceRef.current) {
|
|
17
|
+
announceRef.current.textContent = `${reviewCount} products need your review`;
|
|
18
|
+
}
|
|
19
|
+
setLastReviewCount(reviewCount ?? 0);
|
|
20
|
+
}, [reviewCount, lastReviewCount]);
|
|
21
|
+
return (_jsxs("ol", { role: "list", "aria-label": "Extraction progress", className: cn("flex flex-wrap gap-2", className), children: [visible.map((s, i) => {
|
|
22
|
+
const state = i < currentIdx ? "done" : i === currentIdx ? "current" : "pending";
|
|
23
|
+
return (_jsx("li", { "aria-current": state === "current" ? "step" : undefined, className: cn("rounded px-3 py-1 text-xs font-semibold", state === "done" && "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-100", state === "current" && "bg-indigo-600 text-white dark:bg-indigo-500", state === "pending" && "bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400"), children: s.label }, s.key));
|
|
24
|
+
}), _jsx("span", { ref: announceRef, className: "sr-only", "aria-live": "polite", "aria-atomic": "true" })] }));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=extraction-phase-stepper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-phase-stepper.js","sourceRoot":"","sources":["../../src/components/extraction-phase-stepper.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAgBlC;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAkC;IACvE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAEjE,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;IACjE,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,eAAe,CAAC,CAAC;IAEvE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IAC/E,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAkB,IAAI,CAAC,CAAC;IAExD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,eAAe,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YAChE,WAAW,CAAC,OAAO,CAAC,WAAW,GAAG,GAAG,WAAW,4BAA4B,CAAC;QAC/E,CAAC;QACD,kBAAkB,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC;IAEnC,OAAO,CACL,cACE,IAAI,EAAC,MAAM,gBACA,qBAAqB,EAChC,SAAS,EAAE,EAAE,CAAC,sBAAsB,EAAE,SAAS,CAAC,aAE/C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACpB,MAAM,KAAK,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;gBACjF,OAAO,CACL,6BAEgB,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACtD,SAAS,EAAE,EAAE,CACX,yCAAyC,EACzC,KAAK,KAAK,MAAM,IAAI,8EAA8E,EAClG,KAAK,KAAK,SAAS,IAAI,6CAA6C,EACpE,KAAK,KAAK,SAAS,IAAI,+DAA+D,CACvF,YAEA,CAAC,CAAC,KAAK,IATH,CAAC,CAAC,GAAG,CAUP,CACN,CAAC;YACJ,CAAC,CAAC,EACF,eACE,GAAG,EAAE,WAAW,EAChB,SAAS,EAAC,SAAS,eACT,QAAQ,iBACN,MAAM,GAClB,IACC,CACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { ExtractionPhaseStepper } from "./extraction-phase-stepper";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof ExtractionPhaseStepper;
|
|
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 Running: Story;
|
|
16
|
+
export declare const WithReview: Story;
|
|
17
|
+
export declare const Completed: Story;
|
|
18
|
+
//# sourceMappingURL=extraction-phase-stepper.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-phase-stepper.stories.d.ts","sourceRoot":"","sources":["../../src/components/extraction-phase-stepper.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,QAAA,MAAM,IAAI;;;;;;;;;CAKqC,CAAC;AAEhD,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAYnC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ExtractionPhaseStepper } from "./extraction-phase-stepper";
|
|
2
|
+
const meta = {
|
|
3
|
+
title: "Extraction/ExtractionPhaseStepper",
|
|
4
|
+
component: ExtractionPhaseStepper,
|
|
5
|
+
tags: ["autodocs"],
|
|
6
|
+
parameters: { a11y: { test: "error" } },
|
|
7
|
+
};
|
|
8
|
+
export default meta;
|
|
9
|
+
const baseSteps = [
|
|
10
|
+
{ key: "preflight", label: "Checking file" },
|
|
11
|
+
{ key: "classifying", label: "Finding products" },
|
|
12
|
+
{ key: "extracting", label: "Extracting details" },
|
|
13
|
+
{ key: "verifying", label: "Verifying" },
|
|
14
|
+
{ key: "review", label: "Needs review · 3", conditional: true, visible: false },
|
|
15
|
+
{ key: "persisting", label: "Saving" },
|
|
16
|
+
{ key: "completed", label: "Done" },
|
|
17
|
+
];
|
|
18
|
+
export const Running = {
|
|
19
|
+
args: { steps: baseSteps, currentPhaseKey: "extracting" },
|
|
20
|
+
};
|
|
21
|
+
export const WithReview = {
|
|
22
|
+
args: {
|
|
23
|
+
steps: baseSteps.map((s) => (s.key === "review" ? { ...s, visible: true } : s)),
|
|
24
|
+
currentPhaseKey: "verifying",
|
|
25
|
+
reviewCount: 3,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export const Completed = {
|
|
29
|
+
args: { steps: baseSteps, currentPhaseKey: "completed" },
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=extraction-phase-stepper.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extraction-phase-stepper.stories.js","sourceRoot":"","sources":["../../src/components/extraction-phase-stepper.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEpE,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,mCAAmC;IAC1C,SAAS,EAAE,sBAAsB;IACjC,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;CACM,CAAC;AAEhD,eAAe,IAAI,CAAC;AAGpB,MAAM,SAAS,GAAG;IAChB,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,EAAE;IAC5C,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,kBAAkB,EAAE;IACjD,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAClD,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE;IACxC,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;IAC/E,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE;IACtC,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE;CACpC,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE;CAC1D,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAU;IAC/B,IAAI,EAAE;QACJ,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,eAAe,EAAE,WAAW;QAC5B,WAAW,EAAE,CAAC;KACf;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE;CACzD,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type FlaggedFieldKind = "string" | "number" | "enum" | "boolean";
|
|
2
|
+
export interface FlaggedFieldEditorOption {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface FlaggedFieldEditorProps {
|
|
7
|
+
label: string;
|
|
8
|
+
kind: FlaggedFieldKind;
|
|
9
|
+
extractedAs?: unknown;
|
|
10
|
+
reason: string;
|
|
11
|
+
options?: FlaggedFieldEditorOption[];
|
|
12
|
+
onConfirm: (value: unknown) => void;
|
|
13
|
+
onSkip: () => void;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Inline editor for a flagged extraction field (validation issue surfaced
|
|
18
|
+
* by the extraction pipeline). Renders a kind-appropriate input alongside
|
|
19
|
+
* the reason and confirm/skip controls. Styled with amber tones to signal
|
|
20
|
+
* attention.
|
|
21
|
+
*/
|
|
22
|
+
export declare function FlaggedFieldEditor(props: FlaggedFieldEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
//# sourceMappingURL=flagged-field-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagged-field-editor.d.ts","sourceRoot":"","sources":["../../src/components/flagged-field-editor.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAExE,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,gBAAgB,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACrC,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CAkFhE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
/**
|
|
5
|
+
* Inline editor for a flagged extraction field (validation issue surfaced
|
|
6
|
+
* by the extraction pipeline). Renders a kind-appropriate input alongside
|
|
7
|
+
* the reason and confirm/skip controls. Styled with amber tones to signal
|
|
8
|
+
* attention.
|
|
9
|
+
*/
|
|
10
|
+
export function FlaggedFieldEditor(props) {
|
|
11
|
+
const { label, kind, extractedAs, reason, options, onConfirm, onSkip, className, } = props;
|
|
12
|
+
const [value, setValue] = React.useState(extractedAs ?? "");
|
|
13
|
+
const fieldId = React.useId();
|
|
14
|
+
return (_jsxs("div", { className: cn("rounded border border-amber-300 bg-amber-50 p-3 dark:border-amber-800 dark:bg-amber-950/40", className), children: [_jsxs("label", { className: "block text-xs font-semibold text-amber-900 dark:text-amber-100", htmlFor: fieldId, children: [label, " \u00B7 ", reason] }), kind === "enum" && options ? (_jsxs("select", { id: fieldId, value: String(value ?? ""), onChange: (e) => setValue(e.target.value), className: "mt-1 w-full rounded border border-gray-300 bg-white px-2 py-1 text-sm text-gray-900 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100", children: [_jsx("option", { value: "", children: "Pick one\u2026" }), options.map((o) => (_jsx("option", { value: o.id, children: o.label }, o.id)))] })) : kind === "boolean" ? (_jsxs("select", { id: fieldId, value: String(value), onChange: (e) => setValue(e.target.value === "true"), className: "mt-1 w-full rounded border border-gray-300 bg-white px-2 py-1 text-sm text-gray-900 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100", children: [_jsx("option", { value: "", children: "\u2014" }), _jsx("option", { value: "true", children: "Yes" }), _jsx("option", { value: "false", children: "No" })] })) : (_jsx("input", { id: fieldId, type: kind === "number" ? "number" : "text", value: String(value ?? ""), onChange: (e) => setValue(kind === "number" ? Number(e.target.value) : e.target.value), className: "mt-1 w-full rounded border border-gray-300 bg-white px-2 py-1 text-sm text-gray-900 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100" })), _jsxs("div", { className: "mt-2 flex gap-2", children: [_jsx("button", { type: "button", onClick: () => onConfirm(value), className: "rounded bg-indigo-600 px-3 py-1 text-xs font-semibold text-white hover:bg-indigo-700 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-indigo-400", children: "Confirm" }), _jsx("button", { type: "button", onClick: onSkip, className: "rounded bg-transparent px-3 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-100 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-gray-400 dark:text-gray-300 dark:hover:bg-gray-800", children: "Skip for now" })] })] }));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=flagged-field-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagged-field-editor.js","sourceRoot":"","sources":["../../src/components/flagged-field-editor.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAoBlC;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA8B;IAC/D,MAAM,EACJ,KAAK,EACL,IAAI,EACJ,WAAW,EACX,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,GACV,GAAG,KAAK,CAAC;IAEV,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAU,WAAW,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAE9B,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,4FAA4F,EAC5F,SAAS,CACV,aAED,iBACE,SAAS,EAAC,gEAAgE,EAC1E,OAAO,EAAE,OAAO,aAEf,KAAK,cAAK,MAAM,IACX,EACP,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,CAC5B,kBACE,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACzC,SAAS,EAAC,8IAA8I,aAExJ,iBAAQ,KAAK,EAAC,EAAE,+BAAmB,EAClC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAClB,iBAAmB,KAAK,EAAE,CAAC,CAAC,EAAE,YAC3B,CAAC,CAAC,KAAK,IADG,CAAC,CAAC,EAAE,CAER,CACV,CAAC,IACK,CACV,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CACvB,kBACE,EAAE,EAAE,OAAO,EACX,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EACpB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,EACpD,SAAS,EAAC,8IAA8I,aAExJ,iBAAQ,KAAK,EAAC,EAAE,uBAAW,EAC3B,iBAAQ,KAAK,EAAC,MAAM,oBAAa,EACjC,iBAAQ,KAAK,EAAC,OAAO,mBAAY,IAC1B,CACV,CAAC,CAAC,CAAC,CACF,gBACE,EAAE,EAAE,OAAO,EACX,IAAI,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAC3C,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CACd,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAEvE,SAAS,EAAC,8IAA8I,GACxJ,CACH,EACD,eAAK,SAAS,EAAC,iBAAiB,aAC9B,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAC/B,SAAS,EAAC,oKAAoK,wBAGvK,EACT,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAC,8MAA8M,6BAGjN,IACL,IACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { FlaggedFieldEditor } from "./flagged-field-editor";
|
|
3
|
+
declare const meta: {
|
|
4
|
+
title: string;
|
|
5
|
+
component: typeof FlaggedFieldEditor;
|
|
6
|
+
tags: string[];
|
|
7
|
+
parameters: {
|
|
8
|
+
a11y: {
|
|
9
|
+
test: string;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
argTypes: {
|
|
13
|
+
onConfirm: {
|
|
14
|
+
action: string;
|
|
15
|
+
};
|
|
16
|
+
onSkip: {
|
|
17
|
+
action: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export default meta;
|
|
22
|
+
type Story = StoryObj<typeof meta>;
|
|
23
|
+
export declare const StringField: Story;
|
|
24
|
+
export declare const NumberField: Story;
|
|
25
|
+
export declare const EnumField: Story;
|
|
26
|
+
export declare const BooleanField: Story;
|
|
27
|
+
//# sourceMappingURL=flagged-field-editor.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagged-field-editor.stories.d.ts","sourceRoot":"","sources":["../../src/components/flagged-field-editor.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAQ,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAI5D,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;CASiC,CAAC;AAE5C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAevB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAS1B,CAAC"}
|