@dainamite/cpq 0.5.0 → 0.6.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 +48 -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/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +22 -8
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +89 -232
- package/dist/modules/cpq/backend/cpq/orders/[id]/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/quotes/[id]/_components/PriceAdjustmentDialog.js +205 -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 +144 -221
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
- 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/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 +154 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
- package/dist/modules/cpq/components/PriceWaterfall.js +66 -0
- package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js +117 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
- 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/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/package.json +1 -1
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -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/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +32 -10
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +60 -269
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
- 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 +174 -263
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +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 +201 -0
- package/src/modules/cpq/components/PriceWaterfall.tsx +94 -0
- package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
- package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
- 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/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
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
4
5
|
import { useRouter, useParams, useSearchParams } from "next/navigation";
|
|
5
6
|
import ArcQuoteConfigurator from "./_components/ArcQuoteConfigurator.js";
|
|
6
7
|
import { StatusPath } from "../../../../components/StatusPath.js";
|
|
7
|
-
import { ArrowRight, ExternalLink, Plus } from "lucide-react";
|
|
8
|
+
import { ArrowRight, ExternalLink, Plus, RotateCcw } from "lucide-react";
|
|
8
9
|
import { Alert, AlertTitle, AlertDescription } from "@open-mercato/ui/primitives/alert";
|
|
9
10
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
10
11
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
11
12
|
import { FormHeader } from "@open-mercato/ui/backend/forms";
|
|
12
13
|
import { NumberInput } from "../../../../components/NumberInput.js";
|
|
14
|
+
import { ChargeBreakdownTable } from "../../../../components/ChargeBreakdownTable.js";
|
|
15
|
+
import { PricingSummaryCard } from "../../../../components/PricingSummaryCard.js";
|
|
16
|
+
import { ArcLineDiff, LineActionBadge } from "../../../../components/ArcLineDiff.js";
|
|
17
|
+
import { ADJUST_PRICE_FEATURE } from "../../../../lib/priceAdjustmentView.js";
|
|
18
|
+
import { arcRecurringDelta } from "../../../../lib/quoteSummaryView.js";
|
|
19
|
+
import { usePriceAdjustments } from "./_components/usePriceAdjustments.js";
|
|
20
|
+
import { PriceAdjustmentsPanel } from "./_components/PriceAdjustmentsPanel.js";
|
|
21
|
+
import { ArcLineRemovalProvider, useArcLineRemoval, useArcLineRemovalUi } from "./_components/arcLineRemoval.js";
|
|
22
|
+
import { AdjustChargeAction, DiscountLineAction, ManualAdjustmentActions, PriceAdjustmentProvider, usePriceAdjustmentUi } from "./_components/priceAdjustmentContext.js";
|
|
23
|
+
import { useGrantedFeatures } from "../../../../components/useGrantedFeatures.js";
|
|
24
|
+
import { canRecalculateQuote, QUOTE_MANAGE_FEATURE } from "../../../../lib/quotePermissions.js";
|
|
13
25
|
import {
|
|
14
26
|
formatStatusLabel,
|
|
15
27
|
quoteCpqStatusMap
|
|
@@ -17,18 +29,6 @@ import {
|
|
|
17
29
|
function fmt(amount, currency) {
|
|
18
30
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);
|
|
19
31
|
}
|
|
20
|
-
const RULE_TYPE_LABELS = {
|
|
21
|
-
discount_percent: "% discount",
|
|
22
|
-
discount_absolute: "fixed discount",
|
|
23
|
-
surcharge_percent: "% surcharge",
|
|
24
|
-
surcharge_absolute: "fixed surcharge",
|
|
25
|
-
price_override: "override"
|
|
26
|
-
};
|
|
27
|
-
function fmtRuleValue(adj, currency) {
|
|
28
|
-
if (adj.ruleType === "discount_percent" || adj.ruleType === "surcharge_percent") return `${adj.value}%`;
|
|
29
|
-
if (currency) return fmt(adj.value, currency);
|
|
30
|
-
return String(adj.value);
|
|
31
|
-
}
|
|
32
32
|
async function apiJson(url, init) {
|
|
33
33
|
const res = await fetch(url, init);
|
|
34
34
|
const json = await res.json();
|
|
@@ -82,20 +82,22 @@ function MoreActionsDropdown({ items }) {
|
|
|
82
82
|
}
|
|
83
83
|
function buildMoreActions({
|
|
84
84
|
recalculate,
|
|
85
|
+
canRecalculate,
|
|
85
86
|
submitting,
|
|
86
87
|
deletable,
|
|
87
88
|
onDelete,
|
|
88
89
|
deleting
|
|
89
90
|
}) {
|
|
90
|
-
const items = [
|
|
91
|
-
|
|
91
|
+
const items = [];
|
|
92
|
+
if (canRecalculate) {
|
|
93
|
+
items.push({
|
|
92
94
|
id: "recalculate",
|
|
93
95
|
label: "Recalculate",
|
|
94
96
|
onSelect: recalculate,
|
|
95
97
|
loading: submitting,
|
|
96
98
|
disabled: submitting
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
+
});
|
|
100
|
+
}
|
|
99
101
|
if (deletable) {
|
|
100
102
|
items.push({
|
|
101
103
|
id: "delete",
|
|
@@ -113,7 +115,9 @@ function CpqQuoteDetailPage(props) {
|
|
|
113
115
|
const searchParams = useSearchParams();
|
|
114
116
|
const cpqConfigId = props.params?.id ?? urlParams?.id;
|
|
115
117
|
const isNew = cpqConfigId === "new";
|
|
118
|
+
const t = useT();
|
|
116
119
|
const [cpqQuote, setCpqQuote] = React.useState(null);
|
|
120
|
+
const grantedFeatures = useGrantedFeatures([QUOTE_MANAGE_FEATURE, ADJUST_PRICE_FEATURE]);
|
|
117
121
|
const [loading, setLoading] = React.useState(true);
|
|
118
122
|
const [error, setError] = React.useState(null);
|
|
119
123
|
const [submitting, setSubmitting] = React.useState(false);
|
|
@@ -156,6 +160,20 @@ function CpqQuoteDetailPage(props) {
|
|
|
156
160
|
if (!cpqConfigId || isNew) return;
|
|
157
161
|
void loadArcTargets();
|
|
158
162
|
}, [cpqConfigId, isNew, loadArcTargets]);
|
|
163
|
+
const reloadQuote = React.useCallback(async () => {
|
|
164
|
+
try {
|
|
165
|
+
setCpqQuote(await apiJson(`/api/cpq/quotes/${cpqConfigId}`));
|
|
166
|
+
} catch (err) {
|
|
167
|
+
setError(err.message);
|
|
168
|
+
}
|
|
169
|
+
}, [cpqConfigId]);
|
|
170
|
+
const priceAdjustments = usePriceAdjustments({
|
|
171
|
+
quote: isNew ? null : cpqQuote,
|
|
172
|
+
quoteType: arcQuoteType,
|
|
173
|
+
canAdjust: grantedFeatures.includes(QUOTE_MANAGE_FEATURE) && grantedFeatures.includes(ADJUST_PRICE_FEATURE),
|
|
174
|
+
onQuote: setCpqQuote,
|
|
175
|
+
reloadQuote
|
|
176
|
+
});
|
|
159
177
|
React.useEffect(() => {
|
|
160
178
|
if (isNew) {
|
|
161
179
|
const quoteId = searchParams.get("quoteId");
|
|
@@ -265,16 +283,13 @@ function CpqQuoteDetailPage(props) {
|
|
|
265
283
|
setSubmitting(false);
|
|
266
284
|
}
|
|
267
285
|
};
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
setError(err.message);
|
|
276
|
-
}
|
|
277
|
-
};
|
|
286
|
+
const lineRemoval = useArcLineRemoval({
|
|
287
|
+
quote: isNew ? null : cpqQuote,
|
|
288
|
+
quoteType: arcQuoteType,
|
|
289
|
+
onQuote: setCpqQuote,
|
|
290
|
+
onError: setError
|
|
291
|
+
});
|
|
292
|
+
const removeItem = lineRemoval.remove;
|
|
278
293
|
const recalculate = async () => {
|
|
279
294
|
if (!cpqQuote) return;
|
|
280
295
|
try {
|
|
@@ -350,7 +365,7 @@ function CpqQuoteDetailPage(props) {
|
|
|
350
365
|
};
|
|
351
366
|
const startAddOffering = () => {
|
|
352
367
|
resetConfigState();
|
|
353
|
-
setAddingTargetSubIds(arcTargets.map((
|
|
368
|
+
setAddingTargetSubIds(arcTargets.map((t2) => t2.subscriptionId));
|
|
354
369
|
loadOfferings();
|
|
355
370
|
setView("add-offering");
|
|
356
371
|
};
|
|
@@ -413,13 +428,16 @@ function CpqQuoteDetailPage(props) {
|
|
|
413
428
|
] });
|
|
414
429
|
}
|
|
415
430
|
const { pricingSummary: summary, currencyCode: currency } = cpqQuote;
|
|
431
|
+
const arcDelta = arcQuoteType === "amend" || arcQuoteType === "renew" ? arcRecurringDelta(cpqQuote.lines) : null;
|
|
416
432
|
const arcEditable = ["new", "incomplete", "ready"].includes(cpqQuote.cpqStatus);
|
|
417
|
-
const isMergeRenew = arcQuoteType === "renew" && arcTargets.length >= 2 && arcTargets.every((
|
|
433
|
+
const isMergeRenew = arcQuoteType === "renew" && arcTargets.length >= 2 && arcTargets.every((t2) => t2.mergeAction === "absorb");
|
|
418
434
|
const arcTargetCodeMap = /* @__PURE__ */ new Map();
|
|
419
|
-
for (const
|
|
420
|
-
if (
|
|
435
|
+
for (const t2 of arcTargets) {
|
|
436
|
+
if (t2.subscription?.code) arcTargetCodeMap.set(t2.subscriptionId, t2.subscription.code);
|
|
421
437
|
}
|
|
422
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
438
|
+
return /* @__PURE__ */ jsx(PriceAdjustmentProvider, { value: priceAdjustments.ui, children: /* @__PURE__ */ jsx(ArcLineRemovalProvider, { value: lineRemoval.ui, children: /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
439
|
+
priceAdjustments.elements,
|
|
440
|
+
lineRemoval.ConfirmDialogElement,
|
|
423
441
|
arcDrawerOpen && /* @__PURE__ */ jsx(
|
|
424
442
|
ArcQuoteConfigurator,
|
|
425
443
|
{
|
|
@@ -495,6 +513,7 @@ function CpqQuoteDetailPage(props) {
|
|
|
495
513
|
{
|
|
496
514
|
items: buildMoreActions({
|
|
497
515
|
recalculate,
|
|
516
|
+
canRecalculate: canRecalculateQuote(grantedFeatures, cpqQuote.cpqStatus),
|
|
498
517
|
submitting,
|
|
499
518
|
deletable: !["accepted", "rejected", "cancelled"].includes(cpqQuote.cpqStatus),
|
|
500
519
|
onDelete: () => setShowDeleteConfirm(true),
|
|
@@ -530,17 +549,17 @@ function CpqQuoteDetailPage(props) {
|
|
|
530
549
|
arcTargets.length === 1 ? "" : "s",
|
|
531
550
|
":",
|
|
532
551
|
" ",
|
|
533
|
-
arcTargets.map((
|
|
552
|
+
arcTargets.map((t2, i) => /* @__PURE__ */ jsxs("span", { children: [
|
|
534
553
|
i > 0 && ", ",
|
|
535
554
|
/* @__PURE__ */ jsx(
|
|
536
555
|
"button",
|
|
537
556
|
{
|
|
538
|
-
onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${
|
|
557
|
+
onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${t2.subscriptionId}`),
|
|
539
558
|
className: "text-brand-violet hover:underline",
|
|
540
|
-
children:
|
|
559
|
+
children: t2.subscription?.code ?? t2.subscriptionId.slice(0, 8)
|
|
541
560
|
}
|
|
542
561
|
)
|
|
543
|
-
] },
|
|
562
|
+
] }, t2.id)),
|
|
544
563
|
isMergeRenew && /* @__PURE__ */ jsx("span", { className: "ml-2 text-status-warning-text font-medium", children: "\u2022 Merging into a new contract" })
|
|
545
564
|
] }) }) }),
|
|
546
565
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3", children: [
|
|
@@ -566,32 +585,7 @@ function CpqQuoteDetailPage(props) {
|
|
|
566
585
|
] })
|
|
567
586
|
] }) }),
|
|
568
587
|
view === "summary" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
569
|
-
/* @__PURE__ */
|
|
570
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-3", children: "Pricing Summary" }),
|
|
571
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 sm:grid-cols-4 gap-4", children: [
|
|
572
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
573
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Non-Recurring (NRC)" }),
|
|
574
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(summary.nrcTotal, currency) })
|
|
575
|
-
] }),
|
|
576
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
577
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Monthly Recurring (MRC)" }),
|
|
578
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(summary.mrcTotal, currency) })
|
|
579
|
-
] }),
|
|
580
|
-
summary.usageTotalEstimated != null && /* @__PURE__ */ jsxs("div", { children: [
|
|
581
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Usage (est.)" }),
|
|
582
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(summary.usageTotalEstimated, currency) })
|
|
583
|
-
] }),
|
|
584
|
-
summary.usageCharges.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
585
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Usage Charges" }),
|
|
586
|
-
summary.usageCharges.map((uc, i) => /* @__PURE__ */ jsxs("p", { className: "text-xs mt-0.5", children: [
|
|
587
|
-
uc.chargeName,
|
|
588
|
-
": ",
|
|
589
|
-
fmt(uc.unitPrice, currency),
|
|
590
|
-
"/unit"
|
|
591
|
-
] }, i))
|
|
592
|
-
] })
|
|
593
|
-
] })
|
|
594
|
-
] }),
|
|
588
|
+
/* @__PURE__ */ jsx(PricingSummaryCard, { summary, currency, arcDelta }),
|
|
595
589
|
!cpqQuote.validationResult.valid && /* @__PURE__ */ jsxs(Alert, { variant: "warning", children: [
|
|
596
590
|
/* @__PURE__ */ jsx(AlertTitle, { children: "Validation Issues" }),
|
|
597
591
|
/* @__PURE__ */ jsx(AlertDescription, { children: /* @__PURE__ */ jsx("span", { className: "text-xs space-y-0.5 block", children: cpqQuote.validationResult.errors.map((e, i) => /* @__PURE__ */ jsxs("span", { className: "block", children: [
|
|
@@ -599,6 +593,13 @@ function CpqQuoteDetailPage(props) {
|
|
|
599
593
|
e.message
|
|
600
594
|
] }, i)) }) })
|
|
601
595
|
] }),
|
|
596
|
+
arcQuoteType === "amend" && cpqQuote.lines.some((l) => l.action !== "cancel" && l.configuration?._arcRepriced === true) && /* @__PURE__ */ jsxs(Alert, { status: "information", style: "lighter", children: [
|
|
597
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: t("cpq.quotes.arc.repricedTitle", "Some existing items use the current catalog price") }),
|
|
598
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: t(
|
|
599
|
+
"cpq.quotes.arc.repricedDescription",
|
|
600
|
+
"Reconfigured lines were re-priced at today's catalog price. Negotiated prices from the subscription are not carried over to reconfigured lines yet."
|
|
601
|
+
) })
|
|
602
|
+
] }),
|
|
602
603
|
/* @__PURE__ */ jsx(
|
|
603
604
|
QuoteLineTree,
|
|
604
605
|
{
|
|
@@ -615,6 +616,17 @@ function CpqQuoteDetailPage(props) {
|
|
|
615
616
|
pricingDetail?.lineIdx === lineIdx && pricingDetail?.chargeIdx === chargeIdx ? null : { lineIdx, chargeIdx }
|
|
616
617
|
)
|
|
617
618
|
}
|
|
619
|
+
),
|
|
620
|
+
/* @__PURE__ */ jsx(
|
|
621
|
+
PriceAdjustmentsPanel,
|
|
622
|
+
{
|
|
623
|
+
quoteId: cpqQuote.id,
|
|
624
|
+
items: priceAdjustments.items,
|
|
625
|
+
total: priceAdjustments.total,
|
|
626
|
+
lines: cpqQuote.lines,
|
|
627
|
+
currency,
|
|
628
|
+
errorsById: priceAdjustments.errorsById
|
|
629
|
+
}
|
|
618
630
|
)
|
|
619
631
|
] }),
|
|
620
632
|
view === "add-offering" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -640,10 +652,10 @@ function CpqQuoteDetailPage(props) {
|
|
|
640
652
|
attributes,
|
|
641
653
|
config,
|
|
642
654
|
quantity,
|
|
643
|
-
arcTargetOptions: arcTargets.length >= 2 && !addingToParentLineId ? arcTargets.map((
|
|
644
|
-
subscriptionId:
|
|
645
|
-
code:
|
|
646
|
-
name:
|
|
655
|
+
arcTargetOptions: arcTargets.length >= 2 && !addingToParentLineId ? arcTargets.map((t2) => ({
|
|
656
|
+
subscriptionId: t2.subscriptionId,
|
|
657
|
+
code: t2.subscription?.code ?? t2.subscriptionId.slice(0, 8),
|
|
658
|
+
name: t2.subscription?.name ?? null
|
|
647
659
|
})) : null,
|
|
648
660
|
arcSelectedTargetIds: addingTargetSubIds,
|
|
649
661
|
onArcTargetToggle: (subId) => {
|
|
@@ -685,7 +697,7 @@ function CpqQuoteDetailPage(props) {
|
|
|
685
697
|
submitting
|
|
686
698
|
}
|
|
687
699
|
)
|
|
688
|
-
] });
|
|
700
|
+
] }) }) });
|
|
689
701
|
}
|
|
690
702
|
function QuoteLineTree({ lines, currency, expandedLines, pricingDetail, targetCodes, onToggleLine, onEditLine, onRemoveLine, onAddComponent, onTogglePricingDetail }) {
|
|
691
703
|
const [bundleTrees, setBundleTrees] = React.useState(/* @__PURE__ */ new Map());
|
|
@@ -889,20 +901,48 @@ function BundleSlotPanel({ bundleTree, treeLoading, children, allLines, currency
|
|
|
889
901
|
] });
|
|
890
902
|
}
|
|
891
903
|
function QuoteLineRow({ line, lineIdx, currency, isExpanded, pricingDetail, isBundle, childCount, indent, targetCodes, onToggle, onEdit, onRemove, onAddComponent, onTogglePricingDetail }) {
|
|
904
|
+
const t = useT();
|
|
905
|
+
const adjustmentUi = usePriceAdjustmentUi();
|
|
906
|
+
const removalUi = useArcLineRemovalUi();
|
|
907
|
+
const isRemoved = removalUi?.isRemoved(line) ?? false;
|
|
892
908
|
const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : void 0;
|
|
909
|
+
const chargeActions = {
|
|
910
|
+
reasonLabels: adjustmentUi?.reasonLabels,
|
|
911
|
+
renderChargeActions: (charge) => /* @__PURE__ */ jsx(AdjustChargeAction, { line, charge }),
|
|
912
|
+
renderManualActions: (adjustmentId) => /* @__PURE__ */ jsx(ManualAdjustmentActions, { adjustmentId })
|
|
913
|
+
};
|
|
893
914
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
894
|
-
/* @__PURE__ */ jsxs("div", { className:
|
|
915
|
+
/* @__PURE__ */ jsxs("div", { className: `flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors ${isRemoved ? "bg-muted/20" : ""}`, style: paddingLeft ? { paddingLeft } : void 0, "data-testid": "cpq-quote-line", "data-removed": isRemoved ? "true" : void 0, children: [
|
|
895
916
|
/* @__PURE__ */ jsx("button", { onClick: onToggle, className: "flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx("svg", { className: `h-4 w-4 transition-transform ${isExpanded ? "rotate-90" : ""}`, fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) }),
|
|
896
917
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
897
918
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
898
919
|
indent > 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "\u2514" }),
|
|
899
920
|
isBundle && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "px-1.5 text-xs", children: "bundle" }),
|
|
900
|
-
/* @__PURE__ */ jsx("span", { className:
|
|
921
|
+
/* @__PURE__ */ jsx("span", { className: `font-medium text-sm ${isRemoved ? "text-muted-foreground line-through" : ""}`, children: line.offeringName }),
|
|
901
922
|
line.quantity > 1 && /* @__PURE__ */ jsxs("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs font-medium", children: [
|
|
902
923
|
"\xD7",
|
|
903
924
|
line.quantity
|
|
904
925
|
] }),
|
|
905
|
-
/* @__PURE__ */ jsx(
|
|
926
|
+
/* @__PURE__ */ jsx(
|
|
927
|
+
LineActionBadge,
|
|
928
|
+
{
|
|
929
|
+
action: line.action,
|
|
930
|
+
label: isRemoved ? t("cpq.quotes.arc.lineRemoved", "remove") : void 0,
|
|
931
|
+
title: isRemoved ? t("cpq.quotes.arc.lineRemovedHint", "This item is cancelled on the subscription when the order is activated") : void 0
|
|
932
|
+
}
|
|
933
|
+
),
|
|
934
|
+
!isRemoved && line.configuration?._arcPriceFrozen === true && /* @__PURE__ */ jsx(
|
|
935
|
+
Tag,
|
|
936
|
+
{
|
|
937
|
+
variant: "neutral",
|
|
938
|
+
className: "px-1.5 text-xs",
|
|
939
|
+
title: t(
|
|
940
|
+
"cpq.quotes.arc.priceKeptHint",
|
|
941
|
+
"Price kept from the subscription \u2014 it changes only if you reconfigure this line"
|
|
942
|
+
),
|
|
943
|
+
children: t("cpq.quotes.arc.priceKept", "price kept")
|
|
944
|
+
}
|
|
945
|
+
),
|
|
906
946
|
targetCodes.size >= 2 && line.targetSubscriptionId && targetCodes.get(line.targetSubscriptionId) && /* @__PURE__ */ jsxs(
|
|
907
947
|
Tag,
|
|
908
948
|
{
|
|
@@ -915,7 +955,7 @@ function QuoteLineRow({ line, lineIdx, currency, isExpanded, pricingDetail, isBu
|
|
|
915
955
|
]
|
|
916
956
|
}
|
|
917
957
|
),
|
|
918
|
-
line.isConfigured ? /* @__PURE__ */ jsx(Tag, { variant: "success", className: "px-1.5 text-xs", children: "configured" }) : /* @__PURE__ */ jsx(Tag, { variant: "warning", className: "px-1.5 text-xs", children: "incomplete" }),
|
|
958
|
+
isRemoved ? null : line.isConfigured ? /* @__PURE__ */ jsx(Tag, { variant: "success", className: "px-1.5 text-xs", children: "configured" }) : /* @__PURE__ */ jsx(Tag, { variant: "warning", className: "px-1.5 text-xs", children: "incomplete" }),
|
|
919
959
|
isBundle && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
920
960
|
childCount,
|
|
921
961
|
" component",
|
|
@@ -925,6 +965,10 @@ function QuoteLineRow({ line, lineIdx, currency, isExpanded, pricingDetail, isBu
|
|
|
925
965
|
line.validationErrors && line.validationErrors.length > 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-0.5", children: line.validationErrors[0].message })
|
|
926
966
|
] }),
|
|
927
967
|
/* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right text-xs space-y-0.5", children: [
|
|
968
|
+
isRemoved && line.arcSource && line.arcSource.mrcAmount > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
969
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "MRC " }),
|
|
970
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground line-through", children: fmt(line.arcSource.mrcAmount, currency) })
|
|
971
|
+
] }),
|
|
928
972
|
line.nrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
929
973
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "NRC " }),
|
|
930
974
|
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.nrcTotal, currency) })
|
|
@@ -934,65 +978,33 @@ function QuoteLineRow({ line, lineIdx, currency, isExpanded, pricingDetail, isBu
|
|
|
934
978
|
/* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.mrcTotal, currency) })
|
|
935
979
|
] })
|
|
936
980
|
] }),
|
|
937
|
-
/* @__PURE__ */
|
|
981
|
+
isRemoved ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 shrink-0", children: /* @__PURE__ */ jsxs(
|
|
982
|
+
Button,
|
|
983
|
+
{
|
|
984
|
+
type: "button",
|
|
985
|
+
variant: "ghost",
|
|
986
|
+
size: "sm",
|
|
987
|
+
onClick: () => removalUi?.restore(line.lineId),
|
|
988
|
+
title: t("cpq.quotes.arc.restoreHint", "Keep this item on the subscription"),
|
|
989
|
+
"data-testid": "cpq-line-restore",
|
|
990
|
+
children: [
|
|
991
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "h-4 w-4" }),
|
|
992
|
+
t("cpq.quotes.arc.restore", "Restore")
|
|
993
|
+
]
|
|
994
|
+
}
|
|
995
|
+
) }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
996
|
+
/* @__PURE__ */ jsx(DiscountLineAction, { line }),
|
|
938
997
|
onAddComponent && /* @__PURE__ */ jsx("button", { onClick: onAddComponent, className: "rounded p-1.5 text-muted-foreground hover:text-primary hover:bg-primary/5 transition-colors", title: "Add Component", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 4.5v15m7.5-7.5h-15" }) }) }),
|
|
939
998
|
/* @__PURE__ */ jsx("button", { onClick: onEdit, className: "rounded p-1.5 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors", title: "Edit", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" }) }) }),
|
|
940
999
|
/* @__PURE__ */ jsx("button", { onClick: onRemove, className: "rounded p-1.5 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors", title: "Remove", children: /* @__PURE__ */ jsx("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", strokeWidth: "1.5", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" }) }) })
|
|
941
1000
|
] })
|
|
942
1001
|
] }),
|
|
943
|
-
isExpanded && line.
|
|
944
|
-
isExpanded &&
|
|
945
|
-
isExpanded &&
|
|
1002
|
+
isExpanded && !isBundle && line.charges.length > 0 && /* @__PURE__ */ jsx(ChargeBreakdown, { line, lineIdx, currency, pricingDetail, onTogglePricingDetail, ...chargeActions }),
|
|
1003
|
+
isExpanded && isBundle && line.charges.length > 0 && /* @__PURE__ */ jsx(ChargeBreakdown, { line, lineIdx, currency, pricingDetail, onTogglePricingDetail, ...chargeActions }),
|
|
1004
|
+
isExpanded && line.arcSource && /* @__PURE__ */ jsx("div", { className: "border-t bg-muted/10 px-12 py-2", children: /* @__PURE__ */ jsx(ArcLineDiff, { line, currency }) }),
|
|
946
1005
|
isExpanded && line.charges.length === 0 && !line.arcSource && /* @__PURE__ */ jsx(LineSummaryFallback, { line, currency })
|
|
947
1006
|
] });
|
|
948
1007
|
}
|
|
949
|
-
function ArcLineDiff({ line, currency }) {
|
|
950
|
-
const src = line.arcSource;
|
|
951
|
-
if (!src) return null;
|
|
952
|
-
const isCancel = line.action === "cancel";
|
|
953
|
-
const beforeMrc = src.mrcAmount;
|
|
954
|
-
const beforeNrc = src.nrcAmount;
|
|
955
|
-
const beforeQty = src.quantity;
|
|
956
|
-
const afterMrc = isCancel ? 0 : line.mrcTotal;
|
|
957
|
-
const afterNrc = isCancel ? 0 : line.nrcTotal;
|
|
958
|
-
const afterQty = isCancel ? 0 : line.quantity;
|
|
959
|
-
const fmtVal = (v, asCurrency) => asCurrency ? fmt(v, currency) : String(v);
|
|
960
|
-
const rows = [
|
|
961
|
-
{ label: "Quantity", before: beforeQty, after: afterQty, asCurrency: false },
|
|
962
|
-
{ label: "MRC", before: beforeMrc, after: afterMrc, asCurrency: true },
|
|
963
|
-
{ label: "NRC", before: beforeNrc, after: afterNrc, asCurrency: true }
|
|
964
|
-
];
|
|
965
|
-
return /* @__PURE__ */ jsxs("div", { className: "border-t bg-muted/10 px-12 py-2 space-y-1.5", children: [
|
|
966
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: "Change Preview" }),
|
|
967
|
-
/* @__PURE__ */ jsx("div", { className: "rounded border bg-background overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
|
|
968
|
-
/* @__PURE__ */ jsx("thead", { className: "bg-muted/40", children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
|
|
969
|
-
/* @__PURE__ */ jsx("th", { className: "text-left px-3 py-1.5 font-medium" }),
|
|
970
|
-
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "Before" }),
|
|
971
|
-
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "After" }),
|
|
972
|
-
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "\u0394" })
|
|
973
|
-
] }) }),
|
|
974
|
-
/* @__PURE__ */ jsx("tbody", { children: rows.map((r) => {
|
|
975
|
-
const delta = r.after - r.before;
|
|
976
|
-
const showDelta = delta !== 0;
|
|
977
|
-
return /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/40", children: [
|
|
978
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-1 text-muted-foreground", children: r.label }),
|
|
979
|
-
/* @__PURE__ */ jsx("td", { className: "px-3 py-1 text-right font-mono", children: fmtVal(r.before, r.asCurrency) }),
|
|
980
|
-
/* @__PURE__ */ jsx("td", { className: `px-3 py-1 text-right font-mono ${isCancel ? "text-muted-foreground line-through" : ""}`, children: fmtVal(r.after, r.asCurrency) }),
|
|
981
|
-
/* @__PURE__ */ jsx("td", { className: `px-3 py-1 text-right font-mono font-medium ${showDelta ? delta > 0 ? "text-status-success-text" : "text-destructive" : "text-muted-foreground"}`, children: showDelta ? `${delta > 0 ? "+" : ""}${fmtVal(delta, r.asCurrency)}` : "\u2014" })
|
|
982
|
-
] }, r.label);
|
|
983
|
-
}) })
|
|
984
|
-
] }) }),
|
|
985
|
-
isCancel && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive italic", children: "Cancellation \u2014 item will be terminated on activation." })
|
|
986
|
-
] });
|
|
987
|
-
}
|
|
988
|
-
function ActionBadge({ action }) {
|
|
989
|
-
const variantByAction = {
|
|
990
|
-
add: "info",
|
|
991
|
-
modify: "warning",
|
|
992
|
-
cancel: "error"
|
|
993
|
-
};
|
|
994
|
-
return /* @__PURE__ */ jsx(Tag, { variant: variantByAction[action], className: "px-1.5 text-xs", children: action });
|
|
995
|
-
}
|
|
996
1008
|
function LineSummaryFallback({ line, currency }) {
|
|
997
1009
|
const hasTotals = line.nrcTotal > 0 || line.mrcTotal > 0;
|
|
998
1010
|
const mirroredFromItemId = line.configuration?._arcMirroredFromItemId ?? null;
|
|
@@ -1013,105 +1025,16 @@ function LineSummaryFallback({ line, currency }) {
|
|
|
1013
1025
|
] })
|
|
1014
1026
|
] });
|
|
1015
1027
|
}
|
|
1016
|
-
function ChargeBreakdown({ line, lineIdx,
|
|
1017
|
-
return /* @__PURE__ */ jsx("div", { className: "border-t bg-muted/10 px-12 py-2", children: /* @__PURE__ */
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
/* @__PURE__ */ jsx("tbody", { children: line.charges.map((c, i) => {
|
|
1027
|
-
const hasDetail = !!(c.breakdown?.tiers?.length || c.adjustments?.length);
|
|
1028
|
-
const isDetailOpen = pricingDetail?.lineIdx === lineIdx && pricingDetail?.chargeIdx === i;
|
|
1029
|
-
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
1030
|
-
/* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/50", children: [
|
|
1031
|
-
/* @__PURE__ */ jsx("td", { className: "py-1", children: c.chargeName }),
|
|
1032
|
-
/* @__PURE__ */ jsx("td", { className: "py-1 uppercase", children: c.chargeType }),
|
|
1033
|
-
/* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: fmt(c.unitPrice, currency) }),
|
|
1034
|
-
/* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.quantity ?? "\u2014" }),
|
|
1035
|
-
/* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : c.note ?? "\u2014" }),
|
|
1036
|
-
/* @__PURE__ */ jsx("td", { className: "py-1 text-center", children: hasDetail && /* @__PURE__ */ jsx(
|
|
1037
|
-
"button",
|
|
1038
|
-
{
|
|
1039
|
-
onClick: () => onTogglePricingDetail(lineIdx, i),
|
|
1040
|
-
className: "inline-flex items-center justify-center rounded p-0.5 hover:bg-accent text-muted-foreground hover:text-foreground transition-colors",
|
|
1041
|
-
title: "Show pricing breakdown",
|
|
1042
|
-
children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "w-3.5 h-3.5", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" }) })
|
|
1043
|
-
}
|
|
1044
|
-
) })
|
|
1045
|
-
] }),
|
|
1046
|
-
isDetailOpen && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: "pb-3 pt-1", children: /* @__PURE__ */ jsxs("div", { className: "rounded border bg-background p-3 space-y-3 text-xs", children: [
|
|
1047
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1048
|
-
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-sm", children: [
|
|
1049
|
-
"Pricing Details \u2014 ",
|
|
1050
|
-
c.chargeName
|
|
1051
|
-
] }),
|
|
1052
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: c.pricingMethod ?? "flat" })
|
|
1053
|
-
] }),
|
|
1054
|
-
c.breakdown?.tiers && c.breakdown.tiers.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
1055
|
-
/* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Tier Breakdown" }),
|
|
1056
|
-
/* @__PURE__ */ jsxs("table", { className: "w-full", children: [
|
|
1057
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
|
|
1058
|
-
/* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Tier" }),
|
|
1059
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Range" }),
|
|
1060
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Price/Unit" }),
|
|
1061
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Qty" }),
|
|
1062
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Subtotal" })
|
|
1063
|
-
] }) }),
|
|
1064
|
-
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
1065
|
-
c.breakdown.tiers.map((t) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
|
|
1066
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5", children: t.tier }),
|
|
1067
|
-
/* @__PURE__ */ jsxs("td", { className: "py-0.5 text-right font-mono", children: [
|
|
1068
|
-
t.from,
|
|
1069
|
-
"\u2013",
|
|
1070
|
-
t.to
|
|
1071
|
-
] }),
|
|
1072
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(t.pricePerUnit, currency) }),
|
|
1073
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: t.quantity }),
|
|
1074
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(t.subtotal, currency) })
|
|
1075
|
-
] }, t.tier)),
|
|
1076
|
-
/* @__PURE__ */ jsxs("tr", { className: "border-t font-medium", children: [
|
|
1077
|
-
/* @__PURE__ */ jsx("td", { colSpan: 4, className: "py-0.5 text-right", children: "Tiered Total" }),
|
|
1078
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(c.breakdown.tiers.reduce((s, t) => s + t.subtotal, 0), currency) })
|
|
1079
|
-
] })
|
|
1080
|
-
] })
|
|
1081
|
-
] })
|
|
1082
|
-
] }),
|
|
1083
|
-
c.adjustments && c.adjustments.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
1084
|
-
/* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Applied Price Rules" }),
|
|
1085
|
-
/* @__PURE__ */ jsxs("table", { className: "w-full", children: [
|
|
1086
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
|
|
1087
|
-
/* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Rule" }),
|
|
1088
|
-
/* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Type" }),
|
|
1089
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Value" }),
|
|
1090
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Before" }),
|
|
1091
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "After" }),
|
|
1092
|
-
/* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Delta" })
|
|
1093
|
-
] }) }),
|
|
1094
|
-
/* @__PURE__ */ jsx("tbody", { children: c.adjustments.map((adj, ai) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
|
|
1095
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5", children: adj.ruleName }),
|
|
1096
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5", children: RULE_TYPE_LABELS[adj.ruleType] ?? adj.ruleType }),
|
|
1097
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmtRuleValue(adj, currency) }),
|
|
1098
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(adj.unitPriceBefore, currency) }),
|
|
1099
|
-
/* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(adj.unitPriceAfter, currency) }),
|
|
1100
|
-
/* @__PURE__ */ jsxs("td", { className: `py-0.5 text-right font-mono ${adj.delta < 0 ? "text-destructive" : adj.delta > 0 ? "text-status-success-text" : ""}`, children: [
|
|
1101
|
-
adj.delta > 0 ? "+" : "",
|
|
1102
|
-
fmt(adj.delta, currency)
|
|
1103
|
-
] })
|
|
1104
|
-
] }, ai)) })
|
|
1105
|
-
] })
|
|
1106
|
-
] }),
|
|
1107
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-1 border-t text-sm font-semibold", children: [
|
|
1108
|
-
/* @__PURE__ */ jsx("span", { children: "Final Total" }),
|
|
1109
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : "\u2014" })
|
|
1110
|
-
] })
|
|
1111
|
-
] }) }) })
|
|
1112
|
-
] }, i);
|
|
1113
|
-
}) })
|
|
1114
|
-
] }) });
|
|
1028
|
+
function ChargeBreakdown({ line, lineIdx, pricingDetail, onTogglePricingDetail, ...tableProps }) {
|
|
1029
|
+
return /* @__PURE__ */ jsx("div", { className: "border-t bg-muted/10 px-12 py-2", children: /* @__PURE__ */ jsx(
|
|
1030
|
+
ChargeBreakdownTable,
|
|
1031
|
+
{
|
|
1032
|
+
charges: line.charges,
|
|
1033
|
+
openDetailIndex: pricingDetail?.lineIdx === lineIdx ? pricingDetail.chargeIdx : null,
|
|
1034
|
+
onToggleDetail: (chargeIdx) => onTogglePricingDetail(lineIdx, chargeIdx),
|
|
1035
|
+
...tableProps
|
|
1036
|
+
}
|
|
1037
|
+
) });
|
|
1115
1038
|
}
|
|
1116
1039
|
function Spinner() {
|
|
1117
1040
|
return /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", children: [
|