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