@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
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../../../components/statusMaps.js";
|
|
26
26
|
const PAGE_SIZE = 50;
|
|
27
27
|
function ChargePopover({ charge }) {
|
|
28
|
+
const t = useT();
|
|
28
29
|
const [open, setOpen] = React.useState(false);
|
|
29
30
|
const variant = chargeTypeMap[charge.chargeType] ?? "neutral";
|
|
30
31
|
return /* @__PURE__ */ jsxs(
|
|
@@ -40,17 +41,14 @@ function ChargePopover({ charge }) {
|
|
|
40
41
|
/* @__PURE__ */ jsx("div", { className: "text-muted-foreground font-mono", children: charge.code }),
|
|
41
42
|
charge.description && /* @__PURE__ */ jsx("div", { className: "text-muted-foreground", children: charge.description }),
|
|
42
43
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-1 border-t", children: [
|
|
43
|
-
/* @__PURE__ */
|
|
44
|
-
"Pricing: ",
|
|
45
|
-
charge.pricingMethod
|
|
46
|
-
] }),
|
|
44
|
+
/* @__PURE__ */ jsx("span", { children: t("cpq.offerings.list.chargePopover.pricing", "Pricing: {method}", { method: charge.pricingMethod }) }),
|
|
47
45
|
charge.fixedPrice != null && /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
48
46
|
charge.currencyCode ?? "USD",
|
|
49
47
|
" ",
|
|
50
48
|
charge.fixedPrice
|
|
51
49
|
] })
|
|
52
50
|
] }),
|
|
53
|
-
!charge.isActive && /* @__PURE__ */ jsx(Tag, { variant: "warning", dot: true, children: "Inactive" }),
|
|
51
|
+
!charge.isActive && /* @__PURE__ */ jsx(Tag, { variant: "warning", dot: true, children: t("cpq.common.inactive", "Inactive") }),
|
|
54
52
|
/* @__PURE__ */ jsx("div", { className: "absolute left-1/2 -translate-x-1/2 top-full w-2 h-2 bg-popover border-b border-r rotate-45 -mt-1" })
|
|
55
53
|
] })
|
|
56
54
|
]
|
|
@@ -163,7 +161,7 @@ function OfferingsListPage() {
|
|
|
163
161
|
});
|
|
164
162
|
if (!res.ok) {
|
|
165
163
|
const body = await res.json().catch(() => ({}));
|
|
166
|
-
setError(body.error ??
|
|
164
|
+
setError(body.error ?? t("cpq.offerings.bulk.error.saveChargeFor", "Failed to save charge for {name}", { name: offering.name }));
|
|
167
165
|
setSaving(false);
|
|
168
166
|
return;
|
|
169
167
|
}
|
|
@@ -173,7 +171,7 @@ function OfferingsListPage() {
|
|
|
173
171
|
setBulkChargeTargets([]);
|
|
174
172
|
data.reload();
|
|
175
173
|
} catch {
|
|
176
|
-
setError("Failed to save charges");
|
|
174
|
+
setError(t("cpq.offerings.bulk.error.saveCharges", "Failed to save charges"));
|
|
177
175
|
} finally {
|
|
178
176
|
setSaving(false);
|
|
179
177
|
}
|
|
@@ -197,7 +195,7 @@ function OfferingsListPage() {
|
|
|
197
195
|
header: t("cpq.offerings.name", "Name"),
|
|
198
196
|
cell: ({ row }) => /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
199
197
|
row.original.name,
|
|
200
|
-
row.original.offeringType === "bundle" && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "ml-2 px-2 text-[10px]", children: "bundle" })
|
|
198
|
+
row.original.offeringType === "bundle" && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "ml-2 px-2 text-[10px]", children: t("cpq.offerings.list.tag.bundle", "bundle") })
|
|
201
199
|
] })
|
|
202
200
|
},
|
|
203
201
|
{
|
|
@@ -251,39 +249,47 @@ function OfferingsListPage() {
|
|
|
251
249
|
bulkChargeTargets.length > 1 ? t("cpq.offerings.bulk.offeringsPlural", "offerings") : t("cpq.offerings.bulk.offeringsSingular", "offering")
|
|
252
250
|
] }),
|
|
253
251
|
/* @__PURE__ */ jsxs(Alert, { children: [
|
|
254
|
-
"Bulk creation supports
|
|
255
|
-
|
|
256
|
-
".
|
|
257
|
-
|
|
258
|
-
"
|
|
259
|
-
/* @__PURE__ */ jsx("em", { children: "
|
|
260
|
-
"
|
|
252
|
+
t("cpq.offerings.bulk.notice.supports", "Bulk creation supports"),
|
|
253
|
+
" ",
|
|
254
|
+
/* @__PURE__ */ jsx("strong", { children: t("cpq.offerings.bulk.notice.flatOnly", "flat pricing only") }),
|
|
255
|
+
t("cpq.offerings.bulk.notice.for", ". For"),
|
|
256
|
+
" ",
|
|
257
|
+
/* @__PURE__ */ jsx("em", { children: t("cpq.offerings.bulk.notice.perUnit", "per unit") }),
|
|
258
|
+
" ",
|
|
259
|
+
t("cpq.offerings.bulk.notice.or", "or"),
|
|
260
|
+
" ",
|
|
261
|
+
/* @__PURE__ */ jsx("em", { children: t("cpq.offerings.bulk.notice.tiered", "tiered") }),
|
|
262
|
+
" ",
|
|
263
|
+
t(
|
|
264
|
+
"cpq.offerings.bulk.notice.rest",
|
|
265
|
+
"pricing, open each offering individually \u2014 those methods need a pricing table and a numeric attribute that lives on the offering's spec, which usually differs between offerings."
|
|
266
|
+
)
|
|
261
267
|
] }),
|
|
262
268
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
|
|
263
269
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
264
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Code" }),
|
|
270
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.offerings.bulk.fields.code", "Code") }),
|
|
265
271
|
/* @__PURE__ */ jsx(
|
|
266
272
|
Input,
|
|
267
273
|
{
|
|
268
274
|
value: chargeForm.code,
|
|
269
275
|
onChange: (e) => setChargeForm({ ...chargeForm, code: e.target.value }),
|
|
270
|
-
placeholder: "e.g. mrc-monthly"
|
|
276
|
+
placeholder: t("cpq.offerings.bulk.fields.codePlaceholder", "e.g. mrc-monthly")
|
|
271
277
|
}
|
|
272
278
|
)
|
|
273
279
|
] }),
|
|
274
280
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
275
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Name" }),
|
|
281
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.offerings.bulk.fields.name", "Name") }),
|
|
276
282
|
/* @__PURE__ */ jsx(
|
|
277
283
|
Input,
|
|
278
284
|
{
|
|
279
285
|
value: chargeForm.name,
|
|
280
286
|
onChange: (e) => setChargeForm({ ...chargeForm, name: e.target.value }),
|
|
281
|
-
placeholder: "e.g. Monthly Recurring"
|
|
287
|
+
placeholder: t("cpq.offerings.bulk.fields.namePlaceholder", "e.g. Monthly Recurring")
|
|
282
288
|
}
|
|
283
289
|
)
|
|
284
290
|
] }),
|
|
285
291
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
286
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Type" }),
|
|
292
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.offerings.bulk.fields.type", "Type") }),
|
|
287
293
|
/* @__PURE__ */ jsxs(
|
|
288
294
|
Select,
|
|
289
295
|
{
|
|
@@ -292,9 +298,9 @@ function OfferingsListPage() {
|
|
|
292
298
|
children: [
|
|
293
299
|
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
294
300
|
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
295
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "nrc", children: "NRC (Non-Recurring)" }),
|
|
296
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "mrc", children: "MRC (Monthly Recurring)" }),
|
|
297
|
-
/* @__PURE__ */ jsx(SelectItem, { value: "usage", children: "Usage" })
|
|
301
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "nrc", children: t("cpq.offerings.chargeType.nrc", "NRC (Non-Recurring)") }),
|
|
302
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "mrc", children: t("cpq.offerings.chargeType.mrc", "MRC (Monthly Recurring)") }),
|
|
303
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "usage", children: t("cpq.offerings.chargeType.usage", "Usage") })
|
|
298
304
|
] })
|
|
299
305
|
]
|
|
300
306
|
}
|
|
@@ -302,19 +308,19 @@ function OfferingsListPage() {
|
|
|
302
308
|
] })
|
|
303
309
|
] }),
|
|
304
310
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
305
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Description" }),
|
|
311
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.offerings.bulk.fields.description", "Description") }),
|
|
306
312
|
/* @__PURE__ */ jsx(
|
|
307
313
|
Input,
|
|
308
314
|
{
|
|
309
315
|
value: chargeForm.description,
|
|
310
316
|
onChange: (e) => setChargeForm({ ...chargeForm, description: e.target.value }),
|
|
311
|
-
placeholder: "User-facing charge description"
|
|
317
|
+
placeholder: t("cpq.offerings.bulk.fields.descriptionPlaceholder", "User-facing charge description")
|
|
312
318
|
}
|
|
313
319
|
)
|
|
314
320
|
] }),
|
|
315
321
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
316
322
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
317
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium", children: "Currency" }),
|
|
323
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium", children: t("cpq.offerings.bulk.fields.currency", "Currency") }),
|
|
318
324
|
/* @__PURE__ */ jsx(
|
|
319
325
|
Input,
|
|
320
326
|
{
|
|
@@ -327,12 +333,8 @@ function OfferingsListPage() {
|
|
|
327
333
|
] }),
|
|
328
334
|
/* @__PURE__ */ jsx("div", { className: "rounded border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
329
335
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/30", children: [
|
|
330
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-xs", children: "Offering" }),
|
|
331
|
-
/* @__PURE__ */
|
|
332
|
-
"Price (",
|
|
333
|
-
chargeCurrency,
|
|
334
|
-
")"
|
|
335
|
-
] })
|
|
336
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-xs", children: t("cpq.offerings.bulk.table.offering", "Offering") }),
|
|
337
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-left font-medium text-xs", children: t("cpq.offerings.bulk.table.price", "Price ({currency})", { currency: chargeCurrency }) })
|
|
336
338
|
] }) }),
|
|
337
339
|
/* @__PURE__ */ jsx("tbody", { children: bulkChargeTargets.map((o) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0", children: [
|
|
338
340
|
/* @__PURE__ */ jsxs("td", { className: "px-3 py-2 font-medium text-xs", children: [
|
|
@@ -363,10 +365,10 @@ function OfferingsListPage() {
|
|
|
363
365
|
type: "button",
|
|
364
366
|
onClick: saveChargesForSelected,
|
|
365
367
|
disabled: saving || !chargeForm.code || !chargeForm.name,
|
|
366
|
-
children: saving ? "Saving..." :
|
|
368
|
+
children: saving ? t("cpq.common.saving", "Saving...") : bulkChargeTargets.length > 1 ? t("cpq.offerings.bulk.submitPlural", "Create Charge for {count} Offerings", { count: bulkChargeTargets.length }) : t("cpq.offerings.bulk.submitSingular", "Create Charge for {count} Offering", { count: bulkChargeTargets.length })
|
|
367
369
|
}
|
|
368
370
|
),
|
|
369
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setBulkChargeOpen(false), children: "Cancel" })
|
|
371
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: () => setBulkChargeOpen(false), children: t("cpq.common.cancel", "Cancel") })
|
|
370
372
|
] })
|
|
371
373
|
] });
|
|
372
374
|
const toolbarContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/cpq/backend/cpq/offerings/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { type BulkAction } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport { NumberInput } from '../../../components/NumberInput'\nimport {\n chargeTypeMap,\n formatStatusLabel,\n lifecycleStatusMap,\n type ChargeType,\n type LifecycleStatus,\n} from '../../../components/statusMaps'\n\ntype Charge = {\n id: string\n code: string\n name: string\n description: string | null\n chargeType: string\n pricingMethod: string\n fixedPrice: string | null\n currencyCode: string | null\n isActive: boolean\n}\n\ntype Offering = {\n id: string\n specId: string\n code: string\n name: string\n description: string | null\n offeringType: string\n designTimeValues: Record<string, unknown>\n lifecycleStatus: string\n isActive: boolean\n createdAt: string\n charges?: Charge[]\n}\n\nconst PAGE_SIZE = 50\n\nfunction ChargePopover({ charge }: { charge: Charge }) {\n const [open, setOpen] = React.useState(false)\n const variant = chargeTypeMap[charge.chargeType as ChargeType] ?? 'neutral'\n return (\n <span\n className=\"relative inline-flex\"\n onMouseEnter={() => setOpen(true)}\n onMouseLeave={() => setOpen(false)}\n >\n <Tag variant={variant} className=\"cursor-default px-2 text-[10px]\">\n {charge.chargeType.toUpperCase()}\n </Tag>\n {open && (\n <div className=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-2 z-50 w-56 rounded-lg border bg-popover p-3 shadow-md text-popover-foreground text-xs space-y-1.5\">\n <div className=\"font-medium text-sm\">{charge.name}</div>\n <div className=\"text-muted-foreground font-mono\">{charge.code}</div>\n {charge.description && <div className=\"text-muted-foreground\">{charge.description}</div>}\n <div className=\"flex items-center justify-between pt-1 border-t\">\n <span>Pricing: {charge.pricingMethod}</span>\n {charge.fixedPrice != null && (\n <span className=\"font-medium\">\n {charge.currencyCode ?? 'USD'} {charge.fixedPrice}\n </span>\n )}\n </div>\n {!charge.isActive && <Tag variant=\"warning\" dot>Inactive</Tag>}\n <div className=\"absolute left-1/2 -translate-x-1/2 top-full w-2 h-2 bg-popover border-b border-r rotate-45 -mt-1\" />\n </div>\n )}\n </span>\n )\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.lifecycleStatus === 'string' && values.lifecycleStatus) {\n params.set('lifecycleStatus', values.lifecycleStatus)\n }\n if (typeof values.offeringType === 'string' && values.offeringType) {\n params.set('offeringType', values.offeringType)\n }\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nexport default function OfferingsListPage() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<Offering>({\n endpoint: '/api/cpq/product-offerings',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.offerings.list.error.load', 'Failed to load offerings'),\n })\n\n // Bulk charge creation is restricted to `flat` pricing \u2014 per_unit/tiered\n // need a pricing table + a numeric quantity attribute that lives on the\n // offering's spec, which differs between selected offerings. We push\n // users to the per-offering detail page for those methods.\n const [bulkChargeOpen, setBulkChargeOpen] = React.useState(false)\n const [bulkChargeTargets, setBulkChargeTargets] = React.useState<Offering[]>([])\n const [chargeForm, setChargeForm] = React.useState({\n code: '',\n name: '',\n description: '',\n chargeType: 'mrc',\n })\n const [chargePrices, setChargePrices] = React.useState<Record<string, string>>({})\n const [chargeCurrency, setChargeCurrency] = React.useState('USD')\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const lifecycleOptions = React.useMemo(\n () => [\n { value: 'draft', label: t('cpq.offerings.lifecycle.draft', 'Draft') },\n { value: 'active', label: t('cpq.offerings.lifecycle.active', 'Active') },\n { value: 'deprecated', label: t('cpq.offerings.lifecycle.deprecated', 'Deprecated') },\n ],\n [t],\n )\n\n const offeringTypeOptions = React.useMemo(\n () => [\n { value: 'simple', label: t('cpq.offerings.type.simple', 'Simple') },\n { value: 'bundle', label: t('cpq.offerings.type.bundle', 'Bundle') },\n ],\n [t],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'lifecycleStatus',\n label: t('cpq.offerings.filters.lifecycleStatus', 'Lifecycle Status'),\n type: 'select',\n options: lifecycleOptions,\n },\n {\n id: 'offeringType',\n label: t('cpq.offerings.filters.offeringType', 'Type'),\n type: 'select',\n options: offeringTypeOptions,\n },\n {\n id: 'isActive',\n label: t('cpq.offerings.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [lifecycleOptions, offeringTypeOptions, t],\n )\n\n const openBulkChargeForm = React.useCallback(\n (targets: Offering[]) => {\n setBulkChargeTargets(targets)\n setChargeForm({ code: '', name: '', description: '', chargeType: 'mrc' })\n setChargePrices({})\n setChargeCurrency('USD')\n setError(null)\n setBulkChargeOpen(true)\n },\n [],\n )\n\n const saveChargesForSelected = React.useCallback(async () => {\n const targets = bulkChargeTargets\n if (!targets.length || !chargeForm.code || !chargeForm.name) return\n setSaving(true)\n setError(null)\n try {\n for (const offering of targets) {\n const chargeCode = targets.length > 1 ? `${offering.code}--${chargeForm.code}` : chargeForm.code\n const payload: Record<string, unknown> = {\n offeringId: offering.id,\n code: chargeCode,\n name: chargeForm.name,\n description: chargeForm.description || null,\n chargeType: chargeForm.chargeType,\n pricingMethod: 'flat',\n fixedPrice: chargePrices[offering.id] || null,\n currencyCode: chargeCurrency,\n isActive: true,\n sortOrder: 0,\n }\n const res = await fetch('/api/cpq/product-charges', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!res.ok) {\n const body = await res.json().catch(() => ({} as { error?: string }))\n setError(body.error ?? `Failed to save charge for ${offering.name}`)\n setSaving(false)\n return\n }\n }\n flash(t('cpq.offerings.flash.chargesCreated', 'Charges created'), 'success')\n setBulkChargeOpen(false)\n setBulkChargeTargets([])\n data.reload()\n } catch {\n setError('Failed to save charges')\n } finally {\n setSaving(false)\n }\n }, [\n bulkChargeTargets,\n chargeCurrency,\n chargeForm,\n chargePrices,\n data,\n t,\n ])\n\n const columns = React.useMemo<ColumnDef<Offering>[]>(\n () => [\n {\n accessorKey: 'code',\n header: t('cpq.offerings.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n accessorKey: 'name',\n header: t('cpq.offerings.name', 'Name'),\n cell: ({ row }) => (\n <span className=\"font-medium\">\n {row.original.name}\n {row.original.offeringType === 'bundle' && (\n <Tag variant=\"brand\" className=\"ml-2 px-2 text-[10px]\">\n bundle\n </Tag>\n )}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.offerings.isActive', 'Is Active?'),\n cell: ({ row }) => <Checkbox checked={row.original.isActive} disabled />,\n },\n {\n accessorKey: 'lifecycleStatus',\n header: t('cpq.offerings.lifecycleStatus', 'Lifecycle Status'),\n cell: ({ row }) => (\n <Tag variant={lifecycleStatusMap[row.original.lifecycleStatus as LifecycleStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.lifecycleStatus)}\n </Tag>\n ),\n },\n {\n id: 'charges',\n header: t('cpq.offerings.charges', 'Charges'),\n cell: ({ row }) => {\n const charges = row.original.charges ?? []\n if (charges.length === 0) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n return (\n <div className=\"flex flex-wrap gap-1\" onClick={(e) => e.stopPropagation()}>\n {charges.map((ch) => (\n <ChargePopover key={ch.id} charge={ch} />\n ))}\n </div>\n )\n },\n },\n {\n accessorKey: 'description',\n header: t('cpq.offerings.description', 'Description'),\n cell: ({ row }) => (\n <span className=\"text-muted-foreground\">{row.original.description ?? '\u2014'}</span>\n ),\n meta: { truncate: true, maxWidth: 320 },\n },\n ],\n [t],\n )\n\n // Module-specific bulk actions; \"Delete selected\" is appended automatically\n // by CpqListView when `crud` is provided.\n const bulkActions = React.useMemo<BulkAction<Offering>[]>(\n () => [\n {\n id: 'add-charge',\n label: t('cpq.offerings.bulk.addCharge', 'Add Charge'),\n onExecute: (selectedRows) => {\n if (!selectedRows.length) return { ok: false }\n openBulkChargeForm(selectedRows)\n return { ok: true }\n },\n },\n ],\n [openBulkChargeForm, t],\n )\n\n const bulkChargeForm = bulkChargeOpen && bulkChargeTargets.length > 0 && (\n <div className=\"rounded-lg border bg-card p-5 space-y-4\">\n <h4 className=\"font-medium text-sm\">\n {t('cpq.offerings.bulk.formTitle', 'New Flat-Price Charge for')} {bulkChargeTargets.length}{' '}\n {bulkChargeTargets.length > 1\n ? t('cpq.offerings.bulk.offeringsPlural', 'offerings')\n : t('cpq.offerings.bulk.offeringsSingular', 'offering')}\n </h4>\n\n <Alert>\n Bulk creation supports <strong>flat pricing only</strong>. For <em>per unit</em> or <em>tiered</em> pricing,\n open each offering individually \u2014 those methods need a pricing table and a numeric attribute that lives on\n the offering's spec, which usually differs between offerings.\n </Alert>\n\n <div className=\"grid grid-cols-3 gap-3\">\n <div>\n <label className=\"block text-xs font-medium mb-1\">Code</label>\n <Input\n value={chargeForm.code}\n onChange={(e) => setChargeForm({ ...chargeForm, code: e.target.value })}\n placeholder=\"e.g. mrc-monthly\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">Name</label>\n <Input\n value={chargeForm.name}\n onChange={(e) => setChargeForm({ ...chargeForm, name: e.target.value })}\n placeholder=\"e.g. Monthly Recurring\"\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">Type</label>\n <Select\n value={chargeForm.chargeType}\n onValueChange={(value) => setChargeForm({ ...chargeForm, chargeType: value })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"nrc\">NRC (Non-Recurring)</SelectItem>\n <SelectItem value=\"mrc\">MRC (Monthly Recurring)</SelectItem>\n <SelectItem value=\"usage\">Usage</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div>\n <label className=\"block text-xs font-medium mb-1\">Description</label>\n <Input\n value={chargeForm.description}\n onChange={(e) => setChargeForm({ ...chargeForm, description: e.target.value })}\n placeholder=\"User-facing charge description\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <label className=\"text-xs font-medium\">Currency</label>\n <Input\n value={chargeCurrency}\n onChange={(e) => setChargeCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className=\"w-20\"\n />\n </div>\n <div className=\"rounded border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/30\">\n <th className=\"px-3 py-2 text-left font-medium text-xs\">Offering</th>\n <th className=\"px-3 py-2 text-left font-medium text-xs\">Price ({chargeCurrency})</th>\n </tr>\n </thead>\n <tbody>\n {bulkChargeTargets.map((o) => (\n <tr key={o.id} className=\"border-b last:border-0\">\n <td className=\"px-3 py-2 font-medium text-xs\">\n {o.name} <span className=\"text-muted-foreground font-mono\">({o.code})</span>\n </td>\n <td className=\"px-3 py-2\">\n <NumberInput\n value={chargePrices[o.id] === undefined || chargePrices[o.id] === '' ? null : Number(chargePrices[o.id])}\n onChange={(n) =>\n setChargePrices({ ...chargePrices, [o.id]: n == null ? '' : String(n) })\n }\n placeholder=\"0.00\"\n className=\"w-32\"\n />\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n\n <div className=\"flex gap-2 pt-1\">\n <Button\n type=\"button\"\n onClick={saveChargesForSelected}\n disabled={saving || !chargeForm.code || !chargeForm.name}\n >\n {saving\n ? 'Saving...'\n : `Create Charge for ${bulkChargeTargets.length} Offering${bulkChargeTargets.length > 1 ? 's' : ''}`}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => setBulkChargeOpen(false)}>\n Cancel\n </Button>\n </div>\n </div>\n )\n\n const toolbarContent = (\n <>\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n {bulkChargeForm}\n </>\n )\n\n return (\n <CpqListView<Offering>\n title={t('cpq.offerings.list.title', 'Product Offerings')}\n tableId=\"cpq.offerings.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.offerings.search.placeholder', 'Search offerings...')}\n actions={\n <Button asChild>\n <a href=\"/backend/cpq/offerings/new\">{t('cpq.offerings.add', 'New Offering')}</a>\n </Button>\n }\n bulkActions={bulkActions}\n crud={{\n endpoint: '/api/cpq/product-offerings',\n entityName: t('cpq.offerings.entityName', 'offering'),\n editHref: (row) => `/backend/cpq/offerings/${row.id}`,\n }}\n onRowClick={(row) => router.push(`/backend/cpq/offerings/${row.id}`)}\n toolbarContent={toolbarContent}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.offerings.empty',\n 'No product offerings found. Create one to define a sellable variant of a product specification.',\n )}\n </div>\n }\n />\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { type BulkAction } from '@open-mercato/ui/backend/DataTable'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\nimport { NumberInput } from '../../../components/NumberInput'\nimport {\n chargeTypeMap,\n formatStatusLabel,\n lifecycleStatusMap,\n type ChargeType,\n type LifecycleStatus,\n} from '../../../components/statusMaps'\n\ntype Charge = {\n id: string\n code: string\n name: string\n description: string | null\n chargeType: string\n pricingMethod: string\n fixedPrice: string | null\n currencyCode: string | null\n isActive: boolean\n}\n\ntype Offering = {\n id: string\n specId: string\n code: string\n name: string\n description: string | null\n offeringType: string\n designTimeValues: Record<string, unknown>\n lifecycleStatus: string\n isActive: boolean\n createdAt: string\n charges?: Charge[]\n}\n\nconst PAGE_SIZE = 50\n\nfunction ChargePopover({ charge }: { charge: Charge }) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const variant = chargeTypeMap[charge.chargeType as ChargeType] ?? 'neutral'\n return (\n <span\n className=\"relative inline-flex\"\n onMouseEnter={() => setOpen(true)}\n onMouseLeave={() => setOpen(false)}\n >\n <Tag variant={variant} className=\"cursor-default px-2 text-[10px]\">\n {charge.chargeType.toUpperCase()}\n </Tag>\n {open && (\n <div className=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-2 z-50 w-56 rounded-lg border bg-popover p-3 shadow-md text-popover-foreground text-xs space-y-1.5\">\n <div className=\"font-medium text-sm\">{charge.name}</div>\n <div className=\"text-muted-foreground font-mono\">{charge.code}</div>\n {charge.description && <div className=\"text-muted-foreground\">{charge.description}</div>}\n <div className=\"flex items-center justify-between pt-1 border-t\">\n <span>{t('cpq.offerings.list.chargePopover.pricing', 'Pricing: {method}', { method: charge.pricingMethod })}</span>\n {charge.fixedPrice != null && (\n <span className=\"font-medium\">\n {charge.currencyCode ?? 'USD'} {charge.fixedPrice}\n </span>\n )}\n </div>\n {!charge.isActive && <Tag variant=\"warning\" dot>{t('cpq.common.inactive', 'Inactive')}</Tag>}\n <div className=\"absolute left-1/2 -translate-x-1/2 top-full w-2 h-2 bg-popover border-b border-r rotate-45 -mt-1\" />\n </div>\n )}\n </span>\n )\n}\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.lifecycleStatus === 'string' && values.lifecycleStatus) {\n params.set('lifecycleStatus', values.lifecycleStatus)\n }\n if (typeof values.offeringType === 'string' && values.offeringType) {\n params.set('offeringType', values.offeringType)\n }\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nexport default function OfferingsListPage() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<Offering>({\n endpoint: '/api/cpq/product-offerings',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.offerings.list.error.load', 'Failed to load offerings'),\n })\n\n // Bulk charge creation is restricted to `flat` pricing \u2014 per_unit/tiered\n // need a pricing table + a numeric quantity attribute that lives on the\n // offering's spec, which differs between selected offerings. We push\n // users to the per-offering detail page for those methods.\n const [bulkChargeOpen, setBulkChargeOpen] = React.useState(false)\n const [bulkChargeTargets, setBulkChargeTargets] = React.useState<Offering[]>([])\n const [chargeForm, setChargeForm] = React.useState({\n code: '',\n name: '',\n description: '',\n chargeType: 'mrc',\n })\n const [chargePrices, setChargePrices] = React.useState<Record<string, string>>({})\n const [chargeCurrency, setChargeCurrency] = React.useState('USD')\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const lifecycleOptions = React.useMemo(\n () => [\n { value: 'draft', label: t('cpq.offerings.lifecycle.draft', 'Draft') },\n { value: 'active', label: t('cpq.offerings.lifecycle.active', 'Active') },\n { value: 'deprecated', label: t('cpq.offerings.lifecycle.deprecated', 'Deprecated') },\n ],\n [t],\n )\n\n const offeringTypeOptions = React.useMemo(\n () => [\n { value: 'simple', label: t('cpq.offerings.type.simple', 'Simple') },\n { value: 'bundle', label: t('cpq.offerings.type.bundle', 'Bundle') },\n ],\n [t],\n )\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'lifecycleStatus',\n label: t('cpq.offerings.filters.lifecycleStatus', 'Lifecycle Status'),\n type: 'select',\n options: lifecycleOptions,\n },\n {\n id: 'offeringType',\n label: t('cpq.offerings.filters.offeringType', 'Type'),\n type: 'select',\n options: offeringTypeOptions,\n },\n {\n id: 'isActive',\n label: t('cpq.offerings.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [lifecycleOptions, offeringTypeOptions, t],\n )\n\n const openBulkChargeForm = React.useCallback(\n (targets: Offering[]) => {\n setBulkChargeTargets(targets)\n setChargeForm({ code: '', name: '', description: '', chargeType: 'mrc' })\n setChargePrices({})\n setChargeCurrency('USD')\n setError(null)\n setBulkChargeOpen(true)\n },\n [],\n )\n\n const saveChargesForSelected = React.useCallback(async () => {\n const targets = bulkChargeTargets\n if (!targets.length || !chargeForm.code || !chargeForm.name) return\n setSaving(true)\n setError(null)\n try {\n for (const offering of targets) {\n const chargeCode = targets.length > 1 ? `${offering.code}--${chargeForm.code}` : chargeForm.code\n const payload: Record<string, unknown> = {\n offeringId: offering.id,\n code: chargeCode,\n name: chargeForm.name,\n description: chargeForm.description || null,\n chargeType: chargeForm.chargeType,\n pricingMethod: 'flat',\n fixedPrice: chargePrices[offering.id] || null,\n currencyCode: chargeCurrency,\n isActive: true,\n sortOrder: 0,\n }\n const res = await fetch('/api/cpq/product-charges', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n })\n if (!res.ok) {\n const body = await res.json().catch(() => ({} as { error?: string }))\n setError(body.error ?? t('cpq.offerings.bulk.error.saveChargeFor', 'Failed to save charge for {name}', { name: offering.name }))\n setSaving(false)\n return\n }\n }\n flash(t('cpq.offerings.flash.chargesCreated', 'Charges created'), 'success')\n setBulkChargeOpen(false)\n setBulkChargeTargets([])\n data.reload()\n } catch {\n setError(t('cpq.offerings.bulk.error.saveCharges', 'Failed to save charges'))\n } finally {\n setSaving(false)\n }\n }, [\n bulkChargeTargets,\n chargeCurrency,\n chargeForm,\n chargePrices,\n data,\n t,\n ])\n\n const columns = React.useMemo<ColumnDef<Offering>[]>(\n () => [\n {\n accessorKey: 'code',\n header: t('cpq.offerings.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n accessorKey: 'name',\n header: t('cpq.offerings.name', 'Name'),\n cell: ({ row }) => (\n <span className=\"font-medium\">\n {row.original.name}\n {row.original.offeringType === 'bundle' && (\n <Tag variant=\"brand\" className=\"ml-2 px-2 text-[10px]\">\n {t('cpq.offerings.list.tag.bundle', 'bundle')}\n </Tag>\n )}\n </span>\n ),\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.offerings.isActive', 'Is Active?'),\n cell: ({ row }) => <Checkbox checked={row.original.isActive} disabled />,\n },\n {\n accessorKey: 'lifecycleStatus',\n header: t('cpq.offerings.lifecycleStatus', 'Lifecycle Status'),\n cell: ({ row }) => (\n <Tag variant={lifecycleStatusMap[row.original.lifecycleStatus as LifecycleStatus] ?? 'neutral'} dot>\n {formatStatusLabel(row.original.lifecycleStatus)}\n </Tag>\n ),\n },\n {\n id: 'charges',\n header: t('cpq.offerings.charges', 'Charges'),\n cell: ({ row }) => {\n const charges = row.original.charges ?? []\n if (charges.length === 0) return <span className=\"text-xs text-muted-foreground\">\u2014</span>\n return (\n <div className=\"flex flex-wrap gap-1\" onClick={(e) => e.stopPropagation()}>\n {charges.map((ch) => (\n <ChargePopover key={ch.id} charge={ch} />\n ))}\n </div>\n )\n },\n },\n {\n accessorKey: 'description',\n header: t('cpq.offerings.description', 'Description'),\n cell: ({ row }) => (\n <span className=\"text-muted-foreground\">{row.original.description ?? '\u2014'}</span>\n ),\n meta: { truncate: true, maxWidth: 320 },\n },\n ],\n [t],\n )\n\n // Module-specific bulk actions; \"Delete selected\" is appended automatically\n // by CpqListView when `crud` is provided.\n const bulkActions = React.useMemo<BulkAction<Offering>[]>(\n () => [\n {\n id: 'add-charge',\n label: t('cpq.offerings.bulk.addCharge', 'Add Charge'),\n onExecute: (selectedRows) => {\n if (!selectedRows.length) return { ok: false }\n openBulkChargeForm(selectedRows)\n return { ok: true }\n },\n },\n ],\n [openBulkChargeForm, t],\n )\n\n const bulkChargeForm = bulkChargeOpen && bulkChargeTargets.length > 0 && (\n <div className=\"rounded-lg border bg-card p-5 space-y-4\">\n <h4 className=\"font-medium text-sm\">\n {t('cpq.offerings.bulk.formTitle', 'New Flat-Price Charge for')} {bulkChargeTargets.length}{' '}\n {bulkChargeTargets.length > 1\n ? t('cpq.offerings.bulk.offeringsPlural', 'offerings')\n : t('cpq.offerings.bulk.offeringsSingular', 'offering')}\n </h4>\n\n <Alert>\n {t('cpq.offerings.bulk.notice.supports', 'Bulk creation supports')}{' '}\n <strong>{t('cpq.offerings.bulk.notice.flatOnly', 'flat pricing only')}</strong>\n {t('cpq.offerings.bulk.notice.for', '. For')}{' '}\n <em>{t('cpq.offerings.bulk.notice.perUnit', 'per unit')}</em>{' '}\n {t('cpq.offerings.bulk.notice.or', 'or')}{' '}\n <em>{t('cpq.offerings.bulk.notice.tiered', 'tiered')}</em>{' '}\n {t(\n 'cpq.offerings.bulk.notice.rest',\n \"pricing, open each offering individually \u2014 those methods need a pricing table and a numeric attribute that lives on the offering's spec, which usually differs between offerings.\",\n )}\n </Alert>\n\n <div className=\"grid grid-cols-3 gap-3\">\n <div>\n <label className=\"block text-xs font-medium mb-1\">{t('cpq.offerings.bulk.fields.code', 'Code')}</label>\n <Input\n value={chargeForm.code}\n onChange={(e) => setChargeForm({ ...chargeForm, code: e.target.value })}\n placeholder={t('cpq.offerings.bulk.fields.codePlaceholder', 'e.g. mrc-monthly')}\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">{t('cpq.offerings.bulk.fields.name', 'Name')}</label>\n <Input\n value={chargeForm.name}\n onChange={(e) => setChargeForm({ ...chargeForm, name: e.target.value })}\n placeholder={t('cpq.offerings.bulk.fields.namePlaceholder', 'e.g. Monthly Recurring')}\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium mb-1\">{t('cpq.offerings.bulk.fields.type', 'Type')}</label>\n <Select\n value={chargeForm.chargeType}\n onValueChange={(value) => setChargeForm({ ...chargeForm, chargeType: value })}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"nrc\">{t('cpq.offerings.chargeType.nrc', 'NRC (Non-Recurring)')}</SelectItem>\n <SelectItem value=\"mrc\">{t('cpq.offerings.chargeType.mrc', 'MRC (Monthly Recurring)')}</SelectItem>\n <SelectItem value=\"usage\">{t('cpq.offerings.chargeType.usage', 'Usage')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n\n <div>\n <label className=\"block text-xs font-medium mb-1\">{t('cpq.offerings.bulk.fields.description', 'Description')}</label>\n <Input\n value={chargeForm.description}\n onChange={(e) => setChargeForm({ ...chargeForm, description: e.target.value })}\n placeholder={t('cpq.offerings.bulk.fields.descriptionPlaceholder', 'User-facing charge description')}\n />\n </div>\n\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-3\">\n <label className=\"text-xs font-medium\">{t('cpq.offerings.bulk.fields.currency', 'Currency')}</label>\n <Input\n value={chargeCurrency}\n onChange={(e) => setChargeCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className=\"w-20\"\n />\n </div>\n <div className=\"rounded border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/30\">\n <th className=\"px-3 py-2 text-left font-medium text-xs\">{t('cpq.offerings.bulk.table.offering', 'Offering')}</th>\n <th className=\"px-3 py-2 text-left font-medium text-xs\">{t('cpq.offerings.bulk.table.price', 'Price ({currency})', { currency: chargeCurrency })}</th>\n </tr>\n </thead>\n <tbody>\n {bulkChargeTargets.map((o) => (\n <tr key={o.id} className=\"border-b last:border-0\">\n <td className=\"px-3 py-2 font-medium text-xs\">\n {o.name} <span className=\"text-muted-foreground font-mono\">({o.code})</span>\n </td>\n <td className=\"px-3 py-2\">\n <NumberInput\n value={chargePrices[o.id] === undefined || chargePrices[o.id] === '' ? null : Number(chargePrices[o.id])}\n onChange={(n) =>\n setChargePrices({ ...chargePrices, [o.id]: n == null ? '' : String(n) })\n }\n placeholder=\"0.00\"\n className=\"w-32\"\n />\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n\n <div className=\"flex gap-2 pt-1\">\n <Button\n type=\"button\"\n onClick={saveChargesForSelected}\n disabled={saving || !chargeForm.code || !chargeForm.name}\n >\n {saving\n ? t('cpq.common.saving', 'Saving...')\n : bulkChargeTargets.length > 1\n ? t('cpq.offerings.bulk.submitPlural', 'Create Charge for {count} Offerings', { count: bulkChargeTargets.length })\n : t('cpq.offerings.bulk.submitSingular', 'Create Charge for {count} Offering', { count: bulkChargeTargets.length })}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => setBulkChargeOpen(false)}>\n {t('cpq.common.cancel', 'Cancel')}\n </Button>\n </div>\n </div>\n )\n\n const toolbarContent = (\n <>\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n {bulkChargeForm}\n </>\n )\n\n return (\n <CpqListView<Offering>\n title={t('cpq.offerings.list.title', 'Product Offerings')}\n tableId=\"cpq.offerings.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.offerings.search.placeholder', 'Search offerings...')}\n actions={\n <Button asChild>\n <a href=\"/backend/cpq/offerings/new\">{t('cpq.offerings.add', 'New Offering')}</a>\n </Button>\n }\n bulkActions={bulkActions}\n crud={{\n endpoint: '/api/cpq/product-offerings',\n entityName: t('cpq.offerings.entityName', 'offering'),\n editHref: (row) => `/backend/cpq/offerings/${row.id}`,\n }}\n onRowClick={(row) => router.push(`/backend/cpq/offerings/${row.id}`)}\n toolbarContent={toolbarContent}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t(\n 'cpq.offerings.empty',\n 'No product offerings found. Create one to define a sellable variant of a product specification.',\n )}\n </div>\n }\n />\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAoEM,SAmXF,UAnXE,KAWQ,YAXR;AAnEN,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAG1B,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,aAAa;AACtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,aAAa;AACtB,SAAS,aAAa;AACtB,SAAS,WAAW;AACpB,SAAS,aAAa,sBAAsB;AAC5C,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AA4BP,MAAM,YAAY;AAElB,SAAS,cAAc,EAAE,OAAO,GAAuB;AACrD,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,KAAK;AAC5C,QAAM,UAAU,cAAc,OAAO,UAAwB,KAAK;AAClE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,cAAc,MAAM,QAAQ,IAAI;AAAA,MAChC,cAAc,MAAM,QAAQ,KAAK;AAAA,MAEjC;AAAA,4BAAC,OAAI,SAAkB,WAAU,mCAC9B,iBAAO,WAAW,YAAY,GACjC;AAAA,QACC,QACC,qBAAC,SAAI,WAAU,wJACb;AAAA,8BAAC,SAAI,WAAU,uBAAuB,iBAAO,MAAK;AAAA,UAClD,oBAAC,SAAI,WAAU,mCAAmC,iBAAO,MAAK;AAAA,UAC7D,OAAO,eAAe,oBAAC,SAAI,WAAU,yBAAyB,iBAAO,aAAY;AAAA,UAClF,qBAAC,SAAI,WAAU,mDACb;AAAA,gCAAC,UAAM,YAAE,4CAA4C,qBAAqB,EAAE,QAAQ,OAAO,cAAc,CAAC,GAAE;AAAA,YAC3G,OAAO,cAAc,QACpB,qBAAC,UAAK,WAAU,eACb;AAAA,qBAAO,gBAAgB;AAAA,cAAM;AAAA,cAAE,OAAO;AAAA,eACzC;AAAA,aAEJ;AAAA,UACC,CAAC,OAAO,YAAY,oBAAC,OAAI,SAAQ,WAAU,KAAG,MAAE,YAAE,uBAAuB,UAAU,GAAE;AAAA,UACtF,oBAAC,SAAI,WAAU,oGAAmG;AAAA,WACpH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,kBAAkB,QAAsB,QAAyB;AACxE,MAAI,OAAO,OAAO,oBAAoB,YAAY,OAAO,iBAAiB;AACxE,WAAO,IAAI,mBAAmB,OAAO,eAAe;AAAA,EACtD;AACA,MAAI,OAAO,OAAO,iBAAiB,YAAY,OAAO,cAAc;AAClE,WAAO,IAAI,gBAAgB,OAAO,YAAY;AAAA,EAChD;AACA,MAAI,OAAO,aAAa,KAAM,QAAO,IAAI,YAAY,MAAM;AAC3D,MAAI,OAAO,aAAa,MAAO,QAAO,IAAI,YAAY,OAAO;AAC/D;AAEe,SAAR,oBAAqC;AAC1C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AAEzB,QAAM,OAAO,eAAyB;AAAA,IACpC,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA,kBAAkB,EAAE,iCAAiC,0BAA0B;AAAA,EACjF,CAAC;AAMD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,KAAK;AAChE,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAqB,CAAC,CAAC;AAC/E,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS;AAAA,IACjD,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,EACd,CAAC;AACD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAiC,CAAC,CAAC;AACjF,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,MAAM,SAAS,KAAK;AAChE,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAE5D,QAAM,mBAAmB,MAAM;AAAA,IAC7B,MAAM;AAAA,MACJ,EAAE,OAAO,SAAS,OAAO,EAAE,iCAAiC,OAAO,EAAE;AAAA,MACrE,EAAE,OAAO,UAAU,OAAO,EAAE,kCAAkC,QAAQ,EAAE;AAAA,MACxE,EAAE,OAAO,cAAc,OAAO,EAAE,sCAAsC,YAAY,EAAE;AAAA,IACtF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,sBAAsB,MAAM;AAAA,IAChC,MAAM;AAAA,MACJ,EAAE,OAAO,UAAU,OAAO,EAAE,6BAA6B,QAAQ,EAAE;AAAA,MACnE,EAAE,OAAO,UAAU,OAAO,EAAE,6BAA6B,QAAQ,EAAE;AAAA,IACrE;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,yCAAyC,kBAAkB;AAAA,QACpE,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,sCAAsC,MAAM;AAAA,QACrD,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,kCAAkC,QAAQ;AAAA,QACnD,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,kBAAkB,qBAAqB,CAAC;AAAA,EAC3C;AAEA,QAAM,qBAAqB,MAAM;AAAA,IAC/B,CAAC,YAAwB;AACvB,2BAAqB,OAAO;AAC5B,oBAAc,EAAE,MAAM,IAAI,MAAM,IAAI,aAAa,IAAI,YAAY,MAAM,CAAC;AACxE,sBAAgB,CAAC,CAAC;AAClB,wBAAkB,KAAK;AACvB,eAAS,IAAI;AACb,wBAAkB,IAAI;AAAA,IACxB;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,yBAAyB,MAAM,YAAY,YAAY;AAC3D,UAAM,UAAU;AAChB,QAAI,CAAC,QAAQ,UAAU,CAAC,WAAW,QAAQ,CAAC,WAAW,KAAM;AAC7D,cAAU,IAAI;AACd,aAAS,IAAI;AACb,QAAI;AACF,iBAAW,YAAY,SAAS;AAC9B,cAAM,aAAa,QAAQ,SAAS,IAAI,GAAG,SAAS,IAAI,KAAK,WAAW,IAAI,KAAK,WAAW;AAC5F,cAAM,UAAmC;AAAA,UACvC,YAAY,SAAS;AAAA,UACrB,MAAM;AAAA,UACN,MAAM,WAAW;AAAA,UACjB,aAAa,WAAW,eAAe;AAAA,UACvC,YAAY,WAAW;AAAA,UACvB,eAAe;AAAA,UACf,YAAY,aAAa,SAAS,EAAE,KAAK;AAAA,UACzC,cAAc;AAAA,UACd,UAAU;AAAA,UACV,WAAW;AAAA,QACb;AACA,cAAM,MAAM,MAAM,MAAM,4BAA4B;AAAA,UAClD,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,QAC9B,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAwB;AACpE,mBAAS,KAAK,SAAS,EAAE,0CAA0C,oCAAoC,EAAE,MAAM,SAAS,KAAK,CAAC,CAAC;AAC/H,oBAAU,KAAK;AACf;AAAA,QACF;AAAA,MACF;AACA,YAAM,EAAE,sCAAsC,iBAAiB,GAAG,SAAS;AAC3E,wBAAkB,KAAK;AACvB,2BAAqB,CAAC,CAAC;AACvB,WAAK,OAAO;AAAA,IACd,QAAQ;AACN,eAAS,EAAE,wCAAwC,wBAAwB,CAAC;AAAA,IAC9E,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,sBAAsB,MAAM;AAAA,QACtC,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,qBAAqB,cAAI,SAAS,MAAK;AAAA,MAC5E;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,sBAAsB,MAAM;AAAA,QACtC,MAAM,CAAC,EAAE,IAAI,MACX,qBAAC,UAAK,WAAU,eACb;AAAA,cAAI,SAAS;AAAA,UACb,IAAI,SAAS,iBAAiB,YAC7B,oBAAC,OAAI,SAAQ,SAAQ,WAAU,yBAC5B,YAAE,iCAAiC,QAAQ,GAC9C;AAAA,WAEJ;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,0BAA0B,YAAY;AAAA,QAChD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,YAAS,SAAS,IAAI,SAAS,UAAU,UAAQ,MAAC;AAAA,MACxE;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,iCAAiC,kBAAkB;AAAA,QAC7D,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,OAAI,SAAS,mBAAmB,IAAI,SAAS,eAAkC,KAAK,WAAW,KAAG,MAChG,4BAAkB,IAAI,SAAS,eAAe,GACjD;AAAA,MAEJ;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,yBAAyB,SAAS;AAAA,QAC5C,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,UAAU,IAAI,SAAS,WAAW,CAAC;AACzC,cAAI,QAAQ,WAAW,EAAG,QAAO,oBAAC,UAAK,WAAU,iCAAgC,oBAAC;AAClF,iBACE,oBAAC,SAAI,WAAU,wBAAuB,SAAS,CAAC,MAAM,EAAE,gBAAgB,GACrE,kBAAQ,IAAI,CAAC,OACZ,oBAAC,iBAA0B,QAAQ,MAAf,GAAG,EAAgB,CACxC,GACH;AAAA,QAEJ;AAAA,MACF;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,6BAA6B,aAAa;AAAA,QACpD,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,yBAAyB,cAAI,SAAS,eAAe,UAAI;AAAA,QAE3E,MAAM,EAAE,UAAU,MAAM,UAAU,IAAI;AAAA,MACxC;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAIA,QAAM,cAAc,MAAM;AAAA,IACxB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,gCAAgC,YAAY;AAAA,QACrD,WAAW,CAAC,iBAAiB;AAC3B,cAAI,CAAC,aAAa,OAAQ,QAAO,EAAE,IAAI,MAAM;AAC7C,6BAAmB,YAAY;AAC/B,iBAAO,EAAE,IAAI,KAAK;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,oBAAoB,CAAC;AAAA,EACxB;AAEA,QAAM,iBAAiB,kBAAkB,kBAAkB,SAAS,KAClE,qBAAC,SAAI,WAAU,2CACb;AAAA,yBAAC,QAAG,WAAU,uBACX;AAAA,QAAE,gCAAgC,2BAA2B;AAAA,MAAE;AAAA,MAAE,kBAAkB;AAAA,MAAQ;AAAA,MAC3F,kBAAkB,SAAS,IACxB,EAAE,sCAAsC,WAAW,IACnD,EAAE,wCAAwC,UAAU;AAAA,OAC1D;AAAA,IAEA,qBAAC,SACE;AAAA,QAAE,sCAAsC,wBAAwB;AAAA,MAAG;AAAA,MACpE,oBAAC,YAAQ,YAAE,sCAAsC,mBAAmB,GAAE;AAAA,MACrE,EAAE,iCAAiC,OAAO;AAAA,MAAG;AAAA,MAC9C,oBAAC,QAAI,YAAE,qCAAqC,UAAU,GAAE;AAAA,MAAM;AAAA,MAC7D,EAAE,gCAAgC,IAAI;AAAA,MAAG;AAAA,MAC1C,oBAAC,QAAI,YAAE,oCAAoC,QAAQ,GAAE;AAAA,MAAM;AAAA,MAC1D;AAAA,QACC;AAAA,QACA;AAAA,MACF;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,0BACb;AAAA,2BAAC,SACC;AAAA,4BAAC,WAAM,WAAU,kCAAkC,YAAE,kCAAkC,MAAM,GAAE;AAAA,QAC/F;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,WAAW;AAAA,YAClB,UAAU,CAAC,MAAM,cAAc,EAAE,GAAG,YAAY,MAAM,EAAE,OAAO,MAAM,CAAC;AAAA,YACtE,aAAa,EAAE,6CAA6C,kBAAkB;AAAA;AAAA,QAChF;AAAA,SACF;AAAA,MACA,qBAAC,SACC;AAAA,4BAAC,WAAM,WAAU,kCAAkC,YAAE,kCAAkC,MAAM,GAAE;AAAA,QAC/F;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,WAAW;AAAA,YAClB,UAAU,CAAC,MAAM,cAAc,EAAE,GAAG,YAAY,MAAM,EAAE,OAAO,MAAM,CAAC;AAAA,YACtE,aAAa,EAAE,6CAA6C,wBAAwB;AAAA;AAAA,QACtF;AAAA,SACF;AAAA,MACA,qBAAC,SACC;AAAA,4BAAC,WAAM,WAAU,kCAAkC,YAAE,kCAAkC,MAAM,GAAE;AAAA,QAC/F;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,WAAW;AAAA,YAClB,eAAe,CAAC,UAAU,cAAc,EAAE,GAAG,YAAY,YAAY,MAAM,CAAC;AAAA,YAE5E;AAAA,kCAAC,iBACC,8BAAC,eAAY,GACf;AAAA,cACA,qBAAC,iBACC;AAAA,oCAAC,cAAW,OAAM,OAAO,YAAE,gCAAgC,qBAAqB,GAAE;AAAA,gBAClF,oBAAC,cAAW,OAAM,OAAO,YAAE,gCAAgC,yBAAyB,GAAE;AAAA,gBACtF,oBAAC,cAAW,OAAM,SAAS,YAAE,kCAAkC,OAAO,GAAE;AAAA,iBAC1E;AAAA;AAAA;AAAA,QACF;AAAA,SACF;AAAA,OACF;AAAA,IAEA,qBAAC,SACC;AAAA,0BAAC,WAAM,WAAU,kCAAkC,YAAE,yCAAyC,aAAa,GAAE;AAAA,MAC7G;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,WAAW;AAAA,UAClB,UAAU,CAAC,MAAM,cAAc,EAAE,GAAG,YAAY,aAAa,EAAE,OAAO,MAAM,CAAC;AAAA,UAC7E,aAAa,EAAE,oDAAoD,gCAAgC;AAAA;AAAA,MACrG;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,aACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,WAAM,WAAU,uBAAuB,YAAE,sCAAsC,UAAU,GAAE;AAAA,QAC5F;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,kBAAkB,EAAE,OAAO,MAAM,YAAY,CAAC;AAAA,YAC/D,WAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MACA,oBAAC,SAAI,WAAU,kBACb,+BAAC,WAAM,WAAU,kBACf;AAAA,4BAAC,WACC,+BAAC,QAAG,WAAU,wBACZ;AAAA,8BAAC,QAAG,WAAU,2CAA2C,YAAE,qCAAqC,UAAU,GAAE;AAAA,UAC5G,oBAAC,QAAG,WAAU,2CAA2C,YAAE,kCAAkC,sBAAsB,EAAE,UAAU,eAAe,CAAC,GAAE;AAAA,WACnJ,GACF;AAAA,QACA,oBAAC,WACE,4BAAkB,IAAI,CAAC,MACtB,qBAAC,QAAc,WAAU,0BACvB;AAAA,+BAAC,QAAG,WAAU,iCACX;AAAA,cAAE;AAAA,YAAK;AAAA,YAAC,qBAAC,UAAK,WAAU,mCAAkC;AAAA;AAAA,cAAE,EAAE;AAAA,cAAK;AAAA,eAAC;AAAA,aACvE;AAAA,UACA,oBAAC,QAAG,WAAU,aACZ;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,aAAa,EAAE,EAAE,MAAM,UAAa,aAAa,EAAE,EAAE,MAAM,KAAK,OAAO,OAAO,aAAa,EAAE,EAAE,CAAC;AAAA,cACvG,UAAU,CAAC,MACT,gBAAgB,EAAE,GAAG,cAAc,CAAC,EAAE,EAAE,GAAG,KAAK,OAAO,KAAK,OAAO,CAAC,EAAE,CAAC;AAAA,cAEzE,aAAY;AAAA,cACZ,WAAU;AAAA;AAAA,UACZ,GACF;AAAA,aAbO,EAAE,EAcX,CACD,GACH;AAAA,SACF,GACF;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,mBACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,UAAU,UAAU,CAAC,WAAW,QAAQ,CAAC,WAAW;AAAA,UAEnD,mBACG,EAAE,qBAAqB,WAAW,IAClC,kBAAkB,SAAS,IACzB,EAAE,mCAAmC,uCAAuC,EAAE,OAAO,kBAAkB,OAAO,CAAC,IAC/G,EAAE,qCAAqC,sCAAsC,EAAE,OAAO,kBAAkB,OAAO,CAAC;AAAA;AAAA,MACxH;AAAA,MACA,oBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,MAAM,kBAAkB,KAAK,GAC3E,YAAE,qBAAqB,QAAQ,GAClC;AAAA,OACF;AAAA,KACF;AAGF,QAAM,iBACJ,iCACG;AAAA,aAAS,oBAAC,SAAM,SAAQ,eAAe,iBAAM;AAAA,IAC7C;AAAA,KACH;AAGF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,4BAA4B,mBAAmB;AAAA,MACxD,SAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,mBAAmB,EAAE,oCAAoC,qBAAqB;AAAA,MAC9E,SACE,oBAAC,UAAO,SAAO,MACb,8BAAC,OAAE,MAAK,8BAA8B,YAAE,qBAAqB,cAAc,GAAE,GAC/E;AAAA,MAEF;AAAA,MACA,MAAM;AAAA,QACJ,UAAU;AAAA,QACV,YAAY,EAAE,4BAA4B,UAAU;AAAA,QACpD,UAAU,CAAC,QAAQ,0BAA0B,IAAI,EAAE;AAAA,MACrD;AAAA,MACA,YAAY,CAAC,QAAQ,OAAO,KAAK,0BAA0B,IAAI,EAAE,EAAE;AAAA,MACnE;AAAA,MACA,YACE,oBAAC,SAAI,WAAU,2EACZ;AAAA,QACC;AAAA,QACA;AAAA,MACF,GACF;AAAA;AAAA,EAEJ;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|