@dainamite/cpq 0.6.0 → 0.7.0
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/CHANGELOG.md +16 -0
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +154 -178
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +69 -66
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +6 -3
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +129 -94
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
- package/dist/modules/cpq/components/ArcLineDiff.js +2 -2
- package/dist/modules/cpq/components/ArcLineDiff.js.map +2 -2
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +4 -10
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +2 -2
- package/dist/modules/cpq/components/PriceWaterfall.js +3 -1
- package/dist/modules/cpq/components/PriceWaterfall.js.map +2 -2
- package/dist/modules/cpq/components/PricingSummaryCard.js +1 -6
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +2 -2
- package/dist/modules/cpq/components/StatusPath.js +5 -3
- package/dist/modules/cpq/components/StatusPath.js.map +2 -2
- package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
- package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
- package/dist/modules/cpq/i18n/de.json +1220 -0
- package/dist/modules/cpq/i18n/en.json +1220 -0
- package/dist/modules/cpq/i18n/es.json +1220 -0
- package/dist/modules/cpq/i18n/pl.json +1220 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection-table.js +2 -2
- package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
- package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/index.js +12 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
- package/package.json +1 -1
- package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
- package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
- package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +2 -2
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +134 -112
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +66 -43
- package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +3 -3
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +134 -89
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
- package/src/modules/cpq/components/ArcLineDiff.tsx +2 -2
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +4 -2
- package/src/modules/cpq/components/PriceWaterfall.tsx +4 -2
- package/src/modules/cpq/components/PricingSummaryCard.tsx +1 -1
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/i18n/de.json +1220 -0
- package/src/modules/cpq/i18n/en.json +1220 -0
- package/src/modules/cpq/i18n/es.json +1220 -0
- package/src/modules/cpq/i18n/pl.json +1220 -0
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
- package/src/modules/cpq/widgets/injection-table.ts +8 -2
- package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
- package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
- package/src/modules/cpq/workflows/steps/index.ts +12 -0
- package/src/modules/cpq/workflows/types.ts +4 -0
|
@@ -27,13 +27,15 @@ function buildFilterParams(values, params) {
|
|
|
27
27
|
params.set("currencyCode", values.currencyCode.trim().toUpperCase());
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
function getCustomerLabel(c) {
|
|
31
|
-
return c.display_name || c.displayName || c.name || c.companyName ||
|
|
30
|
+
function getCustomerLabel(c, unnamedLabel) {
|
|
31
|
+
return c.display_name || c.displayName || c.name || c.companyName || unnamedLabel;
|
|
32
32
|
}
|
|
33
33
|
function getCustomerEmail(c) {
|
|
34
34
|
return c.primary_email || c.primaryEmail || "";
|
|
35
35
|
}
|
|
36
36
|
function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
|
|
37
|
+
const t = useT();
|
|
38
|
+
const unnamedLabel = t("cpq.quotes.customerPicker.unnamed", "Unnamed");
|
|
37
39
|
const [customers, setCustomers] = React.useState([]);
|
|
38
40
|
const [customersLoading, setCustomersLoading] = React.useState(false);
|
|
39
41
|
const [search, setSearch] = React.useState("");
|
|
@@ -57,7 +59,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
|
|
|
57
59
|
const filtered = customers.filter((c) => {
|
|
58
60
|
if (!search.trim()) return true;
|
|
59
61
|
const q = search.toLowerCase();
|
|
60
|
-
return getCustomerLabel(c).toLowerCase().includes(q) || getCustomerEmail(c).toLowerCase().includes(q) || c.id.toLowerCase().includes(q);
|
|
62
|
+
return getCustomerLabel(c, unnamedLabel).toLowerCase().includes(q) || getCustomerEmail(c).toLowerCase().includes(q) || c.id.toLowerCase().includes(q);
|
|
61
63
|
});
|
|
62
64
|
return /* @__PURE__ */ jsx(
|
|
63
65
|
"div",
|
|
@@ -71,7 +73,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
|
|
|
71
73
|
onClick: (e) => e.stopPropagation(),
|
|
72
74
|
children: [
|
|
73
75
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
|
|
74
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Select Customer for New Quote" }),
|
|
76
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: t("cpq.quotes.customerPicker.title", "Select Customer for New Quote") }),
|
|
75
77
|
/* @__PURE__ */ jsx(
|
|
76
78
|
"button",
|
|
77
79
|
{
|
|
@@ -90,7 +92,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
|
|
|
90
92
|
/* @__PURE__ */ jsx(
|
|
91
93
|
Input,
|
|
92
94
|
{
|
|
93
|
-
placeholder: "Search customers\u2026",
|
|
95
|
+
placeholder: t("cpq.quotes.customerPicker.searchPlaceholder", "Search customers\u2026"),
|
|
94
96
|
value: search,
|
|
95
97
|
onChange: (e) => setSearch(e.target.value),
|
|
96
98
|
autoFocus: true,
|
|
@@ -98,7 +100,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
|
|
|
98
100
|
}
|
|
99
101
|
)
|
|
100
102
|
] }),
|
|
101
|
-
/* @__PURE__ */ jsx("div", { className: "max-h-72 overflow-y-auto space-y-1", children: customersLoading ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: "Loading customers\u2026" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: search ? "No customers match your search." : "No customers found." }) : filtered.map((c) => /* @__PURE__ */ jsxs(
|
|
103
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-72 overflow-y-auto space-y-1", children: customersLoading ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: t("cpq.quotes.customerPicker.loading", "Loading customers\u2026") }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: search ? t("cpq.quotes.customerPicker.noMatch", "No customers match your search.") : t("cpq.quotes.customerPicker.empty", "No customers found.") }) : filtered.map((c) => /* @__PURE__ */ jsxs(
|
|
102
104
|
"button",
|
|
103
105
|
{
|
|
104
106
|
type: "button",
|
|
@@ -106,7 +108,7 @@ function CustomerPickerModal({ open, onClose, onSelect, creating, error }) {
|
|
|
106
108
|
disabled: creating,
|
|
107
109
|
className: "w-full rounded-md border px-3 py-2.5 text-left hover:border-primary hover:bg-primary/5 transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed",
|
|
108
110
|
children: [
|
|
109
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: getCustomerLabel(c) }),
|
|
111
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: getCustomerLabel(c, unnamedLabel) }),
|
|
110
112
|
getCustomerEmail(c) && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: getCustomerEmail(c) }),
|
|
111
113
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground font-mono mt-0.5", children: c.id })
|
|
112
114
|
]
|
|
@@ -134,17 +136,17 @@ function CpqQuotesListPage() {
|
|
|
134
136
|
const [createError, setCreateError] = React.useState(null);
|
|
135
137
|
const statusOptions = React.useMemo(
|
|
136
138
|
() => [
|
|
137
|
-
{ value: "new", label: "New" },
|
|
138
|
-
{ value: "incomplete", label: "Incomplete" },
|
|
139
|
-
{ value: "ready", label: "Ready" },
|
|
140
|
-
{ value: "in_approval", label: "In Approval" },
|
|
141
|
-
{ value: "approved", label: "Approved" },
|
|
142
|
-
{ value: "with_customer", label: "With Customer" },
|
|
143
|
-
{ value: "accepted", label: "Accepted" },
|
|
144
|
-
{ value: "rejected", label: "Rejected" },
|
|
145
|
-
{ value: "cancelled", label: "Cancelled" }
|
|
139
|
+
{ value: "new", label: t("cpq.quotes.status.new", "New") },
|
|
140
|
+
{ value: "incomplete", label: t("cpq.quotes.status.incomplete", "Incomplete") },
|
|
141
|
+
{ value: "ready", label: t("cpq.quotes.status.ready", "Ready") },
|
|
142
|
+
{ value: "in_approval", label: t("cpq.quotes.status.in_approval", "In Approval") },
|
|
143
|
+
{ value: "approved", label: t("cpq.quotes.status.approved", "Approved") },
|
|
144
|
+
{ value: "with_customer", label: t("cpq.quotes.status.with_customer", "With Customer") },
|
|
145
|
+
{ value: "accepted", label: t("cpq.quotes.status.accepted", "Accepted") },
|
|
146
|
+
{ value: "rejected", label: t("cpq.quotes.status.rejected", "Rejected") },
|
|
147
|
+
{ value: "cancelled", label: t("cpq.quotes.status.cancelled", "Cancelled") }
|
|
146
148
|
],
|
|
147
|
-
[]
|
|
149
|
+
[t]
|
|
148
150
|
);
|
|
149
151
|
const filters = React.useMemo(
|
|
150
152
|
() => [
|
|
@@ -177,7 +179,9 @@ function CpqQuotesListPage() {
|
|
|
177
179
|
setShowCustomerPicker(false);
|
|
178
180
|
router.push(`/backend/cpq/quotes/${body.id}`);
|
|
179
181
|
} else {
|
|
180
|
-
setCreateError(
|
|
182
|
+
setCreateError(
|
|
183
|
+
body.error ?? t("cpq.quotes.create.error", "Failed to create quote ({status})", { status: res.status })
|
|
184
|
+
);
|
|
181
185
|
}
|
|
182
186
|
} catch (err) {
|
|
183
187
|
setCreateError(err.message);
|
|
@@ -185,7 +189,7 @@ function CpqQuotesListPage() {
|
|
|
185
189
|
setCreating(false);
|
|
186
190
|
}
|
|
187
191
|
},
|
|
188
|
-
[router]
|
|
192
|
+
[router, t]
|
|
189
193
|
);
|
|
190
194
|
const columns = React.useMemo(
|
|
191
195
|
() => [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/cpq/backend/cpq/quotes/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Plus, Search as SearchIcon } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n formatStatusLabel,\n quoteCpqStatusMap,\n type QuoteCpqStatus,\n} from '../../../components/statusMaps'\n\ntype QuoteConfig = {\n id: string\n quoteId: string\n /** Resolved from SalesQuote.quoteNumber by the list API. */\n quoteNumber: string | null\n customerId: string\n cpqStatus: string\n version: number\n currencyCode: string\n pricingSummary: {\n nrcTotal?: number\n mrcTotal?: number\n } | null\n createdAt: string\n updatedAt: string\n}\n\ntype Customer = {\n id: string\n display_name?: string\n displayName?: string\n name?: string\n companyName?: string\n primary_email?: string\n primaryEmail?: string\n}\n\nconst PAGE_SIZE = 50\n\nfunction fmt(amount: number | undefined, currency: string): string {\n if (amount == null || amount === 0) return '\u2014'\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.cpqStatus === 'string' && values.cpqStatus) {\n params.set('cpqStatus', values.cpqStatus)\n }\n if (typeof values.currencyCode === 'string' && values.currencyCode.trim()) {\n params.set('currencyCode', values.currencyCode.trim().toUpperCase())\n }\n}\n\n// \u2500\u2500\u2500 Customer picker (extracted to keep main page focused) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype CustomerPickerProps = {\n open: boolean\n onClose: () => void\n onSelect: (customer: Customer) => void | Promise<void>\n creating: boolean\n error: string | null\n}\n\nfunction getCustomerLabel(c: Customer) {\n return c.display_name || c.displayName || c.name || c.companyName || 'Unnamed'\n}\n\nfunction getCustomerEmail(c: Customer) {\n return c.primary_email || c.primaryEmail || ''\n}\n\nfunction CustomerPickerModal({ open, onClose, onSelect, creating, error }: CustomerPickerProps) {\n const [customers, setCustomers] = React.useState<Customer[]>([])\n const [customersLoading, setCustomersLoading] = React.useState(false)\n const [search, setSearch] = React.useState('')\n\n React.useEffect(() => {\n if (!open) return\n let cancelled = false\n setSearch('')\n setCustomersLoading(true)\n fetch('/api/customers/companies?pageSize=100')\n .then((r) => (r.ok ? r.json() : { items: [] }))\n .then((data) => {\n if (!cancelled) setCustomers(data.items ?? data ?? [])\n })\n .catch(() => {\n if (!cancelled) setCustomers([])\n })\n .finally(() => {\n if (!cancelled) setCustomersLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [open])\n\n if (!open) return null\n\n const filtered = customers.filter((c) => {\n if (!search.trim()) return true\n const q = search.toLowerCase()\n return (\n getCustomerLabel(c).toLowerCase().includes(q) ||\n getCustomerEmail(c).toLowerCase().includes(q) ||\n c.id.toLowerCase().includes(q)\n )\n })\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={() => !creating && onClose()}\n >\n <div\n className=\"w-full max-w-lg rounded-lg border bg-card shadow-xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-sm font-medium\">Select Customer for New Quote</h3>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={creating}\n className=\"text-muted-foreground hover:text-foreground text-lg font-bold leading-none cursor-pointer disabled:cursor-not-allowed\"\n >\n ×\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n <div className=\"relative\">\n <SearchIcon className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder=\"Search customers\u2026\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n autoFocus\n className=\"pl-10\"\n />\n </div>\n <div className=\"max-h-72 overflow-y-auto space-y-1\">\n {customersLoading ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n Loading customers\u2026\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n {search ? 'No customers match your search.' : 'No customers found.'}\n </div>\n ) : (\n filtered.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => onSelect(c)}\n disabled={creating}\n className=\"w-full rounded-md border px-3 py-2.5 text-left hover:border-primary hover:bg-primary/5 transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed\"\n >\n <p className=\"text-sm font-medium\">{getCustomerLabel(c)}</p>\n {getCustomerEmail(c) && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{getCustomerEmail(c)}</p>\n )}\n <p className=\"text-xs text-muted-foreground font-mono mt-0.5\">{c.id}</p>\n </button>\n ))\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqQuotesListPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<QuoteConfig>({\n endpoint: '/api/cpq/quotes',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.quotes.list.error.load', 'Failed to load quotes'),\n })\n\n const [showCustomerPicker, setShowCustomerPicker] = React.useState(false)\n const [creating, setCreating] = React.useState(false)\n const [createError, setCreateError] = React.useState<string | null>(null)\n\n const statusOptions = React.useMemo(\n () => [\n { value: 'new', label: 'New' },\n { value: 'incomplete', label: 'Incomplete' },\n { value: 'ready', label: 'Ready' },\n { value: 'in_approval', label: 'In Approval' },\n { value: 'approved', label: 'Approved' },\n { value: 'with_customer', label: 'With Customer' },\n { value: 'accepted', label: 'Accepted' },\n { value: 'rejected', label: 'Rejected' },\n { value: 'cancelled', label: 'Cancelled' },\n ],\n [],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'cpqStatus',\n label: t('cpq.quotes.filters.status', 'Status'),\n type: 'select',\n options: statusOptions,\n },\n {\n id: 'currencyCode',\n label: t('cpq.quotes.filters.currency', 'Currency'),\n type: 'text',\n },\n ],\n [statusOptions, t],\n )\n\n const selectCustomer = React.useCallback(\n async (customer: Customer) => {\n setCreating(true)\n setCreateError(null)\n try {\n const res = await fetch('/api/cpq/quotes', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ customerId: customer.id }),\n })\n const body = await res.json()\n if (res.ok) {\n setShowCustomerPicker(false)\n router.push(`/backend/cpq/quotes/${body.id}`)\n } else {\n setCreateError(body.error ?? `Failed to create quote (${res.status})`)\n }\n } catch (err) {\n setCreateError((err as Error).message)\n } finally {\n setCreating(false)\n }\n },\n [router],\n )\n\n const columns = React.useMemo<ColumnDef<QuoteConfig>[]>(\n () => [\n {\n accessorKey: 'quoteNumber',\n header: t('cpq.quotes.table.quoteNumber', 'Quote Number'),\n cell: ({ row }) => (\n <span className=\"font-medium\">\n {row.original.quoteNumber ?? (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {(row.original.quoteId ?? row.original.id).slice(0, 8)}\u2026\n </span>\n )}\n </span>\n ),\n },\n {\n accessorKey: 'cpqStatus',\n header: t('cpq.quotes.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={quoteCpqStatusMap[row.original.cpqStatus as QuoteCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.cpqStatus)}\n </Tag>\n ),\n },\n {\n accessorKey: 'version',\n header: t('cpq.quotes.table.version', 'Version'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">v{row.original.version}</span>,\n },\n {\n accessorKey: 'currencyCode',\n header: t('cpq.quotes.table.currency', 'Currency'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.currencyCode}</span>,\n },\n {\n id: 'nrcTotal',\n header: t('cpq.quotes.table.nrcTotal', 'NRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.nrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n id: 'mrcTotal',\n header: t('cpq.quotes.table.mrcTotal', 'MRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.mrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.quotes.table.createdAt', 'Created'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(row.original.createdAt).toLocaleDateString()}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<QuoteConfig>\n title={t('cpq.quotes.list.title', 'CPQ Quotes')}\n tableId=\"cpq.quotes.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.quotes.search.placeholder', 'Search by quote / customer id...')}\n actions={\n <Button type=\"button\" onClick={() => setShowCustomerPicker(true)} className=\"gap-1.5\">\n <Plus className=\"h-4 w-4\" />\n {t('cpq.quotes.add', 'New Quote')}\n </Button>\n }\n onRowClick={(row) => router.push(`/backend/cpq/quotes/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('cpq.quotes.actions.open', 'Open'),\n href: `/backend/cpq/quotes/${row.id}`,\n },\n ]}\n />\n )}\n toolbarContent={\n <CustomerPickerModal\n open={showCustomerPicker}\n onClose={() => setShowCustomerPicker(false)}\n onSelect={selectCustomer}\n creating={creating}\n error={createError}\n />\n }\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.quotes.empty',\n 'No CPQ quotes found. Create one to start configuring offerings for a customer.',\n )}\n </div>\n }\n />\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Plus, Search as SearchIcon } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport {\n formatStatusLabel,\n quoteCpqStatusMap,\n type QuoteCpqStatus,\n} from '../../../components/statusMaps'\n\ntype QuoteConfig = {\n id: string\n quoteId: string\n /** Resolved from SalesQuote.quoteNumber by the list API. */\n quoteNumber: string | null\n customerId: string\n cpqStatus: string\n version: number\n currencyCode: string\n pricingSummary: {\n nrcTotal?: number\n mrcTotal?: number\n } | null\n createdAt: string\n updatedAt: string\n}\n\ntype Customer = {\n id: string\n display_name?: string\n displayName?: string\n name?: string\n companyName?: string\n primary_email?: string\n primaryEmail?: string\n}\n\nconst PAGE_SIZE = 50\n\nfunction fmt(amount: number | undefined, currency: string): string {\n if (amount == null || amount === 0) return '\u2014'\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.cpqStatus === 'string' && values.cpqStatus) {\n params.set('cpqStatus', values.cpqStatus)\n }\n if (typeof values.currencyCode === 'string' && values.currencyCode.trim()) {\n params.set('currencyCode', values.currencyCode.trim().toUpperCase())\n }\n}\n\n// \u2500\u2500\u2500 Customer picker (extracted to keep main page focused) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype CustomerPickerProps = {\n open: boolean\n onClose: () => void\n onSelect: (customer: Customer) => void | Promise<void>\n creating: boolean\n error: string | null\n}\n\nfunction getCustomerLabel(c: Customer, unnamedLabel: string) {\n return c.display_name || c.displayName || c.name || c.companyName || unnamedLabel\n}\n\nfunction getCustomerEmail(c: Customer) {\n return c.primary_email || c.primaryEmail || ''\n}\n\nfunction CustomerPickerModal({ open, onClose, onSelect, creating, error }: CustomerPickerProps) {\n const t = useT()\n const unnamedLabel = t('cpq.quotes.customerPicker.unnamed', 'Unnamed')\n const [customers, setCustomers] = React.useState<Customer[]>([])\n const [customersLoading, setCustomersLoading] = React.useState(false)\n const [search, setSearch] = React.useState('')\n\n React.useEffect(() => {\n if (!open) return\n let cancelled = false\n setSearch('')\n setCustomersLoading(true)\n fetch('/api/customers/companies?pageSize=100')\n .then((r) => (r.ok ? r.json() : { items: [] }))\n .then((data) => {\n if (!cancelled) setCustomers(data.items ?? data ?? [])\n })\n .catch(() => {\n if (!cancelled) setCustomers([])\n })\n .finally(() => {\n if (!cancelled) setCustomersLoading(false)\n })\n return () => {\n cancelled = true\n }\n }, [open])\n\n if (!open) return null\n\n const filtered = customers.filter((c) => {\n if (!search.trim()) return true\n const q = search.toLowerCase()\n return (\n getCustomerLabel(c, unnamedLabel).toLowerCase().includes(q) ||\n getCustomerEmail(c).toLowerCase().includes(q) ||\n c.id.toLowerCase().includes(q)\n )\n })\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50\"\n onClick={() => !creating && onClose()}\n >\n <div\n className=\"w-full max-w-lg rounded-lg border bg-card shadow-xl\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-sm font-medium\">\n {t('cpq.quotes.customerPicker.title', 'Select Customer for New Quote')}\n </h3>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={creating}\n className=\"text-muted-foreground hover:text-foreground text-lg font-bold leading-none cursor-pointer disabled:cursor-not-allowed\"\n >\n {'\\u00d7'}\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n <div className=\"relative\">\n <SearchIcon className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n placeholder={t('cpq.quotes.customerPicker.searchPlaceholder', 'Search customers\u2026')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n autoFocus\n className=\"pl-10\"\n />\n </div>\n <div className=\"max-h-72 overflow-y-auto space-y-1\">\n {customersLoading ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n {t('cpq.quotes.customerPicker.loading', 'Loading customers\u2026')}\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"py-8 text-center text-sm text-muted-foreground\">\n {search\n ? t('cpq.quotes.customerPicker.noMatch', 'No customers match your search.')\n : t('cpq.quotes.customerPicker.empty', 'No customers found.')}\n </div>\n ) : (\n filtered.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => onSelect(c)}\n disabled={creating}\n className=\"w-full rounded-md border px-3 py-2.5 text-left hover:border-primary hover:bg-primary/5 transition-colors disabled:opacity-50 cursor-pointer disabled:cursor-not-allowed\"\n >\n <p className=\"text-sm font-medium\">{getCustomerLabel(c, unnamedLabel)}</p>\n {getCustomerEmail(c) && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{getCustomerEmail(c)}</p>\n )}\n <p className=\"text-xs text-muted-foreground font-mono mt-0.5\">{c.id}</p>\n </button>\n ))\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqQuotesListPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<QuoteConfig>({\n endpoint: '/api/cpq/quotes',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.quotes.list.error.load', 'Failed to load quotes'),\n })\n\n const [showCustomerPicker, setShowCustomerPicker] = React.useState(false)\n const [creating, setCreating] = React.useState(false)\n const [createError, setCreateError] = React.useState<string | null>(null)\n\n const statusOptions = React.useMemo(\n () => [\n { value: 'new', label: t('cpq.quotes.status.new', 'New') },\n { value: 'incomplete', label: t('cpq.quotes.status.incomplete', 'Incomplete') },\n { value: 'ready', label: t('cpq.quotes.status.ready', 'Ready') },\n { value: 'in_approval', label: t('cpq.quotes.status.in_approval', 'In Approval') },\n { value: 'approved', label: t('cpq.quotes.status.approved', 'Approved') },\n { value: 'with_customer', label: t('cpq.quotes.status.with_customer', 'With Customer') },\n { value: 'accepted', label: t('cpq.quotes.status.accepted', 'Accepted') },\n { value: 'rejected', label: t('cpq.quotes.status.rejected', 'Rejected') },\n { value: 'cancelled', label: t('cpq.quotes.status.cancelled', 'Cancelled') },\n ],\n [t],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'cpqStatus',\n label: t('cpq.quotes.filters.status', 'Status'),\n type: 'select',\n options: statusOptions,\n },\n {\n id: 'currencyCode',\n label: t('cpq.quotes.filters.currency', 'Currency'),\n type: 'text',\n },\n ],\n [statusOptions, t],\n )\n\n const selectCustomer = React.useCallback(\n async (customer: Customer) => {\n setCreating(true)\n setCreateError(null)\n try {\n const res = await fetch('/api/cpq/quotes', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ customerId: customer.id }),\n })\n const body = await res.json()\n if (res.ok) {\n setShowCustomerPicker(false)\n router.push(`/backend/cpq/quotes/${body.id}`)\n } else {\n setCreateError(\n body.error ?? t('cpq.quotes.create.error', 'Failed to create quote ({status})', { status: res.status }),\n )\n }\n } catch (err) {\n setCreateError((err as Error).message)\n } finally {\n setCreating(false)\n }\n },\n [router, t],\n )\n\n const columns = React.useMemo<ColumnDef<QuoteConfig>[]>(\n () => [\n {\n accessorKey: 'quoteNumber',\n header: t('cpq.quotes.table.quoteNumber', 'Quote Number'),\n cell: ({ row }) => (\n <span className=\"font-medium\">\n {row.original.quoteNumber ?? (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {(row.original.quoteId ?? row.original.id).slice(0, 8)}\u2026\n </span>\n )}\n </span>\n ),\n },\n {\n accessorKey: 'cpqStatus',\n header: t('cpq.quotes.table.status', 'Status'),\n cell: ({ row }) => (\n <Tag variant={quoteCpqStatusMap[row.original.cpqStatus as QuoteCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.cpqStatus)}\n </Tag>\n ),\n },\n {\n accessorKey: 'version',\n header: t('cpq.quotes.table.version', 'Version'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">v{row.original.version}</span>,\n },\n {\n accessorKey: 'currencyCode',\n header: t('cpq.quotes.table.currency', 'Currency'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.currencyCode}</span>,\n },\n {\n id: 'nrcTotal',\n header: t('cpq.quotes.table.nrcTotal', 'NRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.nrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n id: 'mrcTotal',\n header: t('cpq.quotes.table.mrcTotal', 'MRC Total'),\n cell: ({ row }) => (\n <span className=\"font-mono text-xs\">\n {fmt(row.original.pricingSummary?.mrcTotal, row.original.currencyCode)}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.quotes.table.createdAt', 'Created'),\n cell: ({ row }) => (\n <span className=\"text-xs text-muted-foreground\">\n {new Date(row.original.createdAt).toLocaleDateString()}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<QuoteConfig>\n title={t('cpq.quotes.list.title', 'CPQ Quotes')}\n tableId=\"cpq.quotes.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.quotes.search.placeholder', 'Search by quote / customer id...')}\n actions={\n <Button type=\"button\" onClick={() => setShowCustomerPicker(true)} className=\"gap-1.5\">\n <Plus className=\"h-4 w-4\" />\n {t('cpq.quotes.add', 'New Quote')}\n </Button>\n }\n onRowClick={(row) => router.push(`/backend/cpq/quotes/${row.id}`)}\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'open',\n label: t('cpq.quotes.actions.open', 'Open'),\n href: `/backend/cpq/quotes/${row.id}`,\n },\n ]}\n />\n )}\n toolbarContent={\n <CustomerPickerModal\n open={showCustomerPicker}\n onClose={() => setShowCustomerPicker(false)}\n onSelect={selectCustomer}\n creating={creating}\n error={createError}\n />\n }\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.quotes.empty',\n 'No CPQ quotes found. Create one to start configuring offerings for a customer.',\n )}\n </div>\n }\n />\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,6 +5,7 @@ import { Button } from "@open-mercato/ui/primitives/button";
|
|
|
5
5
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
6
6
|
import { Checkbox } from "@open-mercato/ui/primitives/checkbox";
|
|
7
7
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
8
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
9
|
function thenInitialFor(effect) {
|
|
9
10
|
switch (effect) {
|
|
10
11
|
case "filter_options":
|
|
@@ -30,6 +31,7 @@ function DependsOnRulesEditor({
|
|
|
30
31
|
parentOptions,
|
|
31
32
|
childOptions
|
|
32
33
|
}) {
|
|
34
|
+
const t = useT();
|
|
33
35
|
const updateRule = (index, patch) => {
|
|
34
36
|
const next = rules.map((r, i) => i === index ? { ...r, ...patch } : r);
|
|
35
37
|
onChange(next);
|
|
@@ -42,10 +44,10 @@ function DependsOnRulesEditor({
|
|
|
42
44
|
};
|
|
43
45
|
if (rules.length === 0) {
|
|
44
46
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
45
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "No rules yet. Add at least one to make this dependency effective." }),
|
|
47
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.specifications.dependsOn.empty", "No rules yet. Add at least one to make this dependency effective.") }),
|
|
46
48
|
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRule, children: [
|
|
47
49
|
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
48
|
-
"Add rule"
|
|
50
|
+
t("cpq.specifications.dependsOn.addRule", "Add rule")
|
|
49
51
|
] })
|
|
50
52
|
] });
|
|
51
53
|
}
|
|
@@ -65,7 +67,7 @@ function DependsOnRulesEditor({
|
|
|
65
67
|
)) }),
|
|
66
68
|
/* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRule, children: [
|
|
67
69
|
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5" }),
|
|
68
|
-
"Add rule"
|
|
70
|
+
t("cpq.specifications.dependsOn.addRule", "Add rule")
|
|
69
71
|
] })
|
|
70
72
|
] });
|
|
71
73
|
}
|
|
@@ -78,9 +80,10 @@ function RuleRow({
|
|
|
78
80
|
onThenChange,
|
|
79
81
|
onRemove
|
|
80
82
|
}) {
|
|
83
|
+
const t = useT();
|
|
81
84
|
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr_auto] items-start gap-2 rounded-md border bg-muted/20 p-2", children: [
|
|
82
85
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
83
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "When parent =" }),
|
|
86
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.whenParent", "When parent =") }),
|
|
84
87
|
parentOptions && parentOptions.length > 0 ? /* @__PURE__ */ jsxs(
|
|
85
88
|
"select",
|
|
86
89
|
{
|
|
@@ -88,7 +91,7 @@ function RuleRow({
|
|
|
88
91
|
onChange: (e) => onWhenChange(e.target.value),
|
|
89
92
|
className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
|
|
90
93
|
children: [
|
|
91
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select value \u2014" }),
|
|
94
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.dependsOn.selectValue", "\u2014 Select value \u2014") }),
|
|
92
95
|
parentOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
93
96
|
]
|
|
94
97
|
}
|
|
@@ -97,7 +100,7 @@ function RuleRow({
|
|
|
97
100
|
{
|
|
98
101
|
value: rule.when,
|
|
99
102
|
onChange: (e) => onWhenChange(e.target.value),
|
|
100
|
-
placeholder: "Parent value"
|
|
103
|
+
placeholder: t("cpq.specifications.dependsOn.parentValuePlaceholder", "Parent value")
|
|
101
104
|
}
|
|
102
105
|
)
|
|
103
106
|
] }),
|
|
@@ -117,17 +120,18 @@ function RuleRow({
|
|
|
117
120
|
type: "button",
|
|
118
121
|
onClick: onRemove,
|
|
119
122
|
className: "mt-5 self-start rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
|
|
120
|
-
title: "Remove rule",
|
|
123
|
+
title: t("cpq.specifications.dependsOn.removeRule", "Remove rule"),
|
|
121
124
|
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
122
125
|
}
|
|
123
126
|
)
|
|
124
127
|
] });
|
|
125
128
|
}
|
|
126
129
|
function ThenInput({ effect, value, childOptions, onChange }) {
|
|
130
|
+
const t = useT();
|
|
127
131
|
if (effect === "toggle_required") {
|
|
128
132
|
const isRequired = value === true;
|
|
129
133
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
130
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then required is" }),
|
|
134
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenRequiredIs", "Then required is") }),
|
|
131
135
|
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 rounded-md border bg-background px-2 py-1.5 text-sm cursor-pointer", children: [
|
|
132
136
|
/* @__PURE__ */ jsx(
|
|
133
137
|
Checkbox,
|
|
@@ -136,13 +140,13 @@ function ThenInput({ effect, value, childOptions, onChange }) {
|
|
|
136
140
|
onCheckedChange: (checked) => onChange(checked === true)
|
|
137
141
|
}
|
|
138
142
|
),
|
|
139
|
-
/* @__PURE__ */ jsx("span", { children: isRequired ? "Required" : "Optional" })
|
|
143
|
+
/* @__PURE__ */ jsx("span", { children: isRequired ? t("cpq.specifications.dependsOn.required", "Required") : t("cpq.specifications.dependsOn.optional", "Optional") })
|
|
140
144
|
] })
|
|
141
145
|
] });
|
|
142
146
|
}
|
|
143
147
|
if (effect === "set_value") {
|
|
144
148
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
145
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then set value to" }),
|
|
149
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenSetValueTo", "Then set value to") }),
|
|
146
150
|
childOptions && childOptions.length > 0 ? /* @__PURE__ */ jsxs(
|
|
147
151
|
"select",
|
|
148
152
|
{
|
|
@@ -150,7 +154,7 @@ function ThenInput({ effect, value, childOptions, onChange }) {
|
|
|
150
154
|
onChange: (e) => onChange(e.target.value),
|
|
151
155
|
className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
|
|
152
156
|
children: [
|
|
153
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select value \u2014" }),
|
|
157
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.dependsOn.selectValue", "\u2014 Select value \u2014") }),
|
|
154
158
|
childOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
155
159
|
]
|
|
156
160
|
}
|
|
@@ -159,7 +163,7 @@ function ThenInput({ effect, value, childOptions, onChange }) {
|
|
|
159
163
|
{
|
|
160
164
|
value: typeof value === "string" ? value : "",
|
|
161
165
|
onChange: (e) => onChange(e.target.value),
|
|
162
|
-
placeholder: "Value"
|
|
166
|
+
placeholder: t("cpq.specifications.dependsOn.valuePlaceholder", "Value")
|
|
163
167
|
}
|
|
164
168
|
)
|
|
165
169
|
] });
|
|
@@ -172,7 +176,7 @@ function ThenInput({ effect, value, childOptions, onChange }) {
|
|
|
172
176
|
);
|
|
173
177
|
};
|
|
174
178
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
175
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then allow only" }),
|
|
179
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenAllowOnly", "Then allow only") }),
|
|
176
180
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 rounded-md border bg-background px-2 py-1.5 text-sm min-h-[34px]", children: childOptions.map((opt) => {
|
|
177
181
|
const isOn = selected.includes(opt.value);
|
|
178
182
|
return /* @__PURE__ */ jsx(
|
|
@@ -197,13 +201,13 @@ function ThenInput({ effect, value, childOptions, onChange }) {
|
|
|
197
201
|
] });
|
|
198
202
|
}
|
|
199
203
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
200
|
-
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: "Then allow only (comma-separated)" }),
|
|
204
|
+
/* @__PURE__ */ jsx("label", { className: "block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5", children: t("cpq.specifications.dependsOn.thenAllowOnlyCsv", "Then allow only (comma-separated)") }),
|
|
201
205
|
/* @__PURE__ */ jsx(
|
|
202
206
|
Input,
|
|
203
207
|
{
|
|
204
208
|
value: thenAsCsv(value),
|
|
205
209
|
onChange: (e) => onChange(csvAsArray(e.target.value)),
|
|
206
|
-
placeholder: "value1, value2, value3"
|
|
210
|
+
placeholder: t("cpq.specifications.dependsOn.csvPlaceholder", "value1, value2, value3")
|
|
207
211
|
}
|
|
208
212
|
)
|
|
209
213
|
] });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/cpq/backend/cpq/specifications/%5Bid%5D/DependsOnRulesEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Plus, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport type { DependsOnEffect, DependsOnRule } from './page'\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype AttributeOption = { value: string; label: string }\n\nfunction thenInitialFor(effect: DependsOnEffect): unknown {\n switch (effect) {\n case 'filter_options':\n return [] as string[]\n case 'set_value':\n return ''\n case 'toggle_required':\n return false\n }\n}\n\nfunction thenAsCsv(value: unknown): string {\n if (Array.isArray(value)) return value.join(', ')\n if (typeof value === 'string') return value\n return ''\n}\n\nfunction csvAsArray(input: string): string[] {\n return input\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0)\n}\n\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type DependsOnRulesEditorProps = {\n effect: DependsOnEffect\n rules: DependsOnRule[]\n onChange: (rules: DependsOnRule[]) => void\n /** Options of the parent attribute (used as \"When\" dropdown if available). */\n parentOptions?: AttributeOption[] | null\n /**\n * Options of the **child** (= this attribute being edited). Only used by\n * `filter_options` so the \"then\" picker offers checkboxes instead of CSV.\n */\n childOptions?: AttributeOption[] | null\n}\n\n/**\n * Renders the rule rows for an attribute dependency. The shape of each\n * rule's `then` field depends on the effect type \u2014 see `page.tsx`\n * `DependsOnRule` doc for the full contract.\n *\n * Each effect type has tailored input affordances:\n * - `filter_options`: child options shown as checkboxes (or CSV input if\n * the child has no enumerated options).\n * - `set_value`: free-text value input.\n * - `toggle_required`: required on/off checkbox.\n */\nexport function DependsOnRulesEditor({\n effect,\n rules,\n onChange,\n parentOptions,\n childOptions,\n}: DependsOnRulesEditorProps) {\n const updateRule = (index: number, patch: Partial<DependsOnRule>) => {\n const next = rules.map((r, i) => (i === index ? { ...r, ...patch } : r))\n onChange(next)\n }\n\n const removeRule = (index: number) => {\n onChange(rules.filter((_, i) => i !== index))\n }\n\n const addRule = () => {\n onChange([...rules, { when: '', then: thenInitialFor(effect) }])\n }\n\n if (rules.length === 0) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n No rules yet. Add at least one to make this dependency effective.\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n Add rule\n </Button>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-2\">\n <div className=\"space-y-2\">\n {rules.map((rule, idx) => (\n <RuleRow\n key={idx}\n effect={effect}\n rule={rule}\n parentOptions={parentOptions}\n childOptions={childOptions}\n onWhenChange={(when) => updateRule(idx, { when })}\n onThenChange={(then) => updateRule(idx, { then })}\n onRemove={() => removeRule(idx)}\n />\n ))}\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n Add rule\n </Button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Single rule row (decomposed for readability) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype RuleRowProps = {\n effect: DependsOnEffect\n rule: DependsOnRule\n parentOptions?: AttributeOption[] | null\n childOptions?: AttributeOption[] | null\n onWhenChange: (when: string) => void\n onThenChange: (then: unknown) => void\n onRemove: () => void\n}\n\nfunction RuleRow({\n effect,\n rule,\n parentOptions,\n childOptions,\n onWhenChange,\n onThenChange,\n onRemove,\n}: RuleRowProps) {\n return (\n <div className=\"grid grid-cols-[1fr_auto_1fr_auto] items-start gap-2 rounded-md border bg-muted/20 p-2\">\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n When parent =\n </label>\n {parentOptions && parentOptions.length > 0 ? (\n <select\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">\u2014 Select value \u2014</option>\n {parentOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n placeholder=\"Parent value\"\n />\n )}\n </div>\n <div className=\"mt-5 self-center text-muted-foreground\">\u2192</div>\n <ThenInput\n effect={effect}\n value={rule.then}\n childOptions={childOptions}\n onChange={onThenChange}\n />\n <button\n type=\"button\"\n onClick={onRemove}\n className=\"mt-5 self-start rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors\"\n title=\"Remove rule\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Effect-specific \"then\" inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ThenInputProps = {\n effect: DependsOnEffect\n value: unknown\n childOptions?: AttributeOption[] | null\n onChange: (next: unknown) => void\n}\n\nfunction ThenInput({ effect, value, childOptions, onChange }: ThenInputProps) {\n if (effect === 'toggle_required') {\n const isRequired = value === true\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then required is\n </label>\n <label className=\"flex items-center gap-2 rounded-md border bg-background px-2 py-1.5 text-sm cursor-pointer\">\n <Checkbox\n checked={isRequired}\n onCheckedChange={(checked) => onChange(checked === true)}\n />\n <span>{isRequired ? 'Required' : 'Optional'}</span>\n </label>\n </div>\n )\n }\n\n if (effect === 'set_value') {\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then set value to\n </label>\n {childOptions && childOptions.length > 0 ? (\n <select\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">\u2014 Select value \u2014</option>\n {childOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder=\"Value\"\n />\n )}\n </div>\n )\n }\n\n // effect === 'filter_options'\n const selected = Array.isArray(value) ? (value as string[]) : []\n if (childOptions && childOptions.length > 0) {\n const toggle = (optValue: string) => {\n onChange(\n selected.includes(optValue)\n ? selected.filter((v) => v !== optValue)\n : [...selected, optValue],\n )\n }\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then allow only\n </label>\n <div className=\"flex flex-wrap gap-1.5 rounded-md border bg-background px-2 py-1.5 text-sm min-h-[34px]\">\n {childOptions.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => toggle(opt.value)}\n className=\"cursor-pointer\"\n >\n <Tag\n variant={isOn ? 'success' : 'neutral'}\n dot={isOn}\n className=\"px-2 text-xs\"\n >\n {opt.label}\n </Tag>\n </button>\n )\n })}\n </div>\n </div>\n )\n }\n\n // No enumerated child options \u2014 fall back to CSV.\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n Then allow only (comma-separated)\n </label>\n <Input\n value={thenAsCsv(value)}\n onChange={(e) => onChange(csvAsArray(e.target.value))}\n placeholder=\"value1, value2, value3\"\n />\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Plus, X } from 'lucide-react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DependsOnEffect, DependsOnRule } from './page'\n\n// \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype AttributeOption = { value: string; label: string }\n\nfunction thenInitialFor(effect: DependsOnEffect): unknown {\n switch (effect) {\n case 'filter_options':\n return [] as string[]\n case 'set_value':\n return ''\n case 'toggle_required':\n return false\n }\n}\n\nfunction thenAsCsv(value: unknown): string {\n if (Array.isArray(value)) return value.join(', ')\n if (typeof value === 'string') return value\n return ''\n}\n\nfunction csvAsArray(input: string): string[] {\n return input\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0)\n}\n\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport type DependsOnRulesEditorProps = {\n effect: DependsOnEffect\n rules: DependsOnRule[]\n onChange: (rules: DependsOnRule[]) => void\n /** Options of the parent attribute (used as \"When\" dropdown if available). */\n parentOptions?: AttributeOption[] | null\n /**\n * Options of the **child** (= this attribute being edited). Only used by\n * `filter_options` so the \"then\" picker offers checkboxes instead of CSV.\n */\n childOptions?: AttributeOption[] | null\n}\n\n/**\n * Renders the rule rows for an attribute dependency. The shape of each\n * rule's `then` field depends on the effect type \u2014 see `page.tsx`\n * `DependsOnRule` doc for the full contract.\n *\n * Each effect type has tailored input affordances:\n * - `filter_options`: child options shown as checkboxes (or CSV input if\n * the child has no enumerated options).\n * - `set_value`: free-text value input.\n * - `toggle_required`: required on/off checkbox.\n */\nexport function DependsOnRulesEditor({\n effect,\n rules,\n onChange,\n parentOptions,\n childOptions,\n}: DependsOnRulesEditorProps) {\n const t = useT()\n const updateRule = (index: number, patch: Partial<DependsOnRule>) => {\n const next = rules.map((r, i) => (i === index ? { ...r, ...patch } : r))\n onChange(next)\n }\n\n const removeRule = (index: number) => {\n onChange(rules.filter((_, i) => i !== index))\n }\n\n const addRule = () => {\n onChange([...rules, { when: '', then: thenInitialFor(effect) }])\n }\n\n if (rules.length === 0) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-xs text-muted-foreground\">\n {t('cpq.specifications.dependsOn.empty', 'No rules yet. Add at least one to make this dependency effective.')}\n </p>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n {t('cpq.specifications.dependsOn.addRule', 'Add rule')}\n </Button>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-2\">\n <div className=\"space-y-2\">\n {rules.map((rule, idx) => (\n <RuleRow\n key={idx}\n effect={effect}\n rule={rule}\n parentOptions={parentOptions}\n childOptions={childOptions}\n onWhenChange={(when) => updateRule(idx, { when })}\n onThenChange={(then) => updateRule(idx, { then })}\n onRemove={() => removeRule(idx)}\n />\n ))}\n </div>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={addRule}>\n <Plus className=\"h-3.5 w-3.5\" />\n {t('cpq.specifications.dependsOn.addRule', 'Add rule')}\n </Button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Single rule row (decomposed for readability) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype RuleRowProps = {\n effect: DependsOnEffect\n rule: DependsOnRule\n parentOptions?: AttributeOption[] | null\n childOptions?: AttributeOption[] | null\n onWhenChange: (when: string) => void\n onThenChange: (then: unknown) => void\n onRemove: () => void\n}\n\nfunction RuleRow({\n effect,\n rule,\n parentOptions,\n childOptions,\n onWhenChange,\n onThenChange,\n onRemove,\n}: RuleRowProps) {\n const t = useT()\n return (\n <div className=\"grid grid-cols-[1fr_auto_1fr_auto] items-start gap-2 rounded-md border bg-muted/20 p-2\">\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.whenParent', 'When parent =')}\n </label>\n {parentOptions && parentOptions.length > 0 ? (\n <select\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">{t('cpq.specifications.dependsOn.selectValue', '\u2014 Select value \u2014')}</option>\n {parentOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={rule.when}\n onChange={(e) => onWhenChange(e.target.value)}\n placeholder={t('cpq.specifications.dependsOn.parentValuePlaceholder', 'Parent value')}\n />\n )}\n </div>\n <div className=\"mt-5 self-center text-muted-foreground\">\u2192</div>\n <ThenInput\n effect={effect}\n value={rule.then}\n childOptions={childOptions}\n onChange={onThenChange}\n />\n <button\n type=\"button\"\n onClick={onRemove}\n className=\"mt-5 self-start rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors\"\n title={t('cpq.specifications.dependsOn.removeRule', 'Remove rule')}\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Effect-specific \"then\" inputs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ntype ThenInputProps = {\n effect: DependsOnEffect\n value: unknown\n childOptions?: AttributeOption[] | null\n onChange: (next: unknown) => void\n}\n\nfunction ThenInput({ effect, value, childOptions, onChange }: ThenInputProps) {\n const t = useT()\n if (effect === 'toggle_required') {\n const isRequired = value === true\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenRequiredIs', 'Then required is')}\n </label>\n <label className=\"flex items-center gap-2 rounded-md border bg-background px-2 py-1.5 text-sm cursor-pointer\">\n <Checkbox\n checked={isRequired}\n onCheckedChange={(checked) => onChange(checked === true)}\n />\n <span>{isRequired ? t('cpq.specifications.dependsOn.required', 'Required') : t('cpq.specifications.dependsOn.optional', 'Optional')}</span>\n </label>\n </div>\n )\n }\n\n if (effect === 'set_value') {\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenSetValueTo', 'Then set value to')}\n </label>\n {childOptions && childOptions.length > 0 ? (\n <select\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n className=\"w-full rounded-md border bg-background px-2 py-1.5 text-sm\"\n >\n <option value=\"\">{t('cpq.specifications.dependsOn.selectValue', '\u2014 Select value \u2014')}</option>\n {childOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n ) : (\n <Input\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={t('cpq.specifications.dependsOn.valuePlaceholder', 'Value')}\n />\n )}\n </div>\n )\n }\n\n // effect === 'filter_options'\n const selected = Array.isArray(value) ? (value as string[]) : []\n if (childOptions && childOptions.length > 0) {\n const toggle = (optValue: string) => {\n onChange(\n selected.includes(optValue)\n ? selected.filter((v) => v !== optValue)\n : [...selected, optValue],\n )\n }\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenAllowOnly', 'Then allow only')}\n </label>\n <div className=\"flex flex-wrap gap-1.5 rounded-md border bg-background px-2 py-1.5 text-sm min-h-[34px]\">\n {childOptions.map((opt) => {\n const isOn = selected.includes(opt.value)\n return (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => toggle(opt.value)}\n className=\"cursor-pointer\"\n >\n <Tag\n variant={isOn ? 'success' : 'neutral'}\n dot={isOn}\n className=\"px-2 text-xs\"\n >\n {opt.label}\n </Tag>\n </button>\n )\n })}\n </div>\n </div>\n )\n }\n\n // No enumerated child options \u2014 fall back to CSV.\n return (\n <div>\n <label className=\"block text-[10px] font-medium uppercase tracking-wide text-muted-foreground mb-0.5\">\n {t('cpq.specifications.dependsOn.thenAllowOnlyCsv', 'Then allow only (comma-separated)')}\n </label>\n <Input\n value={thenAsCsv(value)}\n onChange={(e) => onChange(csvAsArray(e.target.value))}\n placeholder={t('cpq.specifications.dependsOn.csvPlaceholder', 'value1, value2, value3')}\n />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|