@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
|
@@ -135,7 +135,7 @@ function SpecificationDetailPage(props) {
|
|
|
135
135
|
}
|
|
136
136
|
const res = await fetch(`/api/cpq/product-specifications?id=${encodeURIComponent(specId)}`);
|
|
137
137
|
if (!res.ok) {
|
|
138
|
-
if (!cancelled) setError("Failed to load specification");
|
|
138
|
+
if (!cancelled) setError(t("cpq.specifications.detail.error.load", "Failed to load specification"));
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
const spec = await res.json();
|
|
@@ -159,7 +159,7 @@ function SpecificationDetailPage(props) {
|
|
|
159
159
|
}
|
|
160
160
|
} catch (err) {
|
|
161
161
|
console.error("Failed to load", err);
|
|
162
|
-
if (!cancelled) setError("Failed to load");
|
|
162
|
+
if (!cancelled) setError(t("cpq.specifications.detail.error.loadGeneric", "Failed to load"));
|
|
163
163
|
} finally {
|
|
164
164
|
if (!cancelled) setLoading(false);
|
|
165
165
|
}
|
|
@@ -182,7 +182,7 @@ function SpecificationDetailPage(props) {
|
|
|
182
182
|
});
|
|
183
183
|
if (!res.ok) {
|
|
184
184
|
const d = await res.json();
|
|
185
|
-
setError(d.error ?? "Failed to save");
|
|
185
|
+
setError(d.error ?? t("cpq.specifications.detail.error.save", "Failed to save"));
|
|
186
186
|
return;
|
|
187
187
|
}
|
|
188
188
|
const saved = await res.json();
|
|
@@ -205,7 +205,7 @@ function SpecificationDetailPage(props) {
|
|
|
205
205
|
setGeneralMode("view");
|
|
206
206
|
}
|
|
207
207
|
} catch {
|
|
208
|
-
setError("Failed to save");
|
|
208
|
+
setError(t("cpq.specifications.detail.error.save", "Failed to save"));
|
|
209
209
|
} finally {
|
|
210
210
|
setSaving(false);
|
|
211
211
|
}
|
|
@@ -222,10 +222,10 @@ function SpecificationDetailPage(props) {
|
|
|
222
222
|
router.push("/backend/cpq/specifications");
|
|
223
223
|
} else {
|
|
224
224
|
const d = await res.json();
|
|
225
|
-
setError(d.error ?? "Failed to delete");
|
|
225
|
+
setError(d.error ?? t("cpq.specifications.detail.error.delete", "Failed to delete"));
|
|
226
226
|
}
|
|
227
227
|
} catch {
|
|
228
|
-
setError("Failed to delete");
|
|
228
|
+
setError(t("cpq.specifications.detail.error.delete", "Failed to delete"));
|
|
229
229
|
}
|
|
230
230
|
};
|
|
231
231
|
const saveAttribute = async () => {
|
|
@@ -253,7 +253,7 @@ function SpecificationDetailPage(props) {
|
|
|
253
253
|
});
|
|
254
254
|
if (!res.ok) {
|
|
255
255
|
const d = await res.json();
|
|
256
|
-
setError(d.error ?? "Failed to save attribute");
|
|
256
|
+
setError(d.error ?? t("cpq.specifications.attributes.error.save", "Failed to save attribute"));
|
|
257
257
|
return;
|
|
258
258
|
}
|
|
259
259
|
const saved = await res.json();
|
|
@@ -264,13 +264,13 @@ function SpecificationDetailPage(props) {
|
|
|
264
264
|
}
|
|
265
265
|
setEditingAttr(null);
|
|
266
266
|
} catch {
|
|
267
|
-
setError("Failed to save attribute");
|
|
267
|
+
setError(t("cpq.specifications.attributes.error.save", "Failed to save attribute"));
|
|
268
268
|
} finally {
|
|
269
269
|
setSaving(false);
|
|
270
270
|
}
|
|
271
271
|
};
|
|
272
272
|
const deleteAttribute = async (attrId) => {
|
|
273
|
-
if (!confirm("Delete this attribute?")) return;
|
|
273
|
+
if (!confirm(t("cpq.specifications.attributes.confirmDelete", "Delete this attribute?"))) return;
|
|
274
274
|
try {
|
|
275
275
|
const res = await fetch("/api/cpq/product-attributes", {
|
|
276
276
|
method: "DELETE",
|
|
@@ -279,7 +279,7 @@ function SpecificationDetailPage(props) {
|
|
|
279
279
|
});
|
|
280
280
|
if (res.ok) setAttributes((prev) => prev.filter((a) => a.id !== attrId));
|
|
281
281
|
} catch {
|
|
282
|
-
setError("Failed to delete attribute");
|
|
282
|
+
setError(t("cpq.specifications.attributes.error.delete", "Failed to delete attribute"));
|
|
283
283
|
}
|
|
284
284
|
};
|
|
285
285
|
const startEditAttr = (attr) => {
|
|
@@ -311,7 +311,7 @@ function SpecificationDetailPage(props) {
|
|
|
311
311
|
});
|
|
312
312
|
if (!res.ok) {
|
|
313
313
|
const d = await res.json();
|
|
314
|
-
setError(d.error ?? "Failed to save offering");
|
|
314
|
+
setError(d.error ?? t("cpq.specifications.offerings.error.save", "Failed to save offering"));
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
317
|
const saved = await res.json();
|
|
@@ -322,13 +322,13 @@ function SpecificationDetailPage(props) {
|
|
|
322
322
|
}
|
|
323
323
|
setEditingOffering(null);
|
|
324
324
|
} catch {
|
|
325
|
-
setError("Failed to save offering");
|
|
325
|
+
setError(t("cpq.specifications.offerings.error.save", "Failed to save offering"));
|
|
326
326
|
} finally {
|
|
327
327
|
setSaving(false);
|
|
328
328
|
}
|
|
329
329
|
};
|
|
330
330
|
const deleteOffering = async (offeringId) => {
|
|
331
|
-
if (!confirm("Delete this offering?")) return;
|
|
331
|
+
if (!confirm(t("cpq.specifications.offerings.confirmDelete", "Delete this offering?"))) return;
|
|
332
332
|
try {
|
|
333
333
|
const res = await fetch("/api/cpq/product-offerings", {
|
|
334
334
|
method: "DELETE",
|
|
@@ -337,7 +337,7 @@ function SpecificationDetailPage(props) {
|
|
|
337
337
|
});
|
|
338
338
|
if (res.ok) setOfferings((prev) => prev.filter((o) => o.id !== offeringId));
|
|
339
339
|
} catch {
|
|
340
|
-
setError("Failed to delete offering");
|
|
340
|
+
setError(t("cpq.specifications.offerings.error.delete", "Failed to delete offering"));
|
|
341
341
|
}
|
|
342
342
|
};
|
|
343
343
|
const startEditOffering = (offering) => {
|
|
@@ -423,20 +423,20 @@ function SpecificationDetailPage(props) {
|
|
|
423
423
|
});
|
|
424
424
|
if (!res.ok) {
|
|
425
425
|
const d = await res.json();
|
|
426
|
-
setError(d.error ??
|
|
426
|
+
setError(d.error ?? t("cpq.specifications.charges.error.saveFor", "Failed to save charge for {name}", { name: offering.name }));
|
|
427
427
|
break;
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
setChargeFormOpen(false);
|
|
431
431
|
await loadAllOfferingCharges();
|
|
432
432
|
} catch {
|
|
433
|
-
setError("Failed to save charges");
|
|
433
|
+
setError(t("cpq.specifications.charges.error.saveAll", "Failed to save charges"));
|
|
434
434
|
} finally {
|
|
435
435
|
setSaving(false);
|
|
436
436
|
}
|
|
437
437
|
};
|
|
438
438
|
const deleteCharge = async (offeringId, chargeId) => {
|
|
439
|
-
if (!confirm("Delete this charge?")) return;
|
|
439
|
+
if (!confirm(t("cpq.specifications.charges.confirmDelete", "Delete this charge?"))) return;
|
|
440
440
|
try {
|
|
441
441
|
const res = await fetch("/api/cpq/product-charges", {
|
|
442
442
|
method: "DELETE",
|
|
@@ -450,7 +450,7 @@ function SpecificationDetailPage(props) {
|
|
|
450
450
|
}));
|
|
451
451
|
}
|
|
452
452
|
} catch {
|
|
453
|
-
setError("Failed to delete charge");
|
|
453
|
+
setError(t("cpq.specifications.charges.error.delete", "Failed to delete charge"));
|
|
454
454
|
}
|
|
455
455
|
};
|
|
456
456
|
const selectedPricingTableRef = chargePricingTableId ? pricingTables.find((pt) => pt.id === chargePricingTableId) : null;
|
|
@@ -477,7 +477,7 @@ function SpecificationDetailPage(props) {
|
|
|
477
477
|
});
|
|
478
478
|
if (!res.ok) {
|
|
479
479
|
const d = await res.json();
|
|
480
|
-
setError(d.error ?? "Failed to save relationship");
|
|
480
|
+
setError(d.error ?? t("cpq.specifications.relationships.error.save", "Failed to save relationship"));
|
|
481
481
|
return;
|
|
482
482
|
}
|
|
483
483
|
const saved = await res.json();
|
|
@@ -488,13 +488,13 @@ function SpecificationDetailPage(props) {
|
|
|
488
488
|
}
|
|
489
489
|
setEditingRel(null);
|
|
490
490
|
} catch {
|
|
491
|
-
setError("Failed to save relationship");
|
|
491
|
+
setError(t("cpq.specifications.relationships.error.save", "Failed to save relationship"));
|
|
492
492
|
} finally {
|
|
493
493
|
setSaving(false);
|
|
494
494
|
}
|
|
495
495
|
};
|
|
496
496
|
const deleteRelationship = async (relId) => {
|
|
497
|
-
if (!confirm("Delete this relationship?")) return;
|
|
497
|
+
if (!confirm(t("cpq.specifications.relationships.confirmDelete", "Delete this relationship?"))) return;
|
|
498
498
|
try {
|
|
499
499
|
const res = await fetch("/api/cpq/product-relationships", {
|
|
500
500
|
method: "DELETE",
|
|
@@ -503,7 +503,7 @@ function SpecificationDetailPage(props) {
|
|
|
503
503
|
});
|
|
504
504
|
if (res.ok) setRelationships((prev) => prev.filter((r) => r.id !== relId));
|
|
505
505
|
} catch {
|
|
506
|
-
setError("Failed to delete relationship");
|
|
506
|
+
setError(t("cpq.specifications.relationships.error.delete", "Failed to delete relationship"));
|
|
507
507
|
}
|
|
508
508
|
};
|
|
509
509
|
const startEditRel = (rel) => {
|
|
@@ -513,7 +513,7 @@ function SpecificationDetailPage(props) {
|
|
|
513
513
|
};
|
|
514
514
|
const getSpecName = (id) => allSpecs.find((s) => s.id === id)?.name ?? id.slice(0, 8);
|
|
515
515
|
if (loading) {
|
|
516
|
-
return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("common.loading", "Loading...") });
|
|
516
|
+
return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: t("cpq.common.loading", "Loading...") });
|
|
517
517
|
}
|
|
518
518
|
const designTimeAttrs = attributes.filter((a) => a.resolutionTime === "design_time");
|
|
519
519
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
@@ -526,7 +526,7 @@ function SpecificationDetailPage(props) {
|
|
|
526
526
|
className: "text-sm text-muted-foreground hover:text-foreground",
|
|
527
527
|
children: [
|
|
528
528
|
"\u2190 ",
|
|
529
|
-
t("common.back", "Back")
|
|
529
|
+
t("cpq.common.back", "Back")
|
|
530
530
|
]
|
|
531
531
|
}
|
|
532
532
|
),
|
|
@@ -542,10 +542,10 @@ function SpecificationDetailPage(props) {
|
|
|
542
542
|
setFormSnapshot(form);
|
|
543
543
|
setGeneralMode("edit");
|
|
544
544
|
},
|
|
545
|
-
children: t("common.edit", "Edit")
|
|
545
|
+
children: t("cpq.common.edit", "Edit")
|
|
546
546
|
}
|
|
547
547
|
),
|
|
548
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", onClick: deleteSpec, children: t("common.delete", "Delete") })
|
|
548
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", onClick: deleteSpec, children: t("cpq.common.delete", "Delete") })
|
|
549
549
|
] })
|
|
550
550
|
] }),
|
|
551
551
|
error && /* @__PURE__ */ jsx(Alert, { variant: "destructive", children: error }),
|
|
@@ -564,11 +564,11 @@ function SpecificationDetailPage(props) {
|
|
|
564
564
|
},
|
|
565
565
|
className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${tab === t_ ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
|
|
566
566
|
children: [
|
|
567
|
-
t_ === "general" && "General",
|
|
568
|
-
t_ === "attributes" &&
|
|
569
|
-
t_ === "offerings" &&
|
|
570
|
-
t_ === "relationships" &&
|
|
571
|
-
t_ === "slots" &&
|
|
567
|
+
t_ === "general" && t("cpq.specifications.detail.tabs.general", "General"),
|
|
568
|
+
t_ === "attributes" && t("cpq.specifications.detail.tabs.attributes", "Attributes ({count})", { count: attributes.length }),
|
|
569
|
+
t_ === "offerings" && t("cpq.specifications.detail.tabs.offerings", "Offerings ({count})", { count: offerings.length }),
|
|
570
|
+
t_ === "relationships" && t("cpq.specifications.detail.tabs.relationships", "Relationships ({count})", { count: relationships.length }),
|
|
571
|
+
t_ === "slots" && t("cpq.specifications.detail.tabs.slots", "Bundle Slots ({count})", { count: bundleSlots.length })
|
|
572
572
|
]
|
|
573
573
|
},
|
|
574
574
|
t_
|
|
@@ -586,7 +586,7 @@ function SpecificationDetailPage(props) {
|
|
|
586
586
|
type: "text",
|
|
587
587
|
value: form.code,
|
|
588
588
|
onChange: (e) => setForm({ ...form, code: e.target.value }),
|
|
589
|
-
placeholder: "e.g. gix-access-port",
|
|
589
|
+
placeholder: t("cpq.specifications.detail.codePlaceholder", "e.g. gix-access-port"),
|
|
590
590
|
className: "w-full rounded-md border px-3 py-2 text-sm"
|
|
591
591
|
}
|
|
592
592
|
)
|
|
@@ -612,16 +612,16 @@ function SpecificationDetailPage(props) {
|
|
|
612
612
|
onChange: (e) => setForm({ ...form, lifecycleStatus: e.target.value }),
|
|
613
613
|
className: "w-full rounded-md border px-3 py-2 text-sm",
|
|
614
614
|
children: [
|
|
615
|
-
/* @__PURE__ */ jsx("option", { value: "draft", children: "Draft" }),
|
|
616
|
-
/* @__PURE__ */ jsx("option", { value: "active", children: "Active" }),
|
|
617
|
-
/* @__PURE__ */ jsx("option", { value: "deprecated", children: "Deprecated" })
|
|
615
|
+
/* @__PURE__ */ jsx("option", { value: "draft", children: t("cpq.specifications.lifecycle.draft", "Draft") }),
|
|
616
|
+
/* @__PURE__ */ jsx("option", { value: "active", children: t("cpq.specifications.lifecycle.active", "Active") }),
|
|
617
|
+
/* @__PURE__ */ jsx("option", { value: "deprecated", children: t("cpq.specifications.lifecycle.deprecated", "Deprecated") })
|
|
618
618
|
]
|
|
619
619
|
}
|
|
620
620
|
)
|
|
621
621
|
] }),
|
|
622
622
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
623
623
|
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.isActive", "Is Active?") }),
|
|
624
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center h-[38px]", children: isViewing ? /* @__PURE__ */ jsx(Tag, { variant: form.isActive ? "success" : "neutral", dot: true, children: form.isActive ? t("common.yes", "Yes") : t("common.no", "No") }) : /* @__PURE__ */ jsx(
|
|
624
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center h-[38px]", children: isViewing ? /* @__PURE__ */ jsx(Tag, { variant: form.isActive ? "success" : "neutral", dot: true, children: form.isActive ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No") }) : /* @__PURE__ */ jsx(
|
|
625
625
|
"input",
|
|
626
626
|
{
|
|
627
627
|
type: "checkbox",
|
|
@@ -632,8 +632,8 @@ function SpecificationDetailPage(props) {
|
|
|
632
632
|
) })
|
|
633
633
|
] }),
|
|
634
634
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
635
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Spec Type" }),
|
|
636
|
-
isViewing ? /* @__PURE__ */ jsx("p", { className: "text-sm py-2", children: form.specType === "bundle" ? "Bundle" : "Simple" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
635
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.detail.specType", "Spec Type") }),
|
|
636
|
+
isViewing ? /* @__PURE__ */ jsx("p", { className: "text-sm py-2", children: form.specType === "bundle" ? t("cpq.specifications.type.bundle", "Bundle") : t("cpq.specifications.type.simple", "Simple") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
637
637
|
/* @__PURE__ */ jsxs(
|
|
638
638
|
"select",
|
|
639
639
|
{
|
|
@@ -642,17 +642,17 @@ function SpecificationDetailPage(props) {
|
|
|
642
642
|
disabled: specTypeLocked,
|
|
643
643
|
className: "w-full rounded-md border px-3 py-2 text-sm disabled:opacity-50",
|
|
644
644
|
children: [
|
|
645
|
-
/* @__PURE__ */ jsx("option", { value: "simple", children: "Simple" }),
|
|
646
|
-
/* @__PURE__ */ jsx("option", { value: "bundle", children: "Bundle" })
|
|
645
|
+
/* @__PURE__ */ jsx("option", { value: "simple", children: t("cpq.specifications.type.simple", "Simple") }),
|
|
646
|
+
/* @__PURE__ */ jsx("option", { value: "bundle", children: t("cpq.specifications.type.bundle", "Bundle") })
|
|
647
647
|
]
|
|
648
648
|
}
|
|
649
649
|
),
|
|
650
|
-
specTypeLocked && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: "Cannot change after offerings or slots exist" })
|
|
650
|
+
specTypeLocked && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t("cpq.specifications.detail.specTypeLocked", "Cannot change after offerings or slots exist") })
|
|
651
651
|
] })
|
|
652
652
|
] }),
|
|
653
653
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
654
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Is Assetizable?" }),
|
|
655
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center h-[38px]", children: isViewing ? /* @__PURE__ */ jsx(Tag, { variant: form.isAssetizable ? "warning" : "neutral", dot: true, children: form.isAssetizable ? t("common.yes", "Yes") : t("common.no", "No") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
654
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.detail.isAssetizable", "Is Assetizable?") }),
|
|
655
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center h-[38px]", children: isViewing ? /* @__PURE__ */ jsx(Tag, { variant: form.isAssetizable ? "warning" : "neutral", dot: true, children: form.isAssetizable ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
656
656
|
/* @__PURE__ */ jsx(
|
|
657
657
|
"input",
|
|
658
658
|
{
|
|
@@ -662,7 +662,7 @@ function SpecificationDetailPage(props) {
|
|
|
662
662
|
className: "rounded border"
|
|
663
663
|
}
|
|
664
664
|
),
|
|
665
|
-
/* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: "Creates asset records during fulfilment" })
|
|
665
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: t("cpq.specifications.detail.isAssetizableHelp", "Creates asset records during fulfilment") })
|
|
666
666
|
] }) })
|
|
667
667
|
] })
|
|
668
668
|
] }),
|
|
@@ -678,10 +678,7 @@ function SpecificationDetailPage(props) {
|
|
|
678
678
|
}
|
|
679
679
|
)
|
|
680
680
|
] }),
|
|
681
|
-
!isNew && /* @__PURE__ */
|
|
682
|
-
"Version ",
|
|
683
|
-
form.version
|
|
684
|
-
] }),
|
|
681
|
+
!isNew && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: t("cpq.specifications.detail.versionLabel", "Version {version}", { version: form.version }) }),
|
|
685
682
|
!isViewing && /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
686
683
|
/* @__PURE__ */ jsx(
|
|
687
684
|
Button,
|
|
@@ -689,7 +686,7 @@ function SpecificationDetailPage(props) {
|
|
|
689
686
|
type: "button",
|
|
690
687
|
onClick: saveSpec,
|
|
691
688
|
disabled: saving || !form.code || !form.name,
|
|
692
|
-
children: saving ? t("common.saving", "Saving...") : t("common.save", "Save")
|
|
689
|
+
children: saving ? t("cpq.common.saving", "Saving...") : t("cpq.common.save", "Save")
|
|
693
690
|
}
|
|
694
691
|
),
|
|
695
692
|
/* @__PURE__ */ jsx(
|
|
@@ -706,7 +703,7 @@ function SpecificationDetailPage(props) {
|
|
|
706
703
|
setFormSnapshot(null);
|
|
707
704
|
setGeneralMode("view");
|
|
708
705
|
},
|
|
709
|
-
children: t("common.cancel", "Cancel")
|
|
706
|
+
children: t("cpq.common.cancel", "Cancel")
|
|
710
707
|
}
|
|
711
708
|
)
|
|
712
709
|
] })
|
|
@@ -722,36 +719,36 @@ function SpecificationDetailPage(props) {
|
|
|
722
719
|
}
|
|
723
720
|
) }),
|
|
724
721
|
editingAttr && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
|
|
725
|
-
/* @__PURE__ */ jsx("h3", { className: "font-medium text-sm", children: editingAttr.id ? "Edit Attribute" : "New Attribute" }),
|
|
722
|
+
/* @__PURE__ */ jsx("h3", { className: "font-medium text-sm", children: editingAttr.id ? t("cpq.specifications.attributes.form.editTitle", "Edit Attribute") : t("cpq.specifications.attributes.form.newTitle", "New Attribute") }),
|
|
726
723
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
|
|
727
724
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
728
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Code" }),
|
|
725
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.code", "Code") }),
|
|
729
726
|
/* @__PURE__ */ jsx("input", { type: "text", value: editingAttr.code, onChange: (e) => setEditingAttr({ ...editingAttr, code: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
730
727
|
] }),
|
|
731
728
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
732
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Name" }),
|
|
729
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.name", "Name") }),
|
|
733
730
|
/* @__PURE__ */ jsx("input", { type: "text", value: editingAttr.name, onChange: (e) => setEditingAttr({ ...editingAttr, name: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
734
731
|
] }),
|
|
735
732
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
736
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Type" }),
|
|
733
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.type", "Type") }),
|
|
737
734
|
/* @__PURE__ */ jsxs("select", { value: editingAttr.attributeType, onChange: (e) => setEditingAttr({ ...editingAttr, attributeType: e.target.value, defaultValue: null }), className: "w-full rounded-md border px-2 py-1.5 text-sm", children: [
|
|
738
|
-
/* @__PURE__ */ jsx("option", { value: "text", children: "Text" }),
|
|
739
|
-
/* @__PURE__ */ jsx("option", { value: "number", children: "Number" }),
|
|
740
|
-
/* @__PURE__ */ jsx("option", { value: "boolean", children: "Boolean" }),
|
|
741
|
-
/* @__PURE__ */ jsx("option", { value: "select", children: "Select" }),
|
|
742
|
-
/* @__PURE__ */ jsx("option", { value: "multi-select", children: "Multi-Select" }),
|
|
743
|
-
/* @__PURE__ */ jsx("option", { value: "reference", children: "Reference" })
|
|
735
|
+
/* @__PURE__ */ jsx("option", { value: "text", children: t("cpq.specifications.attributeType.text", "Text") }),
|
|
736
|
+
/* @__PURE__ */ jsx("option", { value: "number", children: t("cpq.specifications.attributeType.number", "Number") }),
|
|
737
|
+
/* @__PURE__ */ jsx("option", { value: "boolean", children: t("cpq.specifications.attributeType.boolean", "Boolean") }),
|
|
738
|
+
/* @__PURE__ */ jsx("option", { value: "select", children: t("cpq.specifications.attributeType.select", "Select") }),
|
|
739
|
+
/* @__PURE__ */ jsx("option", { value: "multi-select", children: t("cpq.specifications.attributeType.multiSelect", "Multi-Select") }),
|
|
740
|
+
/* @__PURE__ */ jsx("option", { value: "reference", children: t("cpq.specifications.attributeType.reference", "Reference") })
|
|
744
741
|
] })
|
|
745
742
|
] }),
|
|
746
743
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
747
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Resolution Time" }),
|
|
744
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.resolutionTime", "Resolution Time") }),
|
|
748
745
|
/* @__PURE__ */ jsxs("select", { value: editingAttr.resolutionTime, onChange: (e) => setEditingAttr({ ...editingAttr, resolutionTime: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm", children: [
|
|
749
|
-
/* @__PURE__ */ jsx("option", { value: "run_time", children: "Run-time (quoting)" }),
|
|
750
|
-
/* @__PURE__ */ jsx("option", { value: "design_time", children: "Design-time (offering)" })
|
|
746
|
+
/* @__PURE__ */ jsx("option", { value: "run_time", children: t("cpq.specifications.resolutionTime.runTime", "Run-time (quoting)") }),
|
|
747
|
+
/* @__PURE__ */ jsx("option", { value: "design_time", children: t("cpq.specifications.resolutionTime.designTime", "Design-time (offering)") })
|
|
751
748
|
] })
|
|
752
749
|
] }),
|
|
753
750
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
754
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Sort Order" }),
|
|
751
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.sortOrder", "Sort Order") }),
|
|
755
752
|
/* @__PURE__ */ jsx(
|
|
756
753
|
NumberInput,
|
|
757
754
|
{
|
|
@@ -762,7 +759,7 @@ function SpecificationDetailPage(props) {
|
|
|
762
759
|
)
|
|
763
760
|
] }),
|
|
764
761
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
765
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Help Text" }),
|
|
762
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.helpText", "Help Text") }),
|
|
766
763
|
/* @__PURE__ */ jsx("input", { type: "text", value: editingAttr.helpText ?? "", onChange: (e) => setEditingAttr({ ...editingAttr, helpText: e.target.value || null }), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
767
764
|
] }),
|
|
768
765
|
editingAttr.attributeType !== "reference" && /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -820,23 +817,23 @@ function SpecificationDetailPage(props) {
|
|
|
820
817
|
)
|
|
821
818
|
] }),
|
|
822
819
|
editingAttr.attributeType === "reference" && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { children: [
|
|
823
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Reference Entity" }),
|
|
824
|
-
/* @__PURE__ */ jsx("input", { type: "text", value: editingAttr.referenceEntity ?? "", onChange: (e) => setEditingAttr({ ...editingAttr, referenceEntity: e.target.value || null }), placeholder: "e.g. CatalogProduct", className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
820
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.fields.referenceEntity", "Reference Entity") }),
|
|
821
|
+
/* @__PURE__ */ jsx("input", { type: "text", value: editingAttr.referenceEntity ?? "", onChange: (e) => setEditingAttr({ ...editingAttr, referenceEntity: e.target.value || null }), placeholder: t("cpq.specifications.attributes.fields.referenceEntityPlaceholder", "e.g. CatalogProduct"), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
825
822
|
] }) }),
|
|
826
823
|
(editingAttr.attributeType === "select" || editingAttr.attributeType === "multi-select") && /* @__PURE__ */ jsxs("div", { className: "col-span-3 space-y-2", children: [
|
|
827
824
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
828
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium", children: "Options" }),
|
|
825
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium", children: t("cpq.specifications.attributes.fields.options", "Options") }),
|
|
829
826
|
/* @__PURE__ */ jsx(
|
|
830
827
|
"button",
|
|
831
828
|
{
|
|
832
829
|
type: "button",
|
|
833
830
|
onClick: () => setEditingOptions([...editingOptions, { value: "", label: "" }]),
|
|
834
831
|
className: "text-xs text-primary hover:underline",
|
|
835
|
-
children: "+ Add option"
|
|
832
|
+
children: t("cpq.specifications.attributes.addOption", "+ Add option")
|
|
836
833
|
}
|
|
837
834
|
)
|
|
838
835
|
] }),
|
|
839
|
-
editingOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-2", children: 'No options defined. Click "Add option" to start.' }) : /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: editingOptions.map((opt, idx) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
836
|
+
editingOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-2", children: t("cpq.specifications.attributes.noOptions", 'No options defined. Click "Add option" to start.') }) : /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: editingOptions.map((opt, idx) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
840
837
|
/* @__PURE__ */ jsx(
|
|
841
838
|
"input",
|
|
842
839
|
{
|
|
@@ -847,7 +844,7 @@ function SpecificationDetailPage(props) {
|
|
|
847
844
|
next[idx] = { ...next[idx], value: e.target.value };
|
|
848
845
|
setEditingOptions(next);
|
|
849
846
|
},
|
|
850
|
-
placeholder: "Value (e.g. aws)",
|
|
847
|
+
placeholder: t("cpq.specifications.attributes.optionValuePlaceholder", "Value (e.g. aws)"),
|
|
851
848
|
className: "flex-1 rounded-md border px-2 py-1.5 text-sm"
|
|
852
849
|
}
|
|
853
850
|
),
|
|
@@ -861,7 +858,7 @@ function SpecificationDetailPage(props) {
|
|
|
861
858
|
next[idx] = { ...next[idx], label: e.target.value };
|
|
862
859
|
setEditingOptions(next);
|
|
863
860
|
},
|
|
864
|
-
placeholder: "Label (e.g. Amazon Web Services)",
|
|
861
|
+
placeholder: t("cpq.specifications.attributes.optionLabelPlaceholder", "Label (e.g. Amazon Web Services)"),
|
|
865
862
|
className: "flex-1 rounded-md border px-2 py-1.5 text-sm"
|
|
866
863
|
}
|
|
867
864
|
),
|
|
@@ -871,7 +868,7 @@ function SpecificationDetailPage(props) {
|
|
|
871
868
|
type: "button",
|
|
872
869
|
onClick: () => setEditingOptions(editingOptions.filter((_, i) => i !== idx)),
|
|
873
870
|
className: "text-destructive hover:text-destructive/80 text-sm px-1",
|
|
874
|
-
title: "Remove",
|
|
871
|
+
title: t("cpq.specifications.attributes.removeOption", "Remove"),
|
|
875
872
|
children: "\xD7"
|
|
876
873
|
}
|
|
877
874
|
)
|
|
@@ -888,14 +885,14 @@ function SpecificationDetailPage(props) {
|
|
|
888
885
|
className: "rounded border"
|
|
889
886
|
}
|
|
890
887
|
),
|
|
891
|
-
"Depends on another attribute"
|
|
888
|
+
t("cpq.specifications.attributes.dependsOn.enable", "Depends on another attribute")
|
|
892
889
|
] }),
|
|
893
890
|
dependsOnEnabled && (() => {
|
|
894
891
|
const parentAttr = attributes.find((a) => a.code === dependsOnAttrCode);
|
|
895
892
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
896
893
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
897
894
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
898
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Parent Attribute Code" }),
|
|
895
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.dependsOn.parentAttribute", "Parent Attribute Code") }),
|
|
899
896
|
/* @__PURE__ */ jsxs(
|
|
900
897
|
"select",
|
|
901
898
|
{
|
|
@@ -905,7 +902,7 @@ function SpecificationDetailPage(props) {
|
|
|
905
902
|
},
|
|
906
903
|
className: "w-full rounded-md border px-2 py-1.5 text-sm",
|
|
907
904
|
children: [
|
|
908
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Select attribute..." }),
|
|
905
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.attributes.dependsOn.selectAttribute", "Select attribute...") }),
|
|
909
906
|
attributes.filter((a) => editingAttr && a.code !== editingAttr.code).map((a) => /* @__PURE__ */ jsxs("option", { value: a.code, children: [
|
|
910
907
|
a.name,
|
|
911
908
|
" (",
|
|
@@ -917,7 +914,7 @@ function SpecificationDetailPage(props) {
|
|
|
917
914
|
)
|
|
918
915
|
] }),
|
|
919
916
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
920
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Effect" }),
|
|
917
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.attributes.dependsOn.effect", "Effect") }),
|
|
921
918
|
/* @__PURE__ */ jsxs(
|
|
922
919
|
"select",
|
|
923
920
|
{
|
|
@@ -929,16 +926,16 @@ function SpecificationDetailPage(props) {
|
|
|
929
926
|
},
|
|
930
927
|
className: "w-full rounded-md border px-2 py-1.5 text-sm",
|
|
931
928
|
children: [
|
|
932
|
-
/* @__PURE__ */ jsx("option", { value: "filter_options", children: "Filter options" }),
|
|
933
|
-
/* @__PURE__ */ jsx("option", { value: "set_value", children: "Set value" }),
|
|
934
|
-
/* @__PURE__ */ jsx("option", { value: "toggle_required", children: "Toggle required" })
|
|
929
|
+
/* @__PURE__ */ jsx("option", { value: "filter_options", children: t("cpq.specifications.attributes.dependsOn.effects.filterOptions", "Filter options") }),
|
|
930
|
+
/* @__PURE__ */ jsx("option", { value: "set_value", children: t("cpq.specifications.attributes.dependsOn.effects.setValue", "Set value") }),
|
|
931
|
+
/* @__PURE__ */ jsx("option", { value: "toggle_required", children: t("cpq.specifications.attributes.dependsOn.effects.toggleRequired", "Toggle required") })
|
|
935
932
|
]
|
|
936
933
|
}
|
|
937
934
|
)
|
|
938
935
|
] })
|
|
939
936
|
] }),
|
|
940
937
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
941
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-2", children: "Rules" }),
|
|
938
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-2", children: t("cpq.specifications.attributes.dependsOn.rules", "Rules") }),
|
|
942
939
|
/* @__PURE__ */ jsx(
|
|
943
940
|
DependsOnRulesEditor,
|
|
944
941
|
{
|
|
@@ -956,39 +953,39 @@ function SpecificationDetailPage(props) {
|
|
|
956
953
|
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-4", children: [
|
|
957
954
|
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1.5 text-sm", children: [
|
|
958
955
|
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: editingAttr.isRequired, onChange: (e) => setEditingAttr({ ...editingAttr, isRequired: e.target.checked }), className: "rounded border" }),
|
|
959
|
-
"Required"
|
|
956
|
+
t("cpq.specifications.attributes.fields.isRequired", "Required")
|
|
960
957
|
] }),
|
|
961
958
|
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1.5 text-sm", children: [
|
|
962
959
|
/* @__PURE__ */ jsx("input", { type: "checkbox", checked: editingAttr.isActive, onChange: (e) => setEditingAttr({ ...editingAttr, isActive: e.target.checked }), className: "rounded border" }),
|
|
963
|
-
"Active"
|
|
960
|
+
t("cpq.common.active", "Active")
|
|
964
961
|
] })
|
|
965
962
|
] })
|
|
966
963
|
] }),
|
|
967
964
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
968
|
-
/* @__PURE__ */ jsx("button", { onClick: saveAttribute, disabled: saving, className: "inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: saving ? "Saving..." : "Save" }),
|
|
969
|
-
/* @__PURE__ */ jsx("button", { onClick: () => setEditingAttr(null), className: "inline-flex items-center rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted", children: "Cancel" })
|
|
965
|
+
/* @__PURE__ */ jsx("button", { onClick: saveAttribute, disabled: saving, className: "inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: saving ? t("cpq.common.saving", "Saving...") : t("cpq.common.save", "Save") }),
|
|
966
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setEditingAttr(null), className: "inline-flex items-center rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted", children: t("cpq.common.cancel", "Cancel") })
|
|
970
967
|
] })
|
|
971
968
|
] }),
|
|
972
|
-
attributes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "No attributes defined. Add design-time and run-time attributes for this specification." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
969
|
+
attributes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.specifications.attributes.empty", "No attributes defined. Add design-time and run-time attributes for this specification.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
973
970
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
|
|
974
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Code" }),
|
|
975
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Name" }),
|
|
976
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Type" }),
|
|
977
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Resolution" }),
|
|
978
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Options" }),
|
|
979
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Required" }),
|
|
980
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: "Actions" })
|
|
971
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.attributes.columns.code", "Code") }),
|
|
972
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.attributes.columns.name", "Name") }),
|
|
973
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.attributes.columns.type", "Type") }),
|
|
974
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.attributes.columns.resolution", "Resolution") }),
|
|
975
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.attributes.columns.options", "Options") }),
|
|
976
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.attributes.columns.required", "Required") }),
|
|
977
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: t("cpq.specifications.attributes.columns.actions", "Actions") })
|
|
981
978
|
] }) }),
|
|
982
979
|
/* @__PURE__ */ jsx("tbody", { children: attributes.map((attr) => /* @__PURE__ */ jsxs("tr", { className: "border-b", children: [
|
|
983
980
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 font-mono text-xs", children: attr.code }),
|
|
984
981
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: attr.name }),
|
|
985
982
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: attr.attributeType }),
|
|
986
|
-
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(Tag, { variant: attr.resolutionTime === "design_time" ? "info" : "neutral", className: "px-2 text-xs", children: attr.resolutionTime === "design_time" ? "design" : "run" }) }),
|
|
987
|
-
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-muted-foreground", children: attr.options ?
|
|
988
|
-
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: attr.isRequired ? "Yes" : "No" }),
|
|
983
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(Tag, { variant: attr.resolutionTime === "design_time" ? "info" : "neutral", className: "px-2 text-xs", children: attr.resolutionTime === "design_time" ? t("cpq.specifications.attributes.resolutionShort.design", "design") : t("cpq.specifications.attributes.resolutionShort.run", "run") }) }),
|
|
984
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-muted-foreground", children: attr.options ? t("cpq.specifications.attributes.optionsCount", "{count} options", { count: attr.options.length }) : "\u2014" }),
|
|
985
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: attr.isRequired ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No") }),
|
|
989
986
|
/* @__PURE__ */ jsxs("td", { className: "px-4 py-3 text-right", children: [
|
|
990
|
-
/* @__PURE__ */ jsx("button", { onClick: () => startEditAttr(attr), className: "text-xs text-primary hover:underline mr-2", children: "Edit" }),
|
|
991
|
-
/* @__PURE__ */ jsx("button", { onClick: () => deleteAttribute(attr.id), className: "text-xs text-destructive hover:underline", children: "Delete" })
|
|
987
|
+
/* @__PURE__ */ jsx("button", { onClick: () => startEditAttr(attr), className: "text-xs text-primary hover:underline mr-2", children: t("cpq.common.edit", "Edit") }),
|
|
988
|
+
/* @__PURE__ */ jsx("button", { onClick: () => deleteAttribute(attr.id), className: "text-xs text-destructive hover:underline", children: t("cpq.common.delete", "Delete") })
|
|
992
989
|
] })
|
|
993
990
|
] }, attr.id)) })
|
|
994
991
|
] }) })
|
|
@@ -1003,35 +1000,35 @@ function SpecificationDetailPage(props) {
|
|
|
1003
1000
|
}
|
|
1004
1001
|
) }),
|
|
1005
1002
|
editingOffering && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
|
|
1006
|
-
/* @__PURE__ */ jsx("h3", { className: "font-medium text-sm", children: editingOffering.id ? "Edit Offering" : "New Offering" }),
|
|
1003
|
+
/* @__PURE__ */ jsx("h3", { className: "font-medium text-sm", children: editingOffering.id ? t("cpq.specifications.offerings.form.editTitle", "Edit Offering") : t("cpq.specifications.offerings.form.newTitle", "New Offering") }),
|
|
1007
1004
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-4 gap-3", children: [
|
|
1008
1005
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1009
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Code" }),
|
|
1010
|
-
/* @__PURE__ */ jsx("input", { type: "text", value: editingOffering.code, onChange: (e) => setEditingOffering({ ...editingOffering, code: e.target.value }), placeholder: "e.g. gix-cloud-connect-aws", className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
1006
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.offerings.fields.code", "Code") }),
|
|
1007
|
+
/* @__PURE__ */ jsx("input", { type: "text", value: editingOffering.code, onChange: (e) => setEditingOffering({ ...editingOffering, code: e.target.value }), placeholder: t("cpq.specifications.offerings.fields.codePlaceholder", "e.g. gix-cloud-connect-aws"), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
1011
1008
|
] }),
|
|
1012
1009
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1013
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Name" }),
|
|
1010
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.offerings.fields.name", "Name") }),
|
|
1014
1011
|
/* @__PURE__ */ jsx("input", { type: "text", value: editingOffering.name, onChange: (e) => setEditingOffering({ ...editingOffering, name: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
1015
1012
|
] }),
|
|
1016
1013
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1017
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Lifecycle Status" }),
|
|
1014
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.offerings.fields.lifecycleStatus", "Lifecycle Status") }),
|
|
1018
1015
|
/* @__PURE__ */ jsxs("select", { value: editingOffering.lifecycleStatus, onChange: (e) => setEditingOffering({ ...editingOffering, lifecycleStatus: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm", children: [
|
|
1019
|
-
/* @__PURE__ */ jsx("option", { value: "draft", children: "Draft" }),
|
|
1020
|
-
/* @__PURE__ */ jsx("option", { value: "active", children: "Active" }),
|
|
1021
|
-
/* @__PURE__ */ jsx("option", { value: "deprecated", children: "Deprecated" })
|
|
1016
|
+
/* @__PURE__ */ jsx("option", { value: "draft", children: t("cpq.offerings.lifecycle.draft", "Draft") }),
|
|
1017
|
+
/* @__PURE__ */ jsx("option", { value: "active", children: t("cpq.offerings.lifecycle.active", "Active") }),
|
|
1018
|
+
/* @__PURE__ */ jsx("option", { value: "deprecated", children: t("cpq.offerings.lifecycle.deprecated", "Deprecated") })
|
|
1022
1019
|
] })
|
|
1023
1020
|
] }),
|
|
1024
1021
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1025
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Is Active?" }),
|
|
1022
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.offerings.fields.isActive", "Is Active?") }),
|
|
1026
1023
|
/* @__PURE__ */ jsx("div", { className: "flex items-center h-[30px]", children: /* @__PURE__ */ jsx("input", { type: "checkbox", checked: editingOffering.isActive, onChange: (e) => setEditingOffering({ ...editingOffering, isActive: e.target.checked }), className: "rounded border" }) })
|
|
1027
1024
|
] })
|
|
1028
1025
|
] }),
|
|
1029
1026
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1030
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Description" }),
|
|
1027
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.offerings.fields.description", "Description") }),
|
|
1031
1028
|
/* @__PURE__ */ jsx("input", { type: "text", value: editingOffering.description ?? "", onChange: (e) => setEditingOffering({ ...editingOffering, description: e.target.value || null }), className: "w-full rounded-md border px-2 py-1.5 text-sm" })
|
|
1032
1029
|
] }),
|
|
1033
1030
|
designTimeAttrs.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1034
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium", children: "Design-Time Values" }),
|
|
1031
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium", children: t("cpq.specifications.offerings.fields.designTimeValues", "Design-Time Values") }),
|
|
1035
1032
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3", children: designTimeAttrs.map((attr) => /* @__PURE__ */ jsxs("div", { children: [
|
|
1036
1033
|
/* @__PURE__ */ jsxs("label", { className: "block text-xs font-medium mb-1", children: [
|
|
1037
1034
|
attr.name,
|
|
@@ -1044,11 +1041,7 @@ function SpecificationDetailPage(props) {
|
|
|
1044
1041
|
onChange: (e) => setEditingDtValues({ ...editingDtValues, [attr.code]: e.target.value || null }),
|
|
1045
1042
|
className: "w-full rounded-md border px-2 py-1.5 text-sm",
|
|
1046
1043
|
children: [
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
"Select ",
|
|
1049
|
-
attr.name,
|
|
1050
|
-
"..."
|
|
1051
|
-
] }),
|
|
1044
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.offerings.fields.selectAttribute", "Select {name}...", { name: attr.name }) }),
|
|
1052
1045
|
attr.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1053
1046
|
]
|
|
1054
1047
|
}
|
|
@@ -1081,19 +1074,19 @@ function SpecificationDetailPage(props) {
|
|
|
1081
1074
|
),
|
|
1082
1075
|
attr.helpText && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: attr.helpText })
|
|
1083
1076
|
] }, attr.code)) })
|
|
1084
|
-
] }) : /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-1", children: "No design-time attributes defined on this specification. All attributes are run-time." }),
|
|
1077
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground py-1", children: t("cpq.specifications.offerings.noDesignTimeAttributes", "No design-time attributes defined on this specification. All attributes are run-time.") }),
|
|
1085
1078
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
1086
|
-
/* @__PURE__ */ jsx("button", { onClick: saveOffering, disabled: saving, className: "inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: saving ? "Saving..." : "Save" }),
|
|
1087
|
-
/* @__PURE__ */ jsx("button", { onClick: () => setEditingOffering(null), className: "inline-flex items-center rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted", children: "Cancel" })
|
|
1079
|
+
/* @__PURE__ */ jsx("button", { onClick: saveOffering, disabled: saving, className: "inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: saving ? t("cpq.common.saving", "Saving...") : t("cpq.common.save", "Save") }),
|
|
1080
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setEditingOffering(null), className: "inline-flex items-center rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted", children: t("cpq.common.cancel", "Cancel") })
|
|
1088
1081
|
] })
|
|
1089
1082
|
] }),
|
|
1090
|
-
offerings.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "No offerings created. An offering is a sellable instance of this specification with fixed design-time values and attached charges." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
1083
|
+
offerings.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.specifications.offerings.empty", "No offerings created. An offering is a sellable instance of this specification with fixed design-time values and attached charges.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
1091
1084
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
|
|
1092
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: "Code" }),
|
|
1093
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: "Name" }),
|
|
1094
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: "Status" }),
|
|
1095
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: "Design-Time Values" }),
|
|
1096
|
-
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-right font-medium", children: "Actions" })
|
|
1085
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: t("cpq.specifications.offerings.columns.code", "Code") }),
|
|
1086
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: t("cpq.specifications.offerings.columns.name", "Name") }),
|
|
1087
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: t("cpq.specifications.offerings.columns.status", "Status") }),
|
|
1088
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-left font-medium", children: t("cpq.specifications.offerings.columns.designTimeValues", "Design-Time Values") }),
|
|
1089
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-3 text-right font-medium", children: t("cpq.specifications.offerings.columns.actions", "Actions") })
|
|
1097
1090
|
] }) }),
|
|
1098
1091
|
/* @__PURE__ */ jsx("tbody", { children: offerings.map((o) => /* @__PURE__ */ jsxs(
|
|
1099
1092
|
"tr",
|
|
@@ -1109,11 +1102,11 @@ function SpecificationDetailPage(props) {
|
|
|
1109
1102
|
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
1110
1103
|
e.stopPropagation();
|
|
1111
1104
|
startEditOffering(o);
|
|
1112
|
-
}, className: "text-xs text-primary hover:underline mr-2", children: "Edit" }),
|
|
1105
|
+
}, className: "text-xs text-primary hover:underline mr-2", children: t("cpq.common.edit", "Edit") }),
|
|
1113
1106
|
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
1114
1107
|
e.stopPropagation();
|
|
1115
1108
|
deleteOffering(o.id);
|
|
1116
|
-
}, className: "text-xs text-destructive hover:underline", children: "Delete" })
|
|
1109
|
+
}, className: "text-xs text-destructive hover:underline", children: t("cpq.common.delete", "Delete") })
|
|
1117
1110
|
] })
|
|
1118
1111
|
]
|
|
1119
1112
|
},
|
|
@@ -1131,20 +1124,20 @@ function SpecificationDetailPage(props) {
|
|
|
1131
1124
|
}
|
|
1132
1125
|
) }),
|
|
1133
1126
|
editingRel && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
|
|
1134
|
-
/* @__PURE__ */ jsx("h3", { className: "font-medium text-sm", children: editingRel.id ? "Edit Relationship" : "New Relationship" }),
|
|
1127
|
+
/* @__PURE__ */ jsx("h3", { className: "font-medium text-sm", children: editingRel.id ? t("cpq.specifications.relationships.form.editTitle", "Edit Relationship") : t("cpq.specifications.relationships.form.newTitle", "New Relationship") }),
|
|
1135
1128
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
|
|
1136
1129
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1137
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Type" }),
|
|
1130
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.relationships.fields.type", "Type") }),
|
|
1138
1131
|
/* @__PURE__ */ jsxs("select", { value: editingRel.relationshipType, onChange: (e) => setEditingRel({ ...editingRel, relationshipType: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm", children: [
|
|
1139
|
-
/* @__PURE__ */ jsx("option", { value: "parent_child", children: "Parent / Child" }),
|
|
1140
|
-
/* @__PURE__ */ jsx("option", { value: "requires", children: "Requires" }),
|
|
1141
|
-
/* @__PURE__ */ jsx("option", { value: "excludes", children: "Excludes" })
|
|
1132
|
+
/* @__PURE__ */ jsx("option", { value: "parent_child", children: t("cpq.specifications.relationships.type.parentChild", "Parent / Child") }),
|
|
1133
|
+
/* @__PURE__ */ jsx("option", { value: "requires", children: t("cpq.specifications.relationships.type.requires", "Requires") }),
|
|
1134
|
+
/* @__PURE__ */ jsx("option", { value: "excludes", children: t("cpq.specifications.relationships.type.excludes", "Excludes") })
|
|
1142
1135
|
] })
|
|
1143
1136
|
] }),
|
|
1144
1137
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1145
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Target Specification" }),
|
|
1138
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.relationships.fields.targetSpecification", "Target Specification") }),
|
|
1146
1139
|
/* @__PURE__ */ jsxs("select", { value: editingRel.targetSpecId, onChange: (e) => setEditingRel({ ...editingRel, targetSpecId: e.target.value }), className: "w-full rounded-md border px-2 py-1.5 text-sm", children: [
|
|
1147
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Select..." }),
|
|
1140
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.relationships.fields.selectTarget", "Select...") }),
|
|
1148
1141
|
allSpecs.filter((s) => s.id !== specId).map((s) => /* @__PURE__ */ jsxs("option", { value: s.id, children: [
|
|
1149
1142
|
s.name,
|
|
1150
1143
|
" (",
|
|
@@ -1155,7 +1148,7 @@ function SpecificationDetailPage(props) {
|
|
|
1155
1148
|
] }),
|
|
1156
1149
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1157
1150
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1158
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Min" }),
|
|
1151
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.relationships.fields.min", "Min") }),
|
|
1159
1152
|
/* @__PURE__ */ jsx(
|
|
1160
1153
|
NumberInput,
|
|
1161
1154
|
{
|
|
@@ -1167,7 +1160,7 @@ function SpecificationDetailPage(props) {
|
|
|
1167
1160
|
)
|
|
1168
1161
|
] }),
|
|
1169
1162
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1170
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Max" }),
|
|
1163
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.relationships.fields.max", "Max") }),
|
|
1171
1164
|
/* @__PURE__ */ jsx(
|
|
1172
1165
|
NumberInput,
|
|
1173
1166
|
{
|
|
@@ -1182,22 +1175,22 @@ function SpecificationDetailPage(props) {
|
|
|
1182
1175
|
] })
|
|
1183
1176
|
] }),
|
|
1184
1177
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1185
|
-
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: "Condition (JSON)" }),
|
|
1186
|
-
/* @__PURE__ */ jsx("textarea", { value: conditionJson, onChange: (e) => setConditionJson(e.target.value), rows: 3, className: "w-full rounded-md border px-2 py-1.5 text-sm font-mono", placeholder: 'null or {"attribute":"direction","operator":"in","value":["inbound"]}' })
|
|
1178
|
+
/* @__PURE__ */ jsx("label", { className: "block text-xs font-medium mb-1", children: t("cpq.specifications.relationships.fields.condition", "Condition (JSON)") }),
|
|
1179
|
+
/* @__PURE__ */ jsx("textarea", { value: conditionJson, onChange: (e) => setConditionJson(e.target.value), rows: 3, className: "w-full rounded-md border px-2 py-1.5 text-sm font-mono", placeholder: t("cpq.specifications.relationships.fields.conditionPlaceholder", 'null or {"attribute":"direction","operator":"in","value":["inbound"]}') })
|
|
1187
1180
|
] }),
|
|
1188
1181
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
1189
|
-
/* @__PURE__ */ jsx("button", { onClick: saveRelationship, disabled: saving, className: "inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: saving ? "Saving..." : "Save" }),
|
|
1190
|
-
/* @__PURE__ */ jsx("button", { onClick: () => setEditingRel(null), className: "inline-flex items-center rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted", children: "Cancel" })
|
|
1182
|
+
/* @__PURE__ */ jsx("button", { onClick: saveRelationship, disabled: saving, className: "inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50", children: saving ? t("cpq.common.saving", "Saving...") : t("cpq.common.save", "Save") }),
|
|
1183
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setEditingRel(null), className: "inline-flex items-center rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-muted", children: t("cpq.common.cancel", "Cancel") })
|
|
1191
1184
|
] })
|
|
1192
1185
|
] }),
|
|
1193
|
-
relationships.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "No relationships defined. Declare parent/child, requires, or excludes relationships with other specifications." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
1186
|
+
relationships.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.specifications.relationships.empty", "No relationships defined. Declare parent/child, requires, or excludes relationships with other specifications.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
1194
1187
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
|
|
1195
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Type" }),
|
|
1196
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Source" }),
|
|
1197
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Target" }),
|
|
1198
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Cardinality" }),
|
|
1199
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Condition" }),
|
|
1200
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: "Actions" })
|
|
1188
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.relationships.columns.type", "Type") }),
|
|
1189
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.relationships.columns.source", "Source") }),
|
|
1190
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.relationships.columns.target", "Target") }),
|
|
1191
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.relationships.columns.cardinality", "Cardinality") }),
|
|
1192
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.relationships.columns.condition", "Condition") }),
|
|
1193
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: t("cpq.specifications.relationships.columns.actions", "Actions") })
|
|
1201
1194
|
] }) }),
|
|
1202
1195
|
/* @__PURE__ */ jsx("tbody", { children: relationships.map((rel) => /* @__PURE__ */ jsxs("tr", { className: "border-b", children: [
|
|
1203
1196
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
@@ -1216,8 +1209,8 @@ function SpecificationDetailPage(props) {
|
|
|
1216
1209
|
] }),
|
|
1217
1210
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-muted-foreground text-xs", children: rel.condition ? JSON.stringify(rel.condition) : "\u2014" }),
|
|
1218
1211
|
/* @__PURE__ */ jsxs("td", { className: "px-4 py-3 text-right", children: [
|
|
1219
|
-
/* @__PURE__ */ jsx("button", { onClick: () => startEditRel(rel), className: "text-xs text-primary hover:underline mr-2", children: "Edit" }),
|
|
1220
|
-
/* @__PURE__ */ jsx("button", { onClick: () => deleteRelationship(rel.id), className: "text-xs text-destructive hover:underline", children: "Delete" })
|
|
1212
|
+
/* @__PURE__ */ jsx("button", { onClick: () => startEditRel(rel), className: "text-xs text-primary hover:underline mr-2", children: t("cpq.common.edit", "Edit") }),
|
|
1213
|
+
/* @__PURE__ */ jsx("button", { onClick: () => deleteRelationship(rel.id), className: "text-xs text-destructive hover:underline", children: t("cpq.common.delete", "Delete") })
|
|
1221
1214
|
] })
|
|
1222
1215
|
] }, rel.id)) })
|
|
1223
1216
|
] }) })
|
|
@@ -1228,40 +1221,40 @@ function SpecificationDetailPage(props) {
|
|
|
1228
1221
|
{
|
|
1229
1222
|
onClick: () => setEditingSlot({ ...EMPTY_SLOT }),
|
|
1230
1223
|
className: "inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
|
|
1231
|
-
children: "+ Add Slot"
|
|
1224
|
+
children: t("cpq.specifications.slots.add", "+ Add Slot")
|
|
1232
1225
|
}
|
|
1233
1226
|
) }),
|
|
1234
1227
|
editingSlot && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-6 space-y-4", children: [
|
|
1235
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: editingSlot.id ? "Edit Bundle Slot" : "New Bundle Slot" }),
|
|
1228
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: editingSlot.id ? t("cpq.specifications.slots.form.editTitle", "Edit Bundle Slot") : t("cpq.specifications.slots.form.newTitle", "New Bundle Slot") }),
|
|
1236
1229
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
1237
1230
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1238
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Name" }),
|
|
1231
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.name", "Name") }),
|
|
1239
1232
|
/* @__PURE__ */ jsx(
|
|
1240
1233
|
"input",
|
|
1241
1234
|
{
|
|
1242
1235
|
type: "text",
|
|
1243
1236
|
value: editingSlot.name,
|
|
1244
1237
|
onChange: (e) => setEditingSlot({ ...editingSlot, name: e.target.value }),
|
|
1245
|
-
placeholder: "e.g. Internet Component",
|
|
1238
|
+
placeholder: t("cpq.specifications.slots.fields.namePlaceholder", "e.g. Internet Component"),
|
|
1246
1239
|
className: "w-full rounded-md border px-3 py-2 text-sm"
|
|
1247
1240
|
}
|
|
1248
1241
|
)
|
|
1249
1242
|
] }),
|
|
1250
1243
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1251
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Component Group" }),
|
|
1244
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.componentGroup", "Component Group") }),
|
|
1252
1245
|
/* @__PURE__ */ jsx(
|
|
1253
1246
|
"input",
|
|
1254
1247
|
{
|
|
1255
1248
|
type: "text",
|
|
1256
1249
|
value: editingSlot.componentGroup,
|
|
1257
1250
|
onChange: (e) => setEditingSlot({ ...editingSlot, componentGroup: e.target.value }),
|
|
1258
|
-
placeholder: "e.g. internet, tv, phone",
|
|
1251
|
+
placeholder: t("cpq.specifications.slots.fields.componentGroupPlaceholder", "e.g. internet, tv, phone"),
|
|
1259
1252
|
className: "w-full rounded-md border px-3 py-2 text-sm"
|
|
1260
1253
|
}
|
|
1261
1254
|
)
|
|
1262
1255
|
] }),
|
|
1263
1256
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1264
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Target Specification" }),
|
|
1257
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.targetSpecification", "Target Specification") }),
|
|
1265
1258
|
/* @__PURE__ */ jsxs(
|
|
1266
1259
|
"select",
|
|
1267
1260
|
{
|
|
@@ -1269,7 +1262,7 @@ function SpecificationDetailPage(props) {
|
|
|
1269
1262
|
onChange: (e) => setEditingSlot({ ...editingSlot, targetSpecId: e.target.value }),
|
|
1270
1263
|
className: "w-full rounded-md border px-3 py-2 text-sm",
|
|
1271
1264
|
children: [
|
|
1272
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Select specification \u2014" }),
|
|
1265
|
+
/* @__PURE__ */ jsx("option", { value: "", children: t("cpq.specifications.slots.fields.selectSpecification", "\u2014 Select specification \u2014") }),
|
|
1273
1266
|
allSpecs.filter((s) => s.id !== specId).map((s) => /* @__PURE__ */ jsxs("option", { value: s.id, children: [
|
|
1274
1267
|
s.name,
|
|
1275
1268
|
" (",
|
|
@@ -1281,7 +1274,7 @@ function SpecificationDetailPage(props) {
|
|
|
1281
1274
|
)
|
|
1282
1275
|
] }),
|
|
1283
1276
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1284
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Sort Order" }),
|
|
1277
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.sortOrder", "Sort Order") }),
|
|
1285
1278
|
/* @__PURE__ */ jsx(
|
|
1286
1279
|
NumberInput,
|
|
1287
1280
|
{
|
|
@@ -1292,7 +1285,7 @@ function SpecificationDetailPage(props) {
|
|
|
1292
1285
|
)
|
|
1293
1286
|
] }),
|
|
1294
1287
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1295
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Cardinality Min" }),
|
|
1288
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.cardinalityMin", "Cardinality Min") }),
|
|
1296
1289
|
/* @__PURE__ */ jsx(
|
|
1297
1290
|
NumberInput,
|
|
1298
1291
|
{
|
|
@@ -1304,13 +1297,13 @@ function SpecificationDetailPage(props) {
|
|
|
1304
1297
|
)
|
|
1305
1298
|
] }),
|
|
1306
1299
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1307
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Cardinality Max" }),
|
|
1300
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.cardinalityMax", "Cardinality Max") }),
|
|
1308
1301
|
/* @__PURE__ */ jsx(
|
|
1309
1302
|
NumberInput,
|
|
1310
1303
|
{
|
|
1311
1304
|
integer: true,
|
|
1312
1305
|
min: 0,
|
|
1313
|
-
placeholder: "\u221E (unlimited)",
|
|
1306
|
+
placeholder: t("cpq.specifications.slots.fields.cardinalityMaxPlaceholder", "\u221E (unlimited)"),
|
|
1314
1307
|
value: editingSlot.cardinalityMax,
|
|
1315
1308
|
onChange: (n) => setEditingSlot({ ...editingSlot, cardinalityMax: n })
|
|
1316
1309
|
}
|
|
@@ -1318,7 +1311,7 @@ function SpecificationDetailPage(props) {
|
|
|
1318
1311
|
] })
|
|
1319
1312
|
] }),
|
|
1320
1313
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1321
|
-
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: "Description" }),
|
|
1314
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium mb-1", children: t("cpq.specifications.slots.fields.description", "Description") }),
|
|
1322
1315
|
/* @__PURE__ */ jsx(
|
|
1323
1316
|
"textarea",
|
|
1324
1317
|
{
|
|
@@ -1346,7 +1339,7 @@ function SpecificationDetailPage(props) {
|
|
|
1346
1339
|
});
|
|
1347
1340
|
if (!res.ok) {
|
|
1348
1341
|
const d = await res.json();
|
|
1349
|
-
setError(d.error ?? "Failed to save slot");
|
|
1342
|
+
setError(d.error ?? t("cpq.specifications.slots.error.save", "Failed to save slot"));
|
|
1350
1343
|
return;
|
|
1351
1344
|
}
|
|
1352
1345
|
setEditingSlot(null);
|
|
@@ -1356,14 +1349,14 @@ function SpecificationDetailPage(props) {
|
|
|
1356
1349
|
setBundleSlots(d.items ?? []);
|
|
1357
1350
|
}
|
|
1358
1351
|
} catch {
|
|
1359
|
-
setError("Failed to save slot");
|
|
1352
|
+
setError(t("cpq.specifications.slots.error.save", "Failed to save slot"));
|
|
1360
1353
|
} finally {
|
|
1361
1354
|
setSaving(false);
|
|
1362
1355
|
}
|
|
1363
1356
|
},
|
|
1364
1357
|
disabled: saving || !editingSlot.name || !editingSlot.componentGroup || !editingSlot.targetSpecId,
|
|
1365
1358
|
className: "inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
|
|
1366
|
-
children: saving ? "Saving..." : editingSlot.id ? "Update" : "Create"
|
|
1359
|
+
children: saving ? t("cpq.common.saving", "Saving...") : editingSlot.id ? t("cpq.specifications.slots.form.update", "Update") : t("cpq.specifications.slots.form.create", "Create")
|
|
1367
1360
|
}
|
|
1368
1361
|
),
|
|
1369
1362
|
/* @__PURE__ */ jsx(
|
|
@@ -1371,27 +1364,27 @@ function SpecificationDetailPage(props) {
|
|
|
1371
1364
|
{
|
|
1372
1365
|
onClick: () => setEditingSlot(null),
|
|
1373
1366
|
className: "inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted",
|
|
1374
|
-
children: "Cancel"
|
|
1367
|
+
children: t("cpq.common.cancel", "Cancel")
|
|
1375
1368
|
}
|
|
1376
1369
|
)
|
|
1377
1370
|
] })
|
|
1378
1371
|
] }),
|
|
1379
|
-
bundleSlots.length === 0 && !editingSlot ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "No bundle slots defined yet. Add slots to define what component specifications can fill this bundle." }) : bundleSlots.length > 0 && /* @__PURE__ */ jsx("div", { className: "rounded-lg border overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
1372
|
+
bundleSlots.length === 0 && !editingSlot ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.specifications.slots.empty", "No bundle slots defined yet. Add slots to define what component specifications can fill this bundle.") }) : bundleSlots.length > 0 && /* @__PURE__ */ jsx("div", { className: "rounded-lg border overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
1380
1373
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
|
|
1381
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Name" }),
|
|
1382
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Group" }),
|
|
1383
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Target Spec" }),
|
|
1384
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Cardinality" }),
|
|
1385
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: "Order" }),
|
|
1386
|
-
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: "Actions" })
|
|
1374
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.slots.columns.name", "Name") }),
|
|
1375
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.slots.columns.group", "Group") }),
|
|
1376
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.slots.columns.targetSpec", "Target Spec") }),
|
|
1377
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.slots.columns.cardinality", "Cardinality") }),
|
|
1378
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left font-medium", children: t("cpq.specifications.slots.columns.order", "Order") }),
|
|
1379
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right font-medium", children: t("cpq.specifications.slots.columns.actions", "Actions") })
|
|
1387
1380
|
] }) }),
|
|
1388
1381
|
/* @__PURE__ */ jsx("tbody", { children: bundleSlots.map((slot) => /* @__PURE__ */ jsxs("tr", { className: "border-b", children: [
|
|
1389
1382
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 font-medium", children: slot.name }),
|
|
1390
1383
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(Tag, { variant: "info", children: slot.componentGroup }) }),
|
|
1391
1384
|
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-sm", children: slot.targetSpec ? /* @__PURE__ */ jsxs("span", { children: [
|
|
1392
1385
|
slot.targetSpec.name,
|
|
1393
|
-
slot.targetSpec.specType === "bundle" && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "ml-1 px-1.5 text-[10px]", children: "bundle" })
|
|
1394
|
-
] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Unknown" }) }),
|
|
1386
|
+
slot.targetSpec.specType === "bundle" && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "ml-1 px-1.5 text-[10px]", children: t("cpq.specifications.slots.tag.bundle", "bundle") })
|
|
1387
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("cpq.specifications.slots.unknown", "Unknown") }) }),
|
|
1395
1388
|
/* @__PURE__ */ jsxs("td", { className: "px-4 py-3 text-muted-foreground", children: [
|
|
1396
1389
|
slot.cardinalityMin,
|
|
1397
1390
|
"..",
|
|
@@ -1413,14 +1406,14 @@ function SpecificationDetailPage(props) {
|
|
|
1413
1406
|
sortOrder: slot.sortOrder
|
|
1414
1407
|
}),
|
|
1415
1408
|
className: "text-xs text-primary hover:underline mr-2",
|
|
1416
|
-
children: "Edit"
|
|
1409
|
+
children: t("cpq.common.edit", "Edit")
|
|
1417
1410
|
}
|
|
1418
1411
|
),
|
|
1419
1412
|
/* @__PURE__ */ jsx(
|
|
1420
1413
|
"button",
|
|
1421
1414
|
{
|
|
1422
1415
|
onClick: async () => {
|
|
1423
|
-
if (!confirm("Delete this bundle slot?")) return;
|
|
1416
|
+
if (!confirm(t("cpq.specifications.slots.confirmDelete", "Delete this bundle slot?"))) return;
|
|
1424
1417
|
try {
|
|
1425
1418
|
const res = await fetch("/api/cpq/bundle-slots", {
|
|
1426
1419
|
method: "DELETE",
|
|
@@ -1431,14 +1424,14 @@ function SpecificationDetailPage(props) {
|
|
|
1431
1424
|
setBundleSlots((prev) => prev.filter((s) => s.id !== slot.id));
|
|
1432
1425
|
} else {
|
|
1433
1426
|
const d = await res.json();
|
|
1434
|
-
setError(d.error ?? "Failed to delete slot");
|
|
1427
|
+
setError(d.error ?? t("cpq.specifications.slots.error.delete", "Failed to delete slot"));
|
|
1435
1428
|
}
|
|
1436
1429
|
} catch {
|
|
1437
|
-
setError("Failed to delete slot");
|
|
1430
|
+
setError(t("cpq.specifications.slots.error.delete", "Failed to delete slot"));
|
|
1438
1431
|
}
|
|
1439
1432
|
},
|
|
1440
1433
|
className: "text-xs text-destructive hover:underline",
|
|
1441
|
-
children: "Delete"
|
|
1434
|
+
children: t("cpq.common.delete", "Delete")
|
|
1442
1435
|
}
|
|
1443
1436
|
)
|
|
1444
1437
|
] })
|