@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @dainamite/cpq
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d86651d: CPQ UI in English, Spanish, German and Polish.
|
|
8
|
+
|
|
9
|
+
- **Dictionaries.** New `i18n/{en,es,de,pl}.json` (1218 keys). CPQ previously shipped no dictionaries, so every screen rendered English in every locale.
|
|
10
|
+
- **Every UI string is translatable.** Hard-coded labels in quotes, orders, the quote wizard, specifications, offerings, inventory, price rules, pricing tables, wizard definitions, shared components and the injected widgets now go through `t()`. English text is unchanged.
|
|
11
|
+
- **Own generic labels.** `cpq.common.*` replaces borrowed `common.*` keys that only some unrelated modules shipped (`common.inactive` existed nowhere).
|
|
12
|
+
- **Sales tabs translated.** The quote / order tabs on sales documents use i18n keys as `groupLabel`. The customer "Inventory" tab stays English (legacy customer pages render `groupLabel` verbatim).
|
|
13
|
+
- **Row-action messages are noun-free** (`Delete selected items ({count})?`), because a spliced entity noun can't be grammatical in ES / DE / PL. `useCpqRowActions({ entityName })` is now optional and deprecated.
|
|
14
|
+
- **API change for direct importers:** `parsePricingTableXlsx` / `commitImportDiff` take a required `t` argument, and `StatusPath`'s `ariaLabel` is optional.
|
|
15
|
+
- **Guardrails:** tests fail on a missing or orphaned key, a dropped `{placeholder}`, or a new hard-coded UI string.
|
|
16
|
+
|
|
17
|
+
No migration. After upgrading run `yarn generate` so the app picks up the dictionaries.
|
|
18
|
+
|
|
3
19
|
## 0.6.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -7,15 +7,9 @@ import { DataTable } from "@open-mercato/ui/backend/DataTable";
|
|
|
7
7
|
import { RowActions } from "@open-mercato/ui/backend/RowActions";
|
|
8
8
|
import { useCpqListData } from "../../../components/CpqListView.js";
|
|
9
9
|
import { useCpqRowActions } from "../../../components/useCpqRowActions.js";
|
|
10
|
-
import { StatusBadge, formatCurrency } from "./shared.js";
|
|
10
|
+
import { StatusBadge, buildInventoryStatusOptions, formatCurrency } from "./shared.js";
|
|
11
11
|
const PAGE_SIZE = 50;
|
|
12
|
-
const
|
|
13
|
-
{ value: "pending", label: "Pending" },
|
|
14
|
-
{ value: "delivered", label: "Delivered" },
|
|
15
|
-
{ value: "active", label: "Active" },
|
|
16
|
-
{ value: "returned", label: "Returned" },
|
|
17
|
-
{ value: "cancelled", label: "Cancelled" }
|
|
18
|
-
];
|
|
12
|
+
const ASSET_STATUSES = ["pending", "delivered", "active", "returned", "cancelled"];
|
|
19
13
|
function buildFilterParams(values, params) {
|
|
20
14
|
if (typeof values.status === "string" && values.status) {
|
|
21
15
|
params.set("status", values.status);
|
|
@@ -46,7 +40,7 @@ function AssetsTable() {
|
|
|
46
40
|
id: "status",
|
|
47
41
|
label: t("cpq.inventory.filters.status", "Status"),
|
|
48
42
|
type: "select",
|
|
49
|
-
options:
|
|
43
|
+
options: buildInventoryStatusOptions(t, ASSET_STATUSES)
|
|
50
44
|
},
|
|
51
45
|
{
|
|
52
46
|
id: "assetType",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { useCpqListData } from '../../../components/CpqListView'\nimport { useCpqRowActions } from '../../../components/useCpqRowActions'\nimport { StatusBadge, formatCurrency } from './shared'\n\ntype Asset = {\n id: string\n code: string\n name: string\n status: string\n assetType: string\n purchasePrice: number\n currencyCode: string\n createdAt: string\n}\n\nconst PAGE_SIZE = 50\n\nconst
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { useCpqListData } from '../../../components/CpqListView'\nimport { useCpqRowActions } from '../../../components/useCpqRowActions'\nimport { StatusBadge, buildInventoryStatusOptions, formatCurrency } from './shared'\n\ntype Asset = {\n id: string\n code: string\n name: string\n status: string\n assetType: string\n purchasePrice: number\n currencyCode: string\n createdAt: string\n}\n\nconst PAGE_SIZE = 50\n\nconst ASSET_STATUSES = ['pending', 'delivered', 'active', 'returned', 'cancelled'] as const\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.status === 'string' && values.status) {\n params.set('status', values.status)\n }\n if (typeof values.assetType === 'string' && values.assetType.trim()) {\n params.set('assetType', values.assetType.trim())\n }\n}\n\nexport function AssetsTable() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<Asset>({\n endpoint: '/api/cpq/inventory/assets',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.inventory.assets.error.load', 'Failed to load assets'),\n })\n\n // Assets follow a `delivered \u2192 active \u2192 returned` lifecycle; deletion only\n // makes sense before the asset is provisioned, so gate by status.\n const rowActionsApi = useCpqRowActions<Asset>({\n endpoint: '/api/cpq/inventory/assets',\n entityName: t('cpq.inventory.assets.entityName', 'asset'),\n editHref: (row) => `/backend/cpq/inventory/assets/${row.id}`,\n onReload: data.reload,\n canDelete: (row) => row.status === 'pending' || row.status === 'cancelled',\n })\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'status',\n label: t('cpq.inventory.filters.status', 'Status'),\n type: 'select',\n options: buildInventoryStatusOptions(t, ASSET_STATUSES),\n },\n {\n id: 'assetType',\n label: t('cpq.inventory.filters.assetType', 'Asset Type'),\n type: 'text',\n },\n ],\n [t],\n )\n\n const columns = React.useMemo<ColumnDef<Asset>[]>(\n () => [\n {\n accessorKey: 'code',\n header: t('cpq.inventory.assets.table.code', 'Code'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.code}</span>,\n },\n {\n accessorKey: 'name',\n header: t('cpq.inventory.assets.table.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium text-primary\">{row.original.name}</span>,\n },\n {\n accessorKey: 'assetType',\n header: t('cpq.inventory.assets.table.type', 'Type'),\n },\n {\n accessorKey: 'status',\n header: t('cpq.inventory.assets.table.status', 'Status'),\n cell: ({ row }) => <StatusBadge status={row.original.status} />,\n },\n {\n accessorKey: 'purchasePrice',\n header: t('cpq.inventory.assets.table.price', 'Price'),\n cell: ({ row }) => (\n <span className=\"font-mono\">\n {formatCurrency(row.original.purchasePrice, row.original.currencyCode || 'USD')}\n </span>\n ),\n },\n {\n accessorKey: 'createdAt',\n header: t('cpq.inventory.assets.table.created', 'Created'),\n cell: ({ row }) => (\n <span>\n {row.original.createdAt ? new Date(row.original.createdAt).toLocaleDateString() : '\u2014'}\n </span>\n ),\n },\n ],\n [t],\n )\n\n return (\n <>\n {rowActionsApi.ConfirmDialogElement}\n <DataTable<Asset>\n title={t('cpq.inventory.assets', 'Assets')}\n refreshButton={{\n label: t('cpq.inventory.actions.refresh', 'Refresh'),\n onRefresh: data.reload,\n isRefreshing: data.isLoading,\n }}\n columns={columns}\n data={data.rows}\n searchValue={data.search}\n onSearchChange={data.setSearch}\n searchPlaceholder={t('cpq.inventory.assets.search.placeholder', 'Search assets...')}\n filters={filters}\n filterValues={data.filterValues}\n onFiltersApply={data.setFilterValues}\n onFiltersClear={data.clearFilters}\n sorting={data.sorting}\n onSortingChange={data.setSorting}\n onRowClick={(row) => router.push(`/backend/cpq/inventory/assets/${row.id}`)}\n rowActions={(row) => <RowActions items={rowActionsApi.buildItems(row)} />}\n perspective={{ tableId: 'cpq.inventory.assets.list' }}\n columnChooser={{ auto: true }}\n pagination={{\n page: data.page,\n pageSize: PAGE_SIZE,\n total: data.total,\n totalPages: data.totalPages,\n onPageChange: data.setPage,\n durationMs: data.durationMs,\n }}\n isLoading={data.isLoading}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.inventory.assets.empty', 'No assets found.')}\n </div>\n }\n />\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -14,24 +14,19 @@ import {
|
|
|
14
14
|
} from "@open-mercato/ui/primitives/select";
|
|
15
15
|
import { useCpqListData } from "../../../components/CpqListView.js";
|
|
16
16
|
import { useCpqRowActions } from "../../../components/useCpqRowActions.js";
|
|
17
|
-
import { StatusBadge, formatCurrency } from "./shared.js";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
import { StatusBadge, buildInventoryStatusOptions, formatCurrency } from "./shared.js";
|
|
18
|
+
function buildExpiringWindows(t) {
|
|
19
|
+
return [
|
|
20
|
+
{ value: "all", label: t("cpq.inventory.subscriptions.expiring.all", "All subscriptions") },
|
|
21
|
+
{ value: "7", label: t("cpq.inventory.subscriptions.expiring.in7Days", "Expiring in 7 days") },
|
|
22
|
+
{ value: "14", label: t("cpq.inventory.subscriptions.expiring.in14Days", "Expiring in 14 days") },
|
|
23
|
+
{ value: "30", label: t("cpq.inventory.subscriptions.expiring.in30Days", "Expiring in 30 days") },
|
|
24
|
+
{ value: "60", label: t("cpq.inventory.subscriptions.expiring.in60Days", "Expiring in 60 days") },
|
|
25
|
+
{ value: "90", label: t("cpq.inventory.subscriptions.expiring.in90Days", "Expiring in 90 days") }
|
|
26
|
+
];
|
|
27
|
+
}
|
|
26
28
|
const PAGE_SIZE = 50;
|
|
27
|
-
const
|
|
28
|
-
{ value: "pending", label: "Pending" },
|
|
29
|
-
{ value: "active", label: "Active" },
|
|
30
|
-
{ value: "suspended", label: "Suspended" },
|
|
31
|
-
{ value: "terminated", label: "Terminated" },
|
|
32
|
-
{ value: "expired", label: "Expired" },
|
|
33
|
-
{ value: "cancelled", label: "Cancelled" }
|
|
34
|
-
];
|
|
29
|
+
const SUBSCRIPTION_STATUSES = ["pending", "active", "suspended", "terminated", "expired", "cancelled"];
|
|
35
30
|
function buildFilterParams(values, params) {
|
|
36
31
|
if (typeof values.status === "string" && values.status) {
|
|
37
32
|
params.set("status", values.status);
|
|
@@ -65,7 +60,7 @@ function SubscriptionsTable() {
|
|
|
65
60
|
id: "status",
|
|
66
61
|
label: t("cpq.inventory.filters.status", "Status"),
|
|
67
62
|
type: "select",
|
|
68
|
-
options:
|
|
63
|
+
options: buildInventoryStatusOptions(t, SUBSCRIPTION_STATUSES)
|
|
69
64
|
},
|
|
70
65
|
{
|
|
71
66
|
id: "billingCycle",
|
|
@@ -78,11 +73,11 @@ function SubscriptionsTable() {
|
|
|
78
73
|
type: "select",
|
|
79
74
|
// Pick from common renewal-watch windows. Backend caps at 365.
|
|
80
75
|
options: [
|
|
81
|
-
{ value: "7", label: "7 days" },
|
|
82
|
-
{ value: "14", label: "14 days" },
|
|
83
|
-
{ value: "30", label: "30 days" },
|
|
84
|
-
{ value: "60", label: "60 days" },
|
|
85
|
-
{ value: "90", label: "90 days" }
|
|
76
|
+
{ value: "7", label: t("cpq.inventory.filters.days7", "7 days") },
|
|
77
|
+
{ value: "14", label: t("cpq.inventory.filters.days14", "14 days") },
|
|
78
|
+
{ value: "30", label: t("cpq.inventory.filters.days30", "30 days") },
|
|
79
|
+
{ value: "60", label: t("cpq.inventory.filters.days60", "60 days") },
|
|
80
|
+
{ value: "90", label: t("cpq.inventory.filters.days90", "90 days") }
|
|
86
81
|
]
|
|
87
82
|
}
|
|
88
83
|
],
|
|
@@ -134,9 +129,10 @@ function SubscriptionsTable() {
|
|
|
134
129
|
else delete next.expiringWithinDays;
|
|
135
130
|
data.setFilterValues(next);
|
|
136
131
|
};
|
|
132
|
+
const expiringWindows = React.useMemo(() => buildExpiringWindows(t), [t]);
|
|
137
133
|
const expiringPicker = /* @__PURE__ */ jsxs(Select, { value: currentExpiring, onValueChange: onExpiringChange, children: [
|
|
138
|
-
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-9 w-[200px]", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "All subscriptions" }) }),
|
|
139
|
-
/* @__PURE__ */ jsx(SelectContent, { children:
|
|
134
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-9 w-[200px]", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("cpq.inventory.subscriptions.expiring.all", "All subscriptions") }) }),
|
|
135
|
+
/* @__PURE__ */ jsx(SelectContent, { children: expiringWindows.map((w) => /* @__PURE__ */ jsx(SelectItem, { value: w.value, children: w.label }, w.value)) })
|
|
140
136
|
] });
|
|
141
137
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
142
138
|
rowActionsApi.ConfirmDialogElement,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { useCpqListData } from '../../../components/CpqListView'\nimport { useCpqRowActions } from '../../../components/useCpqRowActions'\nimport { StatusBadge, formatCurrency } from './shared'\n\n// `all` is a sentinel value because Radix Select disallows empty strings\n// as option values. We map it back to \"no filter\" before writing to the\n// query.\
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { useRouter } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { useCpqListData } from '../../../components/CpqListView'\nimport { useCpqRowActions } from '../../../components/useCpqRowActions'\nimport { StatusBadge, buildInventoryStatusOptions, formatCurrency } from './shared'\n\n// `all` is a sentinel value because Radix Select disallows empty strings\n// as option values. We map it back to \"no filter\" before writing to the\n// query.\nfunction buildExpiringWindows(t: TranslateFn): { value: string; label: string }[] {\n return [\n { value: 'all', label: t('cpq.inventory.subscriptions.expiring.all', 'All subscriptions') },\n { value: '7', label: t('cpq.inventory.subscriptions.expiring.in7Days', 'Expiring in 7 days') },\n { value: '14', label: t('cpq.inventory.subscriptions.expiring.in14Days', 'Expiring in 14 days') },\n { value: '30', label: t('cpq.inventory.subscriptions.expiring.in30Days', 'Expiring in 30 days') },\n { value: '60', label: t('cpq.inventory.subscriptions.expiring.in60Days', 'Expiring in 60 days') },\n { value: '90', label: t('cpq.inventory.subscriptions.expiring.in90Days', 'Expiring in 90 days') },\n ]\n}\n\ntype Subscription = {\n id: string\n code: string\n name: string\n status: string\n mrcAmount: number\n nrcAmount: number\n billingCycle: string\n currencyCode: string\n startDate: string\n currentTermEnd: string | null\n}\n\nconst PAGE_SIZE = 50\n\nconst SUBSCRIPTION_STATUSES = ['pending', 'active', 'suspended', 'terminated', 'expired', 'cancelled'] as const\n\nfunction buildFilterParams(values: FilterValues, params: URLSearchParams) {\n if (typeof values.status === 'string' && values.status) {\n params.set('status', values.status)\n }\n if (typeof values.billingCycle === 'string' && values.billingCycle.trim()) {\n params.set('billingCycle', values.billingCycle.trim())\n }\n // Expiring window: replaces the standalone /expiring page. Save as a\n // perspective (\"Expiring 30d\", \"Expiring 90d\", \u2026) for quick recall.\n if (typeof values.expiringWithinDays === 'string' && values.expiringWithinDays) {\n params.set('expiringWithinDays', values.expiringWithinDays)\n }\n}\n\nexport function SubscriptionsTable() {\n const t = useT()\n const router = useRouter()\n\n const data = useCpqListData<Subscription>({\n endpoint: '/api/cpq/inventory/subscriptions',\n pageSize: PAGE_SIZE,\n buildFilterParams,\n loadErrorMessage: t('cpq.inventory.subscriptions.error.load', 'Failed to load subscriptions'),\n })\n\n // Subscriptions are lifecycle-managed; deleting an active one usually means\n // \"terminate\", which the API rejects. Allow deletion only from non-active\n // states so the row-actions menu doesn't dangle a useless \"Delete\" item.\n const rowActionsApi = useCpqRowActions<Subscription>({\n endpoint: '/api/cpq/inventory/subscriptions',\n entityName: t('cpq.inventory.subscriptions.entityName', 'subscription'),\n editHref: (row) => `/backend/cpq/inventory/subscriptions/${row.id}`,\n onReload: data.reload,\n canDelete: (row) => row.status !== 'active',\n })\n\n const filters = React.useMemo<FilterDef[]>(\n () => [\n {\n id: 'status',\n label: t('cpq.inventory.filters.status', 'Status'),\n type: 'select',\n options: buildInventoryStatusOptions(t, SUBSCRIPTION_STATUSES),\n },\n {\n id: 'billingCycle',\n label: t('cpq.inventory.filters.billingCycle', 'Billing Cycle'),\n type: 'text',\n },\n {\n id: 'expiringWithinDays',\n label: t('cpq.inventory.filters.expiringWithinDays', 'Expiring within (days)'),\n type: 'select',\n // Pick from common renewal-watch windows. Backend caps at 365.\n options: [\n { value: '7', label: t('cpq.inventory.filters.days7', '7 days') },\n { value: '14', label: t('cpq.inventory.filters.days14', '14 days') },\n { value: '30', label: t('cpq.inventory.filters.days30', '30 days') },\n { value: '60', label: t('cpq.inventory.filters.days60', '60 days') },\n { value: '90', label: t('cpq.inventory.filters.days90', '90 days') },\n ],\n },\n ],\n [t],\n )\n\n const columns = React.useMemo<ColumnDef<Subscription>[]>(\n () => [\n {\n accessorKey: 'code',\n header: t('cpq.inventory.subscriptions.table.code', 'Code'),\n cell: ({ row }) => <span className=\"text-muted-foreground\">{row.original.code}</span>,\n },\n {\n accessorKey: 'name',\n header: t('cpq.inventory.subscriptions.table.name', 'Name'),\n cell: ({ row }) => <span className=\"font-medium text-primary\">{row.original.name}</span>,\n },\n {\n accessorKey: 'status',\n header: t('cpq.inventory.subscriptions.table.status', 'Status'),\n cell: ({ row }) => <StatusBadge status={row.original.status} />,\n },\n {\n id: 'mrc',\n header: t('cpq.inventory.subscriptions.table.mrc', 'MRC'),\n cell: ({ row }) => (\n <span className=\"font-mono\">\n {formatCurrency(row.original.mrcAmount, row.original.currencyCode || 'USD')}\n </span>\n ),\n },\n {\n accessorKey: 'billingCycle',\n header: t('cpq.inventory.subscriptions.table.billing', 'Billing'),\n },\n {\n accessorKey: 'startDate',\n header: t('cpq.inventory.subscriptions.table.startDate', 'Start Date'),\n cell: ({ row }) => (\n <span>\n {row.original.startDate ? new Date(row.original.startDate).toLocaleDateString() : '\u2014'}\n </span>\n ),\n },\n {\n accessorKey: 'currentTermEnd',\n header: t('cpq.inventory.subscriptions.table.termEnd', 'Term End'),\n cell: ({ row }) => (\n <span>\n {row.original.currentTermEnd\n ? new Date(row.original.currentTermEnd).toLocaleDateString()\n : '\u2014'}\n </span>\n ),\n },\n ],\n [t],\n )\n\n // One-click \"Expiring within\" picker that writes into the same filter\n // key the FilterBar uses (`expiringWithinDays`). Operators get instant\n // access without opening the filter overlay, and the value still rides\n // into perspectives because it's stored in `data.filterValues`.\n const currentExpiring = typeof data.filterValues.expiringWithinDays === 'string' && data.filterValues.expiringWithinDays\n ? data.filterValues.expiringWithinDays\n : 'all'\n const onExpiringChange = (value: string) => {\n const next: FilterValues = { ...data.filterValues }\n if (value && value !== 'all') next.expiringWithinDays = value\n else delete next.expiringWithinDays\n data.setFilterValues(next)\n }\n\n // Render the picker inline with the DataTable's title-row actions\n // (right next to Refresh / kebab menu) so it sits flush with the\n // card chrome instead of floating outside.\n const expiringWindows = React.useMemo(() => buildExpiringWindows(t), [t])\n const expiringPicker = (\n <Select value={currentExpiring} onValueChange={onExpiringChange}>\n <SelectTrigger className=\"h-9 w-[200px]\">\n <SelectValue placeholder={t('cpq.inventory.subscriptions.expiring.all', 'All subscriptions')} />\n </SelectTrigger>\n <SelectContent>\n {expiringWindows.map((w) => (\n <SelectItem key={w.value} value={w.value}>\n {w.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n\n return (\n <>\n {rowActionsApi.ConfirmDialogElement}\n <DataTable<Subscription>\n title={t('cpq.inventory.subscriptions', 'Subscriptions')}\n actions={expiringPicker}\n refreshButton={{\n label: t('cpq.inventory.actions.refresh', 'Refresh'),\n onRefresh: data.reload,\n isRefreshing: data.isLoading,\n }}\n columns={columns}\n data={data.rows}\n searchValue={data.search}\n onSearchChange={data.setSearch}\n searchPlaceholder={t('cpq.inventory.subscriptions.search.placeholder', 'Search subscriptions...')}\n filters={filters}\n filterValues={data.filterValues}\n onFiltersApply={data.setFilterValues}\n onFiltersClear={data.clearFilters}\n sorting={data.sorting}\n onSortingChange={data.setSorting}\n onRowClick={(row) => router.push(`/backend/cpq/inventory/subscriptions/${row.id}`)}\n rowActions={(row) => <RowActions items={rowActionsApi.buildItems(row)} />}\n perspective={{ tableId: 'cpq.inventory.subscriptions.list' }}\n columnChooser={{ auto: true }}\n pagination={{\n page: data.page,\n pageSize: PAGE_SIZE,\n total: data.total,\n totalPages: data.totalPages,\n onPageChange: data.setPage,\n durationMs: data.durationMs,\n }}\n isLoading={data.isLoading}\n emptyState={\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n {t('cpq.inventory.subscriptions.empty', 'No subscriptions found.')}\n </div>\n }\n />\n </>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useRouter, useParams } from "next/navigation";
|
|
5
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
6
|
import { Alert } from "@open-mercato/ui/primitives/alert";
|
|
6
7
|
import { Tag } from "@open-mercato/ui/primitives/tag";
|
|
7
8
|
import { FormHeader } from "@open-mercato/ui/backend/forms";
|
|
8
9
|
import {
|
|
9
|
-
assetStatusMap
|
|
10
|
-
formatStatusLabel
|
|
10
|
+
assetStatusMap
|
|
11
11
|
} from "../../../../../components/statusMaps.js";
|
|
12
12
|
import { StatusPath } from "../../../../../components/StatusPath.js";
|
|
13
|
+
import { buildInventoryStatusLabels, inventoryStatusLabel } from "../../shared.js";
|
|
13
14
|
const ALLOWED_TRANSITIONS = {
|
|
14
15
|
pending: ["delivered", "active", "cancelled"],
|
|
15
16
|
delivered: ["active", "returned"],
|
|
@@ -17,17 +18,12 @@ const ALLOWED_TRANSITIONS = {
|
|
|
17
18
|
returned: [],
|
|
18
19
|
cancelled: []
|
|
19
20
|
};
|
|
20
|
-
const
|
|
21
|
-
pending: "Pending",
|
|
22
|
-
delivered: "Delivered",
|
|
23
|
-
active: "Active",
|
|
24
|
-
returned: "Returned",
|
|
25
|
-
cancelled: "Cancelled"
|
|
26
|
-
};
|
|
21
|
+
const ASSET_STATUSES = ["pending", "delivered", "active", "returned", "cancelled"];
|
|
27
22
|
function fmt(amount, currency) {
|
|
28
23
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(Number(amount));
|
|
29
24
|
}
|
|
30
25
|
function AssetDetailPage(props) {
|
|
26
|
+
const t = useT();
|
|
31
27
|
const router = useRouter();
|
|
32
28
|
const urlParams = useParams();
|
|
33
29
|
const assetId = props.params?.id ?? urlParams?.id;
|
|
@@ -53,7 +49,7 @@ function AssetDetailPage(props) {
|
|
|
53
49
|
body: JSON.stringify({ id: asset.id, targetStatus })
|
|
54
50
|
});
|
|
55
51
|
const data = await res.json();
|
|
56
|
-
if (!res.ok) throw new Error(data?.error ?? "Status transition failed");
|
|
52
|
+
if (!res.ok) throw new Error(data?.error ?? t("cpq.inventory.assets.detail.error.transitionFailed", "Status transition failed"));
|
|
57
53
|
setAsset((prev) => prev ? { ...prev, ...data } : prev);
|
|
58
54
|
} catch (err) {
|
|
59
55
|
setError(err.message);
|
|
@@ -66,7 +62,7 @@ function AssetDetailPage(props) {
|
|
|
66
62
|
async function load() {
|
|
67
63
|
try {
|
|
68
64
|
const res = await fetch(`/api/cpq/inventory/assets?id=${assetId}`);
|
|
69
|
-
if (!res.ok) throw new Error("Asset not found");
|
|
65
|
+
if (!res.ok) throw new Error(t("cpq.inventory.assets.detail.error.notFound", "Asset not found"));
|
|
70
66
|
const data = await res.json();
|
|
71
67
|
if (!cancelled) {
|
|
72
68
|
setAsset(data);
|
|
@@ -128,33 +124,38 @@ function AssetDetailPage(props) {
|
|
|
128
124
|
return () => {
|
|
129
125
|
cancelled = true;
|
|
130
126
|
};
|
|
131
|
-
}, [assetId]);
|
|
127
|
+
}, [assetId, t]);
|
|
132
128
|
if (loading) {
|
|
133
129
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground", children: [
|
|
134
130
|
/* @__PURE__ */ jsx(Spinner, {}),
|
|
135
|
-
"
|
|
131
|
+
" ",
|
|
132
|
+
t("cpq.inventory.assets.detail.loading", "Loading\u2026")
|
|
136
133
|
] });
|
|
137
134
|
}
|
|
138
135
|
if (!asset) {
|
|
139
136
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
140
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => router.push("/backend/cpq/inventory"), className: "text-sm text-muted-foreground hover:text-foreground", children: [
|
|
138
|
+
"\u2190 ",
|
|
139
|
+
t("cpq.common.back", "Back")
|
|
140
|
+
] }),
|
|
141
141
|
error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
142
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "Asset not found." })
|
|
142
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.inventory.assets.detail.notFound", "Asset not found.") })
|
|
143
143
|
] });
|
|
144
144
|
}
|
|
145
145
|
const currency = asset.currencyCode || "USD";
|
|
146
146
|
const statusTransitions = ALLOWED_TRANSITIONS[asset.status] ?? [];
|
|
147
|
+
const statusLabels = buildInventoryStatusLabels(t, ASSET_STATUSES);
|
|
147
148
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
148
149
|
/* @__PURE__ */ jsx(
|
|
149
150
|
FormHeader,
|
|
150
151
|
{
|
|
151
152
|
mode: "detail",
|
|
152
153
|
backHref: "/backend/cpq/inventory",
|
|
153
|
-
backLabel: "Back to Customer Inventory",
|
|
154
|
-
entityTypeLabel: "Asset",
|
|
154
|
+
backLabel: t("cpq.inventory.detail.backToInventory", "Back to Customer Inventory"),
|
|
155
|
+
entityTypeLabel: t("cpq.inventory.assets.detail.entityType", "Asset"),
|
|
155
156
|
title: asset.name,
|
|
156
157
|
statusBadge: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
|
|
157
|
-
/* @__PURE__ */ jsx(Tag, { variant: assetStatusMap[asset.status] ?? "neutral", dot: true, children:
|
|
158
|
+
/* @__PURE__ */ jsx(Tag, { variant: assetStatusMap[asset.status] ?? "neutral", dot: true, children: inventoryStatusLabel(t, asset.status) }),
|
|
158
159
|
/* @__PURE__ */ jsx(Tag, { variant: "brand", className: "capitalize", children: asset.assetType.replace(/_/g, " ") }),
|
|
159
160
|
transitioning && /* @__PURE__ */ jsx(Spinner, {})
|
|
160
161
|
] })
|
|
@@ -168,11 +169,11 @@ function AssetDetailPage(props) {
|
|
|
168
169
|
path: ["pending", "delivered", "active", "returned"],
|
|
169
170
|
terminals: ["cancelled"],
|
|
170
171
|
statusMap: assetStatusMap,
|
|
171
|
-
labels:
|
|
172
|
+
labels: statusLabels,
|
|
172
173
|
onTransition: transitionStatus,
|
|
173
174
|
allowedTransitions: statusTransitions,
|
|
174
175
|
disabled: transitioning,
|
|
175
|
-
ariaLabel: "Asset status path"
|
|
176
|
+
ariaLabel: t("cpq.inventory.assets.detail.statusPath", "Asset status path")
|
|
176
177
|
}
|
|
177
178
|
),
|
|
178
179
|
transitioning && /* @__PURE__ */ jsx(Spinner, {})
|
|
@@ -180,34 +181,34 @@ function AssetDetailPage(props) {
|
|
|
180
181
|
error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
|
|
181
182
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
|
|
182
183
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
|
|
183
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Asset Details" }),
|
|
184
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: t("cpq.inventory.assets.detail.sections.details", "Asset Details") }),
|
|
184
185
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4 gap-y-2", children: [
|
|
185
|
-
/* @__PURE__ */ jsx(Field, { label: "Code", value: asset.code, mono: true }),
|
|
186
|
-
/* @__PURE__ */ jsx(Field, { label: "Status", value: asset.status }),
|
|
187
|
-
/* @__PURE__ */ jsx(Field, { label: "Asset Type", value: asset.assetType.replace(/_/g, " ") }),
|
|
188
|
-
/* @__PURE__ */ jsx(Field, { label: "Currency", value: currency }),
|
|
189
|
-
/* @__PURE__ */ jsx(Field, { label: "Quantity", value: String(asset.quantity) }),
|
|
190
|
-
asset.purchaseDate && /* @__PURE__ */ jsx(Field, { label: "Purchase Date", value: new Date(asset.purchaseDate).toLocaleDateString() }),
|
|
191
|
-
asset.deliveryDate && /* @__PURE__ */ jsx(Field, { label: "Delivery Date", value: new Date(asset.deliveryDate).toLocaleDateString() })
|
|
186
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.code", "Code"), value: asset.code, mono: true }),
|
|
187
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.status", "Status"), value: asset.status }),
|
|
188
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.assetType", "Asset Type"), value: asset.assetType.replace(/_/g, " ") }),
|
|
189
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.currency", "Currency"), value: currency }),
|
|
190
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.quantity", "Quantity"), value: String(asset.quantity) }),
|
|
191
|
+
asset.purchaseDate && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.purchaseDate", "Purchase Date"), value: new Date(asset.purchaseDate).toLocaleDateString() }),
|
|
192
|
+
asset.deliveryDate && /* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.deliveryDate", "Delivery Date"), value: new Date(asset.deliveryDate).toLocaleDateString() })
|
|
192
193
|
] }),
|
|
193
194
|
asset.description && /* @__PURE__ */ jsxs("div", { className: "pt-1", children: [
|
|
194
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Description" }),
|
|
195
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.assets.detail.fields.description", "Description") }),
|
|
195
196
|
/* @__PURE__ */ jsx("p", { className: "text-sm", children: asset.description })
|
|
196
197
|
] })
|
|
197
198
|
] }),
|
|
198
199
|
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4 space-y-3", children: [
|
|
199
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Pricing & References" }),
|
|
200
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: t("cpq.inventory.assets.detail.sections.pricing", "Pricing & References") }),
|
|
200
201
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
201
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Purchase Price" }),
|
|
202
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("cpq.inventory.assets.detail.fields.purchasePrice", "Purchase Price") }),
|
|
202
203
|
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(asset.purchasePrice, currency) })
|
|
203
204
|
] }),
|
|
204
205
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-x-4 gap-y-2 pt-1", children: [
|
|
205
|
-
/* @__PURE__ */ jsx(Field, { label: "Customer", value: customerName ?? `${asset.customerId.slice(0, 12)}\u2026` }),
|
|
206
|
-
/* @__PURE__ */ jsx(Field, { label: "Created", value: new Date(asset.createdAt).toLocaleString() }),
|
|
206
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.customer", "Customer"), value: customerName ?? `${asset.customerId.slice(0, 12)}\u2026` }),
|
|
207
|
+
/* @__PURE__ */ jsx(Field, { label: t("cpq.inventory.assets.detail.fields.created", "Created"), value: new Date(asset.createdAt).toLocaleString() }),
|
|
207
208
|
asset.subscriptionId && /* @__PURE__ */ jsx(
|
|
208
209
|
RefField,
|
|
209
210
|
{
|
|
210
|
-
label: "Linked Subscription",
|
|
211
|
+
label: t("cpq.inventory.assets.detail.fields.linkedSubscription", "Linked Subscription"),
|
|
211
212
|
name: subscriptionName ?? void 0,
|
|
212
213
|
fallback: asset.subscriptionId,
|
|
213
214
|
onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${asset.subscriptionId}`)
|
|
@@ -216,7 +217,7 @@ function AssetDetailPage(props) {
|
|
|
216
217
|
asset.offeringId && /* @__PURE__ */ jsx(
|
|
217
218
|
RefField,
|
|
218
219
|
{
|
|
219
|
-
label: "Offering",
|
|
220
|
+
label: t("cpq.inventory.assets.detail.fields.offering", "Offering"),
|
|
220
221
|
name: offeringName ?? void 0,
|
|
221
222
|
fallback: asset.offeringId,
|
|
222
223
|
onClick: () => router.push(`/backend/cpq/offerings/${asset.offeringId}`)
|
|
@@ -225,7 +226,7 @@ function AssetDetailPage(props) {
|
|
|
225
226
|
asset.specId && /* @__PURE__ */ jsx(
|
|
226
227
|
RefField,
|
|
227
228
|
{
|
|
228
|
-
label: "Specification",
|
|
229
|
+
label: t("cpq.inventory.assets.detail.fields.specification", "Specification"),
|
|
229
230
|
name: specName ?? void 0,
|
|
230
231
|
fallback: asset.specId,
|
|
231
232
|
onClick: () => router.push(`/backend/cpq/specifications/${asset.specId}`)
|
|
@@ -234,7 +235,7 @@ function AssetDetailPage(props) {
|
|
|
234
235
|
asset.productId && /* @__PURE__ */ jsx(
|
|
235
236
|
RefField,
|
|
236
237
|
{
|
|
237
|
-
label: "Product",
|
|
238
|
+
label: t("cpq.inventory.assets.detail.fields.product", "Product"),
|
|
238
239
|
name: productName ?? void 0,
|
|
239
240
|
fallback: asset.productId,
|
|
240
241
|
onClick: () => router.push(`/backend/catalog/products/${asset.productId}`)
|
|
@@ -243,7 +244,7 @@ function AssetDetailPage(props) {
|
|
|
243
244
|
asset.sourceQuoteId && /* @__PURE__ */ jsx(
|
|
244
245
|
RefField,
|
|
245
246
|
{
|
|
246
|
-
label: "Source Quote",
|
|
247
|
+
label: t("cpq.inventory.assets.detail.fields.sourceQuote", "Source Quote"),
|
|
247
248
|
name: sourceQuoteNumber ?? void 0,
|
|
248
249
|
fallback: asset.sourceQuoteId,
|
|
249
250
|
onClick: () => router.push(`/backend/cpq/quotes/${asset.sourceQuoteId}`)
|
|
@@ -252,7 +253,7 @@ function AssetDetailPage(props) {
|
|
|
252
253
|
asset.sourceOrderId && /* @__PURE__ */ jsx(
|
|
253
254
|
RefField,
|
|
254
255
|
{
|
|
255
|
-
label: "Source Order",
|
|
256
|
+
label: t("cpq.inventory.assets.detail.fields.sourceOrder", "Source Order"),
|
|
256
257
|
name: sourceOrderNumber ?? void 0,
|
|
257
258
|
fallback: asset.sourceOrderId,
|
|
258
259
|
onClick: () => router.push(`/backend/cpq/orders/${asset.sourceOrderId}`)
|
|
@@ -262,7 +263,7 @@ function AssetDetailPage(props) {
|
|
|
262
263
|
] })
|
|
263
264
|
] }),
|
|
264
265
|
asset.configuration && Object.keys(asset.configuration).length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
|
|
265
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: "Configuration" }),
|
|
266
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: t("cpq.inventory.assets.detail.sections.configuration", "Configuration") }),
|
|
266
267
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: Object.entries(asset.configuration).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
|
|
267
268
|
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
268
269
|
key,
|
|
@@ -272,14 +273,14 @@ function AssetDetailPage(props) {
|
|
|
272
273
|
] }, key)) })
|
|
273
274
|
] }),
|
|
274
275
|
asset.charges && asset.charges.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
|
|
275
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: "Charges" }),
|
|
276
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: t("cpq.inventory.assets.detail.sections.charges", "Charges") }),
|
|
276
277
|
/* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
|
|
277
278
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50 text-muted-foreground", children: [
|
|
278
|
-
/* @__PURE__ */ jsx("th", { className: "text-left px-3 py-2 font-medium", children: "Charge" }),
|
|
279
|
-
/* @__PURE__ */ jsx("th", { className: "text-left px-3 py-2 font-medium", children: "Type" }),
|
|
280
|
-
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-2 font-medium", children: "Unit Price" }),
|
|
281
|
-
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-2 font-medium", children: "Qty" }),
|
|
282
|
-
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-2 font-medium", children: "Total" })
|
|
279
|
+
/* @__PURE__ */ jsx("th", { className: "text-left px-3 py-2 font-medium", children: t("cpq.inventory.assets.detail.charges.charge", "Charge") }),
|
|
280
|
+
/* @__PURE__ */ jsx("th", { className: "text-left px-3 py-2 font-medium", children: t("cpq.inventory.assets.detail.charges.type", "Type") }),
|
|
281
|
+
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-2 font-medium", children: t("cpq.inventory.assets.detail.charges.unitPrice", "Unit Price") }),
|
|
282
|
+
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-2 font-medium", children: t("cpq.inventory.assets.detail.charges.qty", "Qty") }),
|
|
283
|
+
/* @__PURE__ */ jsx("th", { className: "text-right px-3 py-2 font-medium", children: t("cpq.inventory.assets.detail.charges.total", "Total") })
|
|
283
284
|
] }) }),
|
|
284
285
|
/* @__PURE__ */ jsx("tbody", { children: asset.charges.map((c, i) => /* @__PURE__ */ jsxs("tr", { className: "border-b", children: [
|
|
285
286
|
/* @__PURE__ */ jsx("td", { className: "px-3 py-2", children: c.chargeName ?? "\u2014" }),
|
|
@@ -291,7 +292,7 @@ function AssetDetailPage(props) {
|
|
|
291
292
|
] })
|
|
292
293
|
] }),
|
|
293
294
|
asset.metadata && Object.keys(asset.metadata).length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
|
|
294
|
-
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: "Metadata" }),
|
|
295
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2", children: t("cpq.inventory.assets.detail.sections.metadata", "Metadata") }),
|
|
295
296
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: Object.entries(asset.metadata).map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
|
|
296
297
|
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
297
298
|
key,
|