@dainamite/cpq 0.5.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 +64 -0
- package/MIGRATION.md +74 -0
- package/dist/modules/cpq/acl.js +1 -0
- package/dist/modules/cpq/acl.js.map +2 -2
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js +45 -0
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/route.js +17 -1
- package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/route.js +47 -2
- package/dist/modules/cpq/api/orders/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +42 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +32 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/price/route.js +43 -3
- package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
- 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 +3 -3
- 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 +174 -184
- 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 +140 -280
- 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/orders/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
- 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 +208 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +269 -311
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
- 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/quotes/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
- 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/ce.js +8 -0
- package/dist/modules/cpq/ce.js.map +2 -2
- package/dist/modules/cpq/cli.js +34 -1
- package/dist/modules/cpq/cli.js.map +2 -2
- package/dist/modules/cpq/commands/price-adjustments.js +107 -0
- package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
- package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +148 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
- package/dist/modules/cpq/components/PriceWaterfall.js +68 -0
- package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js +112 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
- 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/components/useGrantedFeatures.js +27 -0
- package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
- package/dist/modules/cpq/data/entities.js +97 -0
- package/dist/modules/cpq/data/entities.js.map +2 -2
- package/dist/modules/cpq/data/price-adjustments.js +70 -0
- package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/data/validators.js +4 -0
- package/dist/modules/cpq/data/validators.js.map +2 -2
- package/dist/modules/cpq/di.js +6 -0
- package/dist/modules/cpq/di.js.map +2 -2
- package/dist/modules/cpq/events.js +47 -0
- package/dist/modules/cpq/events.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/index.js +1 -1
- package/dist/modules/cpq/index.js.map +2 -2
- package/dist/modules/cpq/lib/auditActor.js +10 -0
- package/dist/modules/cpq/lib/auditActor.js.map +7 -0
- package/dist/modules/cpq/lib/formatMoney.js +7 -0
- package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
- package/dist/modules/cpq/lib/quotePermissions.js +13 -0
- package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
- package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
- package/dist/modules/cpq/services/cpqOrderService.js +25 -28
- package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
- package/dist/modules/cpq/services/cpqPricingService.js +7 -0
- package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
- package/dist/modules/cpq/services/cpqQuotingService.js +330 -109
- package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
- package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
- package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
- package/dist/modules/cpq/services/types.js.map +2 -2
- package/dist/modules/cpq/setup.js +6 -0
- package/dist/modules/cpq/setup.js.map +2 -2
- 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/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
- 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/acl.ts +1 -0
- package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
- package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
- package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
- package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
- package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
- package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
- package/src/modules/cpq/api/orders/[orderId]/route.ts +18 -0
- package/src/modules/cpq/api/orders/route.ts +51 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +48 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +40 -1
- package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
- package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
- package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
- package/src/modules/cpq/api/quotes/price/route.ts +28 -2
- 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 +3 -3
- 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 +164 -120
- 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 +122 -308
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
- 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 +260 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +304 -348
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
- 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/ce.ts +8 -0
- package/src/modules/cpq/cli.ts +48 -1
- package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
- package/src/modules/cpq/commands/price-adjustments.ts +90 -0
- package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +203 -0
- package/src/modules/cpq/components/PriceWaterfall.tsx +96 -0
- package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
- package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
- package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
- package/src/modules/cpq/data/entities.ts +83 -0
- package/src/modules/cpq/data/price-adjustments.ts +67 -0
- package/src/modules/cpq/data/validators.ts +3 -0
- package/src/modules/cpq/di.ts +6 -0
- package/src/modules/cpq/events.ts +53 -2
- 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/index.ts +1 -1
- package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
- package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
- package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
- package/src/modules/cpq/lib/auditActor.ts +13 -0
- package/src/modules/cpq/lib/formatMoney.ts +4 -0
- package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
- package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
- package/src/modules/cpq/lib/quotePermissions.ts +22 -0
- package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
- package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
- package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
- package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
- package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
- package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
- package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
- package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
- package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
- package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
- package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
- package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
- package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
- package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
- package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
- package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
- package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
- package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
- package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
- package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
- package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
- package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
- package/src/modules/cpq/services/cpqOrderService.ts +22 -28
- package/src/modules/cpq/services/cpqPricingService.ts +8 -0
- package/src/modules/cpq/services/cpqQuotingService.ts +374 -125
- package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
- package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
- package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
- package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
- package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
- package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
- package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
- package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
- package/src/modules/cpq/services/types.ts +34 -1
- package/src/modules/cpq/setup.ts +7 -1
- 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
|
@@ -10,7 +10,7 @@ export const metadata = {
|
|
|
10
10
|
pageTitleKey: 'cpq.orders.title',
|
|
11
11
|
pageGroup: 'CPQ',
|
|
12
12
|
pageGroupKey: 'customers~sales~cpq.nav.group',
|
|
13
|
-
pageOrder:
|
|
13
|
+
pageOrder: 2,
|
|
14
14
|
icon: ordersIcon,
|
|
15
15
|
breadcrumb: [{ label: 'CPQ Orders', labelKey: 'cpq.orders.title' }],
|
|
16
16
|
} as const
|
|
@@ -57,13 +57,13 @@ export default function CpqOrdersListPage() {
|
|
|
57
57
|
|
|
58
58
|
const statusOptions = React.useMemo(
|
|
59
59
|
() => [
|
|
60
|
-
{ value: 'draft', label: 'Draft' },
|
|
61
|
-
{ value: 'pending_activation', label: 'Pending Activation' },
|
|
62
|
-
{ value: 'active', label: 'Active' },
|
|
63
|
-
{ value: 'fulfilled', label: 'Fulfilled' },
|
|
64
|
-
{ value: 'cancelled', label: 'Cancelled' },
|
|
60
|
+
{ value: 'draft', label: t('cpq.orders.status.draft', 'Draft') },
|
|
61
|
+
{ value: 'pending_activation', label: t('cpq.orders.status.pending_activation', 'Pending Activation') },
|
|
62
|
+
{ value: 'active', label: t('cpq.orders.status.active', 'Active') },
|
|
63
|
+
{ value: 'fulfilled', label: t('cpq.orders.status.fulfilled', 'Fulfilled') },
|
|
64
|
+
{ value: 'cancelled', label: t('cpq.orders.status.cancelled', 'Cancelled') },
|
|
65
65
|
],
|
|
66
|
-
[],
|
|
66
|
+
[t],
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
const filters = React.useMemo<FilterDef[]>(
|
|
@@ -10,16 +10,24 @@ import {
|
|
|
10
10
|
SelectTrigger,
|
|
11
11
|
SelectValue,
|
|
12
12
|
} from '@open-mercato/ui/primitives/select'
|
|
13
|
+
import { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
13
14
|
import { NumberInput } from '../../../components/NumberInput'
|
|
14
15
|
|
|
15
16
|
// ─── Shared types ────────────────────────────────────────────────
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Display labels for price-rule types, keyed by the persisted `ruleType`
|
|
20
|
+
* value. Single translatable source shared by the list, the create form and
|
|
21
|
+
* the detail page — call inside render with the component's `t`.
|
|
22
|
+
*/
|
|
23
|
+
export function buildRuleTypeLabels(t: TranslateFn): Record<string, string> {
|
|
24
|
+
return {
|
|
25
|
+
discount_percent: t('cpq.priceRules.ruleTypes.discountPercent', 'Discount %'),
|
|
26
|
+
discount_absolute: t('cpq.priceRules.ruleTypes.discountAbsolute', 'Discount $'),
|
|
27
|
+
surcharge_percent: t('cpq.priceRules.ruleTypes.surchargePercent', 'Surcharge %'),
|
|
28
|
+
surcharge_absolute: t('cpq.priceRules.ruleTypes.surchargeAbsolute', 'Surcharge $'),
|
|
29
|
+
price_override: t('cpq.priceRules.ruleTypes.priceOverride', 'Override'),
|
|
30
|
+
}
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
export type PriceRuleApplicabilityCondition = {
|
|
@@ -145,15 +153,17 @@ export function PriceRuleForm({
|
|
|
145
153
|
onSave,
|
|
146
154
|
onCancel,
|
|
147
155
|
}: PriceRuleFormProps) {
|
|
156
|
+
const t = useT()
|
|
157
|
+
const ruleTypeLabels = buildRuleTypeLabels(t)
|
|
148
158
|
return (
|
|
149
159
|
<div className="rounded-lg border bg-card p-6 space-y-4">
|
|
150
|
-
<h2 className="text-lg font-semibold">{editingId ? 'Edit Rule' : 'New Rule'}</h2>
|
|
160
|
+
<h2 className="text-lg font-semibold">{editingId ? t('cpq.priceRules.form.editTitle', 'Edit Rule') : t('cpq.priceRules.form.newTitle', 'New Rule')}</h2>
|
|
151
161
|
{error && (
|
|
152
162
|
<div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">{error}</div>
|
|
153
163
|
)}
|
|
154
164
|
<div className="grid grid-cols-2 gap-4">
|
|
155
165
|
<label className="space-y-1">
|
|
156
|
-
<span className="text-sm font-medium">Code</span>
|
|
166
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.code', 'Code')}</span>
|
|
157
167
|
<Input
|
|
158
168
|
value={form.code}
|
|
159
169
|
onChange={(e) => onFormChange({ ...form, code: e.target.value })}
|
|
@@ -161,14 +171,14 @@ export function PriceRuleForm({
|
|
|
161
171
|
/>
|
|
162
172
|
</label>
|
|
163
173
|
<label className="space-y-1">
|
|
164
|
-
<span className="text-sm font-medium">Name</span>
|
|
174
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.name', 'Name')}</span>
|
|
165
175
|
<Input
|
|
166
176
|
value={form.name}
|
|
167
177
|
onChange={(e) => onFormChange({ ...form, name: e.target.value })}
|
|
168
178
|
/>
|
|
169
179
|
</label>
|
|
170
180
|
<label className="space-y-1">
|
|
171
|
-
<span className="text-sm font-medium">Rule Type</span>
|
|
181
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.ruleType', 'Rule Type')}</span>
|
|
172
182
|
<Select
|
|
173
183
|
value={form.ruleType}
|
|
174
184
|
onValueChange={(value) => onFormChange({ ...form, ruleType: value })}
|
|
@@ -177,7 +187,7 @@ export function PriceRuleForm({
|
|
|
177
187
|
<SelectValue />
|
|
178
188
|
</SelectTrigger>
|
|
179
189
|
<SelectContent>
|
|
180
|
-
{Object.entries(
|
|
190
|
+
{Object.entries(ruleTypeLabels).map(([k, v]) => (
|
|
181
191
|
<SelectItem key={k} value={k}>
|
|
182
192
|
{v}
|
|
183
193
|
</SelectItem>
|
|
@@ -186,14 +196,14 @@ export function PriceRuleForm({
|
|
|
186
196
|
</Select>
|
|
187
197
|
</label>
|
|
188
198
|
<label className="space-y-1">
|
|
189
|
-
<span className="text-sm font-medium">Value</span>
|
|
199
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.value', 'Value')}</span>
|
|
190
200
|
<NumberInput
|
|
191
201
|
value={form.value === '' ? null : Number(form.value)}
|
|
192
202
|
onChange={(n) => onFormChange({ ...form, value: n == null ? '' : String(n) })}
|
|
193
203
|
/>
|
|
194
204
|
</label>
|
|
195
205
|
<label className="space-y-1">
|
|
196
|
-
<span className="text-sm font-medium">Charge Type Filter</span>
|
|
206
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.chargeTypeFilter', 'Charge Type Filter')}</span>
|
|
197
207
|
<Select
|
|
198
208
|
value={form.chargeTypeFilter || '__all__'}
|
|
199
209
|
onValueChange={(value) =>
|
|
@@ -204,23 +214,23 @@ export function PriceRuleForm({
|
|
|
204
214
|
<SelectValue />
|
|
205
215
|
</SelectTrigger>
|
|
206
216
|
<SelectContent>
|
|
207
|
-
<SelectItem value="__all__">All charge types</SelectItem>
|
|
208
|
-
<SelectItem value="nrc">NRC only</SelectItem>
|
|
209
|
-
<SelectItem value="mrc">MRC only</SelectItem>
|
|
210
|
-
<SelectItem value="usage">Usage only</SelectItem>
|
|
217
|
+
<SelectItem value="__all__">{t('cpq.priceRules.form.allChargeTypes', 'All charge types')}</SelectItem>
|
|
218
|
+
<SelectItem value="nrc">{t('cpq.priceRules.form.nrcOnly', 'NRC only')}</SelectItem>
|
|
219
|
+
<SelectItem value="mrc">{t('cpq.priceRules.form.mrcOnly', 'MRC only')}</SelectItem>
|
|
220
|
+
<SelectItem value="usage">{t('cpq.priceRules.form.usageOnly', 'Usage only')}</SelectItem>
|
|
211
221
|
</SelectContent>
|
|
212
222
|
</Select>
|
|
213
223
|
</label>
|
|
214
224
|
<label className="space-y-1">
|
|
215
|
-
<span className="text-sm font-medium">Charge Code Filter</span>
|
|
225
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.chargeCodeFilter', 'Charge Code Filter')}</span>
|
|
216
226
|
<Input
|
|
217
|
-
placeholder=
|
|
227
|
+
placeholder={t('cpq.priceRules.form.chargeCodeFilterPlaceholder', 'e.g. setup_fee (leave empty for all)')}
|
|
218
228
|
value={form.chargeCodeFilter}
|
|
219
229
|
onChange={(e) => onFormChange({ ...form, chargeCodeFilter: e.target.value })}
|
|
220
230
|
/>
|
|
221
231
|
</label>
|
|
222
232
|
<label className="space-y-1">
|
|
223
|
-
<span className="text-sm font-medium">Sort Order</span>
|
|
233
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.sortOrder', 'Sort Order')}</span>
|
|
224
234
|
<NumberInput
|
|
225
235
|
integer
|
|
226
236
|
value={form.sortOrder === '' ? null : Number(form.sortOrder)}
|
|
@@ -228,15 +238,15 @@ export function PriceRuleForm({
|
|
|
228
238
|
/>
|
|
229
239
|
</label>
|
|
230
240
|
<label className="space-y-1">
|
|
231
|
-
<span className="text-sm font-medium">Description</span>
|
|
241
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.description', 'Description')}</span>
|
|
232
242
|
<Input
|
|
233
|
-
placeholder=
|
|
243
|
+
placeholder={t('cpq.priceRules.form.descriptionPlaceholder', 'Optional')}
|
|
234
244
|
value={form.description}
|
|
235
245
|
onChange={(e) => onFormChange({ ...form, description: e.target.value })}
|
|
236
246
|
/>
|
|
237
247
|
</label>
|
|
238
248
|
<label className="space-y-1 col-span-2">
|
|
239
|
-
<span className="text-sm font-medium">Product Offering</span>
|
|
249
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.productOffering', 'Product Offering')}</span>
|
|
240
250
|
<Select
|
|
241
251
|
value={form.productOfferingId || '__global__'}
|
|
242
252
|
onValueChange={(value) =>
|
|
@@ -247,7 +257,7 @@ export function PriceRuleForm({
|
|
|
247
257
|
<SelectValue />
|
|
248
258
|
</SelectTrigger>
|
|
249
259
|
<SelectContent>
|
|
250
|
-
<SelectItem value="__global__">Global (all products)</SelectItem>
|
|
260
|
+
<SelectItem value="__global__">{t('cpq.priceRules.form.globalAllProducts', 'Global (all products)')}</SelectItem>
|
|
251
261
|
{offerings.map((o) => (
|
|
252
262
|
<SelectItem key={o.id} value={o.id}>
|
|
253
263
|
{o.name} ({o.code})
|
|
@@ -256,17 +266,17 @@ export function PriceRuleForm({
|
|
|
256
266
|
</SelectContent>
|
|
257
267
|
</Select>
|
|
258
268
|
<span className="text-xs text-muted-foreground">
|
|
259
|
-
Leave as
|
|
269
|
+
{t('cpq.priceRules.form.productOfferingHint', 'Leave as "Global" to apply to all products, or select a specific offering')}
|
|
260
270
|
</span>
|
|
261
271
|
</label>
|
|
262
272
|
<div className="col-span-2 space-y-1">
|
|
263
|
-
<span className="text-sm font-medium">Applicability Condition</span>
|
|
273
|
+
<span className="text-sm font-medium">{t('cpq.priceRules.form.applicabilityCondition', 'Applicability Condition')}</span>
|
|
264
274
|
<span className="ml-2 text-xs text-muted-foreground">
|
|
265
|
-
(optional — only apply when a product attribute matches)
|
|
275
|
+
{t('cpq.priceRules.form.applicabilityConditionHint', '(optional — only apply when a product attribute matches)')}
|
|
266
276
|
</span>
|
|
267
277
|
<div className="grid grid-cols-3 gap-2">
|
|
268
278
|
<Input
|
|
269
|
-
placeholder=
|
|
279
|
+
placeholder={t('cpq.priceRules.form.conditionAttributePlaceholder', 'Attribute (e.g. port_size)')}
|
|
270
280
|
value={form.conditionAttribute}
|
|
271
281
|
onChange={(e) => onFormChange({ ...form, conditionAttribute: e.target.value })}
|
|
272
282
|
/>
|
|
@@ -278,12 +288,12 @@ export function PriceRuleForm({
|
|
|
278
288
|
<SelectValue />
|
|
279
289
|
</SelectTrigger>
|
|
280
290
|
<SelectContent>
|
|
281
|
-
<SelectItem value="eq">equals (=)</SelectItem>
|
|
282
|
-
<SelectItem value="neq">not equals (≠)</SelectItem>
|
|
291
|
+
<SelectItem value="eq">{t('cpq.priceRules.form.operatorEq', 'equals (=)')}</SelectItem>
|
|
292
|
+
<SelectItem value="neq">{t('cpq.priceRules.form.operatorNeq', 'not equals (≠)')}</SelectItem>
|
|
283
293
|
</SelectContent>
|
|
284
294
|
</Select>
|
|
285
295
|
<Input
|
|
286
|
-
placeholder=
|
|
296
|
+
placeholder={t('cpq.priceRules.form.conditionValuePlaceholder', 'Value (e.g. 100G)')}
|
|
287
297
|
value={form.conditionValue}
|
|
288
298
|
onChange={(e) => onFormChange({ ...form, conditionValue: e.target.value })}
|
|
289
299
|
/>
|
|
@@ -295,14 +305,18 @@ export function PriceRuleForm({
|
|
|
295
305
|
checked={form.isActive}
|
|
296
306
|
onCheckedChange={(checked) => onFormChange({ ...form, isActive: checked === true })}
|
|
297
307
|
/>
|
|
298
|
-
Active
|
|
308
|
+
{t('cpq.common.active', 'Active')}
|
|
299
309
|
</label>
|
|
300
310
|
<div className="flex gap-2">
|
|
301
311
|
<Button type="button" onClick={onSave} disabled={saving}>
|
|
302
|
-
{saving
|
|
312
|
+
{saving
|
|
313
|
+
? t('cpq.common.saving', 'Saving...')
|
|
314
|
+
: editingId
|
|
315
|
+
? t('cpq.priceRules.form.update', 'Update')
|
|
316
|
+
: t('cpq.priceRules.form.create', 'Create')}
|
|
303
317
|
</Button>
|
|
304
318
|
<Button type="button" variant="outline" onClick={onCancel}>
|
|
305
|
-
Cancel
|
|
319
|
+
{t('cpq.common.cancel', 'Cancel')}
|
|
306
320
|
</Button>
|
|
307
321
|
</div>
|
|
308
322
|
</div>
|
|
@@ -4,6 +4,6 @@ export const metadata = {
|
|
|
4
4
|
navHidden: true,
|
|
5
5
|
breadcrumb: [
|
|
6
6
|
{ label: 'Price Rules', labelKey: 'cpq.priceRules.list.title', href: '/backend/cpq/price-rules' },
|
|
7
|
-
{ label: 'Rule Detail' },
|
|
7
|
+
{ label: 'Rule Detail', labelKey: 'cpq.priceRules.detail.breadcrumb' },
|
|
8
8
|
],
|
|
9
9
|
} as const
|