@dainamite/cpq 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +154 -178
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +69 -66
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +6 -3
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +129 -94
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
- package/dist/modules/cpq/components/ArcLineDiff.js +2 -2
- package/dist/modules/cpq/components/ArcLineDiff.js.map +2 -2
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +4 -10
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +2 -2
- package/dist/modules/cpq/components/PriceWaterfall.js +3 -1
- package/dist/modules/cpq/components/PriceWaterfall.js.map +2 -2
- package/dist/modules/cpq/components/PricingSummaryCard.js +1 -6
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +2 -2
- package/dist/modules/cpq/components/StatusPath.js +5 -3
- package/dist/modules/cpq/components/StatusPath.js.map +2 -2
- package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
- package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
- package/dist/modules/cpq/i18n/de.json +1220 -0
- package/dist/modules/cpq/i18n/en.json +1220 -0
- package/dist/modules/cpq/i18n/es.json +1220 -0
- package/dist/modules/cpq/i18n/pl.json +1220 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection-table.js +2 -2
- package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
- package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/index.js +12 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
- package/package.json +1 -1
- package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
- package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
- package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +2 -2
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +134 -112
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +66 -43
- package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +3 -3
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +134 -89
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
- package/src/modules/cpq/components/ArcLineDiff.tsx +2 -2
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +4 -2
- package/src/modules/cpq/components/PriceWaterfall.tsx +4 -2
- package/src/modules/cpq/components/PricingSummaryCard.tsx +1 -1
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/i18n/de.json +1220 -0
- package/src/modules/cpq/i18n/en.json +1220 -0
- package/src/modules/cpq/i18n/es.json +1220 -0
- package/src/modules/cpq/i18n/pl.json +1220 -0
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
- package/src/modules/cpq/widgets/injection-table.ts +8 -2
- package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
- package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
- package/src/modules/cpq/workflows/steps/index.ts +12 -0
- package/src/modules/cpq/workflows/types.ts +4 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/cpq/backend/cpq/wizards/%5BdefinitionCode%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { WizardRunner } from '../../../../workflows/WizardRunner'\nimport { registerBuiltInStepTypes } from '../../../../workflows/steps'\nimport type { WizardDefinitionResult } from '../../../../workflows/types'\n\n// Register built-in step types once on module load\nregisterBuiltInStepTypes()\n\nasync function apiJson<T>(url: string): Promise<T> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { WizardRunner } from '../../../../workflows/WizardRunner'\nimport { registerBuiltInStepTypes } from '../../../../workflows/steps'\nimport type { WizardDefinitionResult } from '../../../../workflows/types'\n\n// Register built-in step types once on module load\nregisterBuiltInStepTypes()\n\nasync function apiJson<T>(url: string, t: TranslateFn): Promise<T> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(t('cpq.wizards.runner.errors.api', 'API error {status}', { status: res.status }))\n return res.json() as Promise<T>\n}\n\n// \u2500\u2500\u2500 Params form shown before starting a wizard with required params \u2500\u2500\n\ntype ParamsSchema = {\n type?: string\n required?: string[]\n properties?: Record<string, { type?: string; format?: string; description?: string }>\n}\n\nfunction ParamsForm({\n schema,\n onStart,\n}: {\n schema: ParamsSchema\n onStart: (params: Record<string, string>) => void\n}) {\n const t = useT()\n const properties = schema.properties ?? {}\n const requiredFields = schema.required ?? []\n const fieldNames = Object.keys(properties)\n\n const [values, setValues] = React.useState<Record<string, string>>(() => {\n const init: Record<string, string> = {}\n for (const key of fieldNames) init[key] = ''\n return init\n })\n\n const isValid = requiredFields.every((key) => values[key]?.trim())\n\n return (\n <div className=\"space-y-4\">\n <div className=\"rounded-lg border bg-muted/30 p-4 space-y-1\">\n <h4 className=\"text-sm font-medium\">{t('cpq.wizards.runner.params.title', 'This wizard requires input parameters')}</h4>\n <p className=\"text-xs text-muted-foreground\">{t('cpq.wizards.runner.params.hint', 'Fill in the required fields before starting.')}</p>\n </div>\n\n <div className=\"space-y-3\">\n {fieldNames.map((key) => {\n const prop = properties[key]\n const isRequired = requiredFields.includes(key)\n return (\n <div key={key}>\n <label className=\"block text-sm font-medium mb-1\">\n {prop?.description || key}\n {isRequired && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <input\n type=\"text\"\n value={values[key] ?? ''}\n onChange={(e) => setValues((prev) => ({ ...prev, [key]: e.target.value }))}\n placeholder={prop?.format === 'uuid' ? 'UUID' : key}\n className=\"w-full rounded-md border px-3 py-2 text-sm font-mono\"\n />\n {prop?.format && (\n <p className=\"text-xs text-muted-foreground mt-0.5\">{t('cpq.wizards.runner.params.format', 'Format: {format}', { format: prop.format })}</p>\n )}\n </div>\n )\n })}\n </div>\n\n <button\n type=\"button\"\n onClick={() => onStart(values)}\n disabled={!isValid}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {t('cpq.wizards.runner.params.start', 'Preview Wizard')}\n </button>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Main page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport default function CpqWizardRunnerPage({ params }: { params: Record<string, string | string[]> }) {\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const definitionCode = params.definitionCode as string\n\n const [definition, setDefinition] = React.useState<WizardDefinitionResult | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [started, setStarted] = React.useState(false)\n\n // Parse launch params from query string\n const queryParams = React.useMemo(() => {\n const raw = searchParams.get('params')\n if (!raw) return {}\n try {\n return JSON.parse(raw) as Record<string, unknown>\n } catch {\n return {}\n }\n }, [searchParams])\n\n const [launchParams, setLaunchParams] = React.useState<Record<string, unknown>>(queryParams)\n\n React.useEffect(() => {\n async function load() {\n if (!definitionCode || definitionCode === 'undefined') {\n setError(t('cpq.wizards.runner.errors.invalidCode', 'Invalid wizard definition code'))\n setLoading(false)\n return\n }\n setLoading(true)\n setError(null)\n try {\n const def = await apiJson<WizardDefinitionResult>(\n `/api/cpq/wizards?code=${encodeURIComponent(definitionCode)}`,\n t,\n )\n setDefinition(def)\n\n // Auto-start if params were provided via query string or wizard has no required params\n const schema = def.paramsSchema as ParamsSchema | null\n const hasRequiredParams = schema?.required?.length\n if (!hasRequiredParams || Object.keys(queryParams).length > 0) {\n setStarted(true)\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : t('cpq.wizards.runner.errors.load', 'Failed to load wizard'))\n } finally {\n setLoading(false)\n }\n }\n load()\n }, [definitionCode, queryParams, t])\n\n const handleComplete = React.useCallback((stepData: Record<string, Record<string, unknown>>) => {\n // Check for orderId first (e.g. purchasing wizard creates an order)\n let orderId: string | undefined\n let quoteId: string | undefined\n for (const data of Object.values(stepData)) {\n if (data.orderId && !orderId) orderId = data.orderId as string\n if (data.quoteId && !quoteId) quoteId = data.quoteId as string\n }\n if (orderId) {\n router.push(`/backend/cpq/orders/${orderId}`)\n } else if (quoteId) {\n router.push(`/backend/cpq/quotes/${quoteId}`)\n } else {\n router.push('/backend/cpq/wizards')\n }\n }, [router])\n\n const handleParamsSubmit = React.useCallback((params: Record<string, string>) => {\n setLaunchParams(params)\n setStarted(true)\n }, [])\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">{t('cpq.wizards.runner.loading', 'Loading wizard...')}</div>\n }\n\n if (error) {\n return (\n <div className=\"space-y-4\">\n <Alert variant=\"destructive\">{error}</Alert>\n <button\n type=\"button\"\n onClick={() => router.push('/backend/cpq/wizards')}\n className=\"inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted\"\n >\n {t('cpq.wizards.runner.backToList', 'Back to Wizards')}\n </button>\n </div>\n )\n }\n\n if (!definition) {\n return (\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.wizards.runner.notFound', 'Wizard not found.')}\n </div>\n )\n }\n\n // Extract scope from auth context (will be provided by the framework at runtime)\n // For now, use empty strings \u2014 the API routes resolve scope from the auth session\n const scope = { organizationId: '', tenantId: '' }\n\n return (\n <div className=\"space-y-4 max-w-[900px] mx-auto\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => router.push('/backend/cpq/wizards')}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n \u2190 {t('cpq.wizards.runner.allWizards', 'All Wizards')}\n </button>\n </div>\n\n {!started ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold\">{definition.name}</h2>\n {definition.description && (\n <p className=\"text-sm text-muted-foreground mt-1\">{definition.description}</p>\n )}\n </div>\n <ParamsForm\n schema={(definition.paramsSchema as ParamsSchema) ?? {}}\n onStart={handleParamsSubmit}\n />\n </div>\n ) : (\n <WizardRunner\n definition={definition}\n params={launchParams}\n initialQuoteId={launchParams.quoteId as string | undefined}\n initialCustomerId={launchParams.customerId as string | undefined}\n scope={scope}\n onComplete={handleComplete}\n />\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["params"]
|
|
7
7
|
}
|
|
@@ -76,7 +76,7 @@ function CpqWizardsPage() {
|
|
|
76
76
|
{
|
|
77
77
|
accessorKey: "isActive",
|
|
78
78
|
header: t("cpq.wizards.table.active", "Active"),
|
|
79
|
-
cell: ({ row }) => /* @__PURE__ */ jsx(Tag, { variant: row.original.isActive ? "success" : "neutral", dot: true, children: row.original.isActive ? "Yes" : "No" })
|
|
79
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(Tag, { variant: row.original.isActive ? "success" : "neutral", dot: true, children: row.original.isActive ? t("cpq.common.yes", "Yes") : t("cpq.common.no", "No") })
|
|
80
80
|
}
|
|
81
81
|
],
|
|
82
82
|
[t]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/cpq/backend/cpq/wizards/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\n\ntype WizardDefinition = {\n id: string\n code: string\n name: string\n description: string | null\n version: number\n surface: string\n isActive: boolean\n steps: Array<{ stepId: string; type: string; title: string }>\n createdAt: string\n}\n\nconst PAGE_SIZE = 50\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.surface === 'string' && values.surface.trim()) {\n params.set('surface', values.surface.trim())\n }\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nexport default function CpqWizardsPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<WizardDefinition>({\n endpoint: '/api/cpq/wizards',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.wizards.list.error.load', 'Failed to load wizards'),\n })\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'surface',\n label: t('cpq.wizards.filters.surface', 'Surface'),\n type: 'text',\n },\n {\n id: 'isActive',\n label: t('cpq.wizards.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [t],\n )\n\n const handlePreviewWizard = React.useCallback(\n (definitionCode: string) => {\n router.push(`/backend/cpq/wizards/${definitionCode}`)\n },\n [router],\n )\n\n const columns = React.useMemo<ColumnDef<WizardDefinition>[]>(\n () => [\n {\n accessorKey: 'name',\n header: t('cpq.wizards.table.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium\">{row.original.name}</span>,\n },\n {\n accessorKey: 'code',\n header: t('cpq.wizards.table.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n accessorKey: 'surface',\n header: t('cpq.wizards.table.surface', 'Surface'),\n },\n {\n id: 'steps',\n header: t('cpq.wizards.table.steps', 'Steps'),\n cell: ({ row }) => <span>{row.original.steps.length}</span>,\n },\n {\n id: 'version',\n header: t('cpq.wizards.table.version', 'Version'),\n cell: ({ row }) => <span>v{row.original.version}</span>,\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.wizards.table.active', 'Active'),\n cell: ({ row }) => (\n <Tag variant={row.original.isActive ? 'success' : 'neutral'} dot>\n {row.original.isActive ? 'Yes' : 'No'}\n </Tag>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<WizardDefinition>\n title={t('cpq.wizards.list.title', 'Wizards')}\n tableId=\"cpq.wizards.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.wizards.search.placeholder', 'Search wizards...')}\n onRowClick={(row) => router.push(`/backend/cpq/wizards/${row.code}/detail`)}\n // Wizards are read-only in the admin UI for now: no Edit / Delete\n // entries \u2014 only `Preview` (run the wizard) and `View details`.\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'preview',\n label: t('cpq.wizards.actions.preview', 'Preview'),\n onSelect: () => handlePreviewWizard(row.code),\n },\n {\n id: 'details',\n label: t('cpq.wizards.actions.details', 'View details'),\n href: `/backend/cpq/wizards/${row.code}/detail`,\n },\n ]}\n />\n )}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.wizards.empty', 'No wizard definitions yet. Create one via the API.')}\n </div>\n }\n />\n )\n}\n"],
|
|
5
|
-
"mappings": ";AAuE2B,cAmBA,YAnBA;AAtE3B,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AAGrB,SAAS,kBAAkB;AAC3B,SAAS,WAAW;AACpB,SAAS,aAAa,sBAAsB;AAc5C,MAAM,YAAY;AAElB,SAAS,kBAAkB,QAAsB,QAAyB;AACxE,MAAI,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,KAAK,GAAG;AAC/D,WAAO,IAAI,WAAW,OAAO,QAAQ,KAAK,CAAC;AAAA,EAC7C;AACA,MAAI,OAAO,aAAa,KAAM,QAAO,IAAI,YAAY,MAAM;AAC3D,MAAI,OAAO,aAAa,MAAO,QAAO,IAAI,YAAY,OAAO;AAC/D;AAEe,SAAR,iBAAkC;AACvC,QAAM,SAAS,UAAU;AACzB,QAAM,IAAI,KAAK;AAEf,QAAM,OAAO,eAAiC;AAAA,IAC5C,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,IACA,kBAAkB,EAAE,+BAA+B,wBAAwB;AAAA,EAC7E,CAAC;AAED,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,+BAA+B,SAAS;AAAA,QACjD,MAAM;AAAA,MACR;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,OAAO,EAAE,gCAAgC,QAAQ;AAAA,QACjD,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,sBAAsB,MAAM;AAAA,IAChC,CAAC,mBAA2B;AAC1B,aAAO,KAAK,wBAAwB,cAAc,EAAE;AAAA,IACtD;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB,MAAM;AAAA,MACJ;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,0BAA0B,MAAM;AAAA,QAC1C,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,eAAe,cAAI,SAAS,MAAK;AAAA,MACtE;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,0BAA0B,MAAM;AAAA,QAC1C,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAK,WAAU,qBAAqB,cAAI,SAAS,MAAK;AAAA,MAC5E;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,6BAA6B,SAAS;AAAA,MAClD;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,2BAA2B,OAAO;AAAA,QAC5C,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,UAAM,cAAI,SAAS,MAAM,QAAO;AAAA,MACtD;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,6BAA6B,SAAS;AAAA,QAChD,MAAM,CAAC,EAAE,IAAI,MAAM,qBAAC,UAAK;AAAA;AAAA,UAAE,IAAI,SAAS;AAAA,WAAQ;AAAA,MAClD;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,4BAA4B,QAAQ;AAAA,QAC9C,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,OAAI,SAAS,IAAI,SAAS,WAAW,YAAY,WAAW,KAAG,MAC7D,cAAI,SAAS,WAAW,
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { CpqListView, useCpqListData } from '../../../components/CpqListView'\n\ntype WizardDefinition = {\n id: string\n code: string\n name: string\n description: string | null\n version: number\n surface: string\n isActive: boolean\n steps: Array<{ stepId: string; type: string; title: string }>\n createdAt: string\n}\n\nconst PAGE_SIZE = 50\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.surface === 'string' && values.surface.trim()) {\n params.set('surface', values.surface.trim())\n }\n if (values.isActive === true) params.set('isActive', 'true')\n if (values.isActive === false) params.set('isActive', 'false')\n}\n\nexport default function CpqWizardsPage() {\n const router = useRouter()\n const t = useT()\n\n const data = useCpqListData<WizardDefinition>({\n endpoint: '/api/cpq/wizards',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.wizards.list.error.load', 'Failed to load wizards'),\n })\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'surface',\n label: t('cpq.wizards.filters.surface', 'Surface'),\n type: 'text',\n },\n {\n id: 'isActive',\n label: t('cpq.wizards.filters.isActive', 'Active'),\n type: 'checkbox',\n },\n ],\n [t],\n )\n\n const handlePreviewWizard = React.useCallback(\n (definitionCode: string) => {\n router.push(`/backend/cpq/wizards/${definitionCode}`)\n },\n [router],\n )\n\n const columns = React.useMemo<ColumnDef<WizardDefinition>[]>(\n () => [\n {\n accessorKey: 'name',\n header: t('cpq.wizards.table.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium\">{row.original.name}</span>,\n },\n {\n accessorKey: 'code',\n header: t('cpq.wizards.table.code', 'Code'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">{row.original.code}</span>,\n },\n {\n accessorKey: 'surface',\n header: t('cpq.wizards.table.surface', 'Surface'),\n },\n {\n id: 'steps',\n header: t('cpq.wizards.table.steps', 'Steps'),\n cell: ({ row }) => <span>{row.original.steps.length}</span>,\n },\n {\n id: 'version',\n header: t('cpq.wizards.table.version', 'Version'),\n cell: ({ row }) => <span>v{row.original.version}</span>,\n },\n {\n accessorKey: 'isActive',\n header: t('cpq.wizards.table.active', 'Active'),\n cell: ({ row }) => (\n <Tag variant={row.original.isActive ? 'success' : 'neutral'} dot>\n {row.original.isActive ? t('cpq.common.yes', 'Yes') : t('cpq.common.no', 'No')}\n </Tag>\n ),\n },\n ],\n [t],\n )\n\n return (\n <CpqListView<WizardDefinition>\n title={t('cpq.wizards.list.title', 'Wizards')}\n tableId=\"cpq.wizards.list\"\n data={data}\n columns={columns}\n filters={filters}\n pageSize={PAGE_SIZE}\n searchPlaceholder={t('cpq.wizards.search.placeholder', 'Search wizards...')}\n onRowClick={(row) => router.push(`/backend/cpq/wizards/${row.code}/detail`)}\n // Wizards are read-only in the admin UI for now: no Edit / Delete\n // entries \u2014 only `Preview` (run the wizard) and `View details`.\n rowActions={(row) => (\n <RowActions\n items={[\n {\n id: 'preview',\n label: t('cpq.wizards.actions.preview', 'Preview'),\n onSelect: () => handlePreviewWizard(row.code),\n },\n {\n id: 'details',\n label: t('cpq.wizards.actions.details', 'View details'),\n href: `/backend/cpq/wizards/${row.code}/detail`,\n },\n ]}\n />\n )}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.wizards.empty', 'No wizard definitions yet. Create one via the API.')}\n </div>\n }\n />\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,8 +19,8 @@ function ArcLineDiff({ line, currency }) {
|
|
|
19
19
|
const isCancel = line.action === "cancel";
|
|
20
20
|
const rows = [
|
|
21
21
|
{ label: t("cpq.arc.diff.quantity", "Quantity"), before: src.quantity, after: isCancel ? 0 : line.quantity, asCurrency: false },
|
|
22
|
-
{ label: "MRC", before: src.mrcAmount, after: isCancel ? 0 : line.mrcTotal, asCurrency: true },
|
|
23
|
-
{ label: "NRC", before: src.nrcAmount, after: isCancel ? 0 : line.nrcTotal, asCurrency: true }
|
|
22
|
+
{ label: t("cpq.arc.diff.mrc", "MRC"), before: src.mrcAmount, after: isCancel ? 0 : line.mrcTotal, asCurrency: true },
|
|
23
|
+
{ label: t("cpq.arc.diff.nrc", "NRC"), before: src.nrcAmount, after: isCancel ? 0 : line.nrcTotal, asCurrency: true }
|
|
24
24
|
];
|
|
25
25
|
const fmtVal = (value, asCurrency) => asCurrency ? formatMoney(value, currency) : String(value);
|
|
26
26
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", "data-testid": "cpq-arc-line-diff", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/components/ArcLineDiff.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { formatMoney } from '../lib/formatMoney'\n\nexport type LineAction = 'add' | 'modify' | 'cancel'\n\nconst ACTION_VARIANTS: Record<LineAction, 'info' | 'warning' | 'error'> = {\n add: 'info',\n modify: 'warning',\n cancel: 'error',\n}\n\n/** Coloured add / modify / cancel tag on quote and order lines. */\nexport function LineActionBadge({ action, label, title }: { action: string; label?: string; title?: string }) {\n const variant = ACTION_VARIANTS[action as LineAction] ?? 'info'\n return (\n <Tag variant={variant} className=\"px-1.5 text-xs\" title={title}>\n {label ?? action}\n </Tag>\n )\n}\n\nexport type ArcDiffLine = {\n action: string\n quantity: number\n nrcTotal: number\n mrcTotal: number\n arcSource: { mrcAmount: number; nrcAmount: number; quantity: number } | null\n}\n\n/** Before / after / \u0394 preview for a line that changes an existing subscription item. */\nexport function ArcLineDiff({ line, currency }: { line: ArcDiffLine; currency: string }) {\n const t = useT()\n const src = line.arcSource\n if (!src) return null\n const isCancel = line.action === 'cancel'\n const rows: Array<{ label: string; before: number; after: number; asCurrency: boolean }> = [\n { label: t('cpq.arc.diff.quantity', 'Quantity'), before: src.quantity, after: isCancel ? 0 : line.quantity, asCurrency: false },\n { label: 'MRC', before: src.mrcAmount, after: isCancel ? 0 : line.mrcTotal, asCurrency: true },\n { label: 'NRC', before: src.nrcAmount, after: isCancel ? 0 : line.nrcTotal, asCurrency: true },\n ]\n const fmtVal = (value: number, asCurrency: boolean) => (asCurrency ? formatMoney(value, currency) : String(value))\n\n return (\n <div className=\"space-y-1.5\" data-testid=\"cpq-arc-line-diff\">\n <p className=\"text-xs font-medium text-muted-foreground\">{t('cpq.arc.diff.title', 'Change Preview')}</p>\n <div className=\"rounded border bg-background overflow-hidden\">\n <table className=\"w-full text-xs\">\n <thead className=\"bg-muted/40\">\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left px-3 py-1.5 font-medium\" />\n <th className=\"text-right px-3 py-1.5 font-medium\">{t('cpq.arc.diff.before', 'Before')}</th>\n <th className=\"text-right px-3 py-1.5 font-medium\">{t('cpq.arc.diff.after', 'After')}</th>\n <th className=\"text-right px-3 py-1.5 font-medium\">\u0394</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((r) => {\n const delta = r.after - r.before\n const showDelta = delta !== 0\n return (\n <tr key={r.label} className=\"border-t border-dashed border-border/40\">\n <td className=\"px-3 py-1 text-muted-foreground\">{r.label}</td>\n <td className=\"px-3 py-1 text-right font-mono\">{fmtVal(r.before, r.asCurrency)}</td>\n <td className={`px-3 py-1 text-right font-mono ${isCancel ? 'text-muted-foreground line-through' : ''}`}>\n {fmtVal(r.after, r.asCurrency)}\n </td>\n <td className={`px-3 py-1 text-right font-mono font-medium ${showDelta ? (delta > 0 ? 'text-status-success-text' : 'text-destructive') : 'text-muted-foreground'}`}>\n {showDelta ? `${delta > 0 ? '+' : ''}${fmtVal(delta, r.asCurrency)}` : '\u2014'}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n {isCancel && (\n <p className=\"text-xs text-destructive italic\">\n {t('cpq.arc.diff.cancelNote', 'Cancellation \u2014 item will be terminated on activation.')}\n </p>\n )}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AAmBI,cAiCQ,YAjCR;AAhBJ,SAAS,YAAY;AACrB,SAAS,WAAW;AACpB,SAAS,mBAAmB;AAI5B,MAAM,kBAAoE;AAAA,EACxE,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AACV;AAGO,SAAS,gBAAgB,EAAE,QAAQ,OAAO,MAAM,GAAuD;AAC5G,QAAM,UAAU,gBAAgB,MAAoB,KAAK;AACzD,SACE,oBAAC,OAAI,SAAkB,WAAU,kBAAiB,OAC/C,mBAAS,QACZ;AAEJ;AAWO,SAAS,YAAY,EAAE,MAAM,SAAS,GAA4C;AACvF,QAAM,IAAI,KAAK;AACf,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,WAAW,KAAK,WAAW;AACjC,QAAM,OAAqF;AAAA,IACzF,EAAE,OAAO,EAAE,yBAAyB,UAAU,GAAG,QAAQ,IAAI,UAAU,OAAO,WAAW,IAAI,KAAK,UAAU,YAAY,MAAM;AAAA,IAC9H,EAAE,OAAO,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { formatMoney } from '../lib/formatMoney'\n\nexport type LineAction = 'add' | 'modify' | 'cancel'\n\nconst ACTION_VARIANTS: Record<LineAction, 'info' | 'warning' | 'error'> = {\n add: 'info',\n modify: 'warning',\n cancel: 'error',\n}\n\n/** Coloured add / modify / cancel tag on quote and order lines. */\nexport function LineActionBadge({ action, label, title }: { action: string; label?: string; title?: string }) {\n const variant = ACTION_VARIANTS[action as LineAction] ?? 'info'\n return (\n <Tag variant={variant} className=\"px-1.5 text-xs\" title={title}>\n {label ?? action}\n </Tag>\n )\n}\n\nexport type ArcDiffLine = {\n action: string\n quantity: number\n nrcTotal: number\n mrcTotal: number\n arcSource: { mrcAmount: number; nrcAmount: number; quantity: number } | null\n}\n\n/** Before / after / \u0394 preview for a line that changes an existing subscription item. */\nexport function ArcLineDiff({ line, currency }: { line: ArcDiffLine; currency: string }) {\n const t = useT()\n const src = line.arcSource\n if (!src) return null\n const isCancel = line.action === 'cancel'\n const rows: Array<{ label: string; before: number; after: number; asCurrency: boolean }> = [\n { label: t('cpq.arc.diff.quantity', 'Quantity'), before: src.quantity, after: isCancel ? 0 : line.quantity, asCurrency: false },\n { label: t('cpq.arc.diff.mrc', 'MRC'), before: src.mrcAmount, after: isCancel ? 0 : line.mrcTotal, asCurrency: true },\n { label: t('cpq.arc.diff.nrc', 'NRC'), before: src.nrcAmount, after: isCancel ? 0 : line.nrcTotal, asCurrency: true },\n ]\n const fmtVal = (value: number, asCurrency: boolean) => (asCurrency ? formatMoney(value, currency) : String(value))\n\n return (\n <div className=\"space-y-1.5\" data-testid=\"cpq-arc-line-diff\">\n <p className=\"text-xs font-medium text-muted-foreground\">{t('cpq.arc.diff.title', 'Change Preview')}</p>\n <div className=\"rounded border bg-background overflow-hidden\">\n <table className=\"w-full text-xs\">\n <thead className=\"bg-muted/40\">\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left px-3 py-1.5 font-medium\" />\n <th className=\"text-right px-3 py-1.5 font-medium\">{t('cpq.arc.diff.before', 'Before')}</th>\n <th className=\"text-right px-3 py-1.5 font-medium\">{t('cpq.arc.diff.after', 'After')}</th>\n <th className=\"text-right px-3 py-1.5 font-medium\">\u0394</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((r) => {\n const delta = r.after - r.before\n const showDelta = delta !== 0\n return (\n <tr key={r.label} className=\"border-t border-dashed border-border/40\">\n <td className=\"px-3 py-1 text-muted-foreground\">{r.label}</td>\n <td className=\"px-3 py-1 text-right font-mono\">{fmtVal(r.before, r.asCurrency)}</td>\n <td className={`px-3 py-1 text-right font-mono ${isCancel ? 'text-muted-foreground line-through' : ''}`}>\n {fmtVal(r.after, r.asCurrency)}\n </td>\n <td className={`px-3 py-1 text-right font-mono font-medium ${showDelta ? (delta > 0 ? 'text-status-success-text' : 'text-destructive') : 'text-muted-foreground'}`}>\n {showDelta ? `${delta > 0 ? '+' : ''}${fmtVal(delta, r.asCurrency)}` : '\u2014'}\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n {isCancel && (\n <p className=\"text-xs text-destructive italic\">\n {t('cpq.arc.diff.cancelNote', 'Cancellation \u2014 item will be terminated on activation.')}\n </p>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAmBI,cAiCQ,YAjCR;AAhBJ,SAAS,YAAY;AACrB,SAAS,WAAW;AACpB,SAAS,mBAAmB;AAI5B,MAAM,kBAAoE;AAAA,EACxE,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,QAAQ;AACV;AAGO,SAAS,gBAAgB,EAAE,QAAQ,OAAO,MAAM,GAAuD;AAC5G,QAAM,UAAU,gBAAgB,MAAoB,KAAK;AACzD,SACE,oBAAC,OAAI,SAAkB,WAAU,kBAAiB,OAC/C,mBAAS,QACZ;AAEJ;AAWO,SAAS,YAAY,EAAE,MAAM,SAAS,GAA4C;AACvF,QAAM,IAAI,KAAK;AACf,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,WAAW,KAAK,WAAW;AACjC,QAAM,OAAqF;AAAA,IACzF,EAAE,OAAO,EAAE,yBAAyB,UAAU,GAAG,QAAQ,IAAI,UAAU,OAAO,WAAW,IAAI,KAAK,UAAU,YAAY,MAAM;AAAA,IAC9H,EAAE,OAAO,EAAE,oBAAoB,KAAK,GAAG,QAAQ,IAAI,WAAW,OAAO,WAAW,IAAI,KAAK,UAAU,YAAY,KAAK;AAAA,IACpH,EAAE,OAAO,EAAE,oBAAoB,KAAK,GAAG,QAAQ,IAAI,WAAW,OAAO,WAAW,IAAI,KAAK,UAAU,YAAY,KAAK;AAAA,EACtH;AACA,QAAM,SAAS,CAAC,OAAe,eAAyB,aAAa,YAAY,OAAO,QAAQ,IAAI,OAAO,KAAK;AAEhH,SACE,qBAAC,SAAI,WAAU,eAAc,eAAY,qBACvC;AAAA,wBAAC,OAAE,WAAU,6CAA6C,YAAE,sBAAsB,gBAAgB,GAAE;AAAA,IACpG,oBAAC,SAAI,WAAU,gDACb,+BAAC,WAAM,WAAU,kBACf;AAAA,0BAAC,WAAM,WAAU,eACf,+BAAC,QAAG,WAAU,yBACZ;AAAA,4BAAC,QAAG,WAAU,qCAAoC;AAAA,QAClD,oBAAC,QAAG,WAAU,sCAAsC,YAAE,uBAAuB,QAAQ,GAAE;AAAA,QACvF,oBAAC,QAAG,WAAU,sCAAsC,YAAE,sBAAsB,OAAO,GAAE;AAAA,QACrF,oBAAC,QAAG,WAAU,sCAAqC,oBAAC;AAAA,SACtD,GACF;AAAA,MACA,oBAAC,WACE,eAAK,IAAI,CAAC,MAAM;AACf,cAAM,QAAQ,EAAE,QAAQ,EAAE;AAC1B,cAAM,YAAY,UAAU;AAC5B,eACE,qBAAC,QAAiB,WAAU,2CAC1B;AAAA,8BAAC,QAAG,WAAU,mCAAmC,YAAE,OAAM;AAAA,UACzD,oBAAC,QAAG,WAAU,kCAAkC,iBAAO,EAAE,QAAQ,EAAE,UAAU,GAAE;AAAA,UAC/E,oBAAC,QAAG,WAAW,kCAAkC,WAAW,uCAAuC,EAAE,IAClG,iBAAO,EAAE,OAAO,EAAE,UAAU,GAC/B;AAAA,UACA,oBAAC,QAAG,WAAW,8CAA8C,YAAa,QAAQ,IAAI,6BAA6B,qBAAsB,uBAAuB,IAC7J,sBAAY,GAAG,QAAQ,IAAI,MAAM,EAAE,GAAG,OAAO,OAAO,EAAE,UAAU,CAAC,KAAK,UACzE;AAAA,aARO,EAAE,KASX;AAAA,MAEJ,CAAC,GACH;AAAA,OACF,GACF;AAAA,IACC,YACC,oBAAC,OAAE,WAAU,mCACV,YAAE,2BAA2B,4DAAuD,GACvF;AAAA,KAEJ;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -63,11 +63,9 @@ function ChargeBreakdownTable({
|
|
|
63
63
|
/* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/50", children: [
|
|
64
64
|
/* @__PURE__ */ jsxs("td", { className: "py-1", children: [
|
|
65
65
|
/* @__PURE__ */ jsx("span", { children: c.chargeName }),
|
|
66
|
-
errors.length > 0 && /* @__PURE__ */
|
|
67
|
-
t(
|
|
68
|
-
|
|
69
|
-
t(`cpq.priceAdjustments.errors.${errors[0].code}`, ADJUSTMENT_ERROR_FALLBACKS[errors[0].code] ?? errors[0].code)
|
|
70
|
-
] })
|
|
66
|
+
errors.length > 0 && /* @__PURE__ */ jsx(Tag, { variant: "warning", className: "ml-1.5 px-1.5 text-xs", "data-testid": "cpq-adjustment-invalid", children: t("cpq.priceAdjustments.invalidReason", "Invalid: {reason}", {
|
|
67
|
+
reason: t(`cpq.priceAdjustments.errors.${errors[0].code}`, ADJUSTMENT_ERROR_FALLBACKS[errors[0].code] ?? errors[0].code)
|
|
68
|
+
}) })
|
|
71
69
|
] }),
|
|
72
70
|
/* @__PURE__ */ jsx("td", { className: "py-1 uppercase", children: c.chargeType }),
|
|
73
71
|
/* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono text-muted-foreground", children: formatMoney(listUnit, currency) }),
|
|
@@ -102,11 +100,7 @@ function ChargePricingDetail({ charge: c, currency, reasonLabels, renderManualAc
|
|
|
102
100
|
const tiers = c.breakdown?.tiers ?? [];
|
|
103
101
|
return /* @__PURE__ */ jsxs("div", { className: "rounded border bg-background p-3 space-y-3 text-xs", children: [
|
|
104
102
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
t("cpq.charges.pricingDetails", "Pricing Details"),
|
|
107
|
-
" \u2014 ",
|
|
108
|
-
c.chargeName
|
|
109
|
-
] }),
|
|
103
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-sm", children: t("cpq.charges.pricingDetailsFor", "Pricing Details \u2014 {name}", { name: c.chargeName }) }),
|
|
110
104
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: c.pricingMethod ?? "flat" })
|
|
111
105
|
] }),
|
|
112
106
|
tiers.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/components/ChargeBreakdownTable.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Info } from 'lucide-react'\nimport { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { DepthBadge, PriceWaterfall } from './PriceWaterfall'\nimport { ADJUSTMENT_ERROR_FALLBACKS, type WaterfallChargeInput } from '../lib/priceAdjustmentView'\nimport { formatMoney } from '../lib/formatMoney'\n\ntype TierBreakdownEntry = { tier: number; from: number; to: number; pricePerUnit: number; quantity: number; subtotal: number }\n\n/** One priced charge as stored on a quote or order line. */\nexport type ChargeRowView = WaterfallChargeInput & {\n chargeName: string\n quantity?: number | null\n note?: string | null\n breakdown?: { tiers: TierBreakdownEntry[] } | null\n adjustmentErrors?: Array<{ code: string }>\n}\n\n/** Right-aligned price column header with an explanatory hover tooltip. */\nfunction PriceColumnHeader({ label, hint }: { label: string; hint: string }) {\n return (\n <th className=\"text-right py-1 font-medium\">\n <SimpleTooltip content={hint} size=\"sm\">\n <span className=\"inline-flex items-center gap-1 cursor-help\" tabIndex={0} aria-label={`${label}: ${hint}`}>\n {label}\n <Info className=\"h-3 w-3\" aria-hidden />\n </span>\n </SimpleTooltip>\n </th>\n )\n}\n\n/**\n * Charge table shared by the quote and order detail pages: list / standard /\n * offer unit prices with depth badge, and a per-charge pricing detail (tiers +\n * price waterfall). Editing hooks are optional render props.\n */\nexport function ChargeBreakdownTable<C extends ChargeRowView>({\n charges,\n currency,\n openDetailIndex,\n onToggleDetail,\n reasonLabels,\n renderChargeActions,\n renderManualActions,\n}: {\n charges: C[]\n currency: string\n openDetailIndex: number | null\n onToggleDetail: (chargeIndex: number) => void\n reasonLabels?: Record<string, string>\n renderChargeActions?: (charge: C) => React.ReactNode\n renderManualActions?: (adjustmentId: string) => React.ReactNode\n}) {\n const t = useT()\n return (\n <table className=\"w-full text-xs\" data-testid=\"cpq-charge-breakdown\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-1 font-medium\">{t('cpq.charges.charge', 'Charge')}</th>\n <th className=\"text-left py-1 font-medium\">{t('cpq.charges.type', 'Type')}</th>\n <PriceColumnHeader\n label={t('cpq.priceAdjustments.listUnit', 'List')}\n hint={t('cpq.priceAdjustments.listUnitHint', 'Catalog unit price from the pricing table, before any price rules or manual adjustments.')}\n />\n <PriceColumnHeader\n label={t('cpq.priceAdjustments.standardUnit', 'Standard')}\n hint={t('cpq.priceAdjustments.standardUnitHint', 'Unit price after automatic price rules (e.g. volume or segment discounts), before manual adjustments.')}\n />\n <PriceColumnHeader\n label={t('cpq.priceAdjustments.offerUnit', 'Offer')}\n hint={t('cpq.priceAdjustments.offerUnitHint', 'Final unit price quoted to the customer, after price rules and manual adjustments. The badge shows the total change vs list.')}\n />\n <th className=\"text-right py-1 font-medium\">{t('cpq.charges.qty', 'Qty')}</th>\n <th className=\"text-right py-1 font-medium\">{t('cpq.charges.total', 'Total')}</th>\n <th className=\"w-16\" />\n </tr>\n </thead>\n <tbody>\n {charges.map((c, i) => {\n const hasDetail = !!(c.breakdown?.tiers?.length || c.adjustments?.length)\n const isDetailOpen = openDetailIndex === i\n const listUnit = c.listUnitPrice ?? c.unitPrice\n const standardUnit = c.standardUnitPrice ?? c.unitPrice\n const errors = c.adjustmentErrors ?? []\n return (\n <React.Fragment key={i}>\n <tr className=\"border-t border-dashed border-border/50\">\n <td className=\"py-1\">\n <span>{c.chargeName}</span>\n {errors.length > 0 && (\n <Tag variant=\"warning\" className=\"ml-1.5 px-1.5 text-xs\" data-testid=\"cpq-adjustment-invalid\">\n {t('cpq.priceAdjustments.invalid', 'Invalid')}: {t(`cpq.priceAdjustments.errors.${errors[0].code}`, ADJUSTMENT_ERROR_FALLBACKS[errors[0].code] ?? errors[0].code)}\n </Tag>\n )}\n </td>\n <td className=\"py-1 uppercase\">{c.chargeType}</td>\n <td className=\"py-1 text-right font-mono text-muted-foreground\">{formatMoney(listUnit, currency)}</td>\n <td className=\"py-1 text-right font-mono text-muted-foreground\">{formatMoney(standardUnit, currency)}</td>\n <td className=\"py-1 text-right font-mono\">\n <span className=\"inline-flex items-center gap-1\">\n <DepthBadge before={listUnit} after={c.unitPrice} />\n {formatMoney(c.unitPrice, currency)}\n </span>\n </td>\n <td className=\"py-1 text-right font-mono\">{c.quantity ?? '\u2014'}</td>\n <td className=\"py-1 text-right font-mono\">{c.totalPrice != null ? formatMoney(c.totalPrice, currency) : c.note ?? '\u2014'}</td>\n <td className=\"py-1 text-right whitespace-nowrap\">\n {renderChargeActions?.(c)}\n {hasDetail && (\n <button\n type=\"button\"\n onClick={() => onToggleDetail(i)}\n className=\"inline-flex items-center justify-center rounded p-0.5 hover:bg-accent text-muted-foreground hover:text-foreground transition-colors\"\n title={t('cpq.priceAdjustments.showBreakdown', 'Show price breakdown')}\n >\n <Info className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </td>\n </tr>\n {isDetailOpen && (\n <tr>\n <td colSpan={8} className=\"pb-3 pt-1\">\n <ChargePricingDetail charge={c} currency={currency} reasonLabels={reasonLabels} renderManualActions={renderManualActions} />\n </td>\n </tr>\n )}\n </React.Fragment>\n )\n })}\n </tbody>\n </table>\n )\n}\n\nfunction ChargePricingDetail({ charge: c, currency, reasonLabels, renderManualActions }: {\n charge: ChargeRowView\n currency: string\n reasonLabels?: Record<string, string>\n renderManualActions?: (adjustmentId: string) => React.ReactNode\n}) {\n const t = useT()\n const tiers = c.breakdown?.tiers ?? []\n return (\n <div className=\"rounded border bg-background p-3 space-y-3 text-xs\">\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-sm\">{t('cpq.charges.pricingDetails', 'Pricing Details')} \u2014 {c.chargeName}</span>\n <span className=\"text-muted-foreground\">{c.pricingMethod ?? 'flat'}</span>\n </div>\n\n {tiers.length > 0 && (\n <div>\n <div className=\"font-medium text-muted-foreground mb-1\">{t('cpq.charges.tierBreakdown', 'Tier Breakdown')}</div>\n <table className=\"w-full\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-0.5 font-medium\">{t('cpq.charges.tier', 'Tier')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.range', 'Range')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.pricePerUnit', 'Price/Unit')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.qty', 'Qty')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.subtotal', 'Subtotal')}</th>\n </tr>\n </thead>\n <tbody>\n {tiers.map((tier) => (\n <tr key={tier.tier} className=\"border-t border-border/30\">\n <td className=\"py-0.5\">{tier.tier}</td>\n <td className=\"py-0.5 text-right font-mono\">{tier.from}\u2013{tier.to}</td>\n <td className=\"py-0.5 text-right font-mono\">{formatMoney(tier.pricePerUnit, currency)}</td>\n <td className=\"py-0.5 text-right font-mono\">{tier.quantity}</td>\n <td className=\"py-0.5 text-right font-mono\">{formatMoney(tier.subtotal, currency)}</td>\n </tr>\n ))}\n <tr className=\"border-t font-medium\">\n <td colSpan={4} className=\"py-0.5 text-right\">{t('cpq.charges.tieredTotal', 'Tiered Total')}</td>\n <td className=\"py-0.5 text-right font-mono\">\n {formatMoney(tiers.reduce((sum, tier) => sum + tier.subtotal, 0), currency)}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n )}\n\n <div>\n <div className=\"font-medium text-muted-foreground mb-1\">{t('cpq.priceAdjustments.waterfall.title', 'Price waterfall')}</div>\n <PriceWaterfall charge={c} currency={currency} reasonLabels={reasonLabels} renderManualActions={renderManualActions} />\n </div>\n\n <div className=\"flex items-center justify-between pt-1 border-t text-sm font-semibold\">\n <span>{t('cpq.charges.finalTotal', 'Final Total')}</span>\n <span className=\"font-mono\">{c.totalPrice != null ? formatMoney(c.totalPrice, currency) : '\u2014'}</span>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AA2BQ,SAEE,KAFF;AAzBR,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B,SAAS,WAAW;AACpB,SAAS,YAAY,sBAAsB;AAC3C,SAAS,kCAA6D;AACtE,SAAS,mBAAmB;AAc5B,SAAS,kBAAkB,EAAE,OAAO,KAAK,GAAoC;AAC3E,SACE,oBAAC,QAAG,WAAU,+BACZ,8BAAC,iBAAc,SAAS,MAAM,MAAK,MACjC,+BAAC,UAAK,WAAU,8CAA6C,UAAU,GAAG,cAAY,GAAG,KAAK,KAAK,IAAI,IACpG;AAAA;AAAA,IACD,oBAAC,QAAK,WAAU,WAAU,eAAW,MAAC;AAAA,KACxC,GACF,GACF;AAEJ;AAOO,SAAS,qBAA8C;AAAA,EAC5D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,IAAI,KAAK;AACf,SACE,qBAAC,WAAM,WAAU,kBAAiB,eAAY,wBAC5C;AAAA,wBAAC,WACC,+BAAC,QAAG,WAAU,yBACZ;AAAA,0BAAC,QAAG,WAAU,8BAA8B,YAAE,sBAAsB,QAAQ,GAAE;AAAA,MAC9E,oBAAC,QAAG,WAAU,8BAA8B,YAAE,oBAAoB,MAAM,GAAE;AAAA,MAC1E;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,iCAAiC,MAAM;AAAA,UAChD,MAAM,EAAE,qCAAqC,0FAA0F;AAAA;AAAA,MACzI;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,qCAAqC,UAAU;AAAA,UACxD,MAAM,EAAE,yCAAyC,uGAAuG;AAAA;AAAA,MAC1J;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,kCAAkC,OAAO;AAAA,UAClD,MAAM,EAAE,sCAAsC,8HAA8H;AAAA;AAAA,MAC9K;AAAA,MACA,oBAAC,QAAG,WAAU,+BAA+B,YAAE,mBAAmB,KAAK,GAAE;AAAA,MACzE,oBAAC,QAAG,WAAU,+BAA+B,YAAE,qBAAqB,OAAO,GAAE;AAAA,MAC7E,oBAAC,QAAG,WAAU,QAAO;AAAA,OACvB,GACF;AAAA,IACA,oBAAC,WACE,kBAAQ,IAAI,CAAC,GAAG,MAAM;AACrB,YAAM,YAAY,CAAC,EAAE,EAAE,WAAW,OAAO,UAAU,EAAE,aAAa;AAClE,YAAM,eAAe,oBAAoB;AACzC,YAAM,WAAW,EAAE,iBAAiB,EAAE;AACtC,YAAM,eAAe,EAAE,qBAAqB,EAAE;AAC9C,YAAM,SAAS,EAAE,oBAAoB,CAAC;AACtC,aACE,qBAAC,MAAM,UAAN,EACC;AAAA,6BAAC,QAAG,WAAU,2CACZ;AAAA,+BAAC,QAAG,WAAU,QACZ;AAAA,gCAAC,UAAM,YAAE,YAAW;AAAA,YACnB,OAAO,SAAS,KACf,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Info } from 'lucide-react'\nimport { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { DepthBadge, PriceWaterfall } from './PriceWaterfall'\nimport { ADJUSTMENT_ERROR_FALLBACKS, type WaterfallChargeInput } from '../lib/priceAdjustmentView'\nimport { formatMoney } from '../lib/formatMoney'\n\ntype TierBreakdownEntry = { tier: number; from: number; to: number; pricePerUnit: number; quantity: number; subtotal: number }\n\n/** One priced charge as stored on a quote or order line. */\nexport type ChargeRowView = WaterfallChargeInput & {\n chargeName: string\n quantity?: number | null\n note?: string | null\n breakdown?: { tiers: TierBreakdownEntry[] } | null\n adjustmentErrors?: Array<{ code: string }>\n}\n\n/** Right-aligned price column header with an explanatory hover tooltip. */\nfunction PriceColumnHeader({ label, hint }: { label: string; hint: string }) {\n return (\n <th className=\"text-right py-1 font-medium\">\n <SimpleTooltip content={hint} size=\"sm\">\n <span className=\"inline-flex items-center gap-1 cursor-help\" tabIndex={0} aria-label={`${label}: ${hint}`}>\n {label}\n <Info className=\"h-3 w-3\" aria-hidden />\n </span>\n </SimpleTooltip>\n </th>\n )\n}\n\n/**\n * Charge table shared by the quote and order detail pages: list / standard /\n * offer unit prices with depth badge, and a per-charge pricing detail (tiers +\n * price waterfall). Editing hooks are optional render props.\n */\nexport function ChargeBreakdownTable<C extends ChargeRowView>({\n charges,\n currency,\n openDetailIndex,\n onToggleDetail,\n reasonLabels,\n renderChargeActions,\n renderManualActions,\n}: {\n charges: C[]\n currency: string\n openDetailIndex: number | null\n onToggleDetail: (chargeIndex: number) => void\n reasonLabels?: Record<string, string>\n renderChargeActions?: (charge: C) => React.ReactNode\n renderManualActions?: (adjustmentId: string) => React.ReactNode\n}) {\n const t = useT()\n return (\n <table className=\"w-full text-xs\" data-testid=\"cpq-charge-breakdown\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-1 font-medium\">{t('cpq.charges.charge', 'Charge')}</th>\n <th className=\"text-left py-1 font-medium\">{t('cpq.charges.type', 'Type')}</th>\n <PriceColumnHeader\n label={t('cpq.priceAdjustments.listUnit', 'List')}\n hint={t('cpq.priceAdjustments.listUnitHint', 'Catalog unit price from the pricing table, before any price rules or manual adjustments.')}\n />\n <PriceColumnHeader\n label={t('cpq.priceAdjustments.standardUnit', 'Standard')}\n hint={t('cpq.priceAdjustments.standardUnitHint', 'Unit price after automatic price rules (e.g. volume or segment discounts), before manual adjustments.')}\n />\n <PriceColumnHeader\n label={t('cpq.priceAdjustments.offerUnit', 'Offer')}\n hint={t('cpq.priceAdjustments.offerUnitHint', 'Final unit price quoted to the customer, after price rules and manual adjustments. The badge shows the total change vs list.')}\n />\n <th className=\"text-right py-1 font-medium\">{t('cpq.charges.qty', 'Qty')}</th>\n <th className=\"text-right py-1 font-medium\">{t('cpq.charges.total', 'Total')}</th>\n <th className=\"w-16\" />\n </tr>\n </thead>\n <tbody>\n {charges.map((c, i) => {\n const hasDetail = !!(c.breakdown?.tiers?.length || c.adjustments?.length)\n const isDetailOpen = openDetailIndex === i\n const listUnit = c.listUnitPrice ?? c.unitPrice\n const standardUnit = c.standardUnitPrice ?? c.unitPrice\n const errors = c.adjustmentErrors ?? []\n return (\n <React.Fragment key={i}>\n <tr className=\"border-t border-dashed border-border/50\">\n <td className=\"py-1\">\n <span>{c.chargeName}</span>\n {errors.length > 0 && (\n <Tag variant=\"warning\" className=\"ml-1.5 px-1.5 text-xs\" data-testid=\"cpq-adjustment-invalid\">\n {t('cpq.priceAdjustments.invalidReason', 'Invalid: {reason}', {\n reason: t(`cpq.priceAdjustments.errors.${errors[0].code}`, ADJUSTMENT_ERROR_FALLBACKS[errors[0].code] ?? errors[0].code),\n })}\n </Tag>\n )}\n </td>\n <td className=\"py-1 uppercase\">{c.chargeType}</td>\n <td className=\"py-1 text-right font-mono text-muted-foreground\">{formatMoney(listUnit, currency)}</td>\n <td className=\"py-1 text-right font-mono text-muted-foreground\">{formatMoney(standardUnit, currency)}</td>\n <td className=\"py-1 text-right font-mono\">\n <span className=\"inline-flex items-center gap-1\">\n <DepthBadge before={listUnit} after={c.unitPrice} />\n {formatMoney(c.unitPrice, currency)}\n </span>\n </td>\n <td className=\"py-1 text-right font-mono\">{c.quantity ?? '\u2014'}</td>\n <td className=\"py-1 text-right font-mono\">{c.totalPrice != null ? formatMoney(c.totalPrice, currency) : c.note ?? '\u2014'}</td>\n <td className=\"py-1 text-right whitespace-nowrap\">\n {renderChargeActions?.(c)}\n {hasDetail && (\n <button\n type=\"button\"\n onClick={() => onToggleDetail(i)}\n className=\"inline-flex items-center justify-center rounded p-0.5 hover:bg-accent text-muted-foreground hover:text-foreground transition-colors\"\n title={t('cpq.priceAdjustments.showBreakdown', 'Show price breakdown')}\n >\n <Info className=\"h-3.5 w-3.5\" />\n </button>\n )}\n </td>\n </tr>\n {isDetailOpen && (\n <tr>\n <td colSpan={8} className=\"pb-3 pt-1\">\n <ChargePricingDetail charge={c} currency={currency} reasonLabels={reasonLabels} renderManualActions={renderManualActions} />\n </td>\n </tr>\n )}\n </React.Fragment>\n )\n })}\n </tbody>\n </table>\n )\n}\n\nfunction ChargePricingDetail({ charge: c, currency, reasonLabels, renderManualActions }: {\n charge: ChargeRowView\n currency: string\n reasonLabels?: Record<string, string>\n renderManualActions?: (adjustmentId: string) => React.ReactNode\n}) {\n const t = useT()\n const tiers = c.breakdown?.tiers ?? []\n return (\n <div className=\"rounded border bg-background p-3 space-y-3 text-xs\">\n <div className=\"flex items-center justify-between\">\n <span className=\"font-semibold text-sm\">{t('cpq.charges.pricingDetailsFor', 'Pricing Details \u2014 {name}', { name: c.chargeName })}</span>\n <span className=\"text-muted-foreground\">{c.pricingMethod ?? 'flat'}</span>\n </div>\n\n {tiers.length > 0 && (\n <div>\n <div className=\"font-medium text-muted-foreground mb-1\">{t('cpq.charges.tierBreakdown', 'Tier Breakdown')}</div>\n <table className=\"w-full\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-0.5 font-medium\">{t('cpq.charges.tier', 'Tier')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.range', 'Range')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.pricePerUnit', 'Price/Unit')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.qty', 'Qty')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.charges.subtotal', 'Subtotal')}</th>\n </tr>\n </thead>\n <tbody>\n {tiers.map((tier) => (\n <tr key={tier.tier} className=\"border-t border-border/30\">\n <td className=\"py-0.5\">{tier.tier}</td>\n <td className=\"py-0.5 text-right font-mono\">{tier.from}\u2013{tier.to}</td>\n <td className=\"py-0.5 text-right font-mono\">{formatMoney(tier.pricePerUnit, currency)}</td>\n <td className=\"py-0.5 text-right font-mono\">{tier.quantity}</td>\n <td className=\"py-0.5 text-right font-mono\">{formatMoney(tier.subtotal, currency)}</td>\n </tr>\n ))}\n <tr className=\"border-t font-medium\">\n <td colSpan={4} className=\"py-0.5 text-right\">{t('cpq.charges.tieredTotal', 'Tiered Total')}</td>\n <td className=\"py-0.5 text-right font-mono\">\n {formatMoney(tiers.reduce((sum, tier) => sum + tier.subtotal, 0), currency)}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n )}\n\n <div>\n <div className=\"font-medium text-muted-foreground mb-1\">{t('cpq.priceAdjustments.waterfall.title', 'Price waterfall')}</div>\n <PriceWaterfall charge={c} currency={currency} reasonLabels={reasonLabels} renderManualActions={renderManualActions} />\n </div>\n\n <div className=\"flex items-center justify-between pt-1 border-t text-sm font-semibold\">\n <span>{t('cpq.charges.finalTotal', 'Final Total')}</span>\n <span className=\"font-mono\">{c.totalPrice != null ? formatMoney(c.totalPrice, currency) : '\u2014'}</span>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -44,7 +44,9 @@ function PriceWaterfall({ charge, currency, reasonLabels, renderManualActions })
|
|
|
44
44
|
renderManualActions && /* @__PURE__ */ jsx("td", {})
|
|
45
45
|
] }, `${row.kind}-${index}`);
|
|
46
46
|
}
|
|
47
|
-
const source = row.kind === "rule" ?
|
|
47
|
+
const source = row.kind === "rule" ? t("cpq.priceAdjustments.waterfall.ruleSource", "Price rule: {name}", { name: row.label }) : t("cpq.priceAdjustments.waterfall.manualSource", "Negotiated: {reason}", {
|
|
48
|
+
reason: reasonLabels?.[row.reasonCode ?? ""] ?? row.reasonCode ?? row.label
|
|
49
|
+
});
|
|
48
50
|
return /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
|
|
49
51
|
/* @__PURE__ */ jsx("td", { className: "py-0.5 pl-3 text-muted-foreground", children: t(`cpq.priceAdjustments.types.${row.ruleType}`, TYPE_FALLBACKS[row.ruleType] ?? row.ruleType) }),
|
|
50
52
|
/* @__PURE__ */ jsx("td", { className: "py-0.5", children: source }),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/components/PriceWaterfall.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport {\n discountDepth,\n formatDepth,\n waterfallRows,\n type WaterfallChargeInput,\n} from '../lib/priceAdjustmentView'\n\nfunction money(amount: number, currency: string): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nconst TYPE_FALLBACKS: Record<string, string> = {\n discount_percent: '% discount',\n discount_absolute: 'amount off',\n surcharge_percent: '% surcharge',\n surcharge_absolute: 'fixed surcharge',\n price_override: 'price override',\n}\n\n/** Signed depth badge: \"\u221220%\" for a discount, \"+5%\" for a surcharge; text, never colour only. */\nexport function DepthBadge({ before, after, title }: { before: number | null | undefined; after: number | null | undefined; title?: string }) {\n const label = formatDepth(discountDepth(before, after))\n if (!label) return null\n return <Tag variant=\"neutral\" className=\"px-1.5 text-xs font-mono\" title={title}>{label}</Tag>\n}\n\nexport type PriceWaterfallProps = {\n charge: WaterfallChargeInput & { chargeName?: string }\n currency: string\n /** Reason code \u2192 label; manual rows fall back to the code. */\n reasonLabels?: Record<string, string>\n /** Optional per-adjustment actions (edit / delete) rendered on manual rows. */\n renderManualActions?: (adjustmentId: string) => React.ReactNode\n}\n\n/** Read-only list \u2192 rule \u2192 standard \u2192 manual \u2192 offer breakdown for one charge (unit prices). */\nexport function PriceWaterfall({ charge, currency, reasonLabels, renderManualActions }: PriceWaterfallProps) {\n const t = useT()\n const rows = waterfallRows(charge)\n const fmtValue = (ruleType: string, value: number) =>\n ruleType.endsWith('_percent') ? `${value}%` : money(value, currency)\n return (\n <table className=\"w-full text-xs\" data-testid=\"cpq-price-waterfall\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.step', 'Step')}</th>\n <th className=\"text-left py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.source', 'Source')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.value', 'Value')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.delta', 'Delta')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.unitPrice', 'Unit price')}</th>\n {renderManualActions && <th className=\"w-16\" />}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, index) => {\n if (row.kind === 'list' || row.kind === 'standard' || row.kind === 'offer') {\n const label = row.kind === 'list'\n ? t('cpq.priceAdjustments.waterfall.list', 'List price')\n : row.kind === 'standard'\n ? t('cpq.priceAdjustments.waterfall.standard', 'Standard price')\n : t('cpq.priceAdjustments.waterfall.offer', 'Offer price')\n return (\n <tr key={`${row.kind}-${index}`} className={`border-t border-border/30 ${row.kind === 'offer' ? 'font-semibold' : 'font-medium'}`}>\n <td className=\"py-0.5\" colSpan={4}>{label}</td>\n <td className=\"py-0.5 text-right font-mono\">{money(row.unitPrice, currency)}</td>\n {renderManualActions && <td />}\n </tr>\n )\n }\n const source = row.kind === 'rule'\n ?
|
|
5
|
-
"mappings": ";AA4BS,cAqBD,YArBC;AAzBT,SAAS,YAAY;AACrB,SAAS,WAAW;AACpB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAEP,SAAS,MAAM,QAAgB,UAA0B;AACvD,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,MAAM;AACtF;AAEA,MAAM,iBAAyC;AAAA,EAC7C,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,gBAAgB;AAClB;AAGO,SAAS,WAAW,EAAE,QAAQ,OAAO,MAAM,GAA4F;AAC5I,QAAM,QAAQ,YAAY,cAAc,QAAQ,KAAK,CAAC;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,oBAAC,OAAI,SAAQ,WAAU,WAAU,4BAA2B,OAAe,iBAAM;AAC1F;AAYO,SAAS,eAAe,EAAE,QAAQ,UAAU,cAAc,oBAAoB,GAAwB;AAC3G,QAAM,IAAI,KAAK;AACf,QAAM,OAAO,cAAc,MAAM;AACjC,QAAM,WAAW,CAAC,UAAkB,UAClC,SAAS,SAAS,UAAU,IAAI,GAAG,KAAK,MAAM,MAAM,OAAO,QAAQ;AACrE,SACE,qBAAC,WAAM,WAAU,kBAAiB,eAAY,uBAC5C;AAAA,wBAAC,WACC,+BAAC,QAAG,WAAU,yBACZ;AAAA,0BAAC,QAAG,WAAU,gCAAgC,YAAE,uCAAuC,MAAM,GAAE;AAAA,MAC/F,oBAAC,QAAG,WAAU,gCAAgC,YAAE,yCAAyC,QAAQ,GAAE;AAAA,MACnG,oBAAC,QAAG,WAAU,iCAAiC,YAAE,wCAAwC,OAAO,GAAE;AAAA,MAClG,oBAAC,QAAG,WAAU,iCAAiC,YAAE,wCAAwC,OAAO,GAAE;AAAA,MAClG,oBAAC,QAAG,WAAU,iCAAiC,YAAE,4CAA4C,YAAY,GAAE;AAAA,MAC1G,uBAAuB,oBAAC,QAAG,WAAU,QAAO;AAAA,OAC/C,GACF;AAAA,IACA,oBAAC,WACE,eAAK,IAAI,CAAC,KAAK,UAAU;AACxB,UAAI,IAAI,SAAS,UAAU,IAAI,SAAS,cAAc,IAAI,SAAS,SAAS;AAC1E,cAAM,QAAQ,IAAI,SAAS,SACvB,EAAE,uCAAuC,YAAY,IACrD,IAAI,SAAS,aACX,EAAE,2CAA2C,gBAAgB,IAC7D,EAAE,wCAAwC,aAAa;AAC7D,eACE,qBAAC,QAAgC,WAAW,6BAA6B,IAAI,SAAS,UAAU,kBAAkB,aAAa,IAC7H;AAAA,8BAAC,QAAG,WAAU,UAAS,SAAS,GAAI,iBAAM;AAAA,UAC1C,oBAAC,QAAG,WAAU,+BAA+B,gBAAM,IAAI,WAAW,QAAQ,GAAE;AAAA,UAC3E,uBAAuB,oBAAC,QAAG;AAAA,aAHrB,GAAG,IAAI,IAAI,IAAI,KAAK,EAI7B;AAAA,MAEJ;AACA,YAAM,SAAS,IAAI,SAAS,SACxB,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport {\n discountDepth,\n formatDepth,\n waterfallRows,\n type WaterfallChargeInput,\n} from '../lib/priceAdjustmentView'\n\nfunction money(amount: number, currency: string): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nconst TYPE_FALLBACKS: Record<string, string> = {\n discount_percent: '% discount',\n discount_absolute: 'amount off',\n surcharge_percent: '% surcharge',\n surcharge_absolute: 'fixed surcharge',\n price_override: 'price override',\n}\n\n/** Signed depth badge: \"\u221220%\" for a discount, \"+5%\" for a surcharge; text, never colour only. */\nexport function DepthBadge({ before, after, title }: { before: number | null | undefined; after: number | null | undefined; title?: string }) {\n const label = formatDepth(discountDepth(before, after))\n if (!label) return null\n return <Tag variant=\"neutral\" className=\"px-1.5 text-xs font-mono\" title={title}>{label}</Tag>\n}\n\nexport type PriceWaterfallProps = {\n charge: WaterfallChargeInput & { chargeName?: string }\n currency: string\n /** Reason code \u2192 label; manual rows fall back to the code. */\n reasonLabels?: Record<string, string>\n /** Optional per-adjustment actions (edit / delete) rendered on manual rows. */\n renderManualActions?: (adjustmentId: string) => React.ReactNode\n}\n\n/** Read-only list \u2192 rule \u2192 standard \u2192 manual \u2192 offer breakdown for one charge (unit prices). */\nexport function PriceWaterfall({ charge, currency, reasonLabels, renderManualActions }: PriceWaterfallProps) {\n const t = useT()\n const rows = waterfallRows(charge)\n const fmtValue = (ruleType: string, value: number) =>\n ruleType.endsWith('_percent') ? `${value}%` : money(value, currency)\n return (\n <table className=\"w-full text-xs\" data-testid=\"cpq-price-waterfall\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"text-left py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.step', 'Step')}</th>\n <th className=\"text-left py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.source', 'Source')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.value', 'Value')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.delta', 'Delta')}</th>\n <th className=\"text-right py-0.5 font-medium\">{t('cpq.priceAdjustments.waterfall.unitPrice', 'Unit price')}</th>\n {renderManualActions && <th className=\"w-16\" />}\n </tr>\n </thead>\n <tbody>\n {rows.map((row, index) => {\n if (row.kind === 'list' || row.kind === 'standard' || row.kind === 'offer') {\n const label = row.kind === 'list'\n ? t('cpq.priceAdjustments.waterfall.list', 'List price')\n : row.kind === 'standard'\n ? t('cpq.priceAdjustments.waterfall.standard', 'Standard price')\n : t('cpq.priceAdjustments.waterfall.offer', 'Offer price')\n return (\n <tr key={`${row.kind}-${index}`} className={`border-t border-border/30 ${row.kind === 'offer' ? 'font-semibold' : 'font-medium'}`}>\n <td className=\"py-0.5\" colSpan={4}>{label}</td>\n <td className=\"py-0.5 text-right font-mono\">{money(row.unitPrice, currency)}</td>\n {renderManualActions && <td />}\n </tr>\n )\n }\n const source = row.kind === 'rule'\n ? t('cpq.priceAdjustments.waterfall.ruleSource', 'Price rule: {name}', { name: row.label })\n : t('cpq.priceAdjustments.waterfall.manualSource', 'Negotiated: {reason}', {\n reason: reasonLabels?.[row.reasonCode ?? ''] ?? row.reasonCode ?? row.label,\n })\n return (\n <tr key={`${row.kind}-${index}`} className=\"border-t border-border/30\">\n <td className=\"py-0.5 pl-3 text-muted-foreground\">{t(`cpq.priceAdjustments.types.${row.ruleType}`, TYPE_FALLBACKS[row.ruleType] ?? row.ruleType)}</td>\n <td className=\"py-0.5\">{source}</td>\n <td className=\"py-0.5 text-right font-mono\">{fmtValue(row.ruleType, row.value)}</td>\n <td className=\"py-0.5 text-right font-mono\">{row.delta > 0 ? '+' : ''}{money(row.delta, currency)}</td>\n <td className=\"py-0.5 text-right font-mono\">{money(row.unitPriceAfter, currency)}</td>\n {renderManualActions && (\n <td className=\"py-0.5 text-right\">{row.kind === 'manual' && row.adjustmentId ? renderManualActions(row.adjustmentId) : null}</td>\n )}\n </tr>\n )\n })}\n </tbody>\n </table>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -69,12 +69,7 @@ function PricingSummaryCard({ summary, currency, arcDelta }) {
|
|
|
69
69
|
),
|
|
70
70
|
usageCharges.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
71
71
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.quotes.summary.usageCharges", "Usage Charges") }),
|
|
72
|
-
usageCharges.map((uc, i) => /* @__PURE__ */
|
|
73
|
-
uc.chargeName,
|
|
74
|
-
": ",
|
|
75
|
-
formatMoney(uc.unitPrice, currency),
|
|
76
|
-
"/unit"
|
|
77
|
-
] }, i))
|
|
72
|
+
usageCharges.map((uc, i) => /* @__PURE__ */ jsx("p", { className: "text-xs mt-0.5", children: t("cpq.quotes.summary.usageChargeLine", "{name}: {price}/unit", { name: uc.chargeName, price: formatMoney(uc.unitPrice, currency) }) }, i))
|
|
78
73
|
] })
|
|
79
74
|
] }),
|
|
80
75
|
arcDelta && /* @__PURE__ */ jsxs("div", { className: "mt-4 grid grid-cols-2 sm:grid-cols-4 gap-4 border-t pt-3", "data-testid": "cpq-arc-delta", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/components/PricingSummaryCard.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatDepth } from '../lib/priceAdjustmentView'\nimport { chargeTypeDepths, formatChangePct, type ArcRecurringDelta } from '../lib/quoteSummaryView'\nimport { formatMoney } from '../lib/formatMoney'\n\n/** Pricing summary fields shared by quote and order results. Waterfall fields are absent on legacy snapshots. */\nexport type PricingSummaryView = {\n nrcTotal: number\n mrcTotal: number\n listNrcTotal?: number\n listMrcTotal?: number\n dealValue?: { list: number; standard: number; offer: number }\n discountDepth?: { total: number | null } | null\n usageTotalEstimated?: number | null\n usageCharges?: Array<{ chargeName: string; unitPrice: number }>\n}\n\nexport function SummaryFigure({\n label,\n value,\n note,\n title,\n size = 'lg',\n valueClassName,\n testId,\n}: {\n label: string\n value: string\n note?: string | null\n title?: string\n size?: 'lg' | 'sm'\n valueClassName?: string\n testId?: string\n}) {\n return (\n <div data-testid={testId}>\n <p className=\"text-xs text-muted-foreground\" title={title}>{label}</p>\n <p className={`${size === 'lg' ? 'text-lg' : 'text-sm'} font-semibold font-mono ${valueClassName ?? ''}`}>\n {value}\n {note && <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">({note})</span>}\n </p>\n </div>\n )\n}\n\n/**\n * NRC / MRC / deal value with discount depth in brackets, plus the ARC\n * current \u2192 new MRC row on Amend / Renew documents. Shared by the quote and\n * order detail pages so both read the same.\n */\nexport function PricingSummaryCard({ summary, currency, arcDelta }: {\n summary: PricingSummaryView\n currency: string\n arcDelta?: ArcRecurringDelta | null\n}) {\n const t = useT()\n const typeDepths = chargeTypeDepths(summary)\n const usageCharges = summary.usageCharges ?? []\n return (\n <div className=\"rounded-lg border bg-card p-4\" data-testid=\"cpq-pricing-summary\">\n <h3 className=\"text-sm font-medium mb-3\">{t('cpq.quotes.summary.title', 'Pricing Summary')}</h3>\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <SummaryFigure\n label={t('cpq.quotes.summary.nrc', 'Non-Recurring (NRC)')}\n value={formatMoney(summary.nrcTotal, currency)}\n note={formatDepth(typeDepths.nrc)}\n testId=\"cpq-summary-nrc\"\n />\n <SummaryFigure\n label={t('cpq.quotes.summary.mrc', 'Monthly Recurring (MRC)')}\n value={formatMoney(summary.mrcTotal, currency)}\n note={formatDepth(typeDepths.mrc)}\n testId=\"cpq-summary-mrc\"\n />\n {summary.dealValue && (\n <SummaryFigure\n label={t('cpq.priceAdjustments.summary.dealValue', 'Deal value')}\n title={t('cpq.priceAdjustments.summary.hint', 'Deal value counts recurring charges over the term plus one-time charges; existing subscription items on change quotes are excluded.')}\n value={formatMoney(summary.dealValue.offer, currency)}\n note={formatDepth(summary.discountDepth?.total ?? null)}\n testId=\"cpq-deal-value\"\n />\n )}\n {summary.usageTotalEstimated != null && (\n <SummaryFigure\n label={t('cpq.quotes.summary.usageEstimated', 'Usage (est.)')}\n value={formatMoney(summary.usageTotalEstimated, currency)}\n />\n )}\n {usageCharges.length > 0 && (\n <div>\n <p className=\"text-xs text-muted-foreground\">{t('cpq.quotes.summary.usageCharges', 'Usage Charges')}</p>\n {usageCharges.map((uc, i) => (\n <p key={i} className=\"text-xs mt-0.5\">{uc.chargeName
|
|
5
|
-
"mappings": ";AAuCM,cAGW,YAHX;AApCN,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB,uBAA+C;AAC1E,SAAS,mBAAmB;AAcrB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AACF,GAQG;AACD,SACE,qBAAC,SAAI,eAAa,QAChB;AAAA,wBAAC,OAAE,WAAU,iCAAgC,OAAe,iBAAM;AAAA,IAClE,qBAAC,OAAE,WAAW,GAAG,SAAS,OAAO,YAAY,SAAS,4BAA4B,kBAAkB,EAAE,IACnG;AAAA;AAAA,MACA,QAAQ,qBAAC,UAAK,WAAU,oDAAmD;AAAA;AAAA,QAAE;AAAA,QAAK;AAAA,SAAC;AAAA,OACtF;AAAA,KACF;AAEJ;AAOO,SAAS,mBAAmB,EAAE,SAAS,UAAU,SAAS,GAI9D;AACD,QAAM,IAAI,KAAK;AACf,QAAM,aAAa,iBAAiB,OAAO;AAC3C,QAAM,eAAe,QAAQ,gBAAgB,CAAC;AAC9C,SACE,qBAAC,SAAI,WAAU,iCAAgC,eAAY,uBACzD;AAAA,wBAAC,QAAG,WAAU,4BAA4B,YAAE,4BAA4B,iBAAiB,GAAE;AAAA,IAC3F,qBAAC,SAAI,WAAU,yCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,0BAA0B,qBAAqB;AAAA,UACxD,OAAO,YAAY,QAAQ,UAAU,QAAQ;AAAA,UAC7C,MAAM,YAAY,WAAW,GAAG;AAAA,UAChC,QAAO;AAAA;AAAA,MACT;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,0BAA0B,yBAAyB;AAAA,UAC5D,OAAO,YAAY,QAAQ,UAAU,QAAQ;AAAA,UAC7C,MAAM,YAAY,WAAW,GAAG;AAAA,UAChC,QAAO;AAAA;AAAA,MACT;AAAA,MACC,QAAQ,aACP;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,0CAA0C,YAAY;AAAA,UAC/D,OAAO,EAAE,qCAAqC,qIAAqI;AAAA,UACnL,OAAO,YAAY,QAAQ,UAAU,OAAO,QAAQ;AAAA,UACpD,MAAM,YAAY,QAAQ,eAAe,SAAS,IAAI;AAAA,UACtD,QAAO;AAAA;AAAA,MACT;AAAA,MAED,QAAQ,uBAAuB,QAC9B;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,qCAAqC,cAAc;AAAA,UAC5D,OAAO,YAAY,QAAQ,qBAAqB,QAAQ;AAAA;AAAA,MAC1D;AAAA,MAED,aAAa,SAAS,KACrB,qBAAC,SACC;AAAA,4BAAC,OAAE,WAAU,iCAAiC,YAAE,mCAAmC,eAAe,GAAE;AAAA,QACnG,aAAa,IAAI,CAAC,IAAI,MACrB,
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatDepth } from '../lib/priceAdjustmentView'\nimport { chargeTypeDepths, formatChangePct, type ArcRecurringDelta } from '../lib/quoteSummaryView'\nimport { formatMoney } from '../lib/formatMoney'\n\n/** Pricing summary fields shared by quote and order results. Waterfall fields are absent on legacy snapshots. */\nexport type PricingSummaryView = {\n nrcTotal: number\n mrcTotal: number\n listNrcTotal?: number\n listMrcTotal?: number\n dealValue?: { list: number; standard: number; offer: number }\n discountDepth?: { total: number | null } | null\n usageTotalEstimated?: number | null\n usageCharges?: Array<{ chargeName: string; unitPrice: number }>\n}\n\nexport function SummaryFigure({\n label,\n value,\n note,\n title,\n size = 'lg',\n valueClassName,\n testId,\n}: {\n label: string\n value: string\n note?: string | null\n title?: string\n size?: 'lg' | 'sm'\n valueClassName?: string\n testId?: string\n}) {\n return (\n <div data-testid={testId}>\n <p className=\"text-xs text-muted-foreground\" title={title}>{label}</p>\n <p className={`${size === 'lg' ? 'text-lg' : 'text-sm'} font-semibold font-mono ${valueClassName ?? ''}`}>\n {value}\n {note && <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">({note})</span>}\n </p>\n </div>\n )\n}\n\n/**\n * NRC / MRC / deal value with discount depth in brackets, plus the ARC\n * current \u2192 new MRC row on Amend / Renew documents. Shared by the quote and\n * order detail pages so both read the same.\n */\nexport function PricingSummaryCard({ summary, currency, arcDelta }: {\n summary: PricingSummaryView\n currency: string\n arcDelta?: ArcRecurringDelta | null\n}) {\n const t = useT()\n const typeDepths = chargeTypeDepths(summary)\n const usageCharges = summary.usageCharges ?? []\n return (\n <div className=\"rounded-lg border bg-card p-4\" data-testid=\"cpq-pricing-summary\">\n <h3 className=\"text-sm font-medium mb-3\">{t('cpq.quotes.summary.title', 'Pricing Summary')}</h3>\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <SummaryFigure\n label={t('cpq.quotes.summary.nrc', 'Non-Recurring (NRC)')}\n value={formatMoney(summary.nrcTotal, currency)}\n note={formatDepth(typeDepths.nrc)}\n testId=\"cpq-summary-nrc\"\n />\n <SummaryFigure\n label={t('cpq.quotes.summary.mrc', 'Monthly Recurring (MRC)')}\n value={formatMoney(summary.mrcTotal, currency)}\n note={formatDepth(typeDepths.mrc)}\n testId=\"cpq-summary-mrc\"\n />\n {summary.dealValue && (\n <SummaryFigure\n label={t('cpq.priceAdjustments.summary.dealValue', 'Deal value')}\n title={t('cpq.priceAdjustments.summary.hint', 'Deal value counts recurring charges over the term plus one-time charges; existing subscription items on change quotes are excluded.')}\n value={formatMoney(summary.dealValue.offer, currency)}\n note={formatDepth(summary.discountDepth?.total ?? null)}\n testId=\"cpq-deal-value\"\n />\n )}\n {summary.usageTotalEstimated != null && (\n <SummaryFigure\n label={t('cpq.quotes.summary.usageEstimated', 'Usage (est.)')}\n value={formatMoney(summary.usageTotalEstimated, currency)}\n />\n )}\n {usageCharges.length > 0 && (\n <div>\n <p className=\"text-xs text-muted-foreground\">{t('cpq.quotes.summary.usageCharges', 'Usage Charges')}</p>\n {usageCharges.map((uc, i) => (\n <p key={i} className=\"text-xs mt-0.5\">{t('cpq.quotes.summary.usageChargeLine', '{name}: {price}/unit', { name: uc.chargeName, price: formatMoney(uc.unitPrice, currency) })}</p>\n ))}\n </div>\n )}\n </div>\n {arcDelta && (\n <div className=\"mt-4 grid grid-cols-2 sm:grid-cols-4 gap-4 border-t pt-3\" data-testid=\"cpq-arc-delta\">\n <SummaryFigure\n size=\"sm\"\n testId=\"cpq-arc-current-mrc\"\n label={t('cpq.quotes.summary.arc.currentMrc', 'Current MRC')}\n value={formatMoney(arcDelta.currentMrc, currency)}\n />\n <SummaryFigure\n size=\"sm\"\n testId=\"cpq-arc-new-mrc\"\n label={t('cpq.quotes.summary.arc.newMrc', 'New MRC')}\n value={formatMoney(arcDelta.newMrc, currency)}\n />\n <SummaryFigure\n size=\"sm\"\n testId=\"cpq-arc-mrc-change\"\n label={t('cpq.quotes.summary.arc.mrcChange', 'MRC change')}\n value={`${arcDelta.delta > 0 ? '+' : ''}${formatMoney(arcDelta.delta, currency)}`}\n valueClassName={arcDelta.delta > 0 ? 'text-status-success-text' : arcDelta.delta < 0 ? 'text-destructive' : undefined}\n note={formatChangePct(arcDelta.deltaPct)}\n />\n </div>\n )}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { ChevronRight } from "lucide-react";
|
|
5
5
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
6
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
7
|
import { formatStatusLabel } from "./statusMaps.js";
|
|
7
8
|
function StatusPath({
|
|
8
9
|
current,
|
|
@@ -13,15 +14,16 @@ function StatusPath({
|
|
|
13
14
|
onTransition,
|
|
14
15
|
allowedTransitions,
|
|
15
16
|
disabled,
|
|
16
|
-
ariaLabel
|
|
17
|
+
ariaLabel
|
|
17
18
|
}) {
|
|
19
|
+
const t = useT();
|
|
18
20
|
const sequence = React.useMemo(
|
|
19
21
|
() => [...happyPath, ...terminals],
|
|
20
22
|
[happyPath, terminals]
|
|
21
23
|
);
|
|
22
24
|
const currentIdx = sequence.findIndex((s) => s === current);
|
|
23
25
|
const interactiveMode = typeof onTransition === "function";
|
|
24
|
-
return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: "flex items-center gap-1.5 flex-wrap text-xs", children: sequence.map((status, idx) => {
|
|
26
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel ?? t("cpq.statusPath.ariaLabel", "Status path"), className: "flex items-center gap-1.5 flex-wrap text-xs", children: sequence.map((status, idx) => {
|
|
25
27
|
const reached = currentIdx >= 0 && idx <= currentIdx;
|
|
26
28
|
const isCurrent = status === current;
|
|
27
29
|
const variant = statusMap[status] ?? "neutral";
|
|
@@ -37,7 +39,7 @@ function StatusPath({
|
|
|
37
39
|
type: "button",
|
|
38
40
|
onClick: () => onTransition(status),
|
|
39
41
|
"aria-current": isCurrent ? "step" : void 0,
|
|
40
|
-
title:
|
|
42
|
+
title: t("cpq.statusPath.transitionTo", "Transition to {status}", { status: label }),
|
|
41
43
|
className: wrapperClass,
|
|
42
44
|
children: /* @__PURE__ */ jsx(Tag, { variant, dot: isCurrent, className: tagClass, children: label })
|
|
43
45
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/components/StatusPath.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { ChevronRight } from 'lucide-react'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport type { TagMap } from '@open-mercato/ui/primitives/tag'\nimport { formatStatusLabel } from './statusMaps'\n\n// Generic horizontal status breadcrumb shared by quotes, orders,\n// subscriptions, and assets. Each consumer passes its own `path`\n// (happy-path order) + `terminals` (off-path failure / cancellation\n// states always rendered at the end) + `statusMap` (status \u2192 Tag\n// variant for colours).\n//\n// Two interaction modes:\n// - read-only: omit `onTransition`. Path renders as a progress\n// indicator only.\n// - interactive: pass `onTransition`. Every non-current step\n// becomes a button that calls `onTransition(target)`. If\n// `allowedTransitions` is provided, only those targets are\n// clickable; otherwise all non-current steps are clickable.\n\ntype TagVariant = TagMap<string>[string]\n\nexport function StatusPath({\n current,\n path: happyPath,\n terminals = [],\n statusMap,\n labels,\n onTransition,\n allowedTransitions,\n disabled,\n ariaLabel
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { ChevronRight } from 'lucide-react'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TagMap } from '@open-mercato/ui/primitives/tag'\nimport { formatStatusLabel } from './statusMaps'\n\n// Generic horizontal status breadcrumb shared by quotes, orders,\n// subscriptions, and assets. Each consumer passes its own `path`\n// (happy-path order) + `terminals` (off-path failure / cancellation\n// states always rendered at the end) + `statusMap` (status \u2192 Tag\n// variant for colours).\n//\n// Two interaction modes:\n// - read-only: omit `onTransition`. Path renders as a progress\n// indicator only.\n// - interactive: pass `onTransition`. Every non-current step\n// becomes a button that calls `onTransition(target)`. If\n// `allowedTransitions` is provided, only those targets are\n// clickable; otherwise all non-current steps are clickable.\n\ntype TagVariant = TagMap<string>[string]\n\nexport function StatusPath({\n current,\n path: happyPath,\n terminals = [],\n statusMap,\n labels,\n onTransition,\n allowedTransitions,\n disabled,\n ariaLabel,\n}: {\n current: string\n /** Happy-path statuses in order. */\n path: string[]\n /** Off-path terminal statuses always rendered after the happy path. */\n terminals?: string[]\n /** Status \u2192 Tag variant. Missing entries fall back to \"neutral\". */\n statusMap: Record<string, TagVariant>\n /** Optional human labels, already translated by the caller (overrides `formatStatusLabel`). */\n labels?: Record<string, string>\n /** Click handler. Omit for read-only path. */\n onTransition?: (target: string) => void\n /** If set, only these targets are clickable. Else any non-current. */\n allowedTransitions?: string[]\n disabled?: boolean\n /** Translated accessible name; defaults to \"Status path\". */\n ariaLabel?: string\n}) {\n const t = useT()\n // Build the displayed sequence. Aliases can swap into the happy\n // path slot (e.g. quote's `pre_approved` shown as `approved`). We\n // don't handle aliasing here \u2014 consumers can swap entries in\n // their own happyPath before passing it in.\n const sequence = React.useMemo<string[]>(\n () => [...happyPath, ...terminals],\n [happyPath, terminals],\n )\n\n const currentIdx = sequence.findIndex((s) => s === current)\n const interactiveMode = typeof onTransition === 'function'\n\n return (\n <nav aria-label={ariaLabel ?? t('cpq.statusPath.ariaLabel', 'Status path')} className=\"flex items-center gap-1.5 flex-wrap text-xs\">\n {sequence.map((status, idx) => {\n const reached = currentIdx >= 0 && idx <= currentIdx\n const isCurrent = status === current\n const variant = statusMap[status] ?? 'neutral'\n const label = labels?.[status] ?? formatStatusLabel(status)\n const tagClass = isCurrent\n ? 'ring-2 ring-primary/40'\n : reached\n ? ''\n : 'opacity-60'\n // Clickability: interactive mode + not current + not disabled\n // + (no allowedTransitions list OR status is in the list).\n const clickable =\n interactiveMode &&\n !isCurrent &&\n !disabled &&\n (allowedTransitions ? allowedTransitions.includes(status) : true)\n const wrapperClass = clickable\n ? 'cursor-pointer hover:ring-2 hover:ring-primary/30 rounded-full'\n : ''\n\n return (\n <React.Fragment key={status}>\n {idx > 0 && <ChevronRight className=\"h-3 w-3 text-muted-foreground shrink-0\" />}\n {clickable ? (\n <button\n type=\"button\"\n onClick={() => onTransition!(status)}\n aria-current={isCurrent ? 'step' : undefined}\n title={t('cpq.statusPath.transitionTo', 'Transition to {status}', { status: label })}\n className={wrapperClass}\n >\n <Tag variant={variant} dot={isCurrent} className={tagClass}>\n {label}\n </Tag>\n </button>\n ) : (\n <Tag\n variant={variant}\n dot={isCurrent}\n className={tagClass}\n aria-current={isCurrent ? 'step' : undefined}\n >\n {label}\n </Tag>\n )}\n </React.Fragment>\n )\n })}\n </nav>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAyFU,SACc,KADd;AAxFV,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAC7B,SAAS,WAAW;AACpB,SAAS,YAAY;AAErB,SAAS,yBAAyB;AAkB3B,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,MAAM;AAAA,EACN,YAAY,CAAC;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAiBG;AACD,QAAM,IAAI,KAAK;AAKf,QAAM,WAAW,MAAM;AAAA,IACrB,MAAM,CAAC,GAAG,WAAW,GAAG,SAAS;AAAA,IACjC,CAAC,WAAW,SAAS;AAAA,EACvB;AAEA,QAAM,aAAa,SAAS,UAAU,CAAC,MAAM,MAAM,OAAO;AAC1D,QAAM,kBAAkB,OAAO,iBAAiB;AAEhD,SACE,oBAAC,SAAI,cAAY,aAAa,EAAE,4BAA4B,aAAa,GAAG,WAAU,+CACnF,mBAAS,IAAI,CAAC,QAAQ,QAAQ;AAC7B,UAAM,UAAU,cAAc,KAAK,OAAO;AAC1C,UAAM,YAAY,WAAW;AAC7B,UAAM,UAAU,UAAU,MAAM,KAAK;AACrC,UAAM,QAAQ,SAAS,MAAM,KAAK,kBAAkB,MAAM;AAC1D,UAAM,WAAW,YACb,2BACA,UACE,KACA;AAGN,UAAM,YACJ,mBACA,CAAC,aACD,CAAC,aACA,qBAAqB,mBAAmB,SAAS,MAAM,IAAI;AAC9D,UAAM,eAAe,YACjB,mEACA;AAEJ,WACE,qBAAC,MAAM,UAAN,EACE;AAAA,YAAM,KAAK,oBAAC,gBAAa,WAAU,0CAAyC;AAAA,MAC5E,YACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,aAAc,MAAM;AAAA,UACnC,gBAAc,YAAY,SAAS;AAAA,UACnC,OAAO,EAAE,+BAA+B,0BAA0B,EAAE,QAAQ,MAAM,CAAC;AAAA,UACnF,WAAW;AAAA,UAEX,8BAAC,OAAI,SAAkB,KAAK,WAAW,WAAW,UAC/C,iBACH;AAAA;AAAA,MACF,IAEA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,KAAK;AAAA,UACL,WAAW;AAAA,UACX,gBAAc,YAAY,SAAS;AAAA,UAElC;AAAA;AAAA,MACH;AAAA,SAtBiB,MAwBrB;AAAA,EAEJ,CAAC,GACH;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,7 +4,7 @@ import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
|
4
4
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
5
|
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
6
6
|
function useCpqRowActions(options) {
|
|
7
|
-
const { endpoint,
|
|
7
|
+
const { endpoint, editHref, onReload, canDelete } = options;
|
|
8
8
|
const t = useT();
|
|
9
9
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
10
10
|
const deleteOne = React.useCallback(
|
|
@@ -22,25 +22,22 @@ function useCpqRowActions(options) {
|
|
|
22
22
|
async (row) => {
|
|
23
23
|
if (canDelete && !canDelete(row)) return;
|
|
24
24
|
const confirmed = await confirm({
|
|
25
|
-
title: t(
|
|
26
|
-
"cpq.rowActions.deleteOne.confirm",
|
|
27
|
-
`Delete this ${entityName}?`
|
|
28
|
-
),
|
|
25
|
+
title: t("cpq.rowActions.deleteOne.confirm", "Delete this item?"),
|
|
29
26
|
variant: "destructive"
|
|
30
27
|
});
|
|
31
28
|
if (!confirmed) return;
|
|
32
29
|
const ok = await deleteOne(row.id);
|
|
33
30
|
if (!ok) {
|
|
34
31
|
flash(
|
|
35
|
-
t("cpq.rowActions.deleteOne.error",
|
|
32
|
+
t("cpq.rowActions.deleteOne.error", "Failed to delete the item"),
|
|
36
33
|
"error"
|
|
37
34
|
);
|
|
38
35
|
return;
|
|
39
36
|
}
|
|
40
|
-
flash(t("cpq.rowActions.deleteOne.success",
|
|
37
|
+
flash(t("cpq.rowActions.deleteOne.success", "Item deleted"), "success");
|
|
41
38
|
onReload();
|
|
42
39
|
},
|
|
43
|
-
[canDelete, confirm, deleteOne,
|
|
40
|
+
[canDelete, confirm, deleteOne, onReload, t]
|
|
44
41
|
);
|
|
45
42
|
const bulkDelete = React.useCallback(
|
|
46
43
|
async (rows) => {
|
|
@@ -48,19 +45,13 @@ function useCpqRowActions(options) {
|
|
|
48
45
|
const eligible = canDelete ? rows.filter(canDelete) : rows;
|
|
49
46
|
if (!eligible.length) {
|
|
50
47
|
flash(
|
|
51
|
-
t(
|
|
52
|
-
"cpq.rowActions.deleteBulk.noEligible",
|
|
53
|
-
`None of the selected ${entityName}s can be deleted in their current state.`
|
|
54
|
-
),
|
|
48
|
+
t("cpq.rowActions.deleteBulk.noEligible", "None of the selected items can be deleted in their current state."),
|
|
55
49
|
"error"
|
|
56
50
|
);
|
|
57
51
|
return { ok: false };
|
|
58
52
|
}
|
|
59
53
|
const confirmed = await confirm({
|
|
60
|
-
title: t(
|
|
61
|
-
"cpq.rowActions.deleteBulk.confirm",
|
|
62
|
-
`Delete ${eligible.length} ${entityName}${eligible.length > 1 ? "s" : ""}?`
|
|
63
|
-
),
|
|
54
|
+
title: t("cpq.rowActions.deleteBulk.confirm", "Delete selected items ({count})?", { count: eligible.length }),
|
|
64
55
|
variant: "destructive"
|
|
65
56
|
});
|
|
66
57
|
if (!confirmed) return { ok: false };
|
|
@@ -71,22 +62,19 @@ function useCpqRowActions(options) {
|
|
|
71
62
|
}
|
|
72
63
|
if (failed > 0) {
|
|
73
64
|
flash(
|
|
74
|
-
t(
|
|
75
|
-
"cpq.rowActions.deleteBulk.partialError",
|
|
76
|
-
`Failed to delete ${failed} ${entityName}${failed > 1 ? "s" : ""}`
|
|
77
|
-
),
|
|
65
|
+
t("cpq.rowActions.deleteBulk.partialError", "Failed to delete {count} of the selected items", { count: failed }),
|
|
78
66
|
"error"
|
|
79
67
|
);
|
|
80
68
|
} else {
|
|
81
69
|
flash(
|
|
82
|
-
t("cpq.rowActions.deleteBulk.success",
|
|
70
|
+
t("cpq.rowActions.deleteBulk.success", "Selected items deleted ({count})", { count: eligible.length }),
|
|
83
71
|
"success"
|
|
84
72
|
);
|
|
85
73
|
}
|
|
86
74
|
onReload();
|
|
87
75
|
return { ok: failed === 0, affectedCount: eligible.length - failed };
|
|
88
76
|
},
|
|
89
|
-
[canDelete, confirm, deleteOne,
|
|
77
|
+
[canDelete, confirm, deleteOne, onReload, t]
|
|
90
78
|
);
|
|
91
79
|
const buildItems = React.useCallback(
|
|
92
80
|
(row, extras) => {
|