@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
|
@@ -44,7 +44,7 @@ function PricingTableDetailPage(props) {
|
|
|
44
44
|
const load = React.useCallback(async () => {
|
|
45
45
|
if (isNew) return;
|
|
46
46
|
if (!id || !UUID_RE.test(id)) {
|
|
47
|
-
setError("Invalid pricing table ID");
|
|
47
|
+
setError(t("cpq.pricing.detail.errors.invalidId", "Invalid pricing table ID"));
|
|
48
48
|
setLoading(false);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
@@ -52,7 +52,7 @@ function PricingTableDetailPage(props) {
|
|
|
52
52
|
try {
|
|
53
53
|
const res = await fetch(`/api/cpq/pricing-tables?id=${encodeURIComponent(id)}`);
|
|
54
54
|
if (!res.ok) {
|
|
55
|
-
setError("Pricing table not found");
|
|
55
|
+
setError(t("cpq.pricing.detail.errors.notFound", "Pricing table not found"));
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
const data = await res.json();
|
|
@@ -70,11 +70,11 @@ function PricingTableDetailPage(props) {
|
|
|
70
70
|
setEntries(data.entries ?? []);
|
|
71
71
|
} catch (err) {
|
|
72
72
|
console.error("Failed to load pricing table", err);
|
|
73
|
-
setError("Failed to load");
|
|
73
|
+
setError(t("cpq.pricing.detail.errors.loadFailed", "Failed to load"));
|
|
74
74
|
} finally {
|
|
75
75
|
setLoading(false);
|
|
76
76
|
}
|
|
77
|
-
}, [id, isNew]);
|
|
77
|
+
}, [id, isNew, t]);
|
|
78
78
|
React.useEffect(() => {
|
|
79
79
|
void load();
|
|
80
80
|
}, [load]);
|
|
@@ -93,22 +93,32 @@ function PricingTableDetailPage(props) {
|
|
|
93
93
|
setError(null);
|
|
94
94
|
};
|
|
95
95
|
const configValidationError = React.useMemo(() => {
|
|
96
|
-
if (!draft.code.trim()) return "Code is required";
|
|
97
|
-
if (!draft.name.trim()) return "Name is required";
|
|
98
|
-
if (draft.currencyCodeList.length === 0)
|
|
99
|
-
|
|
96
|
+
if (!draft.code.trim()) return t("cpq.pricing.validation.codeRequired", "Code is required");
|
|
97
|
+
if (!draft.name.trim()) return t("cpq.pricing.validation.nameRequired", "Name is required");
|
|
98
|
+
if (draft.currencyCodeList.length === 0) {
|
|
99
|
+
return t("cpq.pricing.validation.currencyRequired", "At least one currency is required");
|
|
100
|
+
}
|
|
101
|
+
const checkList = (items, messages) => {
|
|
100
102
|
const seen = /* @__PURE__ */ new Set();
|
|
101
103
|
for (let i = 0; i < items.length; i++) {
|
|
102
104
|
const item = items[i];
|
|
103
|
-
if (!item.key.trim()) return
|
|
104
|
-
if (!item.label.trim()) return
|
|
105
|
-
if (seen.has(item.key)) return
|
|
105
|
+
if (!item.key.trim()) return messages.keyRequired(i + 1);
|
|
106
|
+
if (!item.label.trim()) return messages.labelRequired(i + 1);
|
|
107
|
+
if (seen.has(item.key)) return messages.duplicateKey(item.key);
|
|
106
108
|
seen.add(item.key);
|
|
107
109
|
}
|
|
108
110
|
return null;
|
|
109
111
|
};
|
|
110
|
-
return checkList(draft.dimensions,
|
|
111
|
-
|
|
112
|
+
return checkList(draft.dimensions, {
|
|
113
|
+
keyRequired: (index) => t("cpq.pricing.validation.dimensionKeyRequired", "Dimension #{index}: key is required", { index }),
|
|
114
|
+
labelRequired: (index) => t("cpq.pricing.validation.dimensionLabelRequired", "Dimension #{index}: label is required", { index }),
|
|
115
|
+
duplicateKey: (key) => t("cpq.pricing.validation.dimensionDuplicateKey", 'Dimension: duplicate key "{key}"', { key })
|
|
116
|
+
}) ?? checkList(draft.priceColumns, {
|
|
117
|
+
keyRequired: (index) => t("cpq.pricing.validation.priceColumnKeyRequired", "Price Column #{index}: key is required", { index }),
|
|
118
|
+
labelRequired: (index) => t("cpq.pricing.validation.priceColumnLabelRequired", "Price Column #{index}: label is required", { index }),
|
|
119
|
+
duplicateKey: (key) => t("cpq.pricing.validation.priceColumnDuplicateKey", 'Price Column: duplicate key "{key}"', { key })
|
|
120
|
+
});
|
|
121
|
+
}, [draft, t]);
|
|
112
122
|
const saveConfig = async () => {
|
|
113
123
|
if (configValidationError) {
|
|
114
124
|
setError(configValidationError);
|
|
@@ -132,7 +142,9 @@ function PricingTableDetailPage(props) {
|
|
|
132
142
|
});
|
|
133
143
|
if (!res.ok) {
|
|
134
144
|
const body = await res.json().catch(() => ({}));
|
|
135
|
-
setError(
|
|
145
|
+
setError(
|
|
146
|
+
body.error ?? (isNew ? t("cpq.pricing.detail.errors.createFailed", "Failed to create pricing table") : t("cpq.pricing.detail.errors.saveFailed", "Failed to save pricing table"))
|
|
147
|
+
);
|
|
136
148
|
return;
|
|
137
149
|
}
|
|
138
150
|
const saved = await res.json();
|
|
@@ -144,7 +156,7 @@ function PricingTableDetailPage(props) {
|
|
|
144
156
|
}
|
|
145
157
|
} catch (err) {
|
|
146
158
|
console.error("Failed to save", err);
|
|
147
|
-
setError("Failed to save");
|
|
159
|
+
setError(t("cpq.pricing.detail.errors.saveFailedGeneric", "Failed to save"));
|
|
148
160
|
} finally {
|
|
149
161
|
setSaving(false);
|
|
150
162
|
}
|
|
@@ -158,7 +170,8 @@ function PricingTableDetailPage(props) {
|
|
|
158
170
|
priceColumns: table.priceColumns,
|
|
159
171
|
currencyCodeList: table.currencyCodeList
|
|
160
172
|
},
|
|
161
|
-
entries
|
|
173
|
+
entries,
|
|
174
|
+
t
|
|
162
175
|
);
|
|
163
176
|
if (!r.ok) {
|
|
164
177
|
setError(r.error);
|
|
@@ -171,7 +184,7 @@ function PricingTableDetailPage(props) {
|
|
|
171
184
|
setImportApplying(true);
|
|
172
185
|
setError(null);
|
|
173
186
|
try {
|
|
174
|
-
const r = await commitImportDiff(table.id, importDiff);
|
|
187
|
+
const r = await commitImportDiff(table.id, importDiff, t);
|
|
175
188
|
if (!r.ok) {
|
|
176
189
|
setError(r.error);
|
|
177
190
|
return;
|
|
@@ -198,14 +211,14 @@ function PricingTableDetailPage(props) {
|
|
|
198
211
|
router.push("/backend/cpq/pricing");
|
|
199
212
|
} else {
|
|
200
213
|
const body = await res.json().catch(() => ({}));
|
|
201
|
-
setError(body.error ?? "Failed to delete");
|
|
214
|
+
setError(body.error ?? t("cpq.pricing.detail.errors.deleteFailed", "Failed to delete"));
|
|
202
215
|
}
|
|
203
216
|
} catch {
|
|
204
|
-
setError("Failed to delete");
|
|
217
|
+
setError(t("cpq.pricing.detail.errors.deleteFailed", "Failed to delete"));
|
|
205
218
|
}
|
|
206
219
|
};
|
|
207
220
|
if (loading) {
|
|
208
|
-
return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("common.loading", "Loading...") });
|
|
221
|
+
return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("cpq.common.loading", "Loading...") });
|
|
209
222
|
}
|
|
210
223
|
if (error && !isNew && !table.id) {
|
|
211
224
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
@@ -216,7 +229,7 @@ function PricingTableDetailPage(props) {
|
|
|
216
229
|
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
217
230
|
children: [
|
|
218
231
|
"\u2190 ",
|
|
219
|
-
t("common.back", "Back")
|
|
232
|
+
t("cpq.common.back", "Back")
|
|
220
233
|
]
|
|
221
234
|
}
|
|
222
235
|
),
|
|
@@ -235,12 +248,12 @@ function PricingTableDetailPage(props) {
|
|
|
235
248
|
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
236
249
|
children: [
|
|
237
250
|
"\u2190 ",
|
|
238
|
-
t("common.back", "Back")
|
|
251
|
+
t("cpq.common.back", "Back")
|
|
239
252
|
]
|
|
240
253
|
}
|
|
241
254
|
),
|
|
242
255
|
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: isNew ? t("cpq.pricing.new", "New Pricing Table") : table.name }),
|
|
243
|
-
!isNew && /* @__PURE__ */ jsx(Tag, { variant: table.isActive ? "success" : "neutral", dot: true, children: table.isActive ? t("common.active", "Active") : t("common.inactive", "Inactive") })
|
|
256
|
+
!isNew && /* @__PURE__ */ jsx(Tag, { variant: table.isActive ? "success" : "neutral", dot: true, children: table.isActive ? t("cpq.common.active", "Active") : t("cpq.common.inactive", "Inactive") })
|
|
244
257
|
] }),
|
|
245
258
|
!isNew && mode === "view" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
246
259
|
/* @__PURE__ */ jsxs(
|
|
@@ -284,8 +297,8 @@ function PricingTableDetailPage(props) {
|
|
|
284
297
|
}
|
|
285
298
|
}
|
|
286
299
|
),
|
|
287
|
-
/* @__PURE__ */ jsx(Button, { type: "button", onClick: startEdit, children: t("common.edit", "Edit") }),
|
|
288
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", onClick: deleteTable, children: t("common.delete", "Delete") })
|
|
300
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: startEdit, children: t("cpq.common.edit", "Edit") }),
|
|
301
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", onClick: deleteTable, children: t("cpq.common.delete", "Delete") })
|
|
289
302
|
] })
|
|
290
303
|
] }),
|
|
291
304
|
error && /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: error }),
|
|
@@ -299,25 +312,25 @@ function PricingTableDetailPage(props) {
|
|
|
299
312
|
}
|
|
300
313
|
),
|
|
301
314
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-6 space-y-4", children: [
|
|
302
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: "Configuration" }),
|
|
315
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: t("cpq.pricing.detail.configuration", "Configuration") }),
|
|
303
316
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
|
|
304
|
-
/* @__PURE__ */ jsx(ConfigField, { label: "Code", required: true, children: editing ? /* @__PURE__ */ jsx(
|
|
317
|
+
/* @__PURE__ */ jsx(ConfigField, { label: t("cpq.pricing.code", "Code"), required: true, children: editing ? /* @__PURE__ */ jsx(
|
|
305
318
|
Input,
|
|
306
319
|
{
|
|
307
320
|
value: view.code,
|
|
308
321
|
onChange: (e) => setDraft({ ...draft, code: e.target.value }),
|
|
309
|
-
placeholder: "e.g. cloud-connect-pricing"
|
|
322
|
+
placeholder: t("cpq.pricing.detail.codePlaceholder", "e.g. cloud-connect-pricing")
|
|
310
323
|
}
|
|
311
324
|
) : /* @__PURE__ */ jsx("span", { className: "font-mono text-sm", children: view.code }) }),
|
|
312
|
-
/* @__PURE__ */ jsx(ConfigField, { label: "Name", required: true, children: editing ? /* @__PURE__ */ jsx(
|
|
325
|
+
/* @__PURE__ */ jsx(ConfigField, { label: t("cpq.pricing.name", "Name"), required: true, children: editing ? /* @__PURE__ */ jsx(
|
|
313
326
|
Input,
|
|
314
327
|
{
|
|
315
328
|
value: view.name,
|
|
316
329
|
onChange: (e) => setDraft({ ...draft, name: e.target.value }),
|
|
317
|
-
placeholder: "Cloud Connect Pricing"
|
|
330
|
+
placeholder: t("cpq.pricing.detail.namePlaceholder", "Cloud Connect Pricing")
|
|
318
331
|
}
|
|
319
332
|
) : /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: view.name }) }),
|
|
320
|
-
/* @__PURE__ */ jsx(ConfigField, { label: "Currencies (comma-separated)", required: true, children: editing ? /* @__PURE__ */ jsx(
|
|
333
|
+
/* @__PURE__ */ jsx(ConfigField, { label: t("cpq.pricing.detail.currenciesCommaSeparated", "Currencies (comma-separated)"), required: true, children: editing ? /* @__PURE__ */ jsx(
|
|
321
334
|
Input,
|
|
322
335
|
{
|
|
323
336
|
value: view.currencyCodeList.join(", "),
|
|
@@ -325,10 +338,10 @@ function PricingTableDetailPage(props) {
|
|
|
325
338
|
...draft,
|
|
326
339
|
currencyCodeList: e.target.value.split(",").map((s) => s.trim().toUpperCase()).filter(Boolean)
|
|
327
340
|
}),
|
|
328
|
-
placeholder: "USD, EUR"
|
|
341
|
+
placeholder: t("cpq.pricing.detail.currenciesPlaceholder", "USD, EUR")
|
|
329
342
|
}
|
|
330
343
|
) : /* @__PURE__ */ jsx("span", { className: "text-sm", children: view.currencyCodeList.join(", ") || "\u2014" }) }),
|
|
331
|
-
/* @__PURE__ */ jsx(ConfigField, { label: "Active", children: editing ? /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
344
|
+
/* @__PURE__ */ jsx(ConfigField, { label: t("cpq.common.active", "Active"), children: editing ? /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
332
345
|
/* @__PURE__ */ jsx(
|
|
333
346
|
"input",
|
|
334
347
|
{
|
|
@@ -338,14 +351,18 @@ function PricingTableDetailPage(props) {
|
|
|
338
351
|
className: "rounded border"
|
|
339
352
|
}
|
|
340
353
|
),
|
|
341
|
-
view.isActive ? "Yes" : "No"
|
|
342
|
-
] }) : /* @__PURE__ */ jsx("span", { className: "text-sm", children: view.isActive ? "Yes" : "No" }) })
|
|
354
|
+
view.isActive ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No")
|
|
355
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-sm", children: view.isActive ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No") }) })
|
|
343
356
|
] }),
|
|
344
357
|
/* @__PURE__ */ jsx(
|
|
345
358
|
KeyLabelListEditor,
|
|
346
359
|
{
|
|
347
|
-
title: "Dimensions",
|
|
348
|
-
description:
|
|
360
|
+
title: t("cpq.pricing.detail.dimensions.title", "Dimensions"),
|
|
361
|
+
description: t(
|
|
362
|
+
"cpq.pricing.detail.dimensions.description",
|
|
363
|
+
"Lookup keys for matching entries (e.g. region, tier). Leave empty if you use tier ranges only."
|
|
364
|
+
),
|
|
365
|
+
addLabel: t("cpq.pricing.detail.dimensions.add", "+ Add Dimension"),
|
|
349
366
|
items: view.dimensions,
|
|
350
367
|
editing,
|
|
351
368
|
onChange: (dimensions) => setDraft({ ...draft, dimensions })
|
|
@@ -354,8 +371,12 @@ function PricingTableDetailPage(props) {
|
|
|
354
371
|
/* @__PURE__ */ jsx(
|
|
355
372
|
KeyLabelListEditor,
|
|
356
373
|
{
|
|
357
|
-
title: "Price Columns",
|
|
358
|
-
description:
|
|
374
|
+
title: t("cpq.pricing.priceColumns", "Price Columns"),
|
|
375
|
+
description: t(
|
|
376
|
+
"cpq.pricing.detail.priceColumns.description",
|
|
377
|
+
"Each price column becomes a numeric field on every entry. Charges reference one column by its key."
|
|
378
|
+
),
|
|
379
|
+
addLabel: t("cpq.pricing.detail.priceColumns.add", "+ Add Price Column"),
|
|
359
380
|
items: view.priceColumns,
|
|
360
381
|
editing,
|
|
361
382
|
onChange: (priceColumns) => setDraft({ ...draft, priceColumns })
|
|
@@ -370,10 +391,10 @@ function PricingTableDetailPage(props) {
|
|
|
370
391
|
type: "button",
|
|
371
392
|
onClick: saveConfig,
|
|
372
393
|
disabled: saving || configValidationError !== null,
|
|
373
|
-
children: saving ? "Saving..." : isNew ? "Create Pricing Table" : "Save"
|
|
394
|
+
children: saving ? t("cpq.common.saving", "Saving...") : isNew ? t("cpq.pricing.detail.create", "Create Pricing Table") : t("cpq.common.save", "Save")
|
|
374
395
|
}
|
|
375
396
|
),
|
|
376
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: cancelEdit, disabled: saving, children: "Cancel" })
|
|
397
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: cancelEdit, disabled: saving, children: t("cpq.common.cancel", "Cancel") })
|
|
377
398
|
] })
|
|
378
399
|
] })
|
|
379
400
|
] }),
|
|
@@ -410,10 +431,12 @@ function ConfigField({
|
|
|
410
431
|
function KeyLabelListEditor({
|
|
411
432
|
title,
|
|
412
433
|
description,
|
|
434
|
+
addLabel,
|
|
413
435
|
items,
|
|
414
436
|
editing,
|
|
415
437
|
onChange
|
|
416
438
|
}) {
|
|
439
|
+
const t = useT();
|
|
417
440
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
418
441
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
419
442
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: title }),
|
|
@@ -423,14 +446,16 @@ function KeyLabelListEditor({
|
|
|
423
446
|
items.length > 0 && /* @__PURE__ */ jsx("div", { className: "rounded-md border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
424
447
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/30 text-xs", children: [
|
|
425
448
|
/* @__PURE__ */ jsxs("th", { className: "px-3 py-2 text-left font-medium", children: [
|
|
426
|
-
"
|
|
449
|
+
t("cpq.pricing.detail.keyLabelList.key", "Key"),
|
|
450
|
+
" ",
|
|
427
451
|
editing && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" })
|
|
428
452
|
] }),
|
|
429
453
|
/* @__PURE__ */ jsxs("th", { className: "px-3 py-2 text-left font-medium", children: [
|
|
430
|
-
"
|
|
454
|
+
t("cpq.pricing.detail.keyLabelList.label", "Label"),
|
|
455
|
+
" ",
|
|
431
456
|
editing && /* @__PURE__ */ jsx("span", { className: "text-destructive", children: "*" })
|
|
432
457
|
] }),
|
|
433
|
-
editing && /* @__PURE__ */ jsx("th", { className: "px-3 py-2 w-8", "aria-label": "Actions" })
|
|
458
|
+
editing && /* @__PURE__ */ jsx("th", { className: "px-3 py-2 w-8", "aria-label": t("cpq.pricing.detail.keyLabelList.actions", "Actions") })
|
|
434
459
|
] }) }),
|
|
435
460
|
/* @__PURE__ */ jsx("tbody", { children: items.map((item, idx) => /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0", children: [
|
|
436
461
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: editing ? /* @__PURE__ */ jsx(
|
|
@@ -442,7 +467,7 @@ function KeyLabelListEditor({
|
|
|
442
467
|
next[idx] = { ...item, key: e.target.value };
|
|
443
468
|
onChange(next);
|
|
444
469
|
},
|
|
445
|
-
placeholder: "e.g. region",
|
|
470
|
+
placeholder: t("cpq.pricing.detail.keyLabelList.keyPlaceholder", "e.g. region"),
|
|
446
471
|
className: "h-8"
|
|
447
472
|
}
|
|
448
473
|
) : /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: item.key }) }),
|
|
@@ -455,7 +480,7 @@ function KeyLabelListEditor({
|
|
|
455
480
|
next[idx] = { ...item, label: e.target.value };
|
|
456
481
|
onChange(next);
|
|
457
482
|
},
|
|
458
|
-
placeholder: "Region",
|
|
483
|
+
placeholder: t("cpq.pricing.detail.keyLabelList.labelPlaceholder", "Region"),
|
|
459
484
|
className: "h-8"
|
|
460
485
|
}
|
|
461
486
|
) : /* @__PURE__ */ jsx("span", { children: item.label }) }),
|
|
@@ -465,23 +490,20 @@ function KeyLabelListEditor({
|
|
|
465
490
|
type: "button",
|
|
466
491
|
onClick: () => onChange(items.filter((_, i) => i !== idx)),
|
|
467
492
|
className: "text-destructive hover:bg-destructive/10 rounded p-1",
|
|
468
|
-
"aria-label": "Remove",
|
|
493
|
+
"aria-label": t("cpq.pricing.detail.keyLabelList.remove", "Remove"),
|
|
469
494
|
children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
|
|
470
495
|
}
|
|
471
496
|
) })
|
|
472
497
|
] }, idx)) })
|
|
473
498
|
] }) }),
|
|
474
|
-
editing && /* @__PURE__ */
|
|
499
|
+
editing && /* @__PURE__ */ jsx(
|
|
475
500
|
Button,
|
|
476
501
|
{
|
|
477
502
|
type: "button",
|
|
478
503
|
variant: "outline",
|
|
479
504
|
size: "sm",
|
|
480
505
|
onClick: () => onChange([...items, { key: "", label: "" }]),
|
|
481
|
-
children:
|
|
482
|
-
"+ Add ",
|
|
483
|
-
title.slice(0, -1)
|
|
484
|
-
]
|
|
506
|
+
children: addLabel
|
|
485
507
|
}
|
|
486
508
|
)
|
|
487
509
|
] });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/cpq/backend/cpq/pricing/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useParams, useRouter } from 'next/navigation'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { Download, Trash2, Upload } from 'lucide-react'\nimport { EntriesEditor, type Entry } from './EntriesEditor'\nimport { exportPricingTableToXlsx } from './xlsxExport'\nimport {\n commitImportDiff,\n parsePricingTableXlsx,\n type ImportDiff,\n} from './xlsxImport'\nimport { ImportPreview } from './ImportPreview'\n\ntype Dimension = { key: string; label: string }\ntype PriceColumn = { key: string; label: string }\n\ntype PricingTable = {\n id: string\n code: string\n name: string\n dimensions: Dimension[]\n priceColumns: PriceColumn[]\n currencyCodeList: string[]\n isActive: boolean\n}\n\nconst EMPTY_TABLE: PricingTable = {\n id: '',\n code: '',\n name: '',\n dimensions: [],\n priceColumns: [],\n currencyCodeList: ['USD'],\n isActive: true,\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport default function PricingTableDetailPage(props: { params?: { id?: string } }) {\n const t = useT()\n const router = useRouter()\n const urlParams = useParams()\n const id = (props.params?.id ?? urlParams?.id) as string\n const isNew = id === 'new'\n\n const [table, setTable] = React.useState<PricingTable>(EMPTY_TABLE)\n const [entries, setEntries] = React.useState<Entry[]>([])\n const [loading, setLoading] = React.useState(!isNew)\n const [error, setError] = React.useState<string | null>(null)\n const [mode, setMode] = React.useState<'view' | 'edit'>(isNew ? 'edit' : 'view')\n const [draft, setDraft] = React.useState<PricingTable>(EMPTY_TABLE)\n const [saving, setSaving] = React.useState(false)\n\n // Import state\n const fileInputRef = React.useRef<HTMLInputElement>(null)\n const [importDiff, setImportDiff] = React.useState<ImportDiff | null>(null)\n const [importApplying, setImportApplying] = React.useState(false)\n\n // \u2500\u2500\u2500 Load \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const load = React.useCallback(async () => {\n if (isNew) return\n if (!id || !UUID_RE.test(id)) {\n setError('Invalid pricing table ID')\n setLoading(false)\n return\n }\n setLoading(true)\n try {\n const res = await fetch(`/api/cpq/pricing-tables?id=${encodeURIComponent(id)}`)\n if (!res.ok) {\n setError('Pricing table not found')\n return\n }\n const data = await res.json()\n const loaded: PricingTable = {\n id: data.id,\n code: data.code,\n name: data.name,\n dimensions: data.dimensions ?? [],\n priceColumns: data.priceColumns ?? [],\n currencyCodeList: data.currencyCodeList ?? ['USD'],\n isActive: data.isActive,\n }\n setTable(loaded)\n setDraft(loaded)\n setEntries(data.entries ?? [])\n } catch (err) {\n console.error('Failed to load pricing table', err)\n setError('Failed to load')\n } finally {\n setLoading(false)\n }\n }, [id, isNew])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n // \u2500\u2500\u2500 Config CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const startEdit = () => {\n setDraft(table)\n setMode('edit')\n setError(null)\n }\n\n const cancelEdit = () => {\n if (isNew) {\n router.push('/backend/cpq/pricing')\n return\n }\n setDraft(table)\n setMode('view')\n setError(null)\n }\n\n // Mirror V-PT-1 from validators.ts so the user gets feedback before the\n // server rejects the payload.\n const configValidationError = React.useMemo(() => {\n if (!draft.code.trim()) return 'Code is required'\n if (!draft.name.trim()) return 'Name is required'\n if (draft.currencyCodeList.length === 0) return 'At least one currency is required'\n const checkList = (items: Array<{ key: string; label: string }>, label: string): string | null => {\n const seen = new Set<string>()\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n if (!item.key.trim()) return `${label} #${i + 1}: key is required`\n if (!item.label.trim()) return `${label} #${i + 1}: label is required`\n if (seen.has(item.key)) return `${label}: duplicate key \"${item.key}\"`\n seen.add(item.key)\n }\n return null\n }\n return checkList(draft.dimensions, 'Dimension') ?? checkList(draft.priceColumns, 'Price Column')\n }, [draft])\n\n const saveConfig = async () => {\n if (configValidationError) {\n setError(configValidationError)\n return\n }\n setSaving(true)\n setError(null)\n try {\n const payload = {\n code: draft.code,\n name: draft.name,\n dimensions: draft.dimensions,\n priceColumns: draft.priceColumns,\n currencyCodeList: draft.currencyCodeList,\n isActive: draft.isActive,\n }\n const res = await fetch('/api/cpq/pricing-tables', {\n method: isNew ? 'POST' : 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(isNew ? payload : { id, ...payload }),\n })\n if (!res.ok) {\n const body = await res.json().catch(() => ({}))\n setError(body.error ?? `Failed to ${isNew ? 'create' : 'save'} pricing table`)\n return\n }\n const saved = await res.json()\n if (isNew) {\n router.push(`/backend/cpq/pricing/${saved.id}`)\n } else {\n setTable({ ...draft, id })\n setMode('view')\n }\n } catch (err) {\n console.error('Failed to save', err)\n setError('Failed to save')\n } finally {\n setSaving(false)\n }\n }\n\n // \u2500\u2500\u2500 Excel import \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleImportFile = async (file: File) => {\n setError(null)\n const r = await parsePricingTableXlsx(\n file,\n {\n dimensions: table.dimensions,\n priceColumns: table.priceColumns,\n currencyCodeList: table.currencyCodeList,\n },\n entries,\n )\n if (!r.ok) {\n setError(r.error)\n return\n }\n setImportDiff(r.diff)\n }\n\n const applyImportDiff = async () => {\n if (!importDiff) return\n setImportApplying(true)\n setError(null)\n try {\n const r = await commitImportDiff(table.id, importDiff)\n if (!r.ok) {\n setError(r.error)\n return\n }\n setImportDiff(null)\n await load()\n } finally {\n setImportApplying(false)\n }\n }\n\n const cancelImport = () => {\n setImportDiff(null)\n if (fileInputRef.current) fileInputRef.current.value = ''\n }\n\n const deleteTable = async () => {\n if (!window.confirm(t('cpq.pricing.confirmDelete', 'Delete this pricing table and all its entries?'))) return\n try {\n const res = await fetch('/api/cpq/pricing-tables', {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id }),\n })\n if (res.ok) {\n router.push('/backend/cpq/pricing')\n } else {\n const body = await res.json().catch(() => ({}))\n setError(body.error ?? 'Failed to delete')\n }\n } catch {\n setError('Failed to delete')\n }\n }\n\n // \u2500\u2500\u2500 Render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">{t('common.loading', 'Loading...')}</div>\n }\n\n if (error && !isNew && !table.id) {\n return (\n <div className=\"space-y-4\">\n <button\n onClick={() => router.push('/backend/cpq/pricing')}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n \u2190 {t('common.back', 'Back')}\n </button>\n <Alert variant=\"destructive\">{error}</Alert>\n </div>\n )\n }\n\n const editing = mode === 'edit'\n const view = editing ? draft : table\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => router.push('/backend/cpq/pricing')}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n \u2190 {t('common.back', 'Back')}\n </button>\n <h1 className=\"text-2xl font-bold\">\n {isNew ? t('cpq.pricing.new', 'New Pricing Table') : table.name}\n </h1>\n {!isNew && (\n <Tag variant={table.isActive ? 'success' : 'neutral'} dot>\n {table.isActive ? t('common.active', 'Active') : t('common.inactive', 'Inactive')}\n </Tag>\n )}\n </div>\n {!isNew && mode === 'view' && (\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => exportPricingTableToXlsx(table, entries)}\n title={t('cpq.pricing.export.tooltip', 'Download this pricing table as an Excel file')}\n >\n <Download className=\"h-4 w-4 mr-1.5\" />\n {t('cpq.pricing.export.button', 'Export to Excel')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => fileInputRef.current?.click()}\n title={t('cpq.pricing.import.tooltip', 'Upload an Excel file to add / update / delete entries')}\n disabled={importDiff !== null}\n >\n <Upload className=\"h-4 w-4 mr-1.5\" />\n {t('cpq.pricing.import.button', 'Import from Excel')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) void handleImportFile(file)\n // reset so re-uploading the same file fires onChange again\n e.target.value = ''\n }}\n />\n <Button type=\"button\" onClick={startEdit}>{t('common.edit', 'Edit')}</Button>\n <Button type=\"button\" variant=\"destructive\" onClick={deleteTable}>\n {t('common.delete', 'Delete')}\n </Button>\n </div>\n )}\n </div>\n\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n\n {/* Excel import preview \u2014 shown after a file is parsed, hides until user confirms / cancels */}\n {importDiff && (\n <ImportPreview\n diff={importDiff}\n saving={importApplying}\n onConfirm={applyImportDiff}\n onCancel={cancelImport}\n />\n )}\n\n {/* Configuration section */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">Configuration</h2>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <ConfigField label=\"Code\" required>\n {editing ? (\n <Input\n value={view.code}\n onChange={(e) => setDraft({ ...draft, code: e.target.value })}\n placeholder=\"e.g. cloud-connect-pricing\"\n />\n ) : (\n <span className=\"font-mono text-sm\">{view.code}</span>\n )}\n </ConfigField>\n <ConfigField label=\"Name\" required>\n {editing ? (\n <Input\n value={view.name}\n onChange={(e) => setDraft({ ...draft, name: e.target.value })}\n placeholder=\"Cloud Connect Pricing\"\n />\n ) : (\n <span className=\"font-medium text-sm\">{view.name}</span>\n )}\n </ConfigField>\n <ConfigField label=\"Currencies (comma-separated)\" required>\n {editing ? (\n <Input\n value={view.currencyCodeList.join(', ')}\n onChange={(e) =>\n setDraft({\n ...draft,\n currencyCodeList: e.target.value\n .split(',')\n .map((s) => s.trim().toUpperCase())\n .filter(Boolean),\n })\n }\n placeholder=\"USD, EUR\"\n />\n ) : (\n <span className=\"text-sm\">{view.currencyCodeList.join(', ') || '\u2014'}</span>\n )}\n </ConfigField>\n <ConfigField label=\"Active\">\n {editing ? (\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={view.isActive}\n onChange={(e) => setDraft({ ...draft, isActive: e.target.checked })}\n className=\"rounded border\"\n />\n {view.isActive ? 'Yes' : 'No'}\n </label>\n ) : (\n <span className=\"text-sm\">{view.isActive ? 'Yes' : 'No'}</span>\n )}\n </ConfigField>\n </div>\n\n <KeyLabelListEditor\n title=\"Dimensions\"\n description=\"Lookup keys for matching entries (e.g. region, tier). Leave empty if you use tier ranges only.\"\n items={view.dimensions}\n editing={editing}\n onChange={(dimensions) => setDraft({ ...draft, dimensions })}\n />\n\n <KeyLabelListEditor\n title=\"Price Columns\"\n description=\"Each price column becomes a numeric field on every entry. Charges reference one column by its key.\"\n items={view.priceColumns}\n editing={editing}\n onChange={(priceColumns) => setDraft({ ...draft, priceColumns })}\n />\n\n {editing && (\n <>\n {configValidationError && (\n <Alert variant=\"destructive\">{configValidationError}</Alert>\n )}\n <div className=\"flex gap-2 pt-2 border-t\">\n <Button\n type=\"button\"\n onClick={saveConfig}\n disabled={saving || configValidationError !== null}\n >\n {saving ? 'Saving...' : (isNew ? 'Create Pricing Table' : 'Save')}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={cancelEdit} disabled={saving}>\n Cancel\n </Button>\n </div>\n </>\n )}\n </div>\n\n {/* Entries section \u2014 only when table exists */}\n {!isNew && (\n <EntriesEditor\n table={{\n id: table.id,\n dimensions: table.dimensions,\n priceColumns: table.priceColumns,\n currencyCodeList: table.currencyCodeList,\n }}\n initialEntries={entries}\n onSaved={() => {\n void load()\n }}\n />\n )}\n </div>\n )\n}\n\n// \u2500\u2500\u2500 ConfigField \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ConfigField({\n label,\n required,\n children,\n}: {\n label: string\n required?: boolean\n children: React.ReactNode\n}) {\n return (\n <div>\n <div className=\"text-muted-foreground text-xs mb-1\">\n {label}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n </div>\n <div>{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 KeyLabelListEditor (dimensions / price columns) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction KeyLabelListEditor({\n title,\n description,\n items,\n editing,\n onChange,\n}: {\n title: string\n description: string\n items: Array<{ key: string; label: string }>\n editing: boolean\n onChange: (next: Array<{ key: string; label: string }>) => void\n}) {\n return (\n <div className=\"space-y-2\">\n <div>\n <div className=\"text-sm font-medium\">{title}</div>\n <p className=\"text-xs text-muted-foreground\">{description}</p>\n </div>\n\n {items.length === 0 && !editing && (\n <div className=\"text-xs text-muted-foreground italic\">\u2014</div>\n )}\n\n {items.length > 0 && (\n <div className=\"rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/30 text-xs\">\n <th className=\"px-3 py-2 text-left font-medium\">\n Key {editing && <span className=\"text-destructive\">*</span>}\n </th>\n <th className=\"px-3 py-2 text-left font-medium\">\n Label {editing && <span className=\"text-destructive\">*</span>}\n </th>\n {editing && <th className=\"px-3 py-2 w-8\" aria-label=\"Actions\" />}\n </tr>\n </thead>\n <tbody>\n {items.map((item, idx) => (\n <tr key={idx} className=\"border-b last:border-0\">\n <td className=\"px-3 py-2\">\n {editing ? (\n <Input\n value={item.key}\n onChange={(e) => {\n const next = [...items]\n next[idx] = { ...item, key: e.target.value }\n onChange(next)\n }}\n placeholder=\"e.g. region\"\n className=\"h-8\"\n />\n ) : (\n <span className=\"font-mono text-xs\">{item.key}</span>\n )}\n </td>\n <td className=\"px-3 py-2\">\n {editing ? (\n <Input\n value={item.label}\n onChange={(e) => {\n const next = [...items]\n next[idx] = { ...item, label: e.target.value }\n onChange(next)\n }}\n placeholder=\"Region\"\n className=\"h-8\"\n />\n ) : (\n <span>{item.label}</span>\n )}\n </td>\n {editing && (\n <td className=\"px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => onChange(items.filter((_, i) => i !== idx))}\n className=\"text-destructive hover:bg-destructive/10 rounded p-1\"\n aria-label=\"Remove\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </td>\n )}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n\n {editing && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onChange([...items, { key: '', label: '' }])}\n >\n + Add {title.slice(0, -1)}\n </Button>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useParams, useRouter } from 'next/navigation'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { Download, Trash2, Upload } from 'lucide-react'\nimport { EntriesEditor, type Entry } from './EntriesEditor'\nimport { exportPricingTableToXlsx } from './xlsxExport'\nimport {\n commitImportDiff,\n parsePricingTableXlsx,\n type ImportDiff,\n} from './xlsxImport'\nimport { ImportPreview } from './ImportPreview'\n\ntype Dimension = { key: string; label: string }\ntype PriceColumn = { key: string; label: string }\n\ntype PricingTable = {\n id: string\n code: string\n name: string\n dimensions: Dimension[]\n priceColumns: PriceColumn[]\n currencyCodeList: string[]\n isActive: boolean\n}\n\nconst EMPTY_TABLE: PricingTable = {\n id: '',\n code: '',\n name: '',\n dimensions: [],\n priceColumns: [],\n currencyCodeList: ['USD'],\n isActive: true,\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nexport default function PricingTableDetailPage(props: { params?: { id?: string } }) {\n const t = useT()\n const router = useRouter()\n const urlParams = useParams()\n const id = (props.params?.id ?? urlParams?.id) as string\n const isNew = id === 'new'\n\n const [table, setTable] = React.useState<PricingTable>(EMPTY_TABLE)\n const [entries, setEntries] = React.useState<Entry[]>([])\n const [loading, setLoading] = React.useState(!isNew)\n const [error, setError] = React.useState<string | null>(null)\n const [mode, setMode] = React.useState<'view' | 'edit'>(isNew ? 'edit' : 'view')\n const [draft, setDraft] = React.useState<PricingTable>(EMPTY_TABLE)\n const [saving, setSaving] = React.useState(false)\n\n // Import state\n const fileInputRef = React.useRef<HTMLInputElement>(null)\n const [importDiff, setImportDiff] = React.useState<ImportDiff | null>(null)\n const [importApplying, setImportApplying] = React.useState(false)\n\n // \u2500\u2500\u2500 Load \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const load = React.useCallback(async () => {\n if (isNew) return\n if (!id || !UUID_RE.test(id)) {\n setError(t('cpq.pricing.detail.errors.invalidId', 'Invalid pricing table ID'))\n setLoading(false)\n return\n }\n setLoading(true)\n try {\n const res = await fetch(`/api/cpq/pricing-tables?id=${encodeURIComponent(id)}`)\n if (!res.ok) {\n setError(t('cpq.pricing.detail.errors.notFound', 'Pricing table not found'))\n return\n }\n const data = await res.json()\n const loaded: PricingTable = {\n id: data.id,\n code: data.code,\n name: data.name,\n dimensions: data.dimensions ?? [],\n priceColumns: data.priceColumns ?? [],\n currencyCodeList: data.currencyCodeList ?? ['USD'],\n isActive: data.isActive,\n }\n setTable(loaded)\n setDraft(loaded)\n setEntries(data.entries ?? [])\n } catch (err) {\n console.error('Failed to load pricing table', err)\n setError(t('cpq.pricing.detail.errors.loadFailed', 'Failed to load'))\n } finally {\n setLoading(false)\n }\n }, [id, isNew, t])\n\n React.useEffect(() => {\n void load()\n }, [load])\n\n // \u2500\u2500\u2500 Config CRUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const startEdit = () => {\n setDraft(table)\n setMode('edit')\n setError(null)\n }\n\n const cancelEdit = () => {\n if (isNew) {\n router.push('/backend/cpq/pricing')\n return\n }\n setDraft(table)\n setMode('view')\n setError(null)\n }\n\n // Mirror V-PT-1 from validators.ts so the user gets feedback before the\n // server rejects the payload.\n const configValidationError = React.useMemo(() => {\n if (!draft.code.trim()) return t('cpq.pricing.validation.codeRequired', 'Code is required')\n if (!draft.name.trim()) return t('cpq.pricing.validation.nameRequired', 'Name is required')\n if (draft.currencyCodeList.length === 0) {\n return t('cpq.pricing.validation.currencyRequired', 'At least one currency is required')\n }\n type ListMessages = {\n keyRequired: (index: number) => string\n labelRequired: (index: number) => string\n duplicateKey: (key: string) => string\n }\n const checkList = (items: Array<{ key: string; label: string }>, messages: ListMessages): string | null => {\n const seen = new Set<string>()\n for (let i = 0; i < items.length; i++) {\n const item = items[i]\n if (!item.key.trim()) return messages.keyRequired(i + 1)\n if (!item.label.trim()) return messages.labelRequired(i + 1)\n if (seen.has(item.key)) return messages.duplicateKey(item.key)\n seen.add(item.key)\n }\n return null\n }\n return (\n checkList(draft.dimensions, {\n keyRequired: (index) =>\n t('cpq.pricing.validation.dimensionKeyRequired', 'Dimension #{index}: key is required', { index }),\n labelRequired: (index) =>\n t('cpq.pricing.validation.dimensionLabelRequired', 'Dimension #{index}: label is required', { index }),\n duplicateKey: (key) =>\n t('cpq.pricing.validation.dimensionDuplicateKey', 'Dimension: duplicate key \"{key}\"', { key }),\n }) ??\n checkList(draft.priceColumns, {\n keyRequired: (index) =>\n t('cpq.pricing.validation.priceColumnKeyRequired', 'Price Column #{index}: key is required', { index }),\n labelRequired: (index) =>\n t('cpq.pricing.validation.priceColumnLabelRequired', 'Price Column #{index}: label is required', { index }),\n duplicateKey: (key) =>\n t('cpq.pricing.validation.priceColumnDuplicateKey', 'Price Column: duplicate key \"{key}\"', { key }),\n })\n )\n }, [draft, t])\n\n const saveConfig = async () => {\n if (configValidationError) {\n setError(configValidationError)\n return\n }\n setSaving(true)\n setError(null)\n try {\n const payload = {\n code: draft.code,\n name: draft.name,\n dimensions: draft.dimensions,\n priceColumns: draft.priceColumns,\n currencyCodeList: draft.currencyCodeList,\n isActive: draft.isActive,\n }\n const res = await fetch('/api/cpq/pricing-tables', {\n method: isNew ? 'POST' : 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(isNew ? payload : { id, ...payload }),\n })\n if (!res.ok) {\n const body = await res.json().catch(() => ({}))\n setError(\n body.error ??\n (isNew\n ? t('cpq.pricing.detail.errors.createFailed', 'Failed to create pricing table')\n : t('cpq.pricing.detail.errors.saveFailed', 'Failed to save pricing table')),\n )\n return\n }\n const saved = await res.json()\n if (isNew) {\n router.push(`/backend/cpq/pricing/${saved.id}`)\n } else {\n setTable({ ...draft, id })\n setMode('view')\n }\n } catch (err) {\n console.error('Failed to save', err)\n setError(t('cpq.pricing.detail.errors.saveFailedGeneric', 'Failed to save'))\n } finally {\n setSaving(false)\n }\n }\n\n // \u2500\u2500\u2500 Excel import \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n const handleImportFile = async (file: File) => {\n setError(null)\n const r = await parsePricingTableXlsx(\n file,\n {\n dimensions: table.dimensions,\n priceColumns: table.priceColumns,\n currencyCodeList: table.currencyCodeList,\n },\n entries,\n t,\n )\n if (!r.ok) {\n setError(r.error)\n return\n }\n setImportDiff(r.diff)\n }\n\n const applyImportDiff = async () => {\n if (!importDiff) return\n setImportApplying(true)\n setError(null)\n try {\n const r = await commitImportDiff(table.id, importDiff, t)\n if (!r.ok) {\n setError(r.error)\n return\n }\n setImportDiff(null)\n await load()\n } finally {\n setImportApplying(false)\n }\n }\n\n const cancelImport = () => {\n setImportDiff(null)\n if (fileInputRef.current) fileInputRef.current.value = ''\n }\n\n const deleteTable = async () => {\n if (!window.confirm(t('cpq.pricing.confirmDelete', 'Delete this pricing table and all its entries?'))) return\n try {\n const res = await fetch('/api/cpq/pricing-tables', {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id }),\n })\n if (res.ok) {\n router.push('/backend/cpq/pricing')\n } else {\n const body = await res.json().catch(() => ({}))\n setError(body.error ?? t('cpq.pricing.detail.errors.deleteFailed', 'Failed to delete'))\n }\n } catch {\n setError(t('cpq.pricing.detail.errors.deleteFailed', 'Failed to delete'))\n }\n }\n\n // \u2500\u2500\u2500 Render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">{t('cpq.common.loading', 'Loading...')}</div>\n }\n\n if (error && !isNew && !table.id) {\n return (\n <div className=\"space-y-4\">\n <button\n onClick={() => router.push('/backend/cpq/pricing')}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n \u2190 {t('cpq.common.back', 'Back')}\n </button>\n <Alert variant=\"destructive\">{error}</Alert>\n </div>\n )\n }\n\n const editing = mode === 'edit'\n const view = editing ? draft : table\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n onClick={() => router.push('/backend/cpq/pricing')}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n \u2190 {t('cpq.common.back', 'Back')}\n </button>\n <h1 className=\"text-2xl font-bold\">\n {isNew ? t('cpq.pricing.new', 'New Pricing Table') : table.name}\n </h1>\n {!isNew && (\n <Tag variant={table.isActive ? 'success' : 'neutral'} dot>\n {table.isActive ? t('cpq.common.active', 'Active') : t('cpq.common.inactive', 'Inactive')}\n </Tag>\n )}\n </div>\n {!isNew && mode === 'view' && (\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => exportPricingTableToXlsx(table, entries)}\n title={t('cpq.pricing.export.tooltip', 'Download this pricing table as an Excel file')}\n >\n <Download className=\"h-4 w-4 mr-1.5\" />\n {t('cpq.pricing.export.button', 'Export to Excel')}\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => fileInputRef.current?.click()}\n title={t('cpq.pricing.import.tooltip', 'Upload an Excel file to add / update / delete entries')}\n disabled={importDiff !== null}\n >\n <Upload className=\"h-4 w-4 mr-1.5\" />\n {t('cpq.pricing.import.button', 'Import from Excel')}\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) void handleImportFile(file)\n // reset so re-uploading the same file fires onChange again\n e.target.value = ''\n }}\n />\n <Button type=\"button\" onClick={startEdit}>{t('cpq.common.edit', 'Edit')}</Button>\n <Button type=\"button\" variant=\"destructive\" onClick={deleteTable}>\n {t('cpq.common.delete', 'Delete')}\n </Button>\n </div>\n )}\n </div>\n\n {error && <Alert variant=\"destructive\">{error}</Alert>}\n\n {/* Excel import preview \u2014 shown after a file is parsed, hides until user confirms / cancels */}\n {importDiff && (\n <ImportPreview\n diff={importDiff}\n saving={importApplying}\n onConfirm={applyImportDiff}\n onCancel={cancelImport}\n />\n )}\n\n {/* Configuration section */}\n <div className=\"rounded-lg border bg-card p-6 space-y-4\">\n <h2 className=\"text-lg font-semibold\">{t('cpq.pricing.detail.configuration', 'Configuration')}</h2>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <ConfigField label={t('cpq.pricing.code', 'Code')} required>\n {editing ? (\n <Input\n value={view.code}\n onChange={(e) => setDraft({ ...draft, code: e.target.value })}\n placeholder={t('cpq.pricing.detail.codePlaceholder', 'e.g. cloud-connect-pricing')}\n />\n ) : (\n <span className=\"font-mono text-sm\">{view.code}</span>\n )}\n </ConfigField>\n <ConfigField label={t('cpq.pricing.name', 'Name')} required>\n {editing ? (\n <Input\n value={view.name}\n onChange={(e) => setDraft({ ...draft, name: e.target.value })}\n placeholder={t('cpq.pricing.detail.namePlaceholder', 'Cloud Connect Pricing')}\n />\n ) : (\n <span className=\"font-medium text-sm\">{view.name}</span>\n )}\n </ConfigField>\n <ConfigField label={t('cpq.pricing.detail.currenciesCommaSeparated', 'Currencies (comma-separated)')} required>\n {editing ? (\n <Input\n value={view.currencyCodeList.join(', ')}\n onChange={(e) =>\n setDraft({\n ...draft,\n currencyCodeList: e.target.value\n .split(',')\n .map((s) => s.trim().toUpperCase())\n .filter(Boolean),\n })\n }\n placeholder={t('cpq.pricing.detail.currenciesPlaceholder', 'USD, EUR')}\n />\n ) : (\n <span className=\"text-sm\">{view.currencyCodeList.join(', ') || '\u2014'}</span>\n )}\n </ConfigField>\n <ConfigField label={t('cpq.common.active', 'Active')}>\n {editing ? (\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={view.isActive}\n onChange={(e) => setDraft({ ...draft, isActive: e.target.checked })}\n className=\"rounded border\"\n />\n {view.isActive ? t('cpq.common.yes', 'Yes') : t('cpq.common.no', 'No')}\n </label>\n ) : (\n <span className=\"text-sm\">{view.isActive ? t('cpq.common.yes', 'Yes') : t('cpq.common.no', 'No')}</span>\n )}\n </ConfigField>\n </div>\n\n <KeyLabelListEditor\n title={t('cpq.pricing.detail.dimensions.title', 'Dimensions')}\n description={t(\n 'cpq.pricing.detail.dimensions.description',\n 'Lookup keys for matching entries (e.g. region, tier). Leave empty if you use tier ranges only.',\n )}\n addLabel={t('cpq.pricing.detail.dimensions.add', '+ Add Dimension')}\n items={view.dimensions}\n editing={editing}\n onChange={(dimensions) => setDraft({ ...draft, dimensions })}\n />\n\n <KeyLabelListEditor\n title={t('cpq.pricing.priceColumns', 'Price Columns')}\n description={t(\n 'cpq.pricing.detail.priceColumns.description',\n 'Each price column becomes a numeric field on every entry. Charges reference one column by its key.',\n )}\n addLabel={t('cpq.pricing.detail.priceColumns.add', '+ Add Price Column')}\n items={view.priceColumns}\n editing={editing}\n onChange={(priceColumns) => setDraft({ ...draft, priceColumns })}\n />\n\n {editing && (\n <>\n {configValidationError && (\n <Alert variant=\"destructive\">{configValidationError}</Alert>\n )}\n <div className=\"flex gap-2 pt-2 border-t\">\n <Button\n type=\"button\"\n onClick={saveConfig}\n disabled={saving || configValidationError !== null}\n >\n {saving\n ? t('cpq.common.saving', 'Saving...')\n : isNew\n ? t('cpq.pricing.detail.create', 'Create Pricing Table')\n : t('cpq.common.save', 'Save')}\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={cancelEdit} disabled={saving}>\n {t('cpq.common.cancel', 'Cancel')}\n </Button>\n </div>\n </>\n )}\n </div>\n\n {/* Entries section \u2014 only when table exists */}\n {!isNew && (\n <EntriesEditor\n table={{\n id: table.id,\n dimensions: table.dimensions,\n priceColumns: table.priceColumns,\n currencyCodeList: table.currencyCodeList,\n }}\n initialEntries={entries}\n onSaved={() => {\n void load()\n }}\n />\n )}\n </div>\n )\n}\n\n// \u2500\u2500\u2500 ConfigField \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ConfigField({\n label,\n required,\n children,\n}: {\n label: string\n required?: boolean\n children: React.ReactNode\n}) {\n return (\n <div>\n <div className=\"text-muted-foreground text-xs mb-1\">\n {label}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n </div>\n <div>{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 KeyLabelListEditor (dimensions / price columns) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction KeyLabelListEditor({\n title,\n description,\n addLabel,\n items,\n editing,\n onChange,\n}: {\n title: string\n description: string\n /** Translated \"+ Add \u2026\" button text, supplied by the caller. */\n addLabel: string\n items: Array<{ key: string; label: string }>\n editing: boolean\n onChange: (next: Array<{ key: string; label: string }>) => void\n}) {\n const t = useT()\n return (\n <div className=\"space-y-2\">\n <div>\n <div className=\"text-sm font-medium\">{title}</div>\n <p className=\"text-xs text-muted-foreground\">{description}</p>\n </div>\n\n {items.length === 0 && !editing && (\n <div className=\"text-xs text-muted-foreground italic\">\u2014</div>\n )}\n\n {items.length > 0 && (\n <div className=\"rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/30 text-xs\">\n <th className=\"px-3 py-2 text-left font-medium\">\n {t('cpq.pricing.detail.keyLabelList.key', 'Key')}{' '}\n {editing && <span className=\"text-destructive\">*</span>}\n </th>\n <th className=\"px-3 py-2 text-left font-medium\">\n {t('cpq.pricing.detail.keyLabelList.label', 'Label')}{' '}\n {editing && <span className=\"text-destructive\">*</span>}\n </th>\n {editing && <th className=\"px-3 py-2 w-8\" aria-label={t('cpq.pricing.detail.keyLabelList.actions', 'Actions')} />}\n </tr>\n </thead>\n <tbody>\n {items.map((item, idx) => (\n <tr key={idx} className=\"border-b last:border-0\">\n <td className=\"px-3 py-2\">\n {editing ? (\n <Input\n value={item.key}\n onChange={(e) => {\n const next = [...items]\n next[idx] = { ...item, key: e.target.value }\n onChange(next)\n }}\n placeholder={t('cpq.pricing.detail.keyLabelList.keyPlaceholder', 'e.g. region')}\n className=\"h-8\"\n />\n ) : (\n <span className=\"font-mono text-xs\">{item.key}</span>\n )}\n </td>\n <td className=\"px-3 py-2\">\n {editing ? (\n <Input\n value={item.label}\n onChange={(e) => {\n const next = [...items]\n next[idx] = { ...item, label: e.target.value }\n onChange(next)\n }}\n placeholder={t('cpq.pricing.detail.keyLabelList.labelPlaceholder', 'Region')}\n className=\"h-8\"\n />\n ) : (\n <span>{item.label}</span>\n )}\n </td>\n {editing && (\n <td className=\"px-3 py-2\">\n <button\n type=\"button\"\n onClick={() => onChange(items.filter((_, i) => i !== idx))}\n className=\"text-destructive hover:bg-destructive/10 rounded p-1\"\n aria-label={t('cpq.pricing.detail.keyLabelList.remove', 'Remove')}\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </td>\n )}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n\n {editing && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onChange([...items, { key: '', label: '' }])}\n >\n {addLabel}\n </Button>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|