@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
|
@@ -26,17 +26,30 @@ const ARC_BADGE_VARIANTS = {
|
|
|
26
26
|
renew: "success",
|
|
27
27
|
cancel: "error"
|
|
28
28
|
};
|
|
29
|
+
function quoteTypeLabel(t, quoteType) {
|
|
30
|
+
switch (quoteType) {
|
|
31
|
+
case "amend":
|
|
32
|
+
return t("cpq.quotes.type.amend", "amend");
|
|
33
|
+
case "renew":
|
|
34
|
+
return t("cpq.quotes.type.renew", "renew");
|
|
35
|
+
case "cancel":
|
|
36
|
+
return t("cpq.quotes.type.cancel", "cancel");
|
|
37
|
+
default:
|
|
38
|
+
return quoteType;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
29
41
|
function fmt(amount, currency) {
|
|
30
42
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);
|
|
31
43
|
}
|
|
32
|
-
async function apiJson(url, init) {
|
|
44
|
+
async function apiJson(t, url, init) {
|
|
33
45
|
const res = await fetch(url, init);
|
|
34
46
|
const json = await res.json();
|
|
35
|
-
if (!res.ok) throw new Error(json?.error ??
|
|
47
|
+
if (!res.ok) throw new Error(json?.error ?? t("cpq.common.requestFailed", "Request failed ({status})", { status: res.status }));
|
|
36
48
|
return json;
|
|
37
49
|
}
|
|
38
50
|
function CpqOrderDetailPage(props) {
|
|
39
51
|
const router = useRouter();
|
|
52
|
+
const t = useT();
|
|
40
53
|
const urlParams = useParams();
|
|
41
54
|
const cpqOrderId = props.params?.id ?? urlParams?.id;
|
|
42
55
|
const [order, setOrder] = React.useState(null);
|
|
@@ -56,7 +69,7 @@ function CpqOrderDetailPage(props) {
|
|
|
56
69
|
const targets = d?.items ?? [];
|
|
57
70
|
const fetched = await Promise.all(
|
|
58
71
|
targets.map(
|
|
59
|
-
(
|
|
72
|
+
(target) => fetch(`/api/cpq/inventory/subscriptions?id=${target.subscriptionId}`).then((r) => r.ok ? r.json() : null).catch(() => null)
|
|
60
73
|
)
|
|
61
74
|
);
|
|
62
75
|
return fetched.filter(Boolean);
|
|
@@ -77,7 +90,7 @@ function CpqOrderDetailPage(props) {
|
|
|
77
90
|
let cancelled = false;
|
|
78
91
|
async function load() {
|
|
79
92
|
try {
|
|
80
|
-
const result = await apiJson(`/api/cpq/orders/${cpqOrderId}`);
|
|
93
|
+
const result = await apiJson(t, `/api/cpq/orders/${cpqOrderId}`);
|
|
81
94
|
if (!cancelled) {
|
|
82
95
|
setOrder(result);
|
|
83
96
|
fetch(`/api/customers/companies?id=${result.customerId}&pageSize=1`).then((r) => r.json()).then((data) => {
|
|
@@ -100,13 +113,13 @@ function CpqOrderDetailPage(props) {
|
|
|
100
113
|
return () => {
|
|
101
114
|
cancelled = true;
|
|
102
115
|
};
|
|
103
|
-
}, [cpqOrderId, fetchLinkedSubscriptions]);
|
|
116
|
+
}, [cpqOrderId, fetchLinkedSubscriptions, t]);
|
|
104
117
|
const activateOrder = async () => {
|
|
105
118
|
if (!order) return;
|
|
106
119
|
try {
|
|
107
120
|
setActivating(true);
|
|
108
121
|
setError(null);
|
|
109
|
-
const result = await apiJson(`/api/cpq/orders/${order.id}/activate`, {
|
|
122
|
+
const result = await apiJson(t, `/api/cpq/orders/${order.id}/activate`, {
|
|
110
123
|
method: "POST",
|
|
111
124
|
headers: { "Content-Type": "application/json" },
|
|
112
125
|
body: JSON.stringify({})
|
|
@@ -123,7 +136,7 @@ function CpqOrderDetailPage(props) {
|
|
|
123
136
|
if (!order) return;
|
|
124
137
|
try {
|
|
125
138
|
setError(null);
|
|
126
|
-
const result = await apiJson(`/api/cpq/orders/${order.id}/status`, {
|
|
139
|
+
const result = await apiJson(t, `/api/cpq/orders/${order.id}/status`, {
|
|
127
140
|
method: "POST",
|
|
128
141
|
headers: { "Content-Type": "application/json" },
|
|
129
142
|
body: JSON.stringify({ targetStatus: "cancelled" })
|
|
@@ -143,14 +156,15 @@ function CpqOrderDetailPage(props) {
|
|
|
143
156
|
if (loading) {
|
|
144
157
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground", children: [
|
|
145
158
|
/* @__PURE__ */ jsx(Spinner, {}),
|
|
146
|
-
"
|
|
159
|
+
" ",
|
|
160
|
+
t("cpq.orders.detail.loading", "Loading\u2026")
|
|
147
161
|
] });
|
|
148
162
|
}
|
|
149
163
|
if (!order) {
|
|
150
164
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
151
|
-
/* @__PURE__ */ jsx("button", { onClick: () => router.push("/backend/cpq/orders"), className: "text-sm text-muted-foreground hover:text-foreground", children: "\u2190 Back to Orders" }),
|
|
165
|
+
/* @__PURE__ */ jsx("button", { onClick: () => router.push("/backend/cpq/orders"), className: "text-sm text-muted-foreground hover:text-foreground", children: t("cpq.orders.detail.backToOrders", "\u2190 Back to Orders") }),
|
|
152
166
|
error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
153
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "Order not found." })
|
|
167
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.orders.detail.notFound", "Order not found.") })
|
|
154
168
|
] });
|
|
155
169
|
}
|
|
156
170
|
const canActivate = order.cpqStatus === "draft" || order.cpqStatus === "pending_activation";
|
|
@@ -167,12 +181,12 @@ function CpqOrderDetailPage(props) {
|
|
|
167
181
|
{
|
|
168
182
|
mode: "detail",
|
|
169
183
|
backHref: "/backend/cpq/orders",
|
|
170
|
-
backLabel: "Back to CPQ Orders",
|
|
171
|
-
entityTypeLabel: "CPQ Order",
|
|
184
|
+
backLabel: t("cpq.orders.detail.backLabel", "Back to CPQ Orders"),
|
|
185
|
+
entityTypeLabel: t("cpq.orders.detail.entityType", "CPQ Order"),
|
|
172
186
|
title: order.orderNumber || order.orderId.slice(0, 8),
|
|
173
187
|
statusBadge: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
|
|
174
188
|
/* @__PURE__ */ jsx(Tag, { variant: orderCpqStatusMap[order.cpqStatus] ?? "neutral", dot: true, children: formatStatusLabel(order.cpqStatus) }),
|
|
175
|
-
order.quoteType && order.quoteType !== "new" && /* @__PURE__ */ jsx(Tag, { variant: ARC_BADGE_VARIANTS[order.quoteType] ?? "neutral", className: "uppercase", children: order.quoteType })
|
|
189
|
+
order.quoteType && order.quoteType !== "new" && /* @__PURE__ */ jsx(Tag, { variant: ARC_BADGE_VARIANTS[order.quoteType] ?? "neutral", className: "uppercase", children: quoteTypeLabel(t, order.quoteType) })
|
|
176
190
|
] }),
|
|
177
191
|
actionsContent: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
178
192
|
/* @__PURE__ */ jsxs(
|
|
@@ -183,7 +197,7 @@ function CpqOrderDetailPage(props) {
|
|
|
183
197
|
onClick: () => router.push(`/backend/sales/orders/${order.orderId}`),
|
|
184
198
|
children: [
|
|
185
199
|
/* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5" }),
|
|
186
|
-
"View Sales Order"
|
|
200
|
+
t("cpq.orders.detail.viewSalesOrder", "View Sales Order")
|
|
187
201
|
]
|
|
188
202
|
}
|
|
189
203
|
),
|
|
@@ -193,16 +207,16 @@ function CpqOrderDetailPage(props) {
|
|
|
193
207
|
type: "button",
|
|
194
208
|
variant: "outline",
|
|
195
209
|
onClick: () => router.push(`/backend/cpq/quotes/${order.sourceQuoteId}`),
|
|
196
|
-
children: "Source Quote"
|
|
210
|
+
children: t("cpq.orders.detail.sourceQuote", "Source Quote")
|
|
197
211
|
}
|
|
198
212
|
),
|
|
199
213
|
canActivate && /* @__PURE__ */ jsxs(Button, { type: "button", onClick: activateOrder, disabled: activating, children: [
|
|
200
214
|
activating ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsx(Play, { className: "h-4 w-4" }),
|
|
201
|
-
"Activate"
|
|
215
|
+
t("cpq.orders.detail.activate", "Activate")
|
|
202
216
|
] }),
|
|
203
217
|
canCancel && /* @__PURE__ */ jsxs(Button, { type: "button", variant: "destructive-outline", onClick: cancelOrder, children: [
|
|
204
218
|
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
205
|
-
"Cancel"
|
|
219
|
+
t("cpq.common.cancel", "Cancel")
|
|
206
220
|
] })
|
|
207
221
|
] })
|
|
208
222
|
}
|
|
@@ -214,7 +228,7 @@ function CpqOrderDetailPage(props) {
|
|
|
214
228
|
path: ["draft", "pending_activation", "active", "fulfilled"],
|
|
215
229
|
terminals: ["cancelled"],
|
|
216
230
|
statusMap: orderCpqStatusMap,
|
|
217
|
-
ariaLabel: "Order status path"
|
|
231
|
+
ariaLabel: t("cpq.orders.detail.statusPath", "Order status path")
|
|
218
232
|
}
|
|
219
233
|
) }),
|
|
220
234
|
error && /* @__PURE__ */ jsx(ErrorBanner, { message: error, onDismiss: () => setError(null) }),
|
|
@@ -225,7 +239,7 @@ function CpqOrderDetailPage(props) {
|
|
|
225
239
|
children: [
|
|
226
240
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 space-y-1", children: [
|
|
227
241
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
|
|
228
|
-
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Linked Subscription" }),
|
|
242
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: t("cpq.orders.detail.linkedSubscription", "Linked Subscription") }),
|
|
229
243
|
/* @__PURE__ */ jsx(
|
|
230
244
|
Tag,
|
|
231
245
|
{
|
|
@@ -245,10 +259,7 @@ function CpqOrderDetailPage(props) {
|
|
|
245
259
|
")"
|
|
246
260
|
] })
|
|
247
261
|
] }),
|
|
248
|
-
order.activatedAt && /* @__PURE__ */
|
|
249
|
-
"Activated on ",
|
|
250
|
-
new Date(order.activatedAt).toLocaleString()
|
|
251
|
-
] })
|
|
262
|
+
order.activatedAt && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: t("cpq.orders.detail.activatedOn", "Activated on {date}", { date: new Date(order.activatedAt).toLocaleString() }) })
|
|
252
263
|
] }),
|
|
253
264
|
/* @__PURE__ */ jsxs(
|
|
254
265
|
Button,
|
|
@@ -258,7 +269,7 @@ function CpqOrderDetailPage(props) {
|
|
|
258
269
|
onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${sub.id}`),
|
|
259
270
|
children: [
|
|
260
271
|
/* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5" }),
|
|
261
|
-
"Open"
|
|
272
|
+
t("cpq.orders.actions.open", "Open")
|
|
262
273
|
]
|
|
263
274
|
}
|
|
264
275
|
)
|
|
@@ -267,12 +278,12 @@ function CpqOrderDetailPage(props) {
|
|
|
267
278
|
sub.id
|
|
268
279
|
)) }),
|
|
269
280
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3", children: [
|
|
270
|
-
/* @__PURE__ */ jsx(MetaCard, { label: "Customer", children: customerName ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: customerName }) : /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [
|
|
281
|
+
/* @__PURE__ */ jsx(MetaCard, { label: t("cpq.orders.detail.meta.customer", "Customer"), children: customerName ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: customerName }) : /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [
|
|
271
282
|
order.customerId.slice(0, 8),
|
|
272
283
|
"\u2026"
|
|
273
284
|
] }) }),
|
|
274
|
-
/* @__PURE__ */ jsx(MetaCard, { label: "Created", children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: new Date(order.createdAt).toLocaleDateString() }) }),
|
|
275
|
-
/* @__PURE__ */ jsx(MetaCard, { label: "Currency", children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: currency }) })
|
|
285
|
+
/* @__PURE__ */ jsx(MetaCard, { label: t("cpq.orders.detail.meta.created", "Created"), children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: new Date(order.createdAt).toLocaleDateString() }) }),
|
|
286
|
+
/* @__PURE__ */ jsx(MetaCard, { label: t("cpq.orders.detail.meta.currency", "Currency"), children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: currency }) })
|
|
276
287
|
] }),
|
|
277
288
|
/* @__PURE__ */ jsx(
|
|
278
289
|
PricingSummaryCard,
|
|
@@ -311,6 +322,7 @@ function OrderLineTree({
|
|
|
311
322
|
onToggleLine,
|
|
312
323
|
onTogglePricingDetail
|
|
313
324
|
}) {
|
|
325
|
+
const t = useT();
|
|
314
326
|
const rootLines = lines.filter((l) => !l.parentLineId);
|
|
315
327
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
316
328
|
for (const line of lines) {
|
|
@@ -323,12 +335,8 @@ function OrderLineTree({
|
|
|
323
335
|
const indexOf = /* @__PURE__ */ new Map();
|
|
324
336
|
lines.forEach((l, i) => indexOf.set(l.lineId, i));
|
|
325
337
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
326
|
-
/* @__PURE__ */
|
|
327
|
-
|
|
328
|
-
lines.length,
|
|
329
|
-
")"
|
|
330
|
-
] }),
|
|
331
|
-
lines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: "No line items." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: rootLines.map((line) => {
|
|
338
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2 px-1", children: t("cpq.orders.detail.lineItems", "Line Items ({count})", { count: lines.length }) }),
|
|
339
|
+
lines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: t("cpq.orders.detail.noLineItems", "No line items.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: rootLines.map((line) => {
|
|
332
340
|
const isBundle = line.offeringType === "bundle";
|
|
333
341
|
const children = childrenByParent.get(line.lineId) ?? [];
|
|
334
342
|
const isExpanded = expandedLines.has(line.lineId);
|
|
@@ -351,11 +359,7 @@ function OrderLineTree({
|
|
|
351
359
|
}
|
|
352
360
|
),
|
|
353
361
|
isBundle && isExpanded && children.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t border-dashed border-brand-violet/30 bg-brand-violet/5", children: [
|
|
354
|
-
/* @__PURE__ */ jsx("div", { className: "px-6 py-1.5", children: /* @__PURE__ */
|
|
355
|
-
"Bundle Components (",
|
|
356
|
-
children.length,
|
|
357
|
-
")"
|
|
358
|
-
] }) }),
|
|
362
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-1.5", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-brand-violet uppercase tracking-wider", children: t("cpq.lines.bundleComponents", "Bundle Components ({count})", { count: children.length }) }) }),
|
|
359
363
|
children.map((child) => {
|
|
360
364
|
const childIdx = indexOf.get(child.lineId) ?? 0;
|
|
361
365
|
return /* @__PURE__ */ jsx(
|
|
@@ -398,7 +402,7 @@ function OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle
|
|
|
398
402
|
/* @__PURE__ */ jsx("button", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx("svg", { className: `h-4 w-4 transition-transform ${isExpanded ? "rotate-90" : ""}`, fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) }),
|
|
399
403
|
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
400
404
|
indent > 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "\u2514" }),
|
|
401
|
-
isBundle && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "px-1.5 text-xs", children: "bundle" }),
|
|
405
|
+
isBundle && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "px-1.5 text-xs", children: t("cpq.lines.bundle", "bundle") }),
|
|
402
406
|
/* @__PURE__ */ jsx("span", { className: `font-medium text-sm ${isRemoved ? "text-muted-foreground line-through" : ""}`, children: line.offeringName }),
|
|
403
407
|
line.quantity > 1 && /* @__PURE__ */ jsxs("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs font-medium", children: [
|
|
404
408
|
"\xD7",
|
|
@@ -412,23 +416,28 @@ function OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle
|
|
|
412
416
|
title: isRemoved ? t("cpq.quotes.arc.lineRemovedHint", "This item is cancelled on the subscription when the order is activated") : void 0
|
|
413
417
|
}
|
|
414
418
|
),
|
|
415
|
-
isBundle && /* @__PURE__ */
|
|
416
|
-
childCount,
|
|
417
|
-
" component",
|
|
418
|
-
childCount !== 1 ? "s" : ""
|
|
419
|
-
] })
|
|
419
|
+
isBundle && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: childCount !== 1 ? t("cpq.lines.componentCount.other", "{count} components", { count: childCount }) : t("cpq.lines.componentCount.one", "{count} component", { count: childCount }) })
|
|
420
420
|
] }) }),
|
|
421
421
|
/* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right text-xs space-y-0.5", children: [
|
|
422
422
|
isRemoved && line.arcSource && line.arcSource.mrcAmount > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
423
|
-
/* @__PURE__ */
|
|
423
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
424
|
+
t("cpq.lines.mrc", "MRC"),
|
|
425
|
+
" "
|
|
426
|
+
] }),
|
|
424
427
|
/* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground line-through", children: fmt(line.arcSource.mrcAmount, currency) })
|
|
425
428
|
] }),
|
|
426
429
|
line.nrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
427
|
-
/* @__PURE__ */
|
|
430
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
431
|
+
t("cpq.lines.nrc", "NRC"),
|
|
432
|
+
" "
|
|
433
|
+
] }),
|
|
428
434
|
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.nrcTotal, currency) })
|
|
429
435
|
] }),
|
|
430
436
|
line.mrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
431
|
-
/* @__PURE__ */
|
|
437
|
+
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
438
|
+
t("cpq.lines.mrc", "MRC"),
|
|
439
|
+
" "
|
|
440
|
+
] }),
|
|
432
441
|
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.mrcTotal, currency) })
|
|
433
442
|
] })
|
|
434
443
|
] })
|
|
@@ -453,22 +462,25 @@ function OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle
|
|
|
453
462
|
style: paddingLeft ? { paddingLeft: `calc(${paddingLeft} + 2rem)` } : void 0,
|
|
454
463
|
children: isEmpty ? /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [
|
|
455
464
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
456
|
-
"No detailed breakdown for this line.",
|
|
457
|
-
mirroredFromItemId && "
|
|
465
|
+
t("cpq.orders.detail.noBreakdown", "No detailed breakdown for this line."),
|
|
466
|
+
mirroredFromItemId && " ",
|
|
467
|
+
mirroredFromItemId && t("cpq.orders.detail.mirroredItem", "Mirrored from existing subscription item.")
|
|
458
468
|
] }),
|
|
459
469
|
(line.nrcTotal > 0 || line.mrcTotal > 0) && /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
460
470
|
line.nrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
461
|
-
"
|
|
471
|
+
t("cpq.lines.nrc", "NRC"),
|
|
472
|
+
" ",
|
|
462
473
|
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.nrcTotal, currency) })
|
|
463
474
|
] }),
|
|
464
475
|
line.mrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
465
|
-
"
|
|
476
|
+
t("cpq.lines.mrc", "MRC"),
|
|
477
|
+
" ",
|
|
466
478
|
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.mrcTotal, currency) })
|
|
467
479
|
] })
|
|
468
480
|
] })
|
|
469
481
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
470
482
|
hasConfig && /* @__PURE__ */ jsxs("div", { children: [
|
|
471
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Configuration" }),
|
|
483
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: t("cpq.lines.configuration", "Configuration") }),
|
|
472
484
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: visibleConfigEntries.map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
|
|
473
485
|
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
474
486
|
key,
|
|
@@ -487,19 +499,9 @@ function OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle
|
|
|
487
499
|
}
|
|
488
500
|
),
|
|
489
501
|
hasTerm && /* @__PURE__ */ jsxs("div", { className: "flex gap-4 text-xs text-muted-foreground", children: [
|
|
490
|
-
line.startDate && /* @__PURE__ */
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
] }),
|
|
494
|
-
line.termMonths && /* @__PURE__ */ jsxs("span", { children: [
|
|
495
|
-
"Term: ",
|
|
496
|
-
line.termMonths,
|
|
497
|
-
"mo"
|
|
498
|
-
] }),
|
|
499
|
-
line.endDate && /* @__PURE__ */ jsxs("span", { children: [
|
|
500
|
-
"End: ",
|
|
501
|
-
new Date(line.endDate).toLocaleDateString()
|
|
502
|
-
] })
|
|
502
|
+
line.startDate && /* @__PURE__ */ jsx("span", { children: t("cpq.lines.term.start", "Start: {date}", { date: new Date(line.startDate).toLocaleDateString() }) }),
|
|
503
|
+
line.termMonths && /* @__PURE__ */ jsx("span", { children: t("cpq.lines.term.months", "Term: {months}mo", { months: line.termMonths }) }),
|
|
504
|
+
line.endDate && /* @__PURE__ */ jsx("span", { children: t("cpq.lines.term.end", "End: {date}", { date: new Date(line.endDate).toLocaleDateString() }) })
|
|
503
505
|
] }),
|
|
504
506
|
hasArcDiff && /* @__PURE__ */ jsx(ArcLineDiff, { line, currency })
|
|
505
507
|
] })
|
|
@@ -515,6 +517,7 @@ function Spinner() {
|
|
|
515
517
|
] });
|
|
516
518
|
}
|
|
517
519
|
function ErrorBanner({ message, onDismiss }) {
|
|
520
|
+
const t = useT();
|
|
518
521
|
return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", className: "flex items-start gap-2", children: [
|
|
519
522
|
/* @__PURE__ */ jsx("span", { className: "flex-1", children: message }),
|
|
520
523
|
onDismiss && /* @__PURE__ */ jsx(
|
|
@@ -522,7 +525,7 @@ function ErrorBanner({ message, onDismiss }) {
|
|
|
522
525
|
{
|
|
523
526
|
onClick: onDismiss,
|
|
524
527
|
className: "text-destructive/70 hover:text-destructive text-sm font-bold",
|
|
525
|
-
"aria-label": "Dismiss",
|
|
528
|
+
"aria-label": t("cpq.common.dismiss", "Dismiss"),
|
|
526
529
|
children: "\xD7"
|
|
527
530
|
}
|
|
528
531
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/cpq/backend/cpq/orders/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { ArrowRight, ExternalLink, Play, X } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport {\n formatStatusLabel,\n orderCpqStatusMap,\n type OrderCpqStatus,\n} from '../../../../components/statusMaps'\nimport { StatusPath } from '../../../../components/StatusPath'\nimport { ChargeBreakdownTable, type ChargeRowView } from '../../../../components/ChargeBreakdownTable'\nimport { PricingSummaryCard, type PricingSummaryView } from '../../../../components/PricingSummaryCard'\nimport { ArcLineDiff, LineActionBadge } from '../../../../components/ArcLineDiff'\nimport { arcRecurringDelta } from '../../../../lib/quoteSummaryView'\nimport { isArcCancelledLine } from '../../../../services/arcLineRemoval'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\n// \u2500\u2500\u2500 Types \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\ntype ArcLineSource = {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n}\n\ntype TierBreakdownEntry = { tier: number; from: number; to: number; pricePerUnit: number; quantity: number; subtotal: number }\ntype RuleAdjustment = { ruleCode: string; ruleName: string; ruleType: string; value: number; unitPriceBefore: number; unitPriceAfter: number; delta: number; origin?: 'rule' | 'manual'; adjustmentId?: string; reasonCode?: string }\n\ntype ResolvedCharge = {\n chargeName?: string\n chargeType?: string\n pricingMethod?: 'flat' | 'tiered' | 'per_unit'\n unitPrice?: number\n quantity?: number | null\n totalPrice?: number | null\n note?: string | null\n breakdown?: { tiers: TierBreakdownEntry[] } | null\n adjustments?: RuleAdjustment[] | null\n listUnitPrice?: number\n listTotalPrice?: number | null\n standardUnitPrice?: number\n standardTotalPrice?: number | null\n}\n\n/** Order lines carry the quote's stored waterfall verbatim (no recalculation at conversion). */\nfunction toChargeRow(c: ResolvedCharge): ChargeRowView {\n return { ...c, chargeName: c.chargeName ?? '\u2014', chargeType: (c.chargeType ?? 'mrc') as 'nrc' | 'mrc' | 'usage', unitPrice: c.unitPrice ?? 0, totalPrice: c.totalPrice ?? null }\n}\n\ntype OrderLineResult = {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: ResolvedCharge[]\n sourceQuoteLineId: string | null\n arcSource: ArcLineSource | null\n}\n\ntype OrderResult = {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n quoteType: string\n currencyCode: string\n pricingSummary: PricingSummaryView & { currencyCode: string }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\nconst ARC_BADGE_VARIANTS: Record<string, 'brand' | 'success' | 'error'> = {\n amend: 'brand',\n renew: 'success',\n cancel: 'error',\n}\n\n\n// \u2500\u2500\u2500 Constants \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\nfunction fmt(amount: number, currency: string): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nasync function apiJson<T>(url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, init)\n const json = await res.json()\n if (!res.ok) throw new Error(json?.error ?? `Request failed (${res.status})`)\n return json as T\n}\n\n// \u2500\u2500\u2500 Main 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\n\nexport default function CpqOrderDetailPage(props: { params?: { id?: string } }) {\n const router = useRouter()\n const urlParams = useParams()\n const cpqOrderId = (props.params?.id ?? urlParams?.id) as string\n\n const [order, setOrder] = React.useState<OrderResult | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [activating, setActivating] = React.useState(false)\n const [expandedLines, setExpandedLines] = React.useState<Set<string>>(new Set())\n const [pricingDetail, setPricingDetail] = React.useState<{ lineIdx: number; chargeIdx: number } | null>(null)\n const [customerName, setCustomerName] = React.useState<string | null>(null)\n const [linkedSubscriptions, setLinkedSubscriptions] = React.useState<Array<{\n id: string\n name: string\n code: string\n status: string\n }>>([])\n\n // Find every subscription this order touches. NEW orders write\n // `sourceOrderId` on the freshly-created subscription, so a\n // `?sourceOrderId=\u2026` filter is enough there. ARC orders (amend /\n // renew / cancel) mutate existing subscriptions in place \u2014 the\n // subscription's `sourceOrderId` stays pinned to the original\n // order, so we also resolve targets via the source quote.\n const fetchLinkedSubscriptions = React.useCallback(\n async (orderConfigId: string, sourceQuoteId: string | null) => {\n const byOrderId = fetch(\n `/api/cpq/inventory/subscriptions?sourceOrderId=${orderConfigId}&pageSize=10`,\n )\n .then((r) => (r.ok ? r.json() : null))\n .then((d) => (d?.items ?? (Array.isArray(d) ? d : [])) as Array<{ id: string; name: string; code: string; status: string }>)\n .catch(() => [])\n\n const byTargets = sourceQuoteId\n ? fetch(`/api/cpq/quotes/${sourceQuoteId}/target-subscriptions`)\n .then((r) => (r.ok ? r.json() : null))\n .then(async (d) => {\n const targets: Array<{ subscriptionId: string }> = d?.items ?? []\n const fetched = await Promise.all(\n targets.map((t) =>\n fetch(`/api/cpq/inventory/subscriptions?id=${t.subscriptionId}`)\n .then((r) => (r.ok ? r.json() : null))\n .catch(() => null),\n ),\n )\n return fetched.filter(Boolean) as Array<{ id: string; name: string; code: string; status: string }>\n })\n .catch(() => [] as Array<{ id: string; name: string; code: string; status: string }>)\n : Promise.resolve([] as Array<{ id: string; name: string; code: string; status: string }>)\n\n const [a, b] = await Promise.all([byOrderId, byTargets])\n // Dedupe by id \u2014 a single subscription may appear in both lists\n // (e.g. a NEW order that was later amended into the same sub).\n const seen = new Set<string>()\n const merged: Array<{ id: string; name: string; code: string; status: string }> = []\n for (const sub of [...a, ...b]) {\n if (!sub || seen.has(sub.id)) continue\n seen.add(sub.id)\n merged.push({ id: sub.id, name: sub.name, code: sub.code, status: sub.status })\n }\n setLinkedSubscriptions(merged)\n },\n [],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${cpqOrderId}`)\n if (!cancelled) {\n setOrder(result)\n fetch(`/api/customers/companies?id=${result.customerId}&pageSize=1`)\n .then((r) => r.json())\n .then((data) => {\n const items = data.items ?? data ?? []\n const c = items[0]\n if (c && !cancelled) setCustomerName(c.display_name || c.displayName || c.name || null)\n })\n .catch(() => {})\n if (result.cpqStatus === 'active' || result.cpqStatus === 'fulfilled') {\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n }\n }\n } catch (err) {\n if (!cancelled) setError((err as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [cpqOrderId, fetchLinkedSubscriptions])\n\n const activateOrder = async () => {\n if (!order) return\n try {\n setActivating(true)\n setError(null)\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${order.id}/activate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n })\n setOrder(result)\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n } catch (err) {\n setError((err as Error).message)\n } finally {\n setActivating(false)\n }\n }\n\n const cancelOrder = async () => {\n if (!order) return\n try {\n setError(null)\n const result = await apiJson<OrderResult>(`/api/cpq/orders/${order.id}/status`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetStatus: 'cancelled' }),\n })\n setOrder(result)\n } catch (err) {\n setError((err as Error).message)\n }\n }\n\n const toggleLine = (id: string) => {\n setExpandedLines((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n })\n }\n\n // \u2500\u2500\u2500 Render: loading / error \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\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground\">\n <Spinner /> Loading\u2026\n </div>\n )\n }\n\n if (!order) {\n return (\n <div className=\"space-y-4\">\n <button onClick={() => router.push('/backend/cpq/orders')} className=\"text-sm text-muted-foreground hover:text-foreground\">\u2190 Back to Orders</button>\n {error && <ErrorBanner message={error} />}\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">Order not found.</div>\n </div>\n )\n }\n\n // \u2500\u2500\u2500 Render: detail \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\n const canActivate = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation'\n const canCancel = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation' || order.cpqStatus === 'active'\n const currency = order.currencyCode\n\n const togglePricingDetail = (lineIdx: number, chargeIdx: number) => {\n setPricingDetail((prev) =>\n prev?.lineIdx === lineIdx && prev?.chargeIdx === chargeIdx ? null : { lineIdx, chargeIdx },\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header \u2014 standard FormHeader detail mode, mirrors the CPQ quote\n layout so order \u2194 quote stays consistent. Title is just the order\n number; status / ARC / converted tags sit underneath; primary\n actions (Activate, then Cancel) live on the right next to the\n View Sales Order / Source Quote utility links. */}\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/cpq/orders\"\n backLabel=\"Back to CPQ Orders\"\n entityTypeLabel=\"CPQ Order\"\n title={order.orderNumber || order.orderId.slice(0, 8)}\n statusBadge={\n <div className=\"flex flex-wrap items-center gap-2 pt-1\">\n <Tag variant={orderCpqStatusMap[order.cpqStatus as OrderCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(order.cpqStatus)}\n </Tag>\n {order.quoteType && order.quoteType !== 'new' && (\n <Tag variant={ARC_BADGE_VARIANTS[order.quoteType] ?? 'neutral'} className=\"uppercase\">\n {order.quoteType}\n </Tag>\n )}\n </div>\n }\n actionsContent={\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/sales/orders/${order.orderId}`)}\n >\n <ExternalLink className=\"h-3.5 w-3.5\" />\n View Sales Order\n </Button>\n {order.sourceQuoteId && (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/quotes/${order.sourceQuoteId}`)}\n >\n Source Quote\n </Button>\n )}\n {canActivate && (\n <Button type=\"button\" onClick={activateOrder} disabled={activating}>\n {activating ? <Spinner /> : <Play className=\"h-4 w-4\" />}\n Activate\n </Button>\n )}\n {canCancel && (\n <Button type=\"button\" variant=\"destructive-outline\" onClick={cancelOrder}>\n <X className=\"h-4 w-4\" />\n Cancel\n </Button>\n )}\n </>\n }\n />\n\n {/* Status path \u2014 visual progress only. Orders advance via the\n dedicated Activate / Cancel buttons because activation has\n backend side effects (subscription / asset creation). */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n <StatusPath\n // Order path is read-only \u2014 activation has backend side effects\n // (subscription / asset creation) so transitions go through the\n // dedicated Activate / Cancel buttons.\n current={order.cpqStatus}\n path={['draft', 'pending_activation', 'active', 'fulfilled']}\n terminals={['cancelled']}\n statusMap={orderCpqStatusMap}\n ariaLabel=\"Order status path\"\n />\n </div>\n\n {error && <ErrorBanner message={error} onDismiss={() => setError(null)} />}\n\n {/* Linked subscriptions \u2014 one card per subscription this order\n touches. NEW orders write one fresh subscription; ARC orders\n (amend / renew / cancel) reach one or more existing\n subscriptions through the source quote's target list. */}\n {linkedSubscriptions.length > 0 && (\n <div className=\"space-y-2\">\n {linkedSubscriptions.map((sub) => (\n <div\n key={sub.id}\n className=\"rounded-lg border bg-card p-4 flex items-center gap-4\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n Linked Subscription\n </span>\n <Tag\n variant={sub.status === 'active' ? 'success' : 'info'}\n dot\n className=\"text-[10px] px-1.5\"\n >\n {sub.status}\n </Tag>\n </div>\n <div className=\"text-sm font-medium\">\n {sub.name}{' '}\n <span className=\"text-muted-foreground font-mono text-xs\">({sub.code})</span>\n </div>\n {order.activatedAt && (\n <div className=\"text-xs text-muted-foreground\">\n Activated on {new Date(order.activatedAt).toLocaleString()}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/inventory/subscriptions/${sub.id}`)}\n >\n <ArrowRight className=\"h-3.5 w-3.5\" />\n Open\n </Button>\n </div>\n ))}\n </div>\n )}\n\n {/* Meta cards \u2014 secondary reference info, matches the quote layout. */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n <MetaCard label=\"Customer\">\n {customerName ? (\n <span className=\"font-medium\">{customerName}</span>\n ) : (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {order.customerId.slice(0, 8)}\u2026\n </span>\n )}\n </MetaCard>\n <MetaCard label=\"Created\">\n <span className=\"font-medium\">{new Date(order.createdAt).toLocaleDateString()}</span>\n </MetaCard>\n <MetaCard label=\"Currency\">\n <span className=\"font-medium\">{currency}</span>\n </MetaCard>\n </div>\n\n <PricingSummaryCard\n summary={order.pricingSummary}\n currency={currency}\n arcDelta={order.quoteType === 'amend' || order.quoteType === 'renew'\n ? arcRecurringDelta(order.lines.map((l) => ({ ...l, action: l.action as 'add' | 'modify' | 'cancel' })))\n : null}\n />\n\n {/* Order Lines */}\n <OrderLineTree\n lines={order.lines}\n quoteType={order.quoteType}\n currency={currency}\n expandedLines={expandedLines}\n pricingDetail={pricingDetail}\n onToggleLine={toggleLine}\n onTogglePricingDetail={togglePricingDetail}\n />\n </div>\n )\n}\n\nfunction MetaCard({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </div>\n <div className=\"mt-1 text-sm\">{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Sub-components \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\nfunction OrderLineTree({\n lines,\n quoteType,\n currency,\n expandedLines,\n pricingDetail,\n onToggleLine,\n onTogglePricingDetail,\n}: {\n lines: OrderLineResult[]\n quoteType: string\n currency: string\n expandedLines: Set<string>\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggleLine: (id: string) => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const rootLines = lines.filter((l) => !l.parentLineId)\n const childrenByParent = new Map<string, OrderLineResult[]>()\n for (const line of lines) {\n if (line.parentLineId) {\n const arr = childrenByParent.get(line.parentLineId) ?? []\n arr.push(line)\n childrenByParent.set(line.parentLineId, arr)\n }\n }\n\n // Stable index per visible line so the pricing-detail toggle can be\n // keyed by (lineIdx, chargeIdx) the same way the quote page does.\n const indexOf = new Map<string, number>()\n lines.forEach((l, i) => indexOf.set(l.lineId, i))\n\n return (\n <div>\n <h3 className=\"text-sm font-medium mb-2 px-1\">Line Items ({lines.length})</h3>\n {lines.length === 0 ? (\n <div className=\"rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground\">\n No line items.\n </div>\n ) : (\n <div className=\"rounded-lg border bg-card divide-y\">\n {rootLines.map((line) => {\n const isBundle = line.offeringType === 'bundle'\n const children = childrenByParent.get(line.lineId) ?? []\n const isExpanded = expandedLines.has(line.lineId)\n const lineIdx = indexOf.get(line.lineId) ?? 0\n return (\n <div key={line.lineId}>\n <OrderLineRow\n line={line}\n lineIdx={lineIdx}\n quoteType={quoteType}\n currency={currency}\n isExpanded={isExpanded}\n isBundle={isBundle}\n childCount={children.length}\n indent={0}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(line.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n {isBundle && isExpanded && children.length > 0 && (\n <div className=\"border-t border-dashed border-brand-violet/30 bg-brand-violet/5\">\n <div className=\"px-6 py-1.5\">\n <span className=\"text-[10px] font-medium text-brand-violet uppercase tracking-wider\">Bundle Components ({children.length})</span>\n </div>\n {children.map((child) => {\n const childIdx = indexOf.get(child.lineId) ?? 0\n return (\n <OrderLineRow\n key={child.lineId}\n line={child}\n lineIdx={childIdx}\n quoteType={quoteType}\n currency={currency}\n isExpanded={expandedLines.has(child.lineId)}\n isBundle={false}\n childCount={0}\n indent={1}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(child.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n )\n })}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\nfunction OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle, childCount, indent, pricingDetail, onToggle, onTogglePricingDetail }: {\n line: OrderLineResult; lineIdx: number; quoteType: string; currency: string; isExpanded: boolean; isBundle: boolean; childCount: number; indent: number\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggle: () => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const t = useT()\n const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : undefined\n // Same presentation as the quote page: an existing item removed on an\n // Amend / Renew quote reads as \"remove\", struck through.\n const isRemoved = isArcCancelledLine(line, quoteType)\n\n return (\n <div>\n <div\n className={`flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors cursor-pointer ${isRemoved ? 'bg-muted/20' : ''}`}\n style={paddingLeft ? { paddingLeft } : undefined}\n onClick={onToggle}\n data-testid=\"cpq-order-line\"\n data-removed={isRemoved ? 'true' : undefined}\n >\n <button className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground\">\n <svg className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`} fill=\"none\" viewBox=\"0 0 24 24\" strokeWidth=\"2\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n {indent > 0 && <span className=\"text-muted-foreground text-xs\">\u2514</span>}\n {isBundle && (\n <Tag variant=\"brand\" className=\"px-1.5 text-xs\">bundle</Tag>\n )}\n <span className={`font-medium text-sm ${isRemoved ? 'text-muted-foreground line-through' : ''}`}>{line.offeringName}</span>\n {line.quantity > 1 && <span className=\"rounded bg-muted px-1.5 py-0.5 text-xs font-medium\">\u00D7{line.quantity}</span>}\n <LineActionBadge\n action={line.action}\n label={isRemoved ? t('cpq.quotes.arc.lineRemoved', 'remove') : undefined}\n title={isRemoved ? t('cpq.quotes.arc.lineRemovedHint', 'This item is cancelled on the subscription when the order is activated') : undefined}\n />\n {isBundle && <span className=\"text-xs text-muted-foreground\">{childCount} component{childCount !== 1 ? 's' : ''}</span>}\n </div>\n </div>\n\n <div className=\"shrink-0 text-right text-xs space-y-0.5\">\n {isRemoved && line.arcSource && line.arcSource.mrcAmount > 0 && (\n <div>\n <span className=\"text-muted-foreground\">MRC </span>\n <span className=\"font-mono text-muted-foreground line-through\">{fmt(line.arcSource.mrcAmount, currency)}</span>\n </div>\n )}\n {line.nrcTotal > 0 && <div><span className=\"text-muted-foreground\">NRC </span><span className=\"font-mono font-medium\">{fmt(line.nrcTotal, currency)}</span></div>}\n {line.mrcTotal > 0 && <div><span className=\"text-muted-foreground\">MRC </span><span className=\"font-mono font-medium\">{fmt(line.mrcTotal, currency)}</span></div>}\n </div>\n </div>\n\n {isExpanded && (() => {\n const internalKeys = new Set(['_arcMirroredName', '_arcMirroredFromItemId', '_arcPriceFrozen', '_arcRepriced', 'offeringName'])\n const visibleConfigEntries = Object.entries(line.configuration).filter(\n ([key]) => !internalKeys.has(key),\n )\n const hasConfig = visibleConfigEntries.length > 0\n const hasCharges = line.charges.length > 0\n const hasTerm = !!(line.startDate || line.termMonths || line.endDate)\n const hasArcDiff = !!line.arcSource\n const mirroredFromItemId = (line.configuration?._arcMirroredFromItemId as string | undefined) ?? null\n const isEmpty = !hasConfig && !hasCharges && !hasTerm && !hasArcDiff\n\n return (\n <div\n className=\"border-t bg-muted/10 px-12 py-3 space-y-3\"\n style={paddingLeft ? { paddingLeft: `calc(${paddingLeft} + 2rem)` } : undefined}\n >\n {isEmpty ? (\n <div className=\"text-xs text-muted-foreground space-y-1\">\n <div>\n No detailed breakdown for this line.\n {mirroredFromItemId && ' Mirrored from existing subscription item.'}\n </div>\n {(line.nrcTotal > 0 || line.mrcTotal > 0) && (\n <div className=\"flex gap-4\">\n {line.nrcTotal > 0 && (\n <span>NRC <span className=\"font-mono font-medium text-foreground\">{fmt(line.nrcTotal, currency)}</span></span>\n )}\n {line.mrcTotal > 0 && (\n <span>MRC <span className=\"font-mono font-medium text-foreground\">{fmt(line.mrcTotal, currency)}</span></span>\n )}\n </div>\n )}\n </div>\n ) : (\n <>\n {hasConfig && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Configuration</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1\">\n {visibleConfigEntries.map(([key, value]) => (\n <div key={key} className=\"text-xs\">\n <span className=\"text-muted-foreground\">{key}: </span>\n <span className=\"font-medium\">{String(value)}</span>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {hasCharges && (\n <ChargeBreakdownTable\n charges={line.charges.map(toChargeRow)}\n currency={currency}\n openDetailIndex={pricingDetail?.lineIdx === lineIdx ? pricingDetail.chargeIdx : null}\n onToggleDetail={(chargeIdx) => onTogglePricingDetail(lineIdx, chargeIdx)}\n />\n )}\n\n {hasTerm && (\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n {line.startDate && <span>Start: {new Date(line.startDate).toLocaleDateString()}</span>}\n {line.termMonths && <span>Term: {line.termMonths}mo</span>}\n {line.endDate && <span>End: {new Date(line.endDate).toLocaleDateString()}</span>}\n </div>\n )}\n\n {/* XD-250 ARC: before/after preview below the charges, as on the quote. */}\n {hasArcDiff && <ArcLineDiff line={line} currency={currency} />}\n </>\n )}\n </div>\n )\n })()}\n </div>\n )\n}\n\nfunction Spinner() {\n return (\n <svg className=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\" />\n </svg>\n )\n}\n\nfunction ErrorBanner({ message, onDismiss }: { message: string; onDismiss?: () => void }) {\n return (\n <Alert variant=\"destructive\" className=\"flex items-start gap-2\">\n <span className=\"flex-1\">{message}</span>\n {onDismiss && (\n <button\n onClick={onDismiss}\n className=\"text-destructive/70 hover:text-destructive text-sm font-bold\"\n aria-label=\"Dismiss\"\n >\n \u00D7\n </button>\n )}\n </Alert>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useParams } from 'next/navigation'\nimport { ArrowRight, ExternalLink, Play, X } from 'lucide-react'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport {\n formatStatusLabel,\n orderCpqStatusMap,\n type OrderCpqStatus,\n} from '../../../../components/statusMaps'\nimport { StatusPath } from '../../../../components/StatusPath'\nimport { ChargeBreakdownTable, type ChargeRowView } from '../../../../components/ChargeBreakdownTable'\nimport { PricingSummaryCard, type PricingSummaryView } from '../../../../components/PricingSummaryCard'\nimport { ArcLineDiff, LineActionBadge } from '../../../../components/ArcLineDiff'\nimport { arcRecurringDelta } from '../../../../lib/quoteSummaryView'\nimport { isArcCancelledLine } from '../../../../services/arcLineRemoval'\nimport { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'\n\n// \u2500\u2500\u2500 Types \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\ntype ArcLineSource = {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n}\n\ntype TierBreakdownEntry = { tier: number; from: number; to: number; pricePerUnit: number; quantity: number; subtotal: number }\ntype RuleAdjustment = { ruleCode: string; ruleName: string; ruleType: string; value: number; unitPriceBefore: number; unitPriceAfter: number; delta: number; origin?: 'rule' | 'manual'; adjustmentId?: string; reasonCode?: string }\n\ntype ResolvedCharge = {\n chargeName?: string\n chargeType?: string\n pricingMethod?: 'flat' | 'tiered' | 'per_unit'\n unitPrice?: number\n quantity?: number | null\n totalPrice?: number | null\n note?: string | null\n breakdown?: { tiers: TierBreakdownEntry[] } | null\n adjustments?: RuleAdjustment[] | null\n listUnitPrice?: number\n listTotalPrice?: number | null\n standardUnitPrice?: number\n standardTotalPrice?: number | null\n}\n\n/** Order lines carry the quote's stored waterfall verbatim (no recalculation at conversion). */\nfunction toChargeRow(c: ResolvedCharge): ChargeRowView {\n return { ...c, chargeName: c.chargeName ?? '\u2014', chargeType: (c.chargeType ?? 'mrc') as 'nrc' | 'mrc' | 'usage', unitPrice: c.unitPrice ?? 0, totalPrice: c.totalPrice ?? null }\n}\n\ntype OrderLineResult = {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: ResolvedCharge[]\n sourceQuoteLineId: string | null\n arcSource: ArcLineSource | null\n}\n\ntype OrderResult = {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n quoteType: string\n currencyCode: string\n pricingSummary: PricingSummaryView & { currencyCode: string }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\nconst ARC_BADGE_VARIANTS: Record<string, 'brand' | 'success' | 'error'> = {\n amend: 'brand',\n renew: 'success',\n cancel: 'error',\n}\n\nfunction quoteTypeLabel(t: TranslateFn, quoteType: string): string {\n switch (quoteType) {\n case 'amend':\n return t('cpq.quotes.type.amend', 'amend')\n case 'renew':\n return t('cpq.quotes.type.renew', 'renew')\n case 'cancel':\n return t('cpq.quotes.type.cancel', 'cancel')\n default:\n return quoteType\n }\n}\n\n\n// \u2500\u2500\u2500 Constants \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\nfunction fmt(amount: number, currency: string): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nasync function apiJson<T>(t: TranslateFn, url: string, init?: RequestInit): Promise<T> {\n const res = await fetch(url, init)\n const json = await res.json()\n if (!res.ok) throw new Error(json?.error ?? t('cpq.common.requestFailed', 'Request failed ({status})', { status: res.status }))\n return json as T\n}\n\n// \u2500\u2500\u2500 Main 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\n\nexport default function CpqOrderDetailPage(props: { params?: { id?: string } }) {\n const router = useRouter()\n const t = useT()\n const urlParams = useParams()\n const cpqOrderId = (props.params?.id ?? urlParams?.id) as string\n\n const [order, setOrder] = React.useState<OrderResult | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [activating, setActivating] = React.useState(false)\n const [expandedLines, setExpandedLines] = React.useState<Set<string>>(new Set())\n const [pricingDetail, setPricingDetail] = React.useState<{ lineIdx: number; chargeIdx: number } | null>(null)\n const [customerName, setCustomerName] = React.useState<string | null>(null)\n const [linkedSubscriptions, setLinkedSubscriptions] = React.useState<Array<{\n id: string\n name: string\n code: string\n status: string\n }>>([])\n\n // Find every subscription this order touches. NEW orders write\n // `sourceOrderId` on the freshly-created subscription, so a\n // `?sourceOrderId=\u2026` filter is enough there. ARC orders (amend /\n // renew / cancel) mutate existing subscriptions in place \u2014 the\n // subscription's `sourceOrderId` stays pinned to the original\n // order, so we also resolve targets via the source quote.\n const fetchLinkedSubscriptions = React.useCallback(\n async (orderConfigId: string, sourceQuoteId: string | null) => {\n const byOrderId = fetch(\n `/api/cpq/inventory/subscriptions?sourceOrderId=${orderConfigId}&pageSize=10`,\n )\n .then((r) => (r.ok ? r.json() : null))\n .then((d) => (d?.items ?? (Array.isArray(d) ? d : [])) as Array<{ id: string; name: string; code: string; status: string }>)\n .catch(() => [])\n\n const byTargets = sourceQuoteId\n ? fetch(`/api/cpq/quotes/${sourceQuoteId}/target-subscriptions`)\n .then((r) => (r.ok ? r.json() : null))\n .then(async (d) => {\n const targets: Array<{ subscriptionId: string }> = d?.items ?? []\n const fetched = await Promise.all(\n targets.map((target) =>\n fetch(`/api/cpq/inventory/subscriptions?id=${target.subscriptionId}`)\n .then((r) => (r.ok ? r.json() : null))\n .catch(() => null),\n ),\n )\n return fetched.filter(Boolean) as Array<{ id: string; name: string; code: string; status: string }>\n })\n .catch(() => [] as Array<{ id: string; name: string; code: string; status: string }>)\n : Promise.resolve([] as Array<{ id: string; name: string; code: string; status: string }>)\n\n const [a, b] = await Promise.all([byOrderId, byTargets])\n // Dedupe by id \u2014 a single subscription may appear in both lists\n // (e.g. a NEW order that was later amended into the same sub).\n const seen = new Set<string>()\n const merged: Array<{ id: string; name: string; code: string; status: string }> = []\n for (const sub of [...a, ...b]) {\n if (!sub || seen.has(sub.id)) continue\n seen.add(sub.id)\n merged.push({ id: sub.id, name: sub.name, code: sub.code, status: sub.status })\n }\n setLinkedSubscriptions(merged)\n },\n [],\n )\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n try {\n const result = await apiJson<OrderResult>(t, `/api/cpq/orders/${cpqOrderId}`)\n if (!cancelled) {\n setOrder(result)\n fetch(`/api/customers/companies?id=${result.customerId}&pageSize=1`)\n .then((r) => r.json())\n .then((data) => {\n const items = data.items ?? data ?? []\n const c = items[0]\n if (c && !cancelled) setCustomerName(c.display_name || c.displayName || c.name || null)\n })\n .catch(() => {})\n if (result.cpqStatus === 'active' || result.cpqStatus === 'fulfilled') {\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n }\n }\n } catch (err) {\n if (!cancelled) setError((err as Error).message)\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [cpqOrderId, fetchLinkedSubscriptions, t])\n\n const activateOrder = async () => {\n if (!order) return\n try {\n setActivating(true)\n setError(null)\n const result = await apiJson<OrderResult>(t, `/api/cpq/orders/${order.id}/activate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({}),\n })\n setOrder(result)\n void fetchLinkedSubscriptions(result.id, result.sourceQuoteId)\n } catch (err) {\n setError((err as Error).message)\n } finally {\n setActivating(false)\n }\n }\n\n const cancelOrder = async () => {\n if (!order) return\n try {\n setError(null)\n const result = await apiJson<OrderResult>(t, `/api/cpq/orders/${order.id}/status`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetStatus: 'cancelled' }),\n })\n setOrder(result)\n } catch (err) {\n setError((err as Error).message)\n }\n }\n\n const toggleLine = (id: string) => {\n setExpandedLines((prev) => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n })\n }\n\n // \u2500\u2500\u2500 Render: loading / error \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\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground\">\n <Spinner /> {t('cpq.orders.detail.loading', 'Loading\u2026')}\n </div>\n )\n }\n\n if (!order) {\n return (\n <div className=\"space-y-4\">\n <button onClick={() => router.push('/backend/cpq/orders')} className=\"text-sm text-muted-foreground hover:text-foreground\">{t('cpq.orders.detail.backToOrders', '\u2190 Back to Orders')}</button>\n {error && <ErrorBanner message={error} />}\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">{t('cpq.orders.detail.notFound', 'Order not found.')}</div>\n </div>\n )\n }\n\n // \u2500\u2500\u2500 Render: detail \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\n const canActivate = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation'\n const canCancel = order.cpqStatus === 'draft' || order.cpqStatus === 'pending_activation' || order.cpqStatus === 'active'\n const currency = order.currencyCode\n\n const togglePricingDetail = (lineIdx: number, chargeIdx: number) => {\n setPricingDetail((prev) =>\n prev?.lineIdx === lineIdx && prev?.chargeIdx === chargeIdx ? null : { lineIdx, chargeIdx },\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header \u2014 standard FormHeader detail mode, mirrors the CPQ quote\n layout so order \u2194 quote stays consistent. Title is just the order\n number; status / ARC / converted tags sit underneath; primary\n actions (Activate, then Cancel) live on the right next to the\n View Sales Order / Source Quote utility links. */}\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/cpq/orders\"\n backLabel={t('cpq.orders.detail.backLabel', 'Back to CPQ Orders')}\n entityTypeLabel={t('cpq.orders.detail.entityType', 'CPQ Order')}\n title={order.orderNumber || order.orderId.slice(0, 8)}\n statusBadge={\n <div className=\"flex flex-wrap items-center gap-2 pt-1\">\n <Tag variant={orderCpqStatusMap[order.cpqStatus as OrderCpqStatus] ?? 'neutral'} dot>\n {formatStatusLabel(order.cpqStatus)}\n </Tag>\n {order.quoteType && order.quoteType !== 'new' && (\n <Tag variant={ARC_BADGE_VARIANTS[order.quoteType] ?? 'neutral'} className=\"uppercase\">\n {quoteTypeLabel(t, order.quoteType)}\n </Tag>\n )}\n </div>\n }\n actionsContent={\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/sales/orders/${order.orderId}`)}\n >\n <ExternalLink className=\"h-3.5 w-3.5\" />\n {t('cpq.orders.detail.viewSalesOrder', 'View Sales Order')}\n </Button>\n {order.sourceQuoteId && (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/quotes/${order.sourceQuoteId}`)}\n >\n {t('cpq.orders.detail.sourceQuote', 'Source Quote')}\n </Button>\n )}\n {canActivate && (\n <Button type=\"button\" onClick={activateOrder} disabled={activating}>\n {activating ? <Spinner /> : <Play className=\"h-4 w-4\" />}\n {t('cpq.orders.detail.activate', 'Activate')}\n </Button>\n )}\n {canCancel && (\n <Button type=\"button\" variant=\"destructive-outline\" onClick={cancelOrder}>\n <X className=\"h-4 w-4\" />\n {t('cpq.common.cancel', 'Cancel')}\n </Button>\n )}\n </>\n }\n />\n\n {/* Status path \u2014 visual progress only. Orders advance via the\n dedicated Activate / Cancel buttons because activation has\n backend side effects (subscription / asset creation). */}\n <div className=\"flex items-center gap-3 flex-wrap\">\n <StatusPath\n // Order path is read-only \u2014 activation has backend side effects\n // (subscription / asset creation) so transitions go through the\n // dedicated Activate / Cancel buttons.\n current={order.cpqStatus}\n path={['draft', 'pending_activation', 'active', 'fulfilled']}\n terminals={['cancelled']}\n statusMap={orderCpqStatusMap}\n ariaLabel={t('cpq.orders.detail.statusPath', 'Order status path')}\n />\n </div>\n\n {error && <ErrorBanner message={error} onDismiss={() => setError(null)} />}\n\n {/* Linked subscriptions \u2014 one card per subscription this order\n touches. NEW orders write one fresh subscription; ARC orders\n (amend / renew / cancel) reach one or more existing\n subscriptions through the source quote's target list. */}\n {linkedSubscriptions.length > 0 && (\n <div className=\"space-y-2\">\n {linkedSubscriptions.map((sub) => (\n <div\n key={sub.id}\n className=\"rounded-lg border bg-card p-4 flex items-center gap-4\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {t('cpq.orders.detail.linkedSubscription', 'Linked Subscription')}\n </span>\n <Tag\n variant={sub.status === 'active' ? 'success' : 'info'}\n dot\n className=\"text-[10px] px-1.5\"\n >\n {sub.status}\n </Tag>\n </div>\n <div className=\"text-sm font-medium\">\n {sub.name}{' '}\n <span className=\"text-muted-foreground font-mono text-xs\">({sub.code})</span>\n </div>\n {order.activatedAt && (\n <div className=\"text-xs text-muted-foreground\">\n {t('cpq.orders.detail.activatedOn', 'Activated on {date}', { date: new Date(order.activatedAt).toLocaleString() })}\n </div>\n )}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => router.push(`/backend/cpq/inventory/subscriptions/${sub.id}`)}\n >\n <ArrowRight className=\"h-3.5 w-3.5\" />\n {t('cpq.orders.actions.open', 'Open')}\n </Button>\n </div>\n ))}\n </div>\n )}\n\n {/* Meta cards \u2014 secondary reference info, matches the quote layout. */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3\">\n <MetaCard label={t('cpq.orders.detail.meta.customer', 'Customer')}>\n {customerName ? (\n <span className=\"font-medium\">{customerName}</span>\n ) : (\n <span className=\"font-mono text-xs text-muted-foreground\">\n {order.customerId.slice(0, 8)}\u2026\n </span>\n )}\n </MetaCard>\n <MetaCard label={t('cpq.orders.detail.meta.created', 'Created')}>\n <span className=\"font-medium\">{new Date(order.createdAt).toLocaleDateString()}</span>\n </MetaCard>\n <MetaCard label={t('cpq.orders.detail.meta.currency', 'Currency')}>\n <span className=\"font-medium\">{currency}</span>\n </MetaCard>\n </div>\n\n <PricingSummaryCard\n summary={order.pricingSummary}\n currency={currency}\n arcDelta={order.quoteType === 'amend' || order.quoteType === 'renew'\n ? arcRecurringDelta(order.lines.map((l) => ({ ...l, action: l.action as 'add' | 'modify' | 'cancel' })))\n : null}\n />\n\n {/* Order Lines */}\n <OrderLineTree\n lines={order.lines}\n quoteType={order.quoteType}\n currency={currency}\n expandedLines={expandedLines}\n pricingDetail={pricingDetail}\n onToggleLine={toggleLine}\n onTogglePricingDetail={togglePricingDetail}\n />\n </div>\n )\n}\n\nfunction MetaCard({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {label}\n </div>\n <div className=\"mt-1 text-sm\">{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Sub-components \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\nfunction OrderLineTree({\n lines,\n quoteType,\n currency,\n expandedLines,\n pricingDetail,\n onToggleLine,\n onTogglePricingDetail,\n}: {\n lines: OrderLineResult[]\n quoteType: string\n currency: string\n expandedLines: Set<string>\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggleLine: (id: string) => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const t = useT()\n const rootLines = lines.filter((l) => !l.parentLineId)\n const childrenByParent = new Map<string, OrderLineResult[]>()\n for (const line of lines) {\n if (line.parentLineId) {\n const arr = childrenByParent.get(line.parentLineId) ?? []\n arr.push(line)\n childrenByParent.set(line.parentLineId, arr)\n }\n }\n\n // Stable index per visible line so the pricing-detail toggle can be\n // keyed by (lineIdx, chargeIdx) the same way the quote page does.\n const indexOf = new Map<string, number>()\n lines.forEach((l, i) => indexOf.set(l.lineId, i))\n\n return (\n <div>\n <h3 className=\"text-sm font-medium mb-2 px-1\">{t('cpq.orders.detail.lineItems', 'Line Items ({count})', { count: lines.length })}</h3>\n {lines.length === 0 ? (\n <div className=\"rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground\">\n {t('cpq.orders.detail.noLineItems', 'No line items.')}\n </div>\n ) : (\n <div className=\"rounded-lg border bg-card divide-y\">\n {rootLines.map((line) => {\n const isBundle = line.offeringType === 'bundle'\n const children = childrenByParent.get(line.lineId) ?? []\n const isExpanded = expandedLines.has(line.lineId)\n const lineIdx = indexOf.get(line.lineId) ?? 0\n return (\n <div key={line.lineId}>\n <OrderLineRow\n line={line}\n lineIdx={lineIdx}\n quoteType={quoteType}\n currency={currency}\n isExpanded={isExpanded}\n isBundle={isBundle}\n childCount={children.length}\n indent={0}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(line.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n {isBundle && isExpanded && children.length > 0 && (\n <div className=\"border-t border-dashed border-brand-violet/30 bg-brand-violet/5\">\n <div className=\"px-6 py-1.5\">\n <span className=\"text-[10px] font-medium text-brand-violet uppercase tracking-wider\">{t('cpq.lines.bundleComponents', 'Bundle Components ({count})', { count: children.length })}</span>\n </div>\n {children.map((child) => {\n const childIdx = indexOf.get(child.lineId) ?? 0\n return (\n <OrderLineRow\n key={child.lineId}\n line={child}\n lineIdx={childIdx}\n quoteType={quoteType}\n currency={currency}\n isExpanded={expandedLines.has(child.lineId)}\n isBundle={false}\n childCount={0}\n indent={1}\n pricingDetail={pricingDetail}\n onToggle={() => onToggleLine(child.lineId)}\n onTogglePricingDetail={onTogglePricingDetail}\n />\n )\n })}\n </div>\n )}\n </div>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n\nfunction OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle, childCount, indent, pricingDetail, onToggle, onTogglePricingDetail }: {\n line: OrderLineResult; lineIdx: number; quoteType: string; currency: string; isExpanded: boolean; isBundle: boolean; childCount: number; indent: number\n pricingDetail: { lineIdx: number; chargeIdx: number } | null\n onToggle: () => void\n onTogglePricingDetail: (lineIdx: number, chargeIdx: number) => void\n}) {\n const t = useT()\n const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : undefined\n // Same presentation as the quote page: an existing item removed on an\n // Amend / Renew quote reads as \"remove\", struck through.\n const isRemoved = isArcCancelledLine(line, quoteType)\n\n return (\n <div>\n <div\n className={`flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors cursor-pointer ${isRemoved ? 'bg-muted/20' : ''}`}\n style={paddingLeft ? { paddingLeft } : undefined}\n onClick={onToggle}\n data-testid=\"cpq-order-line\"\n data-removed={isRemoved ? 'true' : undefined}\n >\n <button className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground\">\n <svg className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`} fill=\"none\" viewBox=\"0 0 24 24\" strokeWidth=\"2\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n {indent > 0 && <span className=\"text-muted-foreground text-xs\">\u2514</span>}\n {isBundle && (\n <Tag variant=\"brand\" className=\"px-1.5 text-xs\">{t('cpq.lines.bundle', 'bundle')}</Tag>\n )}\n <span className={`font-medium text-sm ${isRemoved ? 'text-muted-foreground line-through' : ''}`}>{line.offeringName}</span>\n {line.quantity > 1 && <span className=\"rounded bg-muted px-1.5 py-0.5 text-xs font-medium\">\u00D7{line.quantity}</span>}\n <LineActionBadge\n action={line.action}\n label={isRemoved ? t('cpq.quotes.arc.lineRemoved', 'remove') : undefined}\n title={isRemoved ? t('cpq.quotes.arc.lineRemovedHint', 'This item is cancelled on the subscription when the order is activated') : undefined}\n />\n {isBundle && (\n <span className=\"text-xs text-muted-foreground\">\n {childCount !== 1\n ? t('cpq.lines.componentCount.other', '{count} components', { count: childCount })\n : t('cpq.lines.componentCount.one', '{count} component', { count: childCount })}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"shrink-0 text-right text-xs space-y-0.5\">\n {isRemoved && line.arcSource && line.arcSource.mrcAmount > 0 && (\n <div>\n <span className=\"text-muted-foreground\">{t('cpq.lines.mrc', 'MRC')} </span>\n <span className=\"font-mono text-muted-foreground line-through\">{fmt(line.arcSource.mrcAmount, currency)}</span>\n </div>\n )}\n {line.nrcTotal > 0 && <div><span className=\"text-muted-foreground\">{t('cpq.lines.nrc', 'NRC')} </span><span className=\"font-mono font-medium\">{fmt(line.nrcTotal, currency)}</span></div>}\n {line.mrcTotal > 0 && <div><span className=\"text-muted-foreground\">{t('cpq.lines.mrc', 'MRC')} </span><span className=\"font-mono font-medium\">{fmt(line.mrcTotal, currency)}</span></div>}\n </div>\n </div>\n\n {isExpanded && (() => {\n const internalKeys = new Set(['_arcMirroredName', '_arcMirroredFromItemId', '_arcPriceFrozen', '_arcRepriced', 'offeringName'])\n const visibleConfigEntries = Object.entries(line.configuration).filter(\n ([key]) => !internalKeys.has(key),\n )\n const hasConfig = visibleConfigEntries.length > 0\n const hasCharges = line.charges.length > 0\n const hasTerm = !!(line.startDate || line.termMonths || line.endDate)\n const hasArcDiff = !!line.arcSource\n const mirroredFromItemId = (line.configuration?._arcMirroredFromItemId as string | undefined) ?? null\n const isEmpty = !hasConfig && !hasCharges && !hasTerm && !hasArcDiff\n\n return (\n <div\n className=\"border-t bg-muted/10 px-12 py-3 space-y-3\"\n style={paddingLeft ? { paddingLeft: `calc(${paddingLeft} + 2rem)` } : undefined}\n >\n {isEmpty ? (\n <div className=\"text-xs text-muted-foreground space-y-1\">\n <div>\n {t('cpq.orders.detail.noBreakdown', 'No detailed breakdown for this line.')}\n {mirroredFromItemId && ' '}\n {mirroredFromItemId && t('cpq.orders.detail.mirroredItem', 'Mirrored from existing subscription item.')}\n </div>\n {(line.nrcTotal > 0 || line.mrcTotal > 0) && (\n <div className=\"flex gap-4\">\n {line.nrcTotal > 0 && (\n <span>{t('cpq.lines.nrc', 'NRC')} <span className=\"font-mono font-medium text-foreground\">{fmt(line.nrcTotal, currency)}</span></span>\n )}\n {line.mrcTotal > 0 && (\n <span>{t('cpq.lines.mrc', 'MRC')} <span className=\"font-mono font-medium text-foreground\">{fmt(line.mrcTotal, currency)}</span></span>\n )}\n </div>\n )}\n </div>\n ) : (\n <>\n {hasConfig && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">{t('cpq.lines.configuration', 'Configuration')}</p>\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1\">\n {visibleConfigEntries.map(([key, value]) => (\n <div key={key} className=\"text-xs\">\n <span className=\"text-muted-foreground\">{key}: </span>\n <span className=\"font-medium\">{String(value)}</span>\n </div>\n ))}\n </div>\n </div>\n )}\n\n {hasCharges && (\n <ChargeBreakdownTable\n charges={line.charges.map(toChargeRow)}\n currency={currency}\n openDetailIndex={pricingDetail?.lineIdx === lineIdx ? pricingDetail.chargeIdx : null}\n onToggleDetail={(chargeIdx) => onTogglePricingDetail(lineIdx, chargeIdx)}\n />\n )}\n\n {hasTerm && (\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n {line.startDate && <span>{t('cpq.lines.term.start', 'Start: {date}', { date: new Date(line.startDate).toLocaleDateString() })}</span>}\n {line.termMonths && <span>{t('cpq.lines.term.months', 'Term: {months}mo', { months: line.termMonths })}</span>}\n {line.endDate && <span>{t('cpq.lines.term.end', 'End: {date}', { date: new Date(line.endDate).toLocaleDateString() })}</span>}\n </div>\n )}\n\n {/* XD-250 ARC: before/after preview below the charges, as on the quote. */}\n {hasArcDiff && <ArcLineDiff line={line} currency={currency} />}\n </>\n )}\n </div>\n )\n })()}\n </div>\n )\n}\n\nfunction Spinner() {\n return (\n <svg className=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\" />\n </svg>\n )\n}\n\nfunction ErrorBanner({ message, onDismiss }: { message: string; onDismiss?: () => void }) {\n const t = useT()\n return (\n <Alert variant=\"destructive\" className=\"flex items-start gap-2\">\n <span className=\"flex-1\">{message}</span>\n {onDismiss && (\n <button\n onClick={onDismiss}\n className=\"text-destructive/70 hover:text-destructive text-sm font-bold\"\n aria-label={t('cpq.common.dismiss', 'Dismiss')}\n >\n \u00D7\n </button>\n )}\n </Alert>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAwQM,SAsDI,UArDF,KADF;AAvQN,YAAY,WAAW;AACvB,SAAS,WAAW,iBAAiB;AACrC,SAAS,YAAY,cAAc,MAAM,SAAS;AAClD,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,WAAW;AACpB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,kBAAkB;AAC3B,SAAS,4BAAgD;AACzD,SAAS,0BAAmD;AAC5D,SAAS,aAAa,uBAAuB;AAC7C,SAAS,yBAAyB;AAClC,SAAS,0BAA0B;AACnC,SAAS,YAA8B;AAgCvC,SAAS,YAAY,GAAkC;AACrD,SAAO,EAAE,GAAG,GAAG,YAAY,EAAE,cAAc,UAAK,YAAa,EAAE,cAAc,OAAmC,WAAW,EAAE,aAAa,GAAG,YAAY,EAAE,cAAc,KAAK;AAChL;AAsCA,MAAM,qBAAoE;AAAA,EACxE,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,SAAS,eAAe,GAAgB,WAA2B;AACjE,UAAQ,WAAW;AAAA,IACjB,KAAK;AACH,aAAO,EAAE,yBAAyB,OAAO;AAAA,IAC3C,KAAK;AACH,aAAO,EAAE,yBAAyB,OAAO;AAAA,IAC3C,KAAK;AACH,aAAO,EAAE,0BAA0B,QAAQ;AAAA,IAC7C;AACE,aAAO;AAAA,EACX;AACF;AAKA,SAAS,IAAI,QAAgB,UAA0B;AACrD,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,MAAM;AACtF;AAEA,eAAe,QAAW,GAAgB,KAAa,MAAgC;AACrF,QAAM,MAAM,MAAM,MAAM,KAAK,IAAI;AACjC,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,MAAM,SAAS,EAAE,4BAA4B,6BAA6B,EAAE,QAAQ,IAAI,OAAO,CAAC,CAAC;AAC9H,SAAO;AACT;AAIe,SAAR,mBAAoC,OAAqC;AAC9E,QAAM,SAAS,UAAU;AACzB,QAAM,IAAI,KAAK;AACf,QAAM,YAAY,UAAU;AAC5B,QAAM,aAAc,MAAM,QAAQ,MAAM,WAAW;AAEnD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAA6B,IAAI;AACjE,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAsB,oBAAI,IAAI,CAAC;AAC/E,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAwD,IAAI;AAC5G,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAwB,IAAI;AAC1E,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAKxD,CAAC,CAAC;AAQN,QAAM,2BAA2B,MAAM;AAAA,IACrC,OAAO,eAAuB,kBAAiC;AAC7D,YAAM,YAAY;AAAA,QAChB,kDAAkD,aAAa;AAAA,MACjE,EACG,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAK,EACpC,KAAK,CAAC,MAAO,GAAG,UAAU,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC,EAAwE,EAC1H,MAAM,MAAM,CAAC,CAAC;AAEjB,YAAM,YAAY,gBACd,MAAM,mBAAmB,aAAa,uBAAuB,EAC1D,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAK,EACpC,KAAK,OAAO,MAAM;AACjB,cAAM,UAA6C,GAAG,SAAS,CAAC;AAChE,cAAM,UAAU,MAAM,QAAQ;AAAA,UAC5B,QAAQ;AAAA,YAAI,CAAC,WACX,MAAM,uCAAuC,OAAO,cAAc,EAAE,EACjE,KAAK,CAAC,MAAO,EAAE,KAAK,EAAE,KAAK,IAAI,IAAK,EACpC,MAAM,MAAM,IAAI;AAAA,UACrB;AAAA,QACF;AACA,eAAO,QAAQ,OAAO,OAAO;AAAA,MAC/B,CAAC,EACA,MAAM,MAAM,CAAC,CAAsE,IACtF,QAAQ,QAAQ,CAAC,CAAsE;AAE3F,YAAM,CAAC,GAAG,CAAC,IAAI,MAAM,QAAQ,IAAI,CAAC,WAAW,SAAS,CAAC;AAGvD,YAAM,OAAO,oBAAI,IAAY;AAC7B,YAAM,SAA4E,CAAC;AACnF,iBAAW,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG;AAC9B,YAAI,CAAC,OAAO,KAAK,IAAI,IAAI,EAAE,EAAG;AAC9B,aAAK,IAAI,IAAI,EAAE;AACf,eAAO,KAAK,EAAE,IAAI,IAAI,IAAI,MAAM,IAAI,MAAM,MAAM,IAAI,MAAM,QAAQ,IAAI,OAAO,CAAC;AAAA,MAChF;AACA,6BAAuB,MAAM;AAAA,IAC/B;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AACpB,QAAI,YAAY;AAChB,mBAAe,OAAO;AACpB,UAAI;AACF,cAAM,SAAS,MAAM,QAAqB,GAAG,mBAAmB,UAAU,EAAE;AAC5E,YAAI,CAAC,WAAW;AACd,mBAAS,MAAM;AACf,gBAAM,+BAA+B,OAAO,UAAU,aAAa,EAChE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EACpB,KAAK,CAAC,SAAS;AACd,kBAAM,QAAQ,KAAK,SAAS,QAAQ,CAAC;AACrC,kBAAM,IAAI,MAAM,CAAC;AACjB,gBAAI,KAAK,CAAC,UAAW,iBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,IAAI;AAAA,UACxF,CAAC,EACA,MAAM,MAAM;AAAA,UAAC,CAAC;AACjB,cAAI,OAAO,cAAc,YAAY,OAAO,cAAc,aAAa;AACrE,iBAAK,yBAAyB,OAAO,IAAI,OAAO,aAAa;AAAA,UAC/D;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,CAAC,UAAW,UAAU,IAAc,OAAO;AAAA,MACjD,UAAE;AACA,YAAI,CAAC,UAAW,YAAW,KAAK;AAAA,MAClC;AAAA,IACF;AACA,SAAK;AACL,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,YAAY,0BAA0B,CAAC,CAAC;AAE5C,QAAM,gBAAgB,YAAY;AAChC,QAAI,CAAC,MAAO;AACZ,QAAI;AACF,oBAAc,IAAI;AAClB,eAAS,IAAI;AACb,YAAM,SAAS,MAAM,QAAqB,GAAG,mBAAmB,MAAM,EAAE,aAAa;AAAA,QACnF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,MACzB,CAAC;AACD,eAAS,MAAM;AACf,WAAK,yBAAyB,OAAO,IAAI,OAAO,aAAa;AAAA,IAC/D,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,cAAc,YAAY;AAC9B,QAAI,CAAC,MAAO;AACZ,QAAI;AACF,eAAS,IAAI;AACb,YAAM,SAAS,MAAM,QAAqB,GAAG,mBAAmB,MAAM,EAAE,WAAW;AAAA,QACjF,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,cAAc,YAAY,CAAC;AAAA,MACpD,CAAC;AACD,eAAS,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,OAAe;AACjC,qBAAiB,CAAC,SAAS;AAAE,YAAM,IAAI,IAAI,IAAI,IAAI;AAAG,QAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE;AAAG,aAAO;AAAA,IAAE,CAAC;AAAA,EACxG;AAIA,MAAI,SAAS;AACX,WACE,qBAAC,SAAI,WAAU,8EACb;AAAA,0BAAC,WAAQ;AAAA,MAAE;AAAA,MAAE,EAAE,6BAA6B,eAAU;AAAA,OACxD;AAAA,EAEJ;AAEA,MAAI,CAAC,OAAO;AACV,WACE,qBAAC,SAAI,WAAU,aACb;AAAA,0BAAC,YAAO,SAAS,MAAM,OAAO,KAAK,qBAAqB,GAAG,WAAU,uDAAuD,YAAE,kCAAkC,uBAAkB,GAAE;AAAA,MACnL,SAAS,oBAAC,eAAY,SAAS,OAAO;AAAA,MACvC,oBAAC,SAAI,WAAU,2EAA2E,YAAE,8BAA8B,kBAAkB,GAAE;AAAA,OAChJ;AAAA,EAEJ;AAIA,QAAM,cAAc,MAAM,cAAc,WAAW,MAAM,cAAc;AACvE,QAAM,YAAY,MAAM,cAAc,WAAW,MAAM,cAAc,wBAAwB,MAAM,cAAc;AACjH,QAAM,WAAW,MAAM;AAEvB,QAAM,sBAAsB,CAAC,SAAiB,cAAsB;AAClE;AAAA,MAAiB,CAAC,SAChB,MAAM,YAAY,WAAW,MAAM,cAAc,YAAY,OAAO,EAAE,SAAS,UAAU;AAAA,IAC3F;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAU,aAMb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAS;AAAA,QACT,WAAW,EAAE,+BAA+B,oBAAoB;AAAA,QAChE,iBAAiB,EAAE,gCAAgC,WAAW;AAAA,QAC9D,OAAO,MAAM,eAAe,MAAM,QAAQ,MAAM,GAAG,CAAC;AAAA,QACpD,aACE,qBAAC,SAAI,WAAU,0CACb;AAAA,8BAAC,OAAI,SAAS,kBAAkB,MAAM,SAA2B,KAAK,WAAW,KAAG,MACjF,4BAAkB,MAAM,SAAS,GACpC;AAAA,UACC,MAAM,aAAa,MAAM,cAAc,SACtC,oBAAC,OAAI,SAAS,mBAAmB,MAAM,SAAS,KAAK,WAAW,WAAU,aACvE,yBAAe,GAAG,MAAM,SAAS,GACpC;AAAA,WAEJ;AAAA,QAEF,gBACE,iCACE;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS,MAAM,OAAO,KAAK,yBAAyB,MAAM,OAAO,EAAE;AAAA,cAEnE;AAAA,oCAAC,gBAAa,WAAU,eAAc;AAAA,gBACrC,EAAE,oCAAoC,kBAAkB;AAAA;AAAA;AAAA,UAC3D;AAAA,UACC,MAAM,iBACL;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS,MAAM,OAAO,KAAK,uBAAuB,MAAM,aAAa,EAAE;AAAA,cAEtE,YAAE,iCAAiC,cAAc;AAAA;AAAA,UACpD;AAAA,UAED,eACC,qBAAC,UAAO,MAAK,UAAS,SAAS,eAAe,UAAU,YACrD;AAAA,yBAAa,oBAAC,WAAQ,IAAK,oBAAC,QAAK,WAAU,WAAU;AAAA,YACrD,EAAE,8BAA8B,UAAU;AAAA,aAC7C;AAAA,UAED,aACC,qBAAC,UAAO,MAAK,UAAS,SAAQ,uBAAsB,SAAS,aAC3D;AAAA,gCAAC,KAAE,WAAU,WAAU;AAAA,YACtB,EAAE,qBAAqB,QAAQ;AAAA,aAClC;AAAA,WAEJ;AAAA;AAAA,IAEJ;AAAA,IAKA,oBAAC,SAAI,WAAU,qCACb;AAAA,MAAC;AAAA;AAAA,QAIC,SAAS,MAAM;AAAA,QACf,MAAM,CAAC,SAAS,sBAAsB,UAAU,WAAW;AAAA,QAC3D,WAAW,CAAC,WAAW;AAAA,QACvB,WAAW;AAAA,QACX,WAAW,EAAE,gCAAgC,mBAAmB;AAAA;AAAA,IAClE,GACF;AAAA,IAEC,SAAS,oBAAC,eAAY,SAAS,OAAO,WAAW,MAAM,SAAS,IAAI,GAAG;AAAA,IAMvE,oBAAoB,SAAS,KAC5B,oBAAC,SAAI,WAAU,aACZ,8BAAoB,IAAI,CAAC,QACxB;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,4BACb;AAAA,iCAAC,SAAI,WAAU,qCACb;AAAA,kCAAC,UAAK,WAAU,yEACb,YAAE,wCAAwC,qBAAqB,GAClE;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,IAAI,WAAW,WAAW,YAAY;AAAA,kBAC/C,KAAG;AAAA,kBACH,WAAU;AAAA,kBAET,cAAI;AAAA;AAAA,cACP;AAAA,eACF;AAAA,YACA,qBAAC,SAAI,WAAU,uBACZ;AAAA,kBAAI;AAAA,cAAM;AAAA,cACX,qBAAC,UAAK,WAAU,2CAA0C;AAAA;AAAA,gBAAE,IAAI;AAAA,gBAAK;AAAA,iBAAC;AAAA,eACxE;AAAA,YACC,MAAM,eACL,oBAAC,SAAI,WAAU,iCACZ,YAAE,iCAAiC,uBAAuB,EAAE,MAAM,IAAI,KAAK,MAAM,WAAW,EAAE,eAAe,EAAE,CAAC,GACnH;AAAA,aAEJ;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,SAAS,MAAM,OAAO,KAAK,wCAAwC,IAAI,EAAE,EAAE;AAAA,cAE3E;AAAA,oCAAC,cAAW,WAAU,eAAc;AAAA,gBACnC,EAAE,2BAA2B,MAAM;AAAA;AAAA;AAAA,UACtC;AAAA;AAAA;AAAA,MAjCK,IAAI;AAAA,IAkCX,CACD,GACH;AAAA,IAIF,qBAAC,SAAI,WAAU,wDACb;AAAA,0BAAC,YAAS,OAAO,EAAE,mCAAmC,UAAU,GAC7D,yBACC,oBAAC,UAAK,WAAU,eAAe,wBAAa,IAE5C,qBAAC,UAAK,WAAU,2CACb;AAAA,cAAM,WAAW,MAAM,GAAG,CAAC;AAAA,QAAE;AAAA,SAChC,GAEJ;AAAA,MACA,oBAAC,YAAS,OAAO,EAAE,kCAAkC,SAAS,GAC5D,8BAAC,UAAK,WAAU,eAAe,cAAI,KAAK,MAAM,SAAS,EAAE,mBAAmB,GAAE,GAChF;AAAA,MACA,oBAAC,YAAS,OAAO,EAAE,mCAAmC,UAAU,GAC9D,8BAAC,UAAK,WAAU,eAAe,oBAAS,GAC1C;AAAA,OACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM;AAAA,QACf;AAAA,QACA,UAAU,MAAM,cAAc,WAAW,MAAM,cAAc,UACzD,kBAAkB,MAAM,MAAM,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,OAAsC,EAAE,CAAC,IACrG;AAAA;AAAA,IACN;AAAA,IAGA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,MAAM;AAAA,QACb,WAAW,MAAM;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,QACd,uBAAuB;AAAA;AAAA,IACzB;AAAA,KACF;AAEJ;AAEA,SAAS,SAAS,EAAE,OAAO,SAAS,GAAiD;AACnF,SACE,qBAAC,SAAI,WAAU,iCACb;AAAA,wBAAC,SAAI,WAAU,yEACZ,iBACH;AAAA,IACA,oBAAC,SAAI,WAAU,gBAAgB,UAAS;AAAA,KAC1C;AAEJ;AAIA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,IAAI,KAAK;AACf,QAAM,YAAY,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY;AACrD,QAAM,mBAAmB,oBAAI,IAA+B;AAC5D,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,cAAc;AACrB,YAAM,MAAM,iBAAiB,IAAI,KAAK,YAAY,KAAK,CAAC;AACxD,UAAI,KAAK,IAAI;AACb,uBAAiB,IAAI,KAAK,cAAc,GAAG;AAAA,IAC7C;AAAA,EACF;AAIA,QAAM,UAAU,oBAAI,IAAoB;AACxC,QAAM,QAAQ,CAAC,GAAG,MAAM,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AAEhD,SACE,qBAAC,SACC;AAAA,wBAAC,QAAG,WAAU,iCAAiC,YAAE,+BAA+B,wBAAwB,EAAE,OAAO,MAAM,OAAO,CAAC,GAAE;AAAA,IAChI,MAAM,WAAW,IAChB,oBAAC,SAAI,WAAU,4EACZ,YAAE,iCAAiC,gBAAgB,GACtD,IAEA,oBAAC,SAAI,WAAU,sCACZ,oBAAU,IAAI,CAAC,SAAS;AACvB,YAAM,WAAW,KAAK,iBAAiB;AACvC,YAAM,WAAW,iBAAiB,IAAI,KAAK,MAAM,KAAK,CAAC;AACvD,YAAM,aAAa,cAAc,IAAI,KAAK,MAAM;AAChD,YAAM,UAAU,QAAQ,IAAI,KAAK,MAAM,KAAK;AAC5C,aACE,qBAAC,SACC;AAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,QAAQ;AAAA,YACR;AAAA,YACA,UAAU,MAAM,aAAa,KAAK,MAAM;AAAA,YACxC;AAAA;AAAA,QACF;AAAA,QACC,YAAY,cAAc,SAAS,SAAS,KAC3C,qBAAC,SAAI,WAAU,mEACb;AAAA,8BAAC,SAAI,WAAU,eACb,8BAAC,UAAK,WAAU,sEAAsE,YAAE,8BAA8B,+BAA+B,EAAE,OAAO,SAAS,OAAO,CAAC,GAAE,GACnL;AAAA,UACC,SAAS,IAAI,CAAC,UAAU;AACvB,kBAAM,WAAW,QAAQ,IAAI,MAAM,MAAM,KAAK;AAC9C,mBACE;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAM;AAAA,gBACN,SAAS;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,YAAY,cAAc,IAAI,MAAM,MAAM;AAAA,gBAC1C,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,QAAQ;AAAA,gBACR;AAAA,gBACA,UAAU,MAAM,aAAa,MAAM,MAAM;AAAA,gBACzC;AAAA;AAAA,cAXK,MAAM;AAAA,YAYb;AAAA,UAEJ,CAAC;AAAA,WACH;AAAA,WAtCM,KAAK,MAwCf;AAAA,IAEJ,CAAC,GACH;AAAA,KAEJ;AAEJ;AAEA,SAAS,aAAa,EAAE,MAAM,SAAS,WAAW,UAAU,YAAY,UAAU,YAAY,QAAQ,eAAe,UAAU,sBAAsB,GAKlJ;AACD,QAAM,IAAI,KAAK;AACf,QAAM,cAAc,SAAS,IAAI,GAAG,SAAS,IAAI,CAAC,QAAQ;AAG1D,QAAM,YAAY,mBAAmB,MAAM,SAAS;AAEpD,SACE,qBAAC,SACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,wFAAwF,YAAY,gBAAgB,EAAE;AAAA,QACjI,OAAO,cAAc,EAAE,YAAY,IAAI;AAAA,QACvC,SAAS;AAAA,QACT,eAAY;AAAA,QACZ,gBAAc,YAAY,SAAS;AAAA,QAEnC;AAAA,8BAAC,YAAO,WAAU,yGAChB,8BAAC,SAAI,WAAW,gCAAgC,aAAa,cAAc,EAAE,IAAI,MAAK,QAAO,SAAQ,aAAY,aAAY,KAAI,QAAO,gBACtI,8BAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,GAAE,6BAA4B,GACnF,GACF;AAAA,UAEA,oBAAC,SAAI,WAAU,kBACb,+BAAC,SAAI,WAAU,2BACZ;AAAA,qBAAS,KAAK,oBAAC,UAAK,WAAU,iCAAgC,oBAAC;AAAA,YAC/D,YACC,oBAAC,OAAI,SAAQ,SAAQ,WAAU,kBAAkB,YAAE,oBAAoB,QAAQ,GAAE;AAAA,YAEnF,oBAAC,UAAK,WAAW,uBAAuB,YAAY,uCAAuC,EAAE,IAAK,eAAK,cAAa;AAAA,YACnH,KAAK,WAAW,KAAK,qBAAC,UAAK,WAAU,sDAAqD;AAAA;AAAA,cAAE,KAAK;AAAA,eAAS;AAAA,YAC3G;AAAA,cAAC;AAAA;AAAA,gBACC,QAAQ,KAAK;AAAA,gBACb,OAAO,YAAY,EAAE,8BAA8B,QAAQ,IAAI;AAAA,gBAC/D,OAAO,YAAY,EAAE,kCAAkC,wEAAwE,IAAI;AAAA;AAAA,YACrI;AAAA,YACC,YACC,oBAAC,UAAK,WAAU,iCACb,yBAAe,IACZ,EAAE,kCAAkC,sBAAsB,EAAE,OAAO,WAAW,CAAC,IAC/E,EAAE,gCAAgC,qBAAqB,EAAE,OAAO,WAAW,CAAC,GAClF;AAAA,aAEJ,GACF;AAAA,UAEA,qBAAC,SAAI,WAAU,2CACZ;AAAA,yBAAa,KAAK,aAAa,KAAK,UAAU,YAAY,KACzD,qBAAC,SACC;AAAA,mCAAC,UAAK,WAAU,yBAAyB;AAAA,kBAAE,iBAAiB,KAAK;AAAA,gBAAE;AAAA,iBAAC;AAAA,cACpE,oBAAC,UAAK,WAAU,gDAAgD,cAAI,KAAK,UAAU,WAAW,QAAQ,GAAE;AAAA,eAC1G;AAAA,YAED,KAAK,WAAW,KAAK,qBAAC,SAAI;AAAA,mCAAC,UAAK,WAAU,yBAAyB;AAAA,kBAAE,iBAAiB,KAAK;AAAA,gBAAE;AAAA,iBAAC;AAAA,cAAO,oBAAC,UAAK,WAAU,yBAAyB,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,eAAO;AAAA,YAClL,KAAK,WAAW,KAAK,qBAAC,SAAI;AAAA,mCAAC,UAAK,WAAU,yBAAyB;AAAA,kBAAE,iBAAiB,KAAK;AAAA,gBAAE;AAAA,iBAAC;AAAA,cAAO,oBAAC,UAAK,WAAU,yBAAyB,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,eAAO;AAAA,aACrL;AAAA;AAAA;AAAA,IACF;AAAA,IAEC,eAAe,MAAM;AACpB,YAAM,eAAe,oBAAI,IAAI,CAAC,oBAAoB,0BAA0B,mBAAmB,gBAAgB,cAAc,CAAC;AAC9H,YAAM,uBAAuB,OAAO,QAAQ,KAAK,aAAa,EAAE;AAAA,QAC9D,CAAC,CAAC,GAAG,MAAM,CAAC,aAAa,IAAI,GAAG;AAAA,MAClC;AACA,YAAM,YAAY,qBAAqB,SAAS;AAChD,YAAM,aAAa,KAAK,QAAQ,SAAS;AACzC,YAAM,UAAU,CAAC,EAAE,KAAK,aAAa,KAAK,cAAc,KAAK;AAC7D,YAAM,aAAa,CAAC,CAAC,KAAK;AAC1B,YAAM,qBAAsB,KAAK,eAAe,0BAAiD;AACjG,YAAM,UAAU,CAAC,aAAa,CAAC,cAAc,CAAC,WAAW,CAAC;AAE1D,aACE;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO,cAAc,EAAE,aAAa,QAAQ,WAAW,WAAW,IAAI;AAAA,UAErE,oBACC,qBAAC,SAAI,WAAU,2CACb;AAAA,iCAAC,SACE;AAAA,gBAAE,iCAAiC,sCAAsC;AAAA,cACzE,sBAAsB;AAAA,cACtB,sBAAsB,EAAE,kCAAkC,2CAA2C;AAAA,eACxG;AAAA,aACE,KAAK,WAAW,KAAK,KAAK,WAAW,MACrC,qBAAC,SAAI,WAAU,cACZ;AAAA,mBAAK,WAAW,KACf,qBAAC,UAAM;AAAA,kBAAE,iBAAiB,KAAK;AAAA,gBAAE;AAAA,gBAAC,oBAAC,UAAK,WAAU,yCAAyC,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,iBAAO;AAAA,cAEhI,KAAK,WAAW,KACf,qBAAC,UAAM;AAAA,kBAAE,iBAAiB,KAAK;AAAA,gBAAE;AAAA,gBAAC,oBAAC,UAAK,WAAU,yCAAyC,cAAI,KAAK,UAAU,QAAQ,GAAE;AAAA,iBAAO;AAAA,eAEnI;AAAA,aAEJ,IAEA,iCACG;AAAA,yBACC,qBAAC,SACC;AAAA,kCAAC,OAAE,WAAU,kDAAkD,YAAE,2BAA2B,eAAe,GAAE;AAAA,cAC7G,oBAAC,SAAI,WAAU,mDACZ,+BAAqB,IAAI,CAAC,CAAC,KAAK,KAAK,MACpC,qBAAC,SAAc,WAAU,WACvB;AAAA,qCAAC,UAAK,WAAU,yBAAyB;AAAA;AAAA,kBAAI;AAAA,mBAAE;AAAA,gBAC/C,oBAAC,UAAK,WAAU,eAAe,iBAAO,KAAK,GAAE;AAAA,mBAFrC,GAGV,CACD,GACH;AAAA,eACF;AAAA,YAGD,cACC;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,KAAK,QAAQ,IAAI,WAAW;AAAA,gBACrC;AAAA,gBACA,iBAAiB,eAAe,YAAY,UAAU,cAAc,YAAY;AAAA,gBAChF,gBAAgB,CAAC,cAAc,sBAAsB,SAAS,SAAS;AAAA;AAAA,YACzE;AAAA,YAGD,WACC,qBAAC,SAAI,WAAU,4CACZ;AAAA,mBAAK,aAAa,oBAAC,UAAM,YAAE,wBAAwB,iBAAiB,EAAE,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB,EAAE,CAAC,GAAE;AAAA,cAC7H,KAAK,cAAc,oBAAC,UAAM,YAAE,yBAAyB,oBAAoB,EAAE,QAAQ,KAAK,WAAW,CAAC,GAAE;AAAA,cACtG,KAAK,WAAW,oBAAC,UAAM,YAAE,sBAAsB,eAAe,EAAE,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE,mBAAmB,EAAE,CAAC,GAAE;AAAA,eACxH;AAAA,YAID,cAAc,oBAAC,eAAY,MAAY,UAAoB;AAAA,aAC9D;AAAA;AAAA,MAEJ;AAAA,IAEJ,GAAG;AAAA,KACL;AAEJ;AAEA,SAAS,UAAU;AACjB,SACE,qBAAC,SAAI,WAAU,wBAAuB,SAAQ,aAAY,MAAK,QAC7D;AAAA,wBAAC,YAAO,WAAU,cAAa,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,QAAO,gBAAe,aAAY,KAAI;AAAA,IAC5F,oBAAC,UAAK,WAAU,cAAa,MAAK,gBAAe,GAAE,uCAAsC;AAAA,KAC3F;AAEJ;AAEA,SAAS,YAAY,EAAE,SAAS,UAAU,GAAgD;AACxF,QAAM,IAAI,KAAK;AACf,SACE,qBAAC,SAAM,SAAQ,eAAc,WAAU,0BACrC;AAAA,wBAAC,UAAK,WAAU,UAAU,mBAAQ;AAAA,IACjC,aACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACV,cAAY,EAAE,sBAAsB,SAAS;AAAA,QAC9C;AAAA;AAAA,IAED;AAAA,KAEJ;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -34,13 +34,13 @@ function CpqOrdersListPage() {
|
|
|
34
34
|
});
|
|
35
35
|
const statusOptions = React.useMemo(
|
|
36
36
|
() => [
|
|
37
|
-
{ value: "draft", label: "Draft" },
|
|
38
|
-
{ value: "pending_activation", label: "Pending Activation" },
|
|
39
|
-
{ value: "active", label: "Active" },
|
|
40
|
-
{ value: "fulfilled", label: "Fulfilled" },
|
|
41
|
-
{ value: "cancelled", label: "Cancelled" }
|
|
37
|
+
{ value: "draft", label: t("cpq.orders.status.draft", "Draft") },
|
|
38
|
+
{ value: "pending_activation", label: t("cpq.orders.status.pending_activation", "Pending Activation") },
|
|
39
|
+
{ value: "active", label: t("cpq.orders.status.active", "Active") },
|
|
40
|
+
{ value: "fulfilled", label: t("cpq.orders.status.fulfilled", "Fulfilled") },
|
|
41
|
+
{ value: "cancelled", label: t("cpq.orders.status.cancelled", "Cancelled") }
|
|
42
42
|
],
|
|
43
|
-
[]
|
|
43
|
+
[t]
|
|
44
44
|
);
|
|
45
45
|
const filters = React.useMemo(
|
|
46
46
|
() => [
|