@open-mercato/core 0.6.8-develop.6911.1.f853dd04a9 → 0.6.8-develop.6912.1.36161dc9ac
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/dist/modules/api_keys/backend/api-keys/page.js.map +1 -1
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +1 -1
- package/dist/modules/audit_logs/components/AccessLogsTable.js.map +1 -1
- package/dist/modules/audit_logs/components/AuditLogsActions.js.map +1 -1
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/business_rules/backend/logs/page.js.map +1 -1
- package/dist/modules/business_rules/backend/rules/page.js.map +1 -1
- package/dist/modules/business_rules/backend/sets/page.js.map +1 -1
- package/dist/modules/catalog/components/PriceKindSettings.js.map +1 -1
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +1 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +1 -1
- package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +1 -1
- package/dist/modules/currencies/backend/currencies/page.js.map +1 -1
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +1 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +1 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +1 -1
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js.map +1 -1
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js.map +1 -1
- package/dist/modules/data_sync/backend/data-sync/page.js.map +1 -1
- package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js.map +1 -1
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
- package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +1 -1
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +1 -1
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +1 -1
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +1 -1
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js.map +1 -1
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +1 -1
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +1 -1
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +1 -1
- package/dist/modules/sales/components/TaxRatesSettings.js.map +1 -1
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +1 -1
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +1 -1
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +1 -1
- package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +1 -1
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +1 -1
- package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +1 -1
- package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +1 -1
- package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +1 -1
- package/dist/modules/workflows/backend/definitions/page.js.map +1 -1
- package/dist/modules/workflows/backend/events/page.js.map +1 -1
- package/dist/modules/workflows/backend/instances/page.js.map +1 -1
- package/dist/modules/workflows/backend/tasks/page.js.map +1 -1
- package/jest.config.cjs +1 -1
- package/package.json +7 -7
- package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
- package/src/modules/attachments/components/AttachmentLibrary.tsx +2 -1
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +1 -1
- package/src/modules/audit_logs/components/AccessLogsTable.tsx +1 -1
- package/src/modules/audit_logs/components/AuditLogsActions.tsx +1 -1
- package/src/modules/auth/backend/roles/page.tsx +2 -1
- package/src/modules/auth/backend/users/page.tsx +2 -1
- package/src/modules/business_rules/backend/logs/page.tsx +1 -1
- package/src/modules/business_rules/backend/rules/page.tsx +1 -1
- package/src/modules/business_rules/backend/sets/page.tsx +1 -1
- package/src/modules/catalog/components/PriceKindSettings.tsx +1 -1
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -1
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -1
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +1 -1
- package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +1 -1
- package/src/modules/currencies/backend/currencies/page.tsx +1 -1
- package/src/modules/currencies/backend/exchange-rates/page.tsx +1 -1
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +1 -1
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +1 -1
- package/src/modules/customers/backend/customers/companies/page.tsx +2 -1
- package/src/modules/customers/backend/customers/deals/page.tsx +1 -1
- package/src/modules/customers/backend/customers/people/page.tsx +2 -1
- package/src/modules/customers/components/CustomerTodosTable.tsx +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +1 -1
- package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -1
- package/src/modules/directory/backend/directory/organizations/page.tsx +1 -1
- package/src/modules/directory/backend/directory/tenants/page.tsx +2 -1
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -1
- package/src/modules/entities/components/SystemEntitiesTable.tsx +2 -1
- package/src/modules/entities/components/UserEntitiesTable.tsx +2 -1
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
- package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +1 -1
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +1 -1
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +1 -1
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +1 -1
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -1
- package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -1
- package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -1
- package/src/modules/resources/backend/resources/resources/page.tsx +1 -1
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -1
- package/src/modules/sales/backend/sales/channels/page.tsx +2 -1
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +1 -1
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +1 -1
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +1 -1
- package/src/modules/sales/components/TaxRatesSettings.tsx +1 -1
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -1
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +1 -1
- package/src/modules/sales/components/documents/PaymentsSection.tsx +1 -1
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +1 -1
- package/src/modules/staff/backend/staff/leave-requests/page.tsx +2 -1
- package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +2 -1
- package/src/modules/staff/backend/staff/team-members/page.tsx +3 -2
- package/src/modules/staff/backend/staff/team-roles/page.tsx +1 -1
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -1
- package/src/modules/staff/backend/staff/teams/page.tsx +2 -1
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +2 -1
- package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +2 -1
- package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +4 -3
- package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +1 -1
- package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +1 -1
- package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -1
- package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +1 -1
- package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +1 -1
- package/src/modules/workflows/backend/definitions/page.tsx +1 -1
- package/src/modules/workflows/backend/events/page.tsx +1 -1
- package/src/modules/workflows/backend/instances/page.tsx +1 -1
- package/src/modules/workflows/backend/tasks/page.tsx +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/wms/components/backend/WmsLotDetailPage.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/wms/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { z } from 'zod'\nimport { useQuery } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport {\n ArrowLeft,\n ArrowLeftRight,\n ClipboardList,\n Download,\n ExternalLink,\n SlidersHorizontal,\n Warehouse,\n} from 'lucide-react'\nimport { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { ErrorMessage, LoadingMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { LinkButton } from '@open-mercato/ui/primitives/link-button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { StatusBadge, type StatusBadgeVariant } from '@open-mercato/ui/primitives/status-badge'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n inventoryMovementReasonLabel,\n type InventoryDisplayTranslator,\n} from '../../lib/inventoryDisplayUi'\nimport { AdjustInventoryDialog } from './AdjustInventoryDialog'\nimport { ChangeLotStatusDialog } from './ChangeLotStatusDialog'\nimport { CycleCountWizardDialog } from './CycleCountWizardDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\nconst lotIdSchema = z.string().uuid()\n\ntype PagedResponse<T> = {\n items: T[]\n total: number\n totalPages: number\n}\n\ntype CatalogVariantRow = {\n id: string\n sku?: string | null\n name?: string | null\n}\n\ntype InventoryLotRow = {\n id: string\n catalog_variant_id?: string | null\n sku?: string | null\n lot_number?: string | null\n batch_number?: string | null\n manufactured_at?: string | null\n best_before_at?: string | null\n expires_at?: string | null\n status?: string | null\n metadata?: Record<string, unknown> | null\n created_at?: string | null\n updated_at?: string | null\n}\n\nfunction extractLotStatusNotes(metadata: InventoryLotRow['metadata']): string | null {\n if (!metadata || typeof metadata !== 'object') return null\n const notes = metadata.notes\n return typeof notes === 'string' && notes.trim().length > 0 ? notes.trim() : null\n}\n\ntype InventoryProfileRow = {\n reorder_point?: string | number | null\n}\n\ntype InventoryBalanceRow = {\n id: string\n warehouse_id?: string | null\n warehouse_name?: string | null\n warehouse_code?: string | null\n location_id?: string | null\n location_code?: string | null\n location_type?: string | null\n catalog_variant_id?: string | null\n quantity_on_hand?: string | number | null\n quantity_reserved?: string | number | null\n quantity_allocated?: string | number | null\n quantity_available?: number | null\n}\n\ntype InventoryMovementRow = {\n id: string\n warehouse_id?: string | null\n warehouse_name?: string | null\n warehouse_code?: string | null\n location_from_id?: string | null\n location_from_code?: string | null\n location_to_id?: string | null\n location_to_code?: string | null\n catalog_variant_id?: string | null\n variant_sku?: string | null\n variant_name?: string | null\n lot_id?: string | null\n quantity?: string | number | null\n type?: string | null\n reference_type?: string | null\n reference_id?: string | null\n reason?: string | null\n reason_code?: string | null\n performed_at?: string | null\n received_at?: string | null\n}\n\ntype InventoryMutationPreset = {\n warehouseId?: string\n locationId?: string\n catalogVariantId?: string\n lotId?: string\n}\n\ntype WarehouseOption = {\n id: string\n name?: string | null\n code?: string | null\n}\n\ntype DistributionFilter = 'all' | 'sellable' | 'picking' | 'nearExpiry'\n\nconst NON_SELLABLE_LOCATION_TYPES = new Set(['staging', 'dock'])\nconst PICKING_LOCATION_TYPES = new Set(['staging', 'bin', 'slot'])\nconst NEAR_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\nfunction escapeCsvCell(value: string | number): string {\n const str = String(value ?? '')\n if (/[\",\\n]/.test(str)) return `\"${str.replace(/\"/g, '\"\"')}\"`\n return str\n}\n\nfunction downloadCsvFile(filename: string, rows: string[][]) {\n const csv = rows.map((row) => row.map(escapeCsvCell).join(',')).join('\\n')\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = filename\n anchor.click()\n URL.revokeObjectURL(url)\n}\n\nfunction toNumber(value: string | number | null | undefined): number {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nfunction formatWarehouseLabel(row: {\n warehouse_name?: string | null\n warehouse_code?: string | null\n warehouse_id?: string | null\n}): string {\n const code = (row.warehouse_code ?? '').trim()\n const name = (row.warehouse_name ?? '').trim()\n if (code && name) return `${code} \u00B7 ${name}`\n return name || code || row.warehouse_id || '\u2014'\n}\n\nfunction formatLocationLabel(code: string | null | undefined, id: string | null | undefined): string {\n const trimmed = (code ?? '').trim()\n if (trimmed) return trimmed\n return id || '\u2014'\n}\n\nfunction balanceLocationKey(row: InventoryBalanceRow): string {\n return `${row.warehouse_id?.trim() ?? ''}:${row.location_id?.trim() ?? ''}`\n}\n\nfunction movementLocationKey(\n warehouseId: string | null | undefined,\n locationId: string | null | undefined,\n): string {\n return `${warehouseId?.trim() ?? ''}:${locationId?.trim() ?? ''}`\n}\n\nfunction isNearExpiry(expiresAt: string | null | undefined, nowMs: number): boolean {\n if (!expiresAt) return false\n const expires = new Date(expiresAt).getTime()\n if (Number.isNaN(expires)) return false\n return expires > nowMs && expires - nowMs <= NEAR_EXPIRY_MS\n}\n\nfunction isExpired(expiresAt: string | null | undefined, nowMs: number): boolean {\n if (!expiresAt) return false\n const expires = new Date(expiresAt).getTime()\n return !Number.isNaN(expires) && expires <= nowMs\n}\n\nfunction daysUntilExpiry(expiresAt: string | null | undefined, nowMs: number): number | null {\n if (!expiresAt) return null\n const expires = new Date(expiresAt).getTime()\n if (Number.isNaN(expires)) return null\n return Math.ceil((expires - nowMs) / MS_PER_DAY)\n}\n\nfunction daysSince(value: string | null | undefined, nowMs: number): number | null {\n if (!value) return null\n const ts = new Date(value).getTime()\n if (Number.isNaN(ts)) return null\n return Math.max(0, Math.floor((nowMs - ts) / MS_PER_DAY))\n}\n\nfunction resolveLotQualityStatus(\n status: string | null | undefined,\n expiresAt: string | null | undefined,\n nowMs: number,\n): { variant: StatusBadgeVariant; labelKey: string; labelFallback: string; value: string } {\n if (status === 'expired' || isExpired(expiresAt, nowMs)) {\n return {\n variant: 'error',\n labelKey: 'wms.backend.lot.status.expired',\n labelFallback: 'Expired',\n value: 'Expired',\n }\n }\n if (status === 'quarantine') {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.status.quarantine',\n labelFallback: 'Quarantine',\n value: 'QC',\n }\n }\n if (status === 'hold') {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.status.hold',\n labelFallback: 'Hold',\n value: 'Hold',\n }\n }\n if (isNearExpiry(expiresAt, nowMs)) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.status.nearExpiry',\n labelFallback: 'Near expiry',\n value: 'Near expiry',\n }\n }\n return {\n variant: 'success',\n labelKey: 'wms.backend.lot.status.available',\n labelFallback: 'Available',\n value: 'OK',\n }\n}\n\nfunction resolveBalanceStatus(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | null | undefined,\n reorderPoint: number,\n nowMs: number,\n): { variant: StatusBadgeVariant; labelKey: string; labelFallback: string } {\n if (lot?.status === 'expired' || isExpired(lot?.expires_at, nowMs)) {\n return {\n variant: 'error',\n labelKey: 'wms.backend.lot.distribution.status.expired',\n labelFallback: 'Expired',\n }\n }\n if (isNearExpiry(lot?.expires_at, nowMs)) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.distribution.status.nearExpiry',\n labelFallback: 'Near expiry',\n }\n }\n const available = row.quantity_available ?? 0\n if (reorderPoint > 0 && available <= reorderPoint) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.distribution.status.lowStock',\n labelFallback: 'Low stock',\n }\n }\n const reserved = toNumber(row.quantity_reserved)\n const onHand = toNumber(row.quantity_on_hand)\n if (reserved > 0 && onHand > 0 && reserved >= onHand) {\n return {\n variant: 'info',\n labelKey: 'wms.backend.lot.distribution.status.reserved',\n labelFallback: 'Reserved',\n }\n }\n return {\n variant: 'success',\n labelKey: 'wms.backend.lot.distribution.status.available',\n labelFallback: 'Available',\n }\n}\n\nfunction matchesDistributionFilter(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | null | undefined,\n filter: DistributionFilter,\n nowMs: number,\n): boolean {\n const locationType = (row.location_type ?? '').trim().toLowerCase()\n const locationCode = (row.location_code ?? '').trim().toLowerCase()\n switch (filter) {\n case 'all':\n return true\n case 'sellable':\n return !NON_SELLABLE_LOCATION_TYPES.has(locationType) && lot?.status !== 'expired'\n case 'picking':\n return (\n PICKING_LOCATION_TYPES.has(locationType) ||\n locationCode.includes('pick') ||\n locationCode.includes('staging') ||\n toNumber(row.quantity_reserved) > 0 ||\n toNumber(row.quantity_allocated) > 0\n )\n case 'nearExpiry':\n return isNearExpiry(lot?.expires_at, nowMs) || lot?.status === 'expired'\n default:\n return true\n }\n}\n\nfunction movementTypeLabel(type: string, t: ReturnType<typeof useT>): string {\n const key = `wms.backend.lot.activity.types.${type}`\n const fallbacks: Record<string, string> = {\n receipt: 'Receive',\n return_receive: 'Receive',\n adjust: 'Adjust',\n transfer: 'Move',\n pick: 'Allocate',\n pack: 'Allocate',\n cycle_count: 'Reconcile',\n putaway: 'Putaway',\n ship: 'Ship',\n }\n return t(key, fallbacks[type] ?? type)\n}\n\nconst movementStatusMap: Record<string, StatusBadgeVariant> = {\n receipt: 'success',\n return_receive: 'success',\n adjust: 'warning',\n transfer: 'info',\n pick: 'info',\n pack: 'info',\n cycle_count: 'neutral',\n putaway: 'info',\n ship: 'success',\n}\n\nfunction formatMovementTitle(\n row: InventoryMovementRow,\n lotLabel: string,\n t: ReturnType<typeof useT>,\n): string {\n const quantity = Math.abs(toNumber(row.quantity))\n const signedQuantity = toNumber(row.quantity)\n switch (row.type) {\n case 'receipt':\n case 'return_receive':\n return t('wms.backend.lot.activity.titles.received', 'Received {quantity}\u00D7 {lot}', {\n quantity,\n lot: lotLabel,\n })\n case 'adjust':\n return t('wms.backend.lot.activity.titles.adjusted', 'Adjusted {quantity}\u00D7 {lot}', {\n quantity: `${signedQuantity >= 0 ? '+' : ''}${signedQuantity}`,\n lot: lotLabel,\n })\n case 'transfer':\n return t('wms.backend.lot.activity.titles.moved', 'Moved {quantity}\u00D7 {lot}', {\n quantity,\n lot: lotLabel,\n })\n case 'pick':\n case 'pack':\n return t('wms.backend.lot.activity.titles.allocated', 'Allocated {quantity}\u00D7 {lot}', {\n quantity,\n lot: lotLabel,\n })\n case 'cycle_count':\n return t('wms.backend.lot.activity.titles.reconciled', 'Cycle count of {lot}', {\n lot: lotLabel,\n })\n default:\n return t('wms.backend.lot.activity.titles.generic', '{type} {quantity}\u00D7 {lot}', {\n type: row.type ?? 'movement',\n quantity,\n lot: lotLabel,\n })\n }\n}\n\nfunction formatMovementSubtitle(\n row: InventoryMovementRow,\n t: InventoryDisplayTranslator,\n): string | null {\n const reasonLabel = inventoryMovementReasonLabel(\n {\n reasonCode: row.reason_code,\n reason: row.reason,\n movementType: row.type,\n },\n t,\n )\n if (reasonLabel) return reasonLabel\n if (row.reference_type && row.reference_id) return `${row.reference_type} \u00B7 ${row.reference_id}`\n return null\n}\n\nfunction formatMovementLocation(row: InventoryMovementRow): string {\n const warehouse = formatWarehouseLabel(row)\n const from = formatLocationLabel(row.location_from_code, row.location_from_id)\n const to = formatLocationLabel(row.location_to_code, row.location_to_id)\n if (row.type === 'transfer' && from !== '\u2014' && to !== '\u2014') return `${from} \u2192 ${to}`\n const location = to !== '\u2014' ? to : from\n if (location !== '\u2014') return `${warehouse} \u00B7 ${location}`\n return warehouse\n}\n\nfunction formatLastMoveLabel(\n raw: string | undefined,\n locale: string,\n t: ReturnType<typeof useT>,\n): string {\n if (!raw) return '\u2014'\n const date = new Date(raw)\n if (Number.isNaN(date.getTime())) return raw\n const diffDays = Math.floor((Date.now() - date.getTime()) / MS_PER_DAY)\n if (diffDays === 0) {\n return t('wms.backend.lot.distribution.lastMove.today', '{time} today', {\n time: new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }).format(date),\n })\n }\n if (diffDays === 1) {\n return t('wms.backend.lot.distribution.lastMove.yesterday', 'Yesterday')\n }\n if (diffDays < 7) {\n return t('wms.backend.lot.distribution.lastMove.daysAgo', '{days} days ago', { days: diffDays })\n }\n return new Intl.DateTimeFormat(locale, { month: '2-digit', day: '2-digit' }).format(date)\n}\n\ntype LotKpiCardProps = {\n title: string\n caption: string\n value: string\n badgeLabel: string | null\n badgeVariant: StatusBadgeVariant\n ctaLabel: string\n ctaHref?: string\n onCtaClick?: () => void\n}\n\nfunction LotKpiCard({\n title,\n caption,\n value,\n badgeLabel,\n badgeVariant,\n ctaLabel,\n ctaHref,\n onCtaClick,\n}: LotKpiCardProps) {\n return (\n <section className=\"flex min-h-52 flex-col rounded-lg border bg-card p-5 text-card-foreground shadow-sm\">\n <p className=\"text-sm font-medium\">{title}</p>\n <p className=\"mt-3 text-xs text-muted-foreground\">{caption}</p>\n <div className=\"mt-2 flex items-end gap-3\">\n <p className=\"text-3xl font-semibold tracking-tight\">{value}</p>\n {badgeLabel ? (\n <StatusBadge variant={badgeVariant} dot>\n {badgeLabel}\n </StatusBadge>\n ) : null}\n </div>\n {onCtaClick ? (\n <LinkButton variant=\"primary\" size=\"sm\" className=\"mt-auto pt-4 w-fit\" onClick={onCtaClick}>\n {ctaLabel}\n </LinkButton>\n ) : ctaHref ? (\n <LinkButton asChild variant=\"primary\" size=\"sm\" className=\"mt-auto pt-4 w-fit\">\n <Link href={ctaHref}>{ctaLabel}</Link>\n </LinkButton>\n ) : null}\n </section>\n )\n}\n\ntype WmsLotDetailPageProps = {\n lotId: string\n}\n\nexport default function WmsLotDetailPage({ lotId }: WmsLotDetailPageProps) {\n const t = useT()\n const locale = useLocale()\n const router = useRouter()\n const access = useWmsInventoryMutationAccess()\n const parsedLotId = lotIdSchema.safeParse(lotId.trim())\n const scopedLotId = parsedLotId.success ? parsedLotId.data : null\n\n const [warehouseId, setWarehouseId] = React.useState<string>('all')\n const [distributionFilter, setDistributionFilter] = React.useState<DistributionFilter>('all')\n const [distributionPage, setDistributionPage] = React.useState(1)\n const [selectedBalanceIds, setSelectedBalanceIds] = React.useState<Set<string>>(() => new Set())\n const [adjustOpen, setAdjustOpen] = React.useState(false)\n const [adjustPreset, setAdjustPreset] = React.useState<InventoryMutationPreset>({})\n const [cycleOpen, setCycleOpen] = React.useState(false)\n const [cyclePreset, setCyclePreset] = React.useState<Pick<InventoryMutationPreset, 'warehouseId' | 'locationId'>>({})\n const [changeStatusOpen, setChangeStatusOpen] = React.useState(false)\n\n const distributionPageSize = 20\n const lotsHref = '/backend/wms/lots'\n const inventoryHref = '/backend/wms/inventory'\n\n const activityTimeFormatter = React.useMemo(\n () =>\n new Intl.DateTimeFormat(locale, {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }),\n [locale],\n )\n\n const lotQuery = useQuery({\n queryKey: ['wms-lot-detail', 'lot', scopedLotId],\n enabled: Boolean(scopedLotId),\n queryFn: async () => {\n const params = new URLSearchParams({\n ids: scopedLotId!,\n page: '1',\n pageSize: '1',\n })\n const call = await apiCall<PagedResponse<InventoryLotRow>>(`/api/wms/lots?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.lot', 'Failed to load lot.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const catalogVariantId = lotQuery.data?.catalog_variant_id?.trim() ?? null\n\n const variantQuery = useQuery({\n queryKey: ['wms-lot-detail', 'variant', catalogVariantId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n id: catalogVariantId!,\n page: '1',\n pageSize: '1',\n })\n const call = await apiCall<PagedResponse<CatalogVariantRow>>(\n `/api/catalog/variants?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.variant', 'Failed to load SKU.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const profileQuery = useQuery({\n queryKey: ['wms-lot-detail', 'profile', catalogVariantId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\n page: '1',\n pageSize: '1',\n })\n const call = await apiCall<PagedResponse<InventoryProfileRow>>(\n `/api/wms/inventory-profiles?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.profile', 'Failed to load inventory profile.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const warehousesQuery = useQuery({\n queryKey: ['wms-lot-detail', 'warehouses'],\n queryFn: async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '100', sortField: 'name', sortDir: 'asc' })\n const call = await apiCall<PagedResponse<WarehouseOption>>(`/api/wms/warehouses?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.warehouses', 'Failed to load warehouses.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const balancesQuery = useQuery({\n queryKey: ['wms-lot-detail', 'balances', scopedLotId, warehouseId],\n enabled: Boolean(scopedLotId),\n queryFn: async () => {\n const params = new URLSearchParams({\n lotId: scopedLotId!,\n page: '1',\n pageSize: '100',\n sortField: 'updatedAt',\n sortDir: 'desc',\n })\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const call = await apiCall<PagedResponse<InventoryBalanceRow>>(\n `/api/wms/inventory/balances?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.balances', 'Failed to load lot locations.'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const movementsQuery = useQuery({\n queryKey: ['wms-lot-detail', 'movements', scopedLotId, warehouseId],\n enabled: Boolean(scopedLotId),\n queryFn: async () => {\n const params = new URLSearchParams({\n lotId: scopedLotId!,\n page: '1',\n pageSize: '25',\n sortField: 'performedAt',\n sortDir: 'desc',\n })\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const call = await apiCall<PagedResponse<InventoryMovementRow>>(\n `/api/wms/inventory/movements?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.movements', 'Failed to load recent activity.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const reorderPoint = toNumber(profileQuery.data?.reorder_point)\n const lotData = lotQuery.data\n const lotLabel = (lotData?.lot_number ?? '').trim() || scopedLotId || '\u2014'\n const skuLabel =\n (lotData?.sku ?? '').trim() ||\n (variantQuery.data?.sku ?? '').trim() ||\n catalogVariantId ||\n '\u2014'\n const variantName = (variantQuery.data?.name ?? '').trim()\n const pageTitle = lotLabel\n const nowMs = React.useMemo(() => Date.now(), [])\n\n const qualityStatus = React.useMemo(\n () => resolveLotQualityStatus(lotData?.status, lotData?.expires_at, nowMs),\n [lotData?.expires_at, lotData?.status, nowMs],\n )\n const statusNotes = React.useMemo(\n () => extractLotStatusNotes(lotData?.metadata),\n [lotData?.metadata],\n )\n\n const daysToExpiry = daysUntilExpiry(lotData?.expires_at, nowMs)\n const lotAgeDays = daysSince(lotData?.created_at, nowMs)\n\n const totals = React.useMemo(() => {\n const items = balancesQuery.data?.items ?? []\n let onHand = 0\n let reserved = 0\n let available = 0\n for (const row of items) {\n onHand += toNumber(row.quantity_on_hand)\n reserved += toNumber(row.quantity_reserved)\n available += row.quantity_available ?? 0\n }\n return { onHand, reserved, available }\n }, [balancesQuery.data?.items])\n\n const lastMoveByLocation = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const row of movementsQuery.data ?? []) {\n const raw = row.performed_at ?? row.received_at\n if (!raw) continue\n const keys = [\n movementLocationKey(row.warehouse_id, row.location_to_id),\n movementLocationKey(row.warehouse_id, row.location_from_id),\n ]\n for (const key of keys) {\n if (!key || key === ':') continue\n const existing = map.get(key)\n if (!existing || new Date(raw).getTime() > new Date(existing).getTime()) {\n map.set(key, raw)\n }\n }\n }\n return map\n }, [movementsQuery.data])\n\n const filteredBalances = React.useMemo(() => {\n const items = balancesQuery.data?.items ?? []\n return items.filter((row) => matchesDistributionFilter(row, lotData, distributionFilter, nowMs))\n }, [balancesQuery.data?.items, distributionFilter, lotData, nowMs])\n\n const distributionTotal = filteredBalances.length\n const distributionTotalPages = Math.max(1, Math.ceil(distributionTotal / distributionPageSize))\n const pagedBalances = React.useMemo(() => {\n const start = (distributionPage - 1) * distributionPageSize\n return filteredBalances.slice(start, start + distributionPageSize)\n }, [distributionPage, distributionPageSize, filteredBalances])\n\n React.useEffect(() => {\n setDistributionPage(1)\n setSelectedBalanceIds(new Set())\n }, [distributionFilter, warehouseId])\n\n const selectedBalances = React.useMemo(\n () => filteredBalances.filter((row) => selectedBalanceIds.has(row.id)),\n [filteredBalances, selectedBalanceIds],\n )\n\n const movementsHref = React.useMemo(() => {\n const params = new URLSearchParams()\n if (scopedLotId) params.set('lotId', scopedLotId)\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const query = params.toString()\n return query ? `/backend/wms/movements?${query}` : '/backend/wms/movements'\n }, [scopedLotId, warehouseId])\n\n const inventoryConsoleHref = React.useMemo(() => {\n if (!scopedLotId) return inventoryHref\n return `${inventoryHref}?lotId=${encodeURIComponent(scopedLotId)}`\n }, [scopedLotId])\n\n const skuDetailHref = catalogVariantId\n ? `/backend/wms/sku/${encodeURIComponent(catalogVariantId)}`\n : inventoryHref\n\n const openAdjustDialog = React.useCallback((preset: InventoryMutationPreset = {}) => {\n setAdjustPreset(preset)\n setAdjustOpen(true)\n }, [])\n\n const openCycleCountDialog = React.useCallback(\n (preset: Pick<InventoryMutationPreset, 'warehouseId' | 'locationId'> = {}) => {\n setCyclePreset(preset)\n setCycleOpen(true)\n },\n [],\n )\n\n const resolveMutationContext = React.useCallback((): InventoryMutationPreset => {\n const firstSelected = selectedBalances[0]\n const scopedWarehouseId =\n firstSelected?.warehouse_id?.trim() ||\n (warehouseId !== 'all' ? warehouseId : undefined)\n const locationId =\n selectedBalances.length === 1 ? firstSelected?.location_id?.trim() || undefined : undefined\n return {\n warehouseId: scopedWarehouseId,\n locationId,\n catalogVariantId: catalogVariantId ?? undefined,\n lotId: scopedLotId ?? undefined,\n }\n }, [catalogVariantId, scopedLotId, selectedBalances, warehouseId])\n\n const toggleBalanceSelection = React.useCallback((balanceId: string, selected: boolean) => {\n setSelectedBalanceIds((current) => {\n const next = new Set(current)\n if (selected) next.add(balanceId)\n else next.delete(balanceId)\n return next\n })\n }, [])\n\n const togglePageSelection = React.useCallback((selected: boolean) => {\n setSelectedBalanceIds((current) => {\n const next = new Set(current)\n for (const row of pagedBalances) {\n if (selected) next.add(row.id)\n else next.delete(row.id)\n }\n return next\n })\n }, [pagedBalances])\n\n const pageSelectionState = React.useMemo(() => {\n if (pagedBalances.length === 0) {\n return { checked: false, indeterminate: false }\n }\n const selectedOnPage = pagedBalances.filter((row) => selectedBalanceIds.has(row.id)).length\n return {\n checked: selectedOnPage === pagedBalances.length,\n indeterminate: selectedOnPage > 0 && selectedOnPage < pagedBalances.length,\n }\n }, [pagedBalances, selectedBalanceIds])\n\n const handleExportDistributionCsv = React.useCallback(() => {\n const headers = [\n t('wms.backend.lot.distribution.columns.warehouse', 'Warehouse'),\n t('wms.backend.lot.distribution.columns.location', 'Location'),\n t('wms.backend.lot.distribution.columns.lastMove', 'Last move'),\n t('wms.backend.lot.distribution.columns.onHand', 'On hand'),\n t('wms.backend.lot.distribution.columns.reserved', 'Reserved'),\n t('wms.backend.lot.distribution.columns.status', 'Status'),\n ]\n const rows = filteredBalances.map((row) => {\n const status = resolveBalanceStatus(row, lotData, reorderPoint, nowMs)\n return [\n formatWarehouseLabel(row),\n formatLocationLabel(row.location_code, row.location_id),\n formatLastMoveLabel(lastMoveByLocation.get(balanceLocationKey(row)), locale, t),\n String(toNumber(row.quantity_on_hand)),\n String(toNumber(row.quantity_reserved)),\n t(status.labelKey, status.labelFallback),\n ]\n })\n const safeLot = pageTitle.replace(/[^\\w.-]+/g, '-').replace(/^-+|-+$/g, '') || 'lot'\n downloadCsvFile(`${safeLot}-locations.csv`, [headers, ...rows])\n }, [filteredBalances, lastMoveByLocation, locale, lotData, nowMs, pageTitle, reorderPoint, t])\n\n const selectedWarehouse = warehousesQuery.data?.find((warehouse) => warehouse.id === warehouseId)\n const warehouseLabel =\n warehouseId === 'all'\n ? t('wms.backend.lot.filters.allWarehouses', 'All warehouses')\n : selectedWarehouse?.name || selectedWarehouse?.code || warehouseId\n\n const dateFormatter = React.useMemo(\n () => new Intl.DateTimeFormat(locale, { year: 'numeric', month: '2-digit', day: '2-digit' }),\n [locale],\n )\n\n const subtitleParts = [\n skuLabel !== '\u2014' ? skuLabel : null,\n variantName || null,\n lotData?.expires_at\n ? t('wms.backend.lot.header.expires', 'Exp {date}', {\n date: dateFormatter.format(new Date(lotData.expires_at)),\n })\n : null,\n ].filter(Boolean)\n\n const distributionColumns = React.useMemo<ColumnDef<InventoryBalanceRow>[]>(\n () => [\n {\n id: 'select',\n header: () => (\n <Checkbox\n aria-label={t('wms.backend.lot.distribution.columns.select', 'Select')}\n checked={pageSelectionState.indeterminate ? 'indeterminate' : pageSelectionState.checked}\n onCheckedChange={(checked) => togglePageSelection(checked === true)}\n />\n ),\n cell: ({ row }) => (\n <Checkbox\n aria-label={t('wms.backend.lot.distribution.columns.select', 'Select')}\n checked={selectedBalanceIds.has(row.original.id)}\n onCheckedChange={(checked) => toggleBalanceSelection(row.original.id, checked === true)}\n />\n ),\n meta: { maxWidth: '2.75rem' },\n },\n {\n accessorKey: 'warehouse_id',\n header: t('wms.backend.lot.distribution.columns.warehouse', 'Warehouse'),\n cell: ({ row }) => formatWarehouseLabel(row.original),\n },\n {\n accessorKey: 'location_id',\n header: t('wms.backend.lot.distribution.columns.location', 'Location'),\n cell: ({ row }) => {\n const locationId = row.original.location_id?.trim()\n const label = formatLocationLabel(row.original.location_code, row.original.location_id)\n if (!locationId || label === '\u2014') return label\n return (\n <Link\n href={`/backend/wms/location/${encodeURIComponent(locationId)}`}\n className=\"font-medium text-primary hover:underline\"\n >\n {label}\n </Link>\n )\n },\n },\n {\n id: 'lastMove',\n header: t('wms.backend.lot.distribution.columns.lastMove', 'Last move'),\n cell: ({ row }) =>\n formatLastMoveLabel(lastMoveByLocation.get(balanceLocationKey(row.original)), locale, t),\n },\n {\n accessorKey: 'quantity_on_hand',\n header: t('wms.backend.lot.distribution.columns.onHand', 'On hand'),\n cell: ({ row }) => String(toNumber(row.original.quantity_on_hand)),\n },\n {\n accessorKey: 'quantity_reserved',\n header: t('wms.backend.lot.distribution.columns.reserved', 'Reserved'),\n cell: ({ row }) => String(toNumber(row.original.quantity_reserved)),\n },\n {\n id: 'status',\n header: t('wms.backend.lot.distribution.columns.status', 'Status'),\n cell: ({ row }) => {\n const status = resolveBalanceStatus(row.original, lotData, reorderPoint, nowMs)\n return (\n <StatusBadge variant={status.variant} dot>\n {t(status.labelKey, status.labelFallback)}\n </StatusBadge>\n )\n },\n },\n ],\n [\n lastMoveByLocation,\n locale,\n lotData,\n nowMs,\n pageSelectionState.checked,\n pageSelectionState.indeterminate,\n reorderPoint,\n selectedBalanceIds,\n t,\n toggleBalanceSelection,\n togglePageSelection,\n ],\n )\n\n const activityColumns = React.useMemo<ColumnDef<InventoryMovementRow>[]>(\n () => [\n {\n accessorKey: 'type',\n header: t('wms.backend.lot.activity.columns.event', 'Event'),\n cell: ({ row }) => {\n const type = row.original.type ?? 'movement'\n return (\n <StatusBadge variant={movementStatusMap[type] ?? 'neutral'}>\n {movementTypeLabel(type, t)}\n </StatusBadge>\n )\n },\n },\n {\n id: 'details',\n header: t('wms.backend.lot.activity.columns.details', 'Details'),\n cell: ({ row }) => {\n const subtitle = formatMovementSubtitle(row.original, t)\n return (\n <div className=\"space-y-0.5\">\n <p className=\"text-sm font-medium\">{formatMovementTitle(row.original, lotLabel, t)}</p>\n {subtitle ? <p className=\"text-xs text-muted-foreground\">{subtitle}</p> : null}\n </div>\n )\n },\n },\n {\n id: 'location',\n header: t('wms.backend.lot.activity.columns.location', 'Location'),\n cell: ({ row }) => (\n <span className=\"text-sm text-muted-foreground\">{formatMovementLocation(row.original)}</span>\n ),\n },\n {\n id: 'time',\n header: t('wms.backend.lot.activity.columns.time', 'Time'),\n cell: ({ row }) => {\n const raw = row.original.performed_at ?? row.original.received_at\n if (!raw) return '\u2014'\n const date = new Date(raw)\n if (Number.isNaN(date.getTime())) return raw\n return activityTimeFormatter.format(date)\n },\n meta: { maxWidth: '6rem' },\n },\n ],\n [activityTimeFormatter, lotLabel, t],\n )\n\n const filterChips: Array<{ id: DistributionFilter; label: string }> = [\n { id: 'all', label: t('wms.backend.lot.distribution.filters.all', 'All locations') },\n { id: 'sellable', label: t('wms.backend.lot.distribution.filters.sellable', 'Sellable') },\n { id: 'picking', label: t('wms.backend.lot.distribution.filters.picking', 'Picking') },\n { id: 'nearExpiry', label: t('wms.backend.lot.distribution.filters.nearExpiry', 'Near expiry') },\n ]\n\n const isLoading =\n lotQuery.isLoading ||\n variantQuery.isLoading ||\n profileQuery.isLoading ||\n balancesQuery.isLoading ||\n movementsQuery.isLoading\n\n const hasError =\n lotQuery.isError ||\n variantQuery.isError ||\n profileQuery.isError ||\n balancesQuery.isError ||\n movementsQuery.isError\n\n if (!scopedLotId) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={t('wms.backend.lot.errors.invalidId', 'Invalid lot identifier.')}\n action={(\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => router.push(lotsHref)}>\n {t('wms.backend.lot.actions.backToLots', 'Back to lots')}\n </Button>\n )}\n />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"w-fit px-0 text-muted-foreground hover:text-foreground\">\n <Link href={lotsHref}>\n <ArrowLeft className=\"size-4\" />\n {t('wms.backend.lot.actions.backToLots', 'Back to lots')}\n </Link>\n </LinkButton>\n\n <PageHeader\n title={pageTitle}\n description={subtitleParts.join(' \u00B7 ')}\n actions={(\n <>\n <Select value={warehouseId} onValueChange={setWarehouseId}>\n <SelectTrigger className=\"w-full max-w-xs sm:w-56\">\n <Warehouse className=\"mr-2 size-4 shrink-0 text-muted-foreground\" />\n <SelectValue placeholder={warehouseLabel} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {t('wms.backend.lot.filters.allWarehouses', 'All warehouses')}\n </SelectItem>\n {(warehousesQuery.data ?? []).map((warehouse) => (\n <SelectItem key={warehouse.id} value={warehouse.id}>\n {warehouse.name || warehouse.code || warehouse.id}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => openAdjustDialog(resolveMutationContext())}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.lot.actions.adjust', 'Adjust stock')}\n </Button>\n ) : null}\n </>\n )}\n />\n\n {isLoading ? (\n <LoadingMessage label={t('wms.backend.lot.loading', 'Loading lot view\u2026')} />\n ) : null}\n\n {hasError ? (\n <ErrorMessage label={t('wms.backend.lot.errors.load', 'Failed to load lot view.')} />\n ) : null}\n\n {!isLoading && !hasError && lotQuery.data ? (\n <>\n <section className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n <LotKpiCard\n title={t('wms.backend.lot.kpis.onHand.title', 'Total on hand')}\n caption={t('wms.backend.lot.kpis.onHand.caption', 'Physical quantity for this lot')}\n value={String(totals.onHand)}\n badgeLabel={\n totals.onHand > 0\n ? t('wms.backend.lot.kpis.onHand.badge', '{count} units', { count: totals.onHand })\n : null\n }\n badgeVariant=\"info\"\n ctaLabel={t('wms.backend.lot.kpis.onHand.cta', 'View locations')}\n ctaHref=\"#lot-locations\"\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.reserved.title', 'Reserved')}\n caption={t('wms.backend.lot.kpis.reserved.caption', 'Committed from this lot')}\n value={String(totals.reserved)}\n badgeLabel={\n totals.reserved > 0\n ? t('wms.backend.lot.kpis.reserved.badge', '{count} units held', { count: totals.reserved })\n : null\n }\n badgeVariant=\"info\"\n ctaLabel={t('wms.backend.lot.kpis.reserved.cta', 'View reservations')}\n ctaHref=\"/backend/wms/reservations\"\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.daysToExpiry.title', 'Days to expiry')}\n caption={t('wms.backend.lot.kpis.daysToExpiry.caption', 'Until expiration date')}\n value={daysToExpiry === null ? '\u2014' : String(daysToExpiry)}\n badgeLabel={\n daysToExpiry !== null && daysToExpiry <= 30\n ? t('wms.backend.lot.kpis.daysToExpiry.badgeSoon', 'Expiring soon')\n : daysToExpiry !== null\n ? t('wms.backend.lot.kpis.daysToExpiry.badgeOk', 'Within window')\n : null\n }\n badgeVariant={daysToExpiry !== null && daysToExpiry <= 30 ? 'warning' : 'success'}\n ctaLabel={t('wms.backend.lot.kpis.daysToExpiry.cta', 'View SKU profile')}\n ctaHref={skuDetailHref}\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.lotAge.title', 'Lot age')}\n caption={t('wms.backend.lot.kpis.lotAge.caption', 'Since lot was created')}\n value={lotAgeDays === null ? '\u2014' : t('wms.backend.lot.kpis.lotAge.value', '{days}d', { days: lotAgeDays })}\n badgeLabel={\n lotAgeDays !== null && lotAgeDays <= 7\n ? t('wms.backend.lot.kpis.lotAge.badgeFresh', 'Fresh lot')\n : lotAgeDays !== null\n ? t('wms.backend.lot.kpis.lotAge.badgeAged', 'Aged stock')\n : null\n }\n badgeVariant={lotAgeDays !== null && lotAgeDays <= 7 ? 'success' : 'neutral'}\n ctaLabel={t('wms.backend.lot.kpis.lotAge.cta', 'View movements')}\n ctaHref={movementsHref}\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.qualityStatus.title', 'Quality status')}\n caption={\n statusNotes\n ? t('wms.backend.lot.kpis.qualityStatus.notesCaption', 'Note: {notes}', {\n notes: statusNotes,\n })\n : t('wms.backend.lot.kpis.qualityStatus.caption', 'Lot hold and QC state')\n }\n value={qualityStatus.value}\n badgeLabel={t(qualityStatus.labelKey, qualityStatus.labelFallback)}\n badgeVariant={qualityStatus.variant}\n ctaLabel={t('wms.backend.lot.kpis.qualityStatus.cta', 'Change status')}\n onCtaClick={access.canManage ? () => setChangeStatusOpen(true) : undefined}\n ctaHref={!access.canManage ? '/backend/config/wms' : undefined}\n />\n </section>\n\n <section\n id=\"lot-locations\"\n className=\"rounded-lg border bg-card text-card-foreground shadow-sm\"\n >\n <div className=\"border-b px-5 py-4\">\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.lot.distribution.title', 'Where this lot lives')}\n </h2>\n </div>\n <div className=\"flex flex-col gap-4 border-b px-5 py-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap gap-1.5\">\n {filterChips.map((chip) => (\n <Button\n key={chip.id}\n type=\"button\"\n size=\"sm\"\n variant={distributionFilter === chip.id ? 'default' : 'outline'}\n className={cn('rounded-full', distributionFilter === chip.id && 'shadow-sm')}\n onClick={() => setDistributionFilter(chip.id)}\n >\n {chip.label}\n </Button>\n ))}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleExportDistributionCsv}>\n <Download className=\"size-4\" />\n {t('wms.backend.lot.distribution.actions.exportCsv', 'Export CSV')}\n </Button>\n {access.canCycleCount ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => openCycleCountDialog(resolveMutationContext())}\n >\n {t('wms.backend.lot.distribution.actions.cycleCountZone', 'Cycle count zone')}\n </Button>\n ) : null}\n {access.canAdjust && selectedBalances.length > 0 ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"default\"\n onClick={() => openAdjustDialog(resolveMutationContext())}\n >\n {t('wms.backend.lot.distribution.actions.adjustSelected', 'Adjust selected ({count})', { count: selectedBalances.length })}\n </Button>\n ) : null}\n </div>\n </div>\n <DataTable<InventoryBalanceRow>\n embedded\n columns={distributionColumns}\n data={pagedBalances}\n disableRowClick\n entityId={E.wms.inventory_balance}\n perspective={{ tableId: extensionPoints.hosts.lotDistributionTable.tableId }}\n pagination={{\n page: distributionPage,\n pageSize: distributionPageSize,\n total: distributionTotal,\n totalPages: distributionTotalPages,\n onPageChange: setDistributionPage,\n }}\n emptyState={(\n <EmptyState\n title={t('wms.backend.lot.distribution.empty.title', 'No locations in this view')}\n description={t(\n 'wms.backend.lot.distribution.empty.description',\n 'Try another filter or post inventory through receipts or adjustments.',\n )}\n />\n )}\n />\n {distributionTotal > 0 ? (\n <div className=\"flex flex-col gap-2 border-t px-5 py-3 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between\">\n <span>\n {t('wms.backend.lot.distribution.footer.showing', 'Showing {shown} of {total} locations', {\n shown: pagedBalances.length,\n total: distributionTotal,\n })}\n </span>\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"h-auto px-0\">\n <Link href={movementsHref}>\n {t('wms.backend.lot.distribution.footer.openLedger', 'Open ledger for this lot \u2192')}\n </Link>\n </LinkButton>\n </div>\n ) : null}\n </section>\n\n <DataTable<InventoryMovementRow>\n title={t('wms.backend.lot.activity.title', 'Recent activity')}\n columns={activityColumns}\n data={movementsQuery.data ?? []}\n disableRowClick\n entityId={E.wms.inventory_movement}\n perspective={{ tableId: extensionPoints.hosts.lotActivityTable.tableId }}\n emptyState={t('wms.backend.lot.activity.empty', 'No recent movements for this lot.')}\n actions={(\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href={movementsHref}>\n {t('wms.backend.lot.activity.viewAll', 'View all movements \u2192')}\n </Link>\n </Button>\n )}\n />\n\n <section className=\"flex flex-col gap-4 rounded-lg border bg-card px-5 py-4 shadow-sm lg:flex-row lg:items-center lg:justify-between\">\n <div>\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.lot.quickActions.title', 'Quick actions')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'wms.backend.lot.quickActions.description',\n 'Run common inventory actions without leaving this lot view',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"default\" onClick={() => openAdjustDialog(resolveMutationContext())}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.lot.quickActions.adjust', 'Adjust inventory')}\n </Button>\n ) : null}\n {access.canAdjust ? (\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={inventoryConsoleHref}>\n <ArrowLeftRight className=\"size-4\" />\n {t('wms.backend.lot.quickActions.move', 'Move stock')}\n </Link>\n </Button>\n ) : null}\n {access.canCycleCount ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => openCycleCountDialog(resolveMutationContext())}\n >\n <ClipboardList className=\"size-4\" />\n {t('wms.backend.lot.quickActions.cycleCount', 'Cycle count')}\n </Button>\n ) : null}\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={movementsHref}>\n {t('wms.backend.lot.quickActions.openLedger', 'Open ledger')}\n <ExternalLink className=\"size-4\" />\n </Link>\n </Button>\n </div>\n </section>\n </>\n ) : null}\n\n {!isLoading && !hasError && !lotQuery.data ? (\n <RecordNotFoundState\n label={t('wms.backend.lot.errors.notFound', 'Lot not found.')}\n backHref={lotsHref}\n backLabel={t('wms.backend.lot.actions.backToLots', 'Back to lots')}\n />\n ) : null}\n </PageBody>\n\n {access.canAdjust && scopedLotId ? (\n <AdjustInventoryDialog\n open={adjustOpen}\n onOpenChange={setAdjustOpen}\n access={access}\n initialCatalogVariantId={adjustPreset.catalogVariantId ?? catalogVariantId ?? undefined}\n initialWarehouseId={adjustPreset.warehouseId}\n initialLocationId={adjustPreset.locationId}\n initialLotId={adjustPreset.lotId ?? scopedLotId}\n />\n ) : null}\n {access.canCycleCount ? (\n <CycleCountWizardDialog\n open={cycleOpen}\n onOpenChange={setCycleOpen}\n access={access}\n initialCatalogVariantId={catalogVariantId ?? undefined}\n initialWarehouseId={cyclePreset.warehouseId ?? (warehouseId !== 'all' ? warehouseId : undefined)}\n initialLocationId={cyclePreset.locationId}\n initialLotId={scopedLotId ?? undefined}\n />\n ) : null}\n {access.canAdjust && scopedLotId ? (\n <ChangeLotStatusDialog\n open={changeStatusOpen}\n onOpenChange={setChangeStatusOpen}\n access={access}\n lotId={scopedLotId}\n currentStatus={lotData?.status}\n lotUpdatedAt={lotData?.updated_at}\n />\n ) : null}\n </Page>\n )\n}\n"],
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/wms/extension-points'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { z } from 'zod'\nimport { useQuery } from '@tanstack/react-query'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport {\n ArrowLeft,\n ArrowLeftRight,\n ClipboardList,\n Download,\n ExternalLink,\n SlidersHorizontal,\n Warehouse,\n} from 'lucide-react'\nimport { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { ErrorMessage, LoadingMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { LinkButton } from '@open-mercato/ui/primitives/link-button'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { StatusBadge, type StatusBadgeVariant } from '@open-mercato/ui/primitives/status-badge'\nimport { Checkbox } from '@open-mercato/ui/primitives/checkbox'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n inventoryMovementReasonLabel,\n type InventoryDisplayTranslator,\n} from '../../lib/inventoryDisplayUi'\nimport { AdjustInventoryDialog } from './AdjustInventoryDialog'\nimport { ChangeLotStatusDialog } from './ChangeLotStatusDialog'\nimport { CycleCountWizardDialog } from './CycleCountWizardDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\nconst lotIdSchema = z.string().uuid()\n\ntype PagedResponse<T> = {\n items: T[]\n total: number\n totalPages: number\n}\n\ntype CatalogVariantRow = {\n id: string\n sku?: string | null\n name?: string | null\n}\n\ntype InventoryLotRow = {\n id: string\n catalog_variant_id?: string | null\n sku?: string | null\n lot_number?: string | null\n batch_number?: string | null\n manufactured_at?: string | null\n best_before_at?: string | null\n expires_at?: string | null\n status?: string | null\n metadata?: Record<string, unknown> | null\n created_at?: string | null\n updated_at?: string | null\n}\n\nfunction extractLotStatusNotes(metadata: InventoryLotRow['metadata']): string | null {\n if (!metadata || typeof metadata !== 'object') return null\n const notes = metadata.notes\n return typeof notes === 'string' && notes.trim().length > 0 ? notes.trim() : null\n}\n\ntype InventoryProfileRow = {\n reorder_point?: string | number | null\n}\n\ntype InventoryBalanceRow = {\n id: string\n warehouse_id?: string | null\n warehouse_name?: string | null\n warehouse_code?: string | null\n location_id?: string | null\n location_code?: string | null\n location_type?: string | null\n catalog_variant_id?: string | null\n quantity_on_hand?: string | number | null\n quantity_reserved?: string | number | null\n quantity_allocated?: string | number | null\n quantity_available?: number | null\n}\n\ntype InventoryMovementRow = {\n id: string\n warehouse_id?: string | null\n warehouse_name?: string | null\n warehouse_code?: string | null\n location_from_id?: string | null\n location_from_code?: string | null\n location_to_id?: string | null\n location_to_code?: string | null\n catalog_variant_id?: string | null\n variant_sku?: string | null\n variant_name?: string | null\n lot_id?: string | null\n quantity?: string | number | null\n type?: string | null\n reference_type?: string | null\n reference_id?: string | null\n reason?: string | null\n reason_code?: string | null\n performed_at?: string | null\n received_at?: string | null\n}\n\ntype InventoryMutationPreset = {\n warehouseId?: string\n locationId?: string\n catalogVariantId?: string\n lotId?: string\n}\n\ntype WarehouseOption = {\n id: string\n name?: string | null\n code?: string | null\n}\n\ntype DistributionFilter = 'all' | 'sellable' | 'picking' | 'nearExpiry'\n\nconst NON_SELLABLE_LOCATION_TYPES = new Set(['staging', 'dock'])\nconst PICKING_LOCATION_TYPES = new Set(['staging', 'bin', 'slot'])\nconst NEAR_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000\nconst MS_PER_DAY = 24 * 60 * 60 * 1000\n\nfunction escapeCsvCell(value: string | number): string {\n const str = String(value ?? '')\n if (/[\",\\n]/.test(str)) return `\"${str.replace(/\"/g, '\"\"')}\"`\n return str\n}\n\nfunction downloadCsvFile(filename: string, rows: string[][]) {\n const csv = rows.map((row) => row.map(escapeCsvCell).join(',')).join('\\n')\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = filename\n anchor.click()\n URL.revokeObjectURL(url)\n}\n\nfunction toNumber(value: string | number | null | undefined): number {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nfunction formatWarehouseLabel(row: {\n warehouse_name?: string | null\n warehouse_code?: string | null\n warehouse_id?: string | null\n}): string {\n const code = (row.warehouse_code ?? '').trim()\n const name = (row.warehouse_name ?? '').trim()\n if (code && name) return `${code} \u00B7 ${name}`\n return name || code || row.warehouse_id || '\u2014'\n}\n\nfunction formatLocationLabel(code: string | null | undefined, id: string | null | undefined): string {\n const trimmed = (code ?? '').trim()\n if (trimmed) return trimmed\n return id || '\u2014'\n}\n\nfunction balanceLocationKey(row: InventoryBalanceRow): string {\n return `${row.warehouse_id?.trim() ?? ''}:${row.location_id?.trim() ?? ''}`\n}\n\nfunction movementLocationKey(\n warehouseId: string | null | undefined,\n locationId: string | null | undefined,\n): string {\n return `${warehouseId?.trim() ?? ''}:${locationId?.trim() ?? ''}`\n}\n\nfunction isNearExpiry(expiresAt: string | null | undefined, nowMs: number): boolean {\n if (!expiresAt) return false\n const expires = new Date(expiresAt).getTime()\n if (Number.isNaN(expires)) return false\n return expires > nowMs && expires - nowMs <= NEAR_EXPIRY_MS\n}\n\nfunction isExpired(expiresAt: string | null | undefined, nowMs: number): boolean {\n if (!expiresAt) return false\n const expires = new Date(expiresAt).getTime()\n return !Number.isNaN(expires) && expires <= nowMs\n}\n\nfunction daysUntilExpiry(expiresAt: string | null | undefined, nowMs: number): number | null {\n if (!expiresAt) return null\n const expires = new Date(expiresAt).getTime()\n if (Number.isNaN(expires)) return null\n return Math.ceil((expires - nowMs) / MS_PER_DAY)\n}\n\nfunction daysSince(value: string | null | undefined, nowMs: number): number | null {\n if (!value) return null\n const ts = new Date(value).getTime()\n if (Number.isNaN(ts)) return null\n return Math.max(0, Math.floor((nowMs - ts) / MS_PER_DAY))\n}\n\nfunction resolveLotQualityStatus(\n status: string | null | undefined,\n expiresAt: string | null | undefined,\n nowMs: number,\n): { variant: StatusBadgeVariant; labelKey: string; labelFallback: string; value: string } {\n if (status === 'expired' || isExpired(expiresAt, nowMs)) {\n return {\n variant: 'error',\n labelKey: 'wms.backend.lot.status.expired',\n labelFallback: 'Expired',\n value: 'Expired',\n }\n }\n if (status === 'quarantine') {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.status.quarantine',\n labelFallback: 'Quarantine',\n value: 'QC',\n }\n }\n if (status === 'hold') {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.status.hold',\n labelFallback: 'Hold',\n value: 'Hold',\n }\n }\n if (isNearExpiry(expiresAt, nowMs)) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.status.nearExpiry',\n labelFallback: 'Near expiry',\n value: 'Near expiry',\n }\n }\n return {\n variant: 'success',\n labelKey: 'wms.backend.lot.status.available',\n labelFallback: 'Available',\n value: 'OK',\n }\n}\n\nfunction resolveBalanceStatus(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | null | undefined,\n reorderPoint: number,\n nowMs: number,\n): { variant: StatusBadgeVariant; labelKey: string; labelFallback: string } {\n if (lot?.status === 'expired' || isExpired(lot?.expires_at, nowMs)) {\n return {\n variant: 'error',\n labelKey: 'wms.backend.lot.distribution.status.expired',\n labelFallback: 'Expired',\n }\n }\n if (isNearExpiry(lot?.expires_at, nowMs)) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.distribution.status.nearExpiry',\n labelFallback: 'Near expiry',\n }\n }\n const available = row.quantity_available ?? 0\n if (reorderPoint > 0 && available <= reorderPoint) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.lot.distribution.status.lowStock',\n labelFallback: 'Low stock',\n }\n }\n const reserved = toNumber(row.quantity_reserved)\n const onHand = toNumber(row.quantity_on_hand)\n if (reserved > 0 && onHand > 0 && reserved >= onHand) {\n return {\n variant: 'info',\n labelKey: 'wms.backend.lot.distribution.status.reserved',\n labelFallback: 'Reserved',\n }\n }\n return {\n variant: 'success',\n labelKey: 'wms.backend.lot.distribution.status.available',\n labelFallback: 'Available',\n }\n}\n\nfunction matchesDistributionFilter(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | null | undefined,\n filter: DistributionFilter,\n nowMs: number,\n): boolean {\n const locationType = (row.location_type ?? '').trim().toLowerCase()\n const locationCode = (row.location_code ?? '').trim().toLowerCase()\n switch (filter) {\n case 'all':\n return true\n case 'sellable':\n return !NON_SELLABLE_LOCATION_TYPES.has(locationType) && lot?.status !== 'expired'\n case 'picking':\n return (\n PICKING_LOCATION_TYPES.has(locationType) ||\n locationCode.includes('pick') ||\n locationCode.includes('staging') ||\n toNumber(row.quantity_reserved) > 0 ||\n toNumber(row.quantity_allocated) > 0\n )\n case 'nearExpiry':\n return isNearExpiry(lot?.expires_at, nowMs) || lot?.status === 'expired'\n default:\n return true\n }\n}\n\nfunction movementTypeLabel(type: string, t: ReturnType<typeof useT>): string {\n const key = `wms.backend.lot.activity.types.${type}`\n const fallbacks: Record<string, string> = {\n receipt: 'Receive',\n return_receive: 'Receive',\n adjust: 'Adjust',\n transfer: 'Move',\n pick: 'Allocate',\n pack: 'Allocate',\n cycle_count: 'Reconcile',\n putaway: 'Putaway',\n ship: 'Ship',\n }\n return t(key, fallbacks[type] ?? type)\n}\n\nconst movementStatusMap: Record<string, StatusBadgeVariant> = {\n receipt: 'success',\n return_receive: 'success',\n adjust: 'warning',\n transfer: 'info',\n pick: 'info',\n pack: 'info',\n cycle_count: 'neutral',\n putaway: 'info',\n ship: 'success',\n}\n\nfunction formatMovementTitle(\n row: InventoryMovementRow,\n lotLabel: string,\n t: ReturnType<typeof useT>,\n): string {\n const quantity = Math.abs(toNumber(row.quantity))\n const signedQuantity = toNumber(row.quantity)\n switch (row.type) {\n case 'receipt':\n case 'return_receive':\n return t('wms.backend.lot.activity.titles.received', 'Received {quantity}\u00D7 {lot}', {\n quantity,\n lot: lotLabel,\n })\n case 'adjust':\n return t('wms.backend.lot.activity.titles.adjusted', 'Adjusted {quantity}\u00D7 {lot}', {\n quantity: `${signedQuantity >= 0 ? '+' : ''}${signedQuantity}`,\n lot: lotLabel,\n })\n case 'transfer':\n return t('wms.backend.lot.activity.titles.moved', 'Moved {quantity}\u00D7 {lot}', {\n quantity,\n lot: lotLabel,\n })\n case 'pick':\n case 'pack':\n return t('wms.backend.lot.activity.titles.allocated', 'Allocated {quantity}\u00D7 {lot}', {\n quantity,\n lot: lotLabel,\n })\n case 'cycle_count':\n return t('wms.backend.lot.activity.titles.reconciled', 'Cycle count of {lot}', {\n lot: lotLabel,\n })\n default:\n return t('wms.backend.lot.activity.titles.generic', '{type} {quantity}\u00D7 {lot}', {\n type: row.type ?? 'movement',\n quantity,\n lot: lotLabel,\n })\n }\n}\n\nfunction formatMovementSubtitle(\n row: InventoryMovementRow,\n t: InventoryDisplayTranslator,\n): string | null {\n const reasonLabel = inventoryMovementReasonLabel(\n {\n reasonCode: row.reason_code,\n reason: row.reason,\n movementType: row.type,\n },\n t,\n )\n if (reasonLabel) return reasonLabel\n if (row.reference_type && row.reference_id) return `${row.reference_type} \u00B7 ${row.reference_id}`\n return null\n}\n\nfunction formatMovementLocation(row: InventoryMovementRow): string {\n const warehouse = formatWarehouseLabel(row)\n const from = formatLocationLabel(row.location_from_code, row.location_from_id)\n const to = formatLocationLabel(row.location_to_code, row.location_to_id)\n if (row.type === 'transfer' && from !== '\u2014' && to !== '\u2014') return `${from} \u2192 ${to}`\n const location = to !== '\u2014' ? to : from\n if (location !== '\u2014') return `${warehouse} \u00B7 ${location}`\n return warehouse\n}\n\nfunction formatLastMoveLabel(\n raw: string | undefined,\n locale: string,\n t: ReturnType<typeof useT>,\n): string {\n if (!raw) return '\u2014'\n const date = new Date(raw)\n if (Number.isNaN(date.getTime())) return raw\n const diffDays = Math.floor((Date.now() - date.getTime()) / MS_PER_DAY)\n if (diffDays === 0) {\n return t('wms.backend.lot.distribution.lastMove.today', '{time} today', {\n time: new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }).format(date),\n })\n }\n if (diffDays === 1) {\n return t('wms.backend.lot.distribution.lastMove.yesterday', 'Yesterday')\n }\n if (diffDays < 7) {\n return t('wms.backend.lot.distribution.lastMove.daysAgo', '{days} days ago', { days: diffDays })\n }\n return new Intl.DateTimeFormat(locale, { month: '2-digit', day: '2-digit' }).format(date)\n}\n\ntype LotKpiCardProps = {\n title: string\n caption: string\n value: string\n badgeLabel: string | null\n badgeVariant: StatusBadgeVariant\n ctaLabel: string\n ctaHref?: string\n onCtaClick?: () => void\n}\n\nfunction LotKpiCard({\n title,\n caption,\n value,\n badgeLabel,\n badgeVariant,\n ctaLabel,\n ctaHref,\n onCtaClick,\n}: LotKpiCardProps) {\n return (\n <section className=\"flex min-h-52 flex-col rounded-lg border bg-card p-5 text-card-foreground shadow-sm\">\n <p className=\"text-sm font-medium\">{title}</p>\n <p className=\"mt-3 text-xs text-muted-foreground\">{caption}</p>\n <div className=\"mt-2 flex items-end gap-3\">\n <p className=\"text-3xl font-semibold tracking-tight\">{value}</p>\n {badgeLabel ? (\n <StatusBadge variant={badgeVariant} dot>\n {badgeLabel}\n </StatusBadge>\n ) : null}\n </div>\n {onCtaClick ? (\n <LinkButton variant=\"primary\" size=\"sm\" className=\"mt-auto pt-4 w-fit\" onClick={onCtaClick}>\n {ctaLabel}\n </LinkButton>\n ) : ctaHref ? (\n <LinkButton asChild variant=\"primary\" size=\"sm\" className=\"mt-auto pt-4 w-fit\">\n <Link href={ctaHref}>{ctaLabel}</Link>\n </LinkButton>\n ) : null}\n </section>\n )\n}\n\ntype WmsLotDetailPageProps = {\n lotId: string\n}\n\nexport default function WmsLotDetailPage({ lotId }: WmsLotDetailPageProps) {\n const t = useT()\n const locale = useLocale()\n const router = useRouter()\n const access = useWmsInventoryMutationAccess()\n const parsedLotId = lotIdSchema.safeParse(lotId.trim())\n const scopedLotId = parsedLotId.success ? parsedLotId.data : null\n\n const [warehouseId, setWarehouseId] = React.useState<string>('all')\n const [distributionFilter, setDistributionFilter] = React.useState<DistributionFilter>('all')\n const [distributionPage, setDistributionPage] = React.useState(1)\n const [selectedBalanceIds, setSelectedBalanceIds] = React.useState<Set<string>>(() => new Set())\n const [adjustOpen, setAdjustOpen] = React.useState(false)\n const [adjustPreset, setAdjustPreset] = React.useState<InventoryMutationPreset>({})\n const [cycleOpen, setCycleOpen] = React.useState(false)\n const [cyclePreset, setCyclePreset] = React.useState<Pick<InventoryMutationPreset, 'warehouseId' | 'locationId'>>({})\n const [changeStatusOpen, setChangeStatusOpen] = React.useState(false)\n\n const distributionPageSize = 20\n const lotsHref = '/backend/wms/lots'\n const inventoryHref = '/backend/wms/inventory'\n\n const activityTimeFormatter = React.useMemo(\n () =>\n new Intl.DateTimeFormat(locale, {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }),\n [locale],\n )\n\n const lotQuery = useQuery({\n queryKey: ['wms-lot-detail', 'lot', scopedLotId],\n enabled: Boolean(scopedLotId),\n queryFn: async () => {\n const params = new URLSearchParams({\n ids: scopedLotId!,\n page: '1',\n pageSize: '1',\n })\n const call = await apiCall<PagedResponse<InventoryLotRow>>(`/api/wms/lots?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.lot', 'Failed to load lot.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const catalogVariantId = lotQuery.data?.catalog_variant_id?.trim() ?? null\n\n const variantQuery = useQuery({\n queryKey: ['wms-lot-detail', 'variant', catalogVariantId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n id: catalogVariantId!,\n page: '1',\n pageSize: '1',\n })\n const call = await apiCall<PagedResponse<CatalogVariantRow>>(\n `/api/catalog/variants?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.variant', 'Failed to load SKU.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const profileQuery = useQuery({\n queryKey: ['wms-lot-detail', 'profile', catalogVariantId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\n page: '1',\n pageSize: '1',\n })\n const call = await apiCall<PagedResponse<InventoryProfileRow>>(\n `/api/wms/inventory-profiles?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.profile', 'Failed to load inventory profile.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const warehousesQuery = useQuery({\n queryKey: ['wms-lot-detail', 'warehouses'],\n queryFn: async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '100', sortField: 'name', sortDir: 'asc' })\n const call = await apiCall<PagedResponse<WarehouseOption>>(`/api/wms/warehouses?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.warehouses', 'Failed to load warehouses.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const balancesQuery = useQuery({\n queryKey: ['wms-lot-detail', 'balances', scopedLotId, warehouseId],\n enabled: Boolean(scopedLotId),\n queryFn: async () => {\n const params = new URLSearchParams({\n lotId: scopedLotId!,\n page: '1',\n pageSize: '100',\n sortField: 'updatedAt',\n sortDir: 'desc',\n })\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const call = await apiCall<PagedResponse<InventoryBalanceRow>>(\n `/api/wms/inventory/balances?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.balances', 'Failed to load lot locations.'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const movementsQuery = useQuery({\n queryKey: ['wms-lot-detail', 'movements', scopedLotId, warehouseId],\n enabled: Boolean(scopedLotId),\n queryFn: async () => {\n const params = new URLSearchParams({\n lotId: scopedLotId!,\n page: '1',\n pageSize: '25',\n sortField: 'performedAt',\n sortDir: 'desc',\n })\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const call = await apiCall<PagedResponse<InventoryMovementRow>>(\n `/api/wms/inventory/movements?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lot.errors.movements', 'Failed to load recent activity.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const reorderPoint = toNumber(profileQuery.data?.reorder_point)\n const lotData = lotQuery.data\n const lotLabel = (lotData?.lot_number ?? '').trim() || scopedLotId || '\u2014'\n const skuLabel =\n (lotData?.sku ?? '').trim() ||\n (variantQuery.data?.sku ?? '').trim() ||\n catalogVariantId ||\n '\u2014'\n const variantName = (variantQuery.data?.name ?? '').trim()\n const pageTitle = lotLabel\n const nowMs = React.useMemo(() => Date.now(), [])\n\n const qualityStatus = React.useMemo(\n () => resolveLotQualityStatus(lotData?.status, lotData?.expires_at, nowMs),\n [lotData?.expires_at, lotData?.status, nowMs],\n )\n const statusNotes = React.useMemo(\n () => extractLotStatusNotes(lotData?.metadata),\n [lotData?.metadata],\n )\n\n const daysToExpiry = daysUntilExpiry(lotData?.expires_at, nowMs)\n const lotAgeDays = daysSince(lotData?.created_at, nowMs)\n\n const totals = React.useMemo(() => {\n const items = balancesQuery.data?.items ?? []\n let onHand = 0\n let reserved = 0\n let available = 0\n for (const row of items) {\n onHand += toNumber(row.quantity_on_hand)\n reserved += toNumber(row.quantity_reserved)\n available += row.quantity_available ?? 0\n }\n return { onHand, reserved, available }\n }, [balancesQuery.data?.items])\n\n const lastMoveByLocation = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const row of movementsQuery.data ?? []) {\n const raw = row.performed_at ?? row.received_at\n if (!raw) continue\n const keys = [\n movementLocationKey(row.warehouse_id, row.location_to_id),\n movementLocationKey(row.warehouse_id, row.location_from_id),\n ]\n for (const key of keys) {\n if (!key || key === ':') continue\n const existing = map.get(key)\n if (!existing || new Date(raw).getTime() > new Date(existing).getTime()) {\n map.set(key, raw)\n }\n }\n }\n return map\n }, [movementsQuery.data])\n\n const filteredBalances = React.useMemo(() => {\n const items = balancesQuery.data?.items ?? []\n return items.filter((row) => matchesDistributionFilter(row, lotData, distributionFilter, nowMs))\n }, [balancesQuery.data?.items, distributionFilter, lotData, nowMs])\n\n const distributionTotal = filteredBalances.length\n const distributionTotalPages = Math.max(1, Math.ceil(distributionTotal / distributionPageSize))\n const pagedBalances = React.useMemo(() => {\n const start = (distributionPage - 1) * distributionPageSize\n return filteredBalances.slice(start, start + distributionPageSize)\n }, [distributionPage, distributionPageSize, filteredBalances])\n\n React.useEffect(() => {\n setDistributionPage(1)\n setSelectedBalanceIds(new Set())\n }, [distributionFilter, warehouseId])\n\n const selectedBalances = React.useMemo(\n () => filteredBalances.filter((row) => selectedBalanceIds.has(row.id)),\n [filteredBalances, selectedBalanceIds],\n )\n\n const movementsHref = React.useMemo(() => {\n const params = new URLSearchParams()\n if (scopedLotId) params.set('lotId', scopedLotId)\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const query = params.toString()\n return query ? `/backend/wms/movements?${query}` : '/backend/wms/movements'\n }, [scopedLotId, warehouseId])\n\n const inventoryConsoleHref = React.useMemo(() => {\n if (!scopedLotId) return inventoryHref\n return `${inventoryHref}?lotId=${encodeURIComponent(scopedLotId)}`\n }, [scopedLotId])\n\n const skuDetailHref = catalogVariantId\n ? `/backend/wms/sku/${encodeURIComponent(catalogVariantId)}`\n : inventoryHref\n\n const openAdjustDialog = React.useCallback((preset: InventoryMutationPreset = {}) => {\n setAdjustPreset(preset)\n setAdjustOpen(true)\n }, [])\n\n const openCycleCountDialog = React.useCallback(\n (preset: Pick<InventoryMutationPreset, 'warehouseId' | 'locationId'> = {}) => {\n setCyclePreset(preset)\n setCycleOpen(true)\n },\n [],\n )\n\n const resolveMutationContext = React.useCallback((): InventoryMutationPreset => {\n const firstSelected = selectedBalances[0]\n const scopedWarehouseId =\n firstSelected?.warehouse_id?.trim() ||\n (warehouseId !== 'all' ? warehouseId : undefined)\n const locationId =\n selectedBalances.length === 1 ? firstSelected?.location_id?.trim() || undefined : undefined\n return {\n warehouseId: scopedWarehouseId,\n locationId,\n catalogVariantId: catalogVariantId ?? undefined,\n lotId: scopedLotId ?? undefined,\n }\n }, [catalogVariantId, scopedLotId, selectedBalances, warehouseId])\n\n const toggleBalanceSelection = React.useCallback((balanceId: string, selected: boolean) => {\n setSelectedBalanceIds((current) => {\n const next = new Set(current)\n if (selected) next.add(balanceId)\n else next.delete(balanceId)\n return next\n })\n }, [])\n\n const togglePageSelection = React.useCallback((selected: boolean) => {\n setSelectedBalanceIds((current) => {\n const next = new Set(current)\n for (const row of pagedBalances) {\n if (selected) next.add(row.id)\n else next.delete(row.id)\n }\n return next\n })\n }, [pagedBalances])\n\n const pageSelectionState = React.useMemo(() => {\n if (pagedBalances.length === 0) {\n return { checked: false, indeterminate: false }\n }\n const selectedOnPage = pagedBalances.filter((row) => selectedBalanceIds.has(row.id)).length\n return {\n checked: selectedOnPage === pagedBalances.length,\n indeterminate: selectedOnPage > 0 && selectedOnPage < pagedBalances.length,\n }\n }, [pagedBalances, selectedBalanceIds])\n\n const handleExportDistributionCsv = React.useCallback(() => {\n const headers = [\n t('wms.backend.lot.distribution.columns.warehouse', 'Warehouse'),\n t('wms.backend.lot.distribution.columns.location', 'Location'),\n t('wms.backend.lot.distribution.columns.lastMove', 'Last move'),\n t('wms.backend.lot.distribution.columns.onHand', 'On hand'),\n t('wms.backend.lot.distribution.columns.reserved', 'Reserved'),\n t('wms.backend.lot.distribution.columns.status', 'Status'),\n ]\n const rows = filteredBalances.map((row) => {\n const status = resolveBalanceStatus(row, lotData, reorderPoint, nowMs)\n return [\n formatWarehouseLabel(row),\n formatLocationLabel(row.location_code, row.location_id),\n formatLastMoveLabel(lastMoveByLocation.get(balanceLocationKey(row)), locale, t),\n String(toNumber(row.quantity_on_hand)),\n String(toNumber(row.quantity_reserved)),\n t(status.labelKey, status.labelFallback),\n ]\n })\n const safeLot = pageTitle.replace(/[^\\w.-]+/g, '-').replace(/^-+|-+$/g, '') || 'lot'\n downloadCsvFile(`${safeLot}-locations.csv`, [headers, ...rows])\n }, [filteredBalances, lastMoveByLocation, locale, lotData, nowMs, pageTitle, reorderPoint, t])\n\n const selectedWarehouse = warehousesQuery.data?.find((warehouse) => warehouse.id === warehouseId)\n const warehouseLabel =\n warehouseId === 'all'\n ? t('wms.backend.lot.filters.allWarehouses', 'All warehouses')\n : selectedWarehouse?.name || selectedWarehouse?.code || warehouseId\n\n const dateFormatter = React.useMemo(\n () => new Intl.DateTimeFormat(locale, { year: 'numeric', month: '2-digit', day: '2-digit' }),\n [locale],\n )\n\n const subtitleParts = [\n skuLabel !== '\u2014' ? skuLabel : null,\n variantName || null,\n lotData?.expires_at\n ? t('wms.backend.lot.header.expires', 'Exp {date}', {\n date: dateFormatter.format(new Date(lotData.expires_at)),\n })\n : null,\n ].filter(Boolean)\n\n const distributionColumns = React.useMemo<ColumnDef<InventoryBalanceRow>[]>(\n () => [\n {\n id: 'select',\n header: () => (\n <Checkbox\n aria-label={t('wms.backend.lot.distribution.columns.select', 'Select')}\n checked={pageSelectionState.indeterminate ? 'indeterminate' : pageSelectionState.checked}\n onCheckedChange={(checked) => togglePageSelection(checked === true)}\n />\n ),\n cell: ({ row }) => (\n <Checkbox\n aria-label={t('wms.backend.lot.distribution.columns.select', 'Select')}\n checked={selectedBalanceIds.has(row.original.id)}\n onCheckedChange={(checked) => toggleBalanceSelection(row.original.id, checked === true)}\n />\n ),\n meta: { maxWidth: '2.75rem' },\n },\n {\n accessorKey: 'warehouse_id',\n header: t('wms.backend.lot.distribution.columns.warehouse', 'Warehouse'),\n cell: ({ row }) => formatWarehouseLabel(row.original),\n },\n {\n accessorKey: 'location_id',\n header: t('wms.backend.lot.distribution.columns.location', 'Location'),\n cell: ({ row }) => {\n const locationId = row.original.location_id?.trim()\n const label = formatLocationLabel(row.original.location_code, row.original.location_id)\n if (!locationId || label === '\u2014') return label\n return (\n <Link\n href={`/backend/wms/location/${encodeURIComponent(locationId)}`}\n className=\"font-medium text-primary hover:underline\"\n >\n {label}\n </Link>\n )\n },\n },\n {\n id: 'lastMove',\n header: t('wms.backend.lot.distribution.columns.lastMove', 'Last move'),\n cell: ({ row }) =>\n formatLastMoveLabel(lastMoveByLocation.get(balanceLocationKey(row.original)), locale, t),\n },\n {\n accessorKey: 'quantity_on_hand',\n header: t('wms.backend.lot.distribution.columns.onHand', 'On hand'),\n cell: ({ row }) => String(toNumber(row.original.quantity_on_hand)),\n },\n {\n accessorKey: 'quantity_reserved',\n header: t('wms.backend.lot.distribution.columns.reserved', 'Reserved'),\n cell: ({ row }) => String(toNumber(row.original.quantity_reserved)),\n },\n {\n id: 'status',\n header: t('wms.backend.lot.distribution.columns.status', 'Status'),\n cell: ({ row }) => {\n const status = resolveBalanceStatus(row.original, lotData, reorderPoint, nowMs)\n return (\n <StatusBadge variant={status.variant} dot>\n {t(status.labelKey, status.labelFallback)}\n </StatusBadge>\n )\n },\n },\n ],\n [\n lastMoveByLocation,\n locale,\n lotData,\n nowMs,\n pageSelectionState.checked,\n pageSelectionState.indeterminate,\n reorderPoint,\n selectedBalanceIds,\n t,\n toggleBalanceSelection,\n togglePageSelection,\n ],\n )\n\n const activityColumns = React.useMemo<ColumnDef<InventoryMovementRow>[]>(\n () => [\n {\n accessorKey: 'type',\n header: t('wms.backend.lot.activity.columns.event', 'Event'),\n cell: ({ row }) => {\n const type = row.original.type ?? 'movement'\n return (\n <StatusBadge variant={movementStatusMap[type] ?? 'neutral'}>\n {movementTypeLabel(type, t)}\n </StatusBadge>\n )\n },\n },\n {\n id: 'details',\n header: t('wms.backend.lot.activity.columns.details', 'Details'),\n cell: ({ row }) => {\n const subtitle = formatMovementSubtitle(row.original, t)\n return (\n <div className=\"space-y-0.5\">\n <p className=\"text-sm font-medium\">{formatMovementTitle(row.original, lotLabel, t)}</p>\n {subtitle ? <p className=\"text-xs text-muted-foreground\">{subtitle}</p> : null}\n </div>\n )\n },\n },\n {\n id: 'location',\n header: t('wms.backend.lot.activity.columns.location', 'Location'),\n cell: ({ row }) => (\n <span className=\"text-sm text-muted-foreground\">{formatMovementLocation(row.original)}</span>\n ),\n },\n {\n id: 'time',\n header: t('wms.backend.lot.activity.columns.time', 'Time'),\n cell: ({ row }) => {\n const raw = row.original.performed_at ?? row.original.received_at\n if (!raw) return '\u2014'\n const date = new Date(raw)\n if (Number.isNaN(date.getTime())) return raw\n return activityTimeFormatter.format(date)\n },\n meta: { maxWidth: '6rem' },\n },\n ],\n [activityTimeFormatter, lotLabel, t],\n )\n\n const filterChips: Array<{ id: DistributionFilter; label: string }> = [\n { id: 'all', label: t('wms.backend.lot.distribution.filters.all', 'All locations') },\n { id: 'sellable', label: t('wms.backend.lot.distribution.filters.sellable', 'Sellable') },\n { id: 'picking', label: t('wms.backend.lot.distribution.filters.picking', 'Picking') },\n { id: 'nearExpiry', label: t('wms.backend.lot.distribution.filters.nearExpiry', 'Near expiry') },\n ]\n\n const isLoading =\n lotQuery.isLoading ||\n variantQuery.isLoading ||\n profileQuery.isLoading ||\n balancesQuery.isLoading ||\n movementsQuery.isLoading\n\n const hasError =\n lotQuery.isError ||\n variantQuery.isError ||\n profileQuery.isError ||\n balancesQuery.isError ||\n movementsQuery.isError\n\n if (!scopedLotId) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={t('wms.backend.lot.errors.invalidId', 'Invalid lot identifier.')}\n action={(\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => router.push(lotsHref)}>\n {t('wms.backend.lot.actions.backToLots', 'Back to lots')}\n </Button>\n )}\n />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"w-fit px-0 text-muted-foreground hover:text-foreground\">\n <Link href={lotsHref}>\n <ArrowLeft className=\"size-4\" />\n {t('wms.backend.lot.actions.backToLots', 'Back to lots')}\n </Link>\n </LinkButton>\n\n <PageHeader\n title={pageTitle}\n description={subtitleParts.join(' \u00B7 ')}\n actions={(\n <>\n <Select value={warehouseId} onValueChange={setWarehouseId}>\n <SelectTrigger className=\"w-full max-w-xs sm:w-56\">\n <Warehouse className=\"mr-2 size-4 shrink-0 text-muted-foreground\" />\n <SelectValue placeholder={warehouseLabel} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {t('wms.backend.lot.filters.allWarehouses', 'All warehouses')}\n </SelectItem>\n {(warehousesQuery.data ?? []).map((warehouse) => (\n <SelectItem key={warehouse.id} value={warehouse.id}>\n {warehouse.name || warehouse.code || warehouse.id}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => openAdjustDialog(resolveMutationContext())}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.lot.actions.adjust', 'Adjust stock')}\n </Button>\n ) : null}\n </>\n )}\n />\n\n {isLoading ? (\n <LoadingMessage label={t('wms.backend.lot.loading', 'Loading lot view\u2026')} />\n ) : null}\n\n {hasError ? (\n <ErrorMessage label={t('wms.backend.lot.errors.load', 'Failed to load lot view.')} />\n ) : null}\n\n {!isLoading && !hasError && lotQuery.data ? (\n <>\n <section className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n <LotKpiCard\n title={t('wms.backend.lot.kpis.onHand.title', 'Total on hand')}\n caption={t('wms.backend.lot.kpis.onHand.caption', 'Physical quantity for this lot')}\n value={String(totals.onHand)}\n badgeLabel={\n totals.onHand > 0\n ? t('wms.backend.lot.kpis.onHand.badge', '{count} units', { count: totals.onHand })\n : null\n }\n badgeVariant=\"info\"\n ctaLabel={t('wms.backend.lot.kpis.onHand.cta', 'View locations')}\n ctaHref=\"#lot-locations\"\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.reserved.title', 'Reserved')}\n caption={t('wms.backend.lot.kpis.reserved.caption', 'Committed from this lot')}\n value={String(totals.reserved)}\n badgeLabel={\n totals.reserved > 0\n ? t('wms.backend.lot.kpis.reserved.badge', '{count} units held', { count: totals.reserved })\n : null\n }\n badgeVariant=\"info\"\n ctaLabel={t('wms.backend.lot.kpis.reserved.cta', 'View reservations')}\n ctaHref=\"/backend/wms/reservations\"\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.daysToExpiry.title', 'Days to expiry')}\n caption={t('wms.backend.lot.kpis.daysToExpiry.caption', 'Until expiration date')}\n value={daysToExpiry === null ? '\u2014' : String(daysToExpiry)}\n badgeLabel={\n daysToExpiry !== null && daysToExpiry <= 30\n ? t('wms.backend.lot.kpis.daysToExpiry.badgeSoon', 'Expiring soon')\n : daysToExpiry !== null\n ? t('wms.backend.lot.kpis.daysToExpiry.badgeOk', 'Within window')\n : null\n }\n badgeVariant={daysToExpiry !== null && daysToExpiry <= 30 ? 'warning' : 'success'}\n ctaLabel={t('wms.backend.lot.kpis.daysToExpiry.cta', 'View SKU profile')}\n ctaHref={skuDetailHref}\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.lotAge.title', 'Lot age')}\n caption={t('wms.backend.lot.kpis.lotAge.caption', 'Since lot was created')}\n value={lotAgeDays === null ? '\u2014' : t('wms.backend.lot.kpis.lotAge.value', '{days}d', { days: lotAgeDays })}\n badgeLabel={\n lotAgeDays !== null && lotAgeDays <= 7\n ? t('wms.backend.lot.kpis.lotAge.badgeFresh', 'Fresh lot')\n : lotAgeDays !== null\n ? t('wms.backend.lot.kpis.lotAge.badgeAged', 'Aged stock')\n : null\n }\n badgeVariant={lotAgeDays !== null && lotAgeDays <= 7 ? 'success' : 'neutral'}\n ctaLabel={t('wms.backend.lot.kpis.lotAge.cta', 'View movements')}\n ctaHref={movementsHref}\n />\n <LotKpiCard\n title={t('wms.backend.lot.kpis.qualityStatus.title', 'Quality status')}\n caption={\n statusNotes\n ? t('wms.backend.lot.kpis.qualityStatus.notesCaption', 'Note: {notes}', {\n notes: statusNotes,\n })\n : t('wms.backend.lot.kpis.qualityStatus.caption', 'Lot hold and QC state')\n }\n value={qualityStatus.value}\n badgeLabel={t(qualityStatus.labelKey, qualityStatus.labelFallback)}\n badgeVariant={qualityStatus.variant}\n ctaLabel={t('wms.backend.lot.kpis.qualityStatus.cta', 'Change status')}\n onCtaClick={access.canManage ? () => setChangeStatusOpen(true) : undefined}\n ctaHref={!access.canManage ? '/backend/config/wms' : undefined}\n />\n </section>\n\n <section\n id=\"lot-locations\"\n className=\"rounded-lg border bg-card text-card-foreground shadow-sm\"\n >\n <div className=\"border-b px-5 py-4\">\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.lot.distribution.title', 'Where this lot lives')}\n </h2>\n </div>\n <div className=\"flex flex-col gap-4 border-b px-5 py-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap gap-1.5\">\n {filterChips.map((chip) => (\n <Button\n key={chip.id}\n type=\"button\"\n size=\"sm\"\n variant={distributionFilter === chip.id ? 'default' : 'outline'}\n className={cn('rounded-full', distributionFilter === chip.id && 'shadow-sm')}\n onClick={() => setDistributionFilter(chip.id)}\n >\n {chip.label}\n </Button>\n ))}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleExportDistributionCsv}>\n <Download className=\"size-4\" />\n {t('wms.backend.lot.distribution.actions.exportCsv', 'Export CSV')}\n </Button>\n {access.canCycleCount ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => openCycleCountDialog(resolveMutationContext())}\n >\n {t('wms.backend.lot.distribution.actions.cycleCountZone', 'Cycle count zone')}\n </Button>\n ) : null}\n {access.canAdjust && selectedBalances.length > 0 ? (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"default\"\n onClick={() => openAdjustDialog(resolveMutationContext())}\n >\n {t('wms.backend.lot.distribution.actions.adjustSelected', 'Adjust selected ({count})', { count: selectedBalances.length })}\n </Button>\n ) : null}\n </div>\n </div>\n <DataTable<InventoryBalanceRow>\n embedded\n columns={distributionColumns}\n data={pagedBalances}\n disableRowClick\n entityId={E.wms.inventory_balance}\n perspective={{ tableId: extensionPoints.hosts.lotDistributionTable.tableId }}\n pagination={{\n page: distributionPage,\n pageSize: distributionPageSize,\n total: distributionTotal,\n totalPages: distributionTotalPages,\n onPageChange: setDistributionPage,\n }}\n emptyState={(\n <EmptyState\n title={t('wms.backend.lot.distribution.empty.title', 'No locations in this view')}\n description={t(\n 'wms.backend.lot.distribution.empty.description',\n 'Try another filter or post inventory through receipts or adjustments.',\n )}\n />\n )}\n />\n {distributionTotal > 0 ? (\n <div className=\"flex flex-col gap-2 border-t px-5 py-3 text-sm text-muted-foreground sm:flex-row sm:items-center sm:justify-between\">\n <span>\n {t('wms.backend.lot.distribution.footer.showing', 'Showing {shown} of {total} locations', {\n shown: pagedBalances.length,\n total: distributionTotal,\n })}\n </span>\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"h-auto px-0\">\n <Link href={movementsHref}>\n {t('wms.backend.lot.distribution.footer.openLedger', 'Open ledger for this lot \u2192')}\n </Link>\n </LinkButton>\n </div>\n ) : null}\n </section>\n\n <DataTable<InventoryMovementRow>\n title={t('wms.backend.lot.activity.title', 'Recent activity')}\n columns={activityColumns}\n data={movementsQuery.data ?? []}\n disableRowClick\n entityId={E.wms.inventory_movement}\n perspective={{ tableId: extensionPoints.hosts.lotActivityTable.tableId }}\n emptyState={t('wms.backend.lot.activity.empty', 'No recent movements for this lot.')}\n actions={(\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href={movementsHref}>\n {t('wms.backend.lot.activity.viewAll', 'View all movements \u2192')}\n </Link>\n </Button>\n )}\n />\n\n <section className=\"flex flex-col gap-4 rounded-lg border bg-card px-5 py-4 shadow-sm lg:flex-row lg:items-center lg:justify-between\">\n <div>\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.lot.quickActions.title', 'Quick actions')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'wms.backend.lot.quickActions.description',\n 'Run common inventory actions without leaving this lot view',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"default\" onClick={() => openAdjustDialog(resolveMutationContext())}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.lot.quickActions.adjust', 'Adjust inventory')}\n </Button>\n ) : null}\n {access.canAdjust ? (\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={inventoryConsoleHref}>\n <ArrowLeftRight className=\"size-4\" />\n {t('wms.backend.lot.quickActions.move', 'Move stock')}\n </Link>\n </Button>\n ) : null}\n {access.canCycleCount ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => openCycleCountDialog(resolveMutationContext())}\n >\n <ClipboardList className=\"size-4\" />\n {t('wms.backend.lot.quickActions.cycleCount', 'Cycle count')}\n </Button>\n ) : null}\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={movementsHref}>\n {t('wms.backend.lot.quickActions.openLedger', 'Open ledger')}\n <ExternalLink className=\"size-4\" />\n </Link>\n </Button>\n </div>\n </section>\n </>\n ) : null}\n\n {!isLoading && !hasError && !lotQuery.data ? (\n <RecordNotFoundState\n label={t('wms.backend.lot.errors.notFound', 'Lot not found.')}\n backHref={lotsHref}\n backLabel={t('wms.backend.lot.actions.backToLots', 'Back to lots')}\n />\n ) : null}\n </PageBody>\n\n {access.canAdjust && scopedLotId ? (\n <AdjustInventoryDialog\n open={adjustOpen}\n onOpenChange={setAdjustOpen}\n access={access}\n initialCatalogVariantId={adjustPreset.catalogVariantId ?? catalogVariantId ?? undefined}\n initialWarehouseId={adjustPreset.warehouseId}\n initialLocationId={adjustPreset.locationId}\n initialLotId={adjustPreset.lotId ?? scopedLotId}\n />\n ) : null}\n {access.canCycleCount ? (\n <CycleCountWizardDialog\n open={cycleOpen}\n onOpenChange={setCycleOpen}\n access={access}\n initialCatalogVariantId={catalogVariantId ?? undefined}\n initialWarehouseId={cyclePreset.warehouseId ?? (warehouseId !== 'all' ? warehouseId : undefined)}\n initialLocationId={cyclePreset.locationId}\n initialLotId={scopedLotId ?? undefined}\n />\n ) : null}\n {access.canAdjust && scopedLotId ? (\n <ChangeLotStatusDialog\n open={changeStatusOpen}\n onOpenChange={setChangeStatusOpen}\n access={access}\n lotId={scopedLotId}\n currentStatus={lotData?.status}\n lotUpdatedAt={lotData?.updated_at}\n />\n ) : null}\n </Page>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/wms/components/backend/WmsLotsListPage.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/wms/extension-points'\nimport Link from 'next/link'\nimport { useSearchParams } from 'next/navigation'\nimport type { ColumnDef, SortingState } from '@tanstack/react-table'\nimport { useQuery } from '@tanstack/react-query'\nimport { Layers } from 'lucide-react'\nimport { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { E } from '#generated/entities.ids.generated'\nimport type { ExpiryWindow } from '../../lib/expiry'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ChangeLotStatusDialog } from './ChangeLotStatusDialog'\nimport { ImportInventoryDialog } from './ImportInventoryDialog'\nimport { MoveInventoryDialog } from './MoveInventoryDialog'\nimport { ReceiveInventoryDialog } from './ReceiveInventoryDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\ntype InventoryLotRow = {\n id: string\n lot_number?: string | null\n batch_number?: string | null\n sku?: string | null\n catalog_variant_id?: string | null\n expires_at?: string | null\n status?: string | null\n updated_at?: string | null\n}\n\ntype PagedResponse<T> = {\n items: T[]\n total: number\n totalPages: number\n}\n\nfunction parseExpiryWindow(value: string | null): ExpiryWindow | null {\n if (value === 'expiringSoon' || value === 'pastDue') return value\n return null\n}\n\nfunction resolveLotStatusVariant(\n expiresAt: string | null | undefined,\n status: string | null | undefined,\n nowMs: number,\n): 'error' | 'warning' | 'neutral' {\n if (status === 'expired') return 'error'\n if (!expiresAt) return 'neutral'\n const expires = new Date(expiresAt).getTime()\n if (Number.isNaN(expires)) return 'neutral'\n if (expires <= nowMs) return 'error'\n if (expires - nowMs <= 30 * 24 * 60 * 60 * 1000) return 'warning'\n return 'neutral'\n}\n\nexport default function WmsLotsListPage() {\n const t = useT()\n const locale = useLocale()\n const access = useWmsInventoryMutationAccess()\n const searchParams = useSearchParams()\n const expiryWindow = parseExpiryWindow(searchParams.get('expiryWindow'))\n const warehouseId = searchParams.get('warehouseId')?.trim() || null\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'expiresAt', desc: false }])\n const [changeStatusOpen, setChangeStatusOpen] = React.useState(false)\n const [moveOpen, setMoveOpen] = React.useState(false)\n const [activeLot, setActiveLot] = React.useState<InventoryLotRow | null>(null)\n const [receiveOpen, setReceiveOpen] = React.useState(false)\n const [importOpen, setImportOpen] = React.useState(false)\n\n const handleSortingChange = React.useCallback((nextSorting: SortingState) => {\n setSorting(nextSorting)\n setPage(1)\n }, [])\n\n const expiryFormatter = React.useMemo(\n () =>\n new Intl.DateTimeFormat(locale, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }),\n [locale],\n )\n\n const queryKey = React.useMemo(\n () => ['wms-lots-list', expiryWindow, warehouseId, page, search, sorting],\n [expiryWindow, warehouseId, page, search, sorting],\n )\n\n const lotsQuery = useQuery({\n queryKey,\n queryFn: async () => {\n const sortCol = sorting[0]\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n sortField: sortCol ? sortCol.id : 'expiresAt',\n sortDir: sortCol ? (sortCol.desc ? 'desc' : 'asc') : 'asc',\n })\n if (search.trim()) params.set('search', search.trim())\n if (expiryWindow) params.set('expiryWindow', expiryWindow)\n if (warehouseId) params.set('warehouseId', warehouseId)\n const call = await apiCall<PagedResponse<InventoryLotRow>>(`/api/wms/lots?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lots.errors.load', 'Failed to load lots.'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const nowMs = Date.now()\n\n const openChangeStatus = React.useCallback((row: InventoryLotRow) => {\n setActiveLot(row)\n setChangeStatusOpen(true)\n }, [])\n\n const openMove = React.useCallback((row: InventoryLotRow) => {\n setActiveLot(row)\n setMoveOpen(true)\n }, [])\n\n const columns = React.useMemo<ColumnDef<InventoryLotRow>[]>(\n () => [\n {\n accessorKey: 'lot_number',\n id: 'lotNumber',\n header: t('wms.backend.lots.columns.lotNumber', 'Lot number'),\n enableSorting: true,\n cell: ({ row }) => {\n const lotId = row.original.id\n const label = row.original.lot_number?.trim() || lotId\n return (\n <Link\n href={`/backend/wms/lot/${encodeURIComponent(lotId)}`}\n className=\"font-medium text-primary hover:underline\"\n >\n {label}\n </Link>\n )\n },\n },\n {\n accessorKey: 'sku',\n id: 'sku',\n header: t('wms.backend.lots.columns.sku', 'SKU'),\n enableSorting: true,\n cell: ({ row }) => row.original.sku?.trim() || '\u2014',\n },\n {\n accessorKey: 'expires_at',\n id: 'expiresAt',\n header: t('wms.backend.lots.columns.expiresAt', 'Expires'),\n enableSorting: true,\n cell: ({ row }) => {\n const expiresAt = row.original.expires_at\n if (!expiresAt) return '\u2014'\n const variant = resolveLotStatusVariant(expiresAt, row.original.status, nowMs)\n return (\n <StatusBadge variant={variant} dot>\n {expiryFormatter.format(new Date(expiresAt))}\n </StatusBadge>\n )\n },\n },\n {\n accessorKey: 'status',\n id: 'status',\n header: t('wms.backend.lots.columns.status', 'Status'),\n enableSorting: true,\n cell: ({ row }) => {\n const status = row.original.status?.trim()\n if (!status) return '\u2014'\n const variant = resolveLotStatusVariant(row.original.expires_at, status, nowMs)\n return (\n <StatusBadge variant={variant} dot>\n {t(`wms.backend.lot.status.${status}`, status)}\n </StatusBadge>\n )\n },\n },\n ],\n [expiryFormatter, nowMs, t],\n )\n\n const rowActions = React.useCallback(\n (row: InventoryLotRow) => {\n const items = []\n if (access.canAdjust) {\n items.push({\n id: 'change-status',\n label: t('wms.backend.lots.actions.changeStatus', 'Change status'),\n onSelect: () => openChangeStatus(row),\n })\n items.push({\n id: 'move',\n label: t('wms.backend.lots.actions.move', 'Move'),\n onSelect: () => openMove(row),\n })\n }\n if (items.length === 0) return null\n return <RowActions items={items} />\n },\n [access.canAdjust, openChangeStatus, openMove, t],\n )\n\n const title = expiryWindow === 'pastDue'\n ? t('wms.backend.lots.title.pastDue', 'Past-due lots')\n : expiryWindow === 'expiringSoon'\n ? t('wms.backend.lots.title.expiringSoon', 'Lots expiring soon')\n : t('wms.backend.lots.title.all', 'Inventory lots')\n\n const description = expiryWindow === 'pastDue'\n ? t(\n 'wms.backend.lots.description.pastDue',\n 'Lots past their expiry date with on-hand stock in the selected warehouse scope.',\n )\n : expiryWindow === 'expiringSoon'\n ? t(\n 'wms.backend.lots.description.expiringSoon',\n 'Lots expiring within the next 30 days in the selected warehouse scope.',\n )\n : t('wms.backend.lots.description.all', 'Browse lot and batch records with expiry context.')\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <PageHeader title={title} description={description} />\n\n {lotsQuery.isLoading ? (\n <LoadingMessage label={t('wms.backend.lots.loading', 'Loading lots\u2026')} />\n ) : null}\n\n {lotsQuery.isError ? (\n <ErrorMessage label={t('wms.backend.lots.errors.load', 'Failed to load lots.')} />\n ) : null}\n\n {lotsQuery.data ? (\n <section className=\"rounded-lg border bg-card p-5 text-card-foreground shadow-sm\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"rounded-md border bg-muted/40 p-2 text-muted-foreground\">\n <Layers className=\"size-5\" />\n </div>\n <div className=\"space-y-1\">\n <h2 className=\"text-xl font-semibold\">{title}</h2>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n </div>\n <DataTable\n embedded\n title={title}\n columns={columns}\n data={lotsQuery.data.items}\n isLoading={lotsQuery.isFetching}\n entityId={E.wms.inventory_lot}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={t('wms.backend.lots.search', 'Search lots')}\n sortable\n manualSorting\n sorting={sorting}\n onSortingChange={handleSortingChange}\n rowActions={rowActions}\n pagination={{\n page,\n pageSize: 25,\n total: lotsQuery.data.total,\n totalPages: lotsQuery.data.totalPages,\n onPageChange: setPage,\n }}\n perspective={{ tableId: extensionPoints.hosts.lotsTable.tableId }}\n emptyState={\n <EmptyState\n title={t('wms.backend.lots.empty.title', 'No lots found')}\n description={t(\n 'wms.backend.lots.empty.description',\n 'Adjust filters or create lots through inventory operations.',\n )}\n actions={\n access.canReceive || access.canImport ? (\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n {access.canReceive ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setReceiveOpen(true)}\n >\n {t('wms.backend.lots.empty.receive', 'Receive stock')}\n </Button>\n ) : null}\n {access.canImport ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setImportOpen(true)}\n >\n {t('wms.backend.lots.empty.importCsv', 'Import CSV')}\n </Button>\n ) : null}\n </div>\n ) : null\n }\n />\n }\n />\n </section>\n ) : null}\n </PageBody>\n\n {access.canReceive ? (\n <ReceiveInventoryDialog\n open={receiveOpen}\n onOpenChange={setReceiveOpen}\n access={access}\n />\n ) : null}\n {access.canImport ? (\n <ImportInventoryDialog open={importOpen} onOpenChange={setImportOpen} access={access} />\n ) : null}\n {access.canAdjust && activeLot ? (\n <>\n <ChangeLotStatusDialog\n open={changeStatusOpen}\n onOpenChange={setChangeStatusOpen}\n access={access}\n lotId={activeLot.id}\n currentStatus={activeLot.status}\n lotUpdatedAt={activeLot.updated_at}\n onSuccess={() => {\n void lotsQuery.refetch()\n }}\n />\n <MoveInventoryDialog\n open={moveOpen}\n onOpenChange={setMoveOpen}\n access={access}\n initialCatalogVariantId={activeLot.catalog_variant_id ?? undefined}\n initialWarehouseId={warehouseId ?? undefined}\n initialLotId={activeLot.id}\n />\n </>\n ) : null}\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/core/modules/wms/extension-points'\nimport Link from 'next/link'\nimport { useSearchParams } from 'next/navigation'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport type { SortingState } from '@tanstack/react-table'\nimport { useQuery } from '@tanstack/react-query'\nimport { Layers } from 'lucide-react'\nimport { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { EmptyState } from '@open-mercato/ui/backend/EmptyState'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useLocale, useT } from '@open-mercato/shared/lib/i18n/context'\nimport { StatusBadge } from '@open-mercato/ui/primitives/status-badge'\nimport { E } from '#generated/entities.ids.generated'\nimport type { ExpiryWindow } from '../../lib/expiry'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { ChangeLotStatusDialog } from './ChangeLotStatusDialog'\nimport { ImportInventoryDialog } from './ImportInventoryDialog'\nimport { MoveInventoryDialog } from './MoveInventoryDialog'\nimport { ReceiveInventoryDialog } from './ReceiveInventoryDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\ntype InventoryLotRow = {\n id: string\n lot_number?: string | null\n batch_number?: string | null\n sku?: string | null\n catalog_variant_id?: string | null\n expires_at?: string | null\n status?: string | null\n updated_at?: string | null\n}\n\ntype PagedResponse<T> = {\n items: T[]\n total: number\n totalPages: number\n}\n\nfunction parseExpiryWindow(value: string | null): ExpiryWindow | null {\n if (value === 'expiringSoon' || value === 'pastDue') return value\n return null\n}\n\nfunction resolveLotStatusVariant(\n expiresAt: string | null | undefined,\n status: string | null | undefined,\n nowMs: number,\n): 'error' | 'warning' | 'neutral' {\n if (status === 'expired') return 'error'\n if (!expiresAt) return 'neutral'\n const expires = new Date(expiresAt).getTime()\n if (Number.isNaN(expires)) return 'neutral'\n if (expires <= nowMs) return 'error'\n if (expires - nowMs <= 30 * 24 * 60 * 60 * 1000) return 'warning'\n return 'neutral'\n}\n\nexport default function WmsLotsListPage() {\n const t = useT()\n const locale = useLocale()\n const access = useWmsInventoryMutationAccess()\n const searchParams = useSearchParams()\n const expiryWindow = parseExpiryWindow(searchParams.get('expiryWindow'))\n const warehouseId = searchParams.get('warehouseId')?.trim() || null\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [sorting, setSorting] = React.useState<SortingState>([{ id: 'expiresAt', desc: false }])\n const [changeStatusOpen, setChangeStatusOpen] = React.useState(false)\n const [moveOpen, setMoveOpen] = React.useState(false)\n const [activeLot, setActiveLot] = React.useState<InventoryLotRow | null>(null)\n const [receiveOpen, setReceiveOpen] = React.useState(false)\n const [importOpen, setImportOpen] = React.useState(false)\n\n const handleSortingChange = React.useCallback((nextSorting: SortingState) => {\n setSorting(nextSorting)\n setPage(1)\n }, [])\n\n const expiryFormatter = React.useMemo(\n () =>\n new Intl.DateTimeFormat(locale, {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }),\n [locale],\n )\n\n const queryKey = React.useMemo(\n () => ['wms-lots-list', expiryWindow, warehouseId, page, search, sorting],\n [expiryWindow, warehouseId, page, search, sorting],\n )\n\n const lotsQuery = useQuery({\n queryKey,\n queryFn: async () => {\n const sortCol = sorting[0]\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n sortField: sortCol ? sortCol.id : 'expiresAt',\n sortDir: sortCol ? (sortCol.desc ? 'desc' : 'asc') : 'asc',\n })\n if (search.trim()) params.set('search', search.trim())\n if (expiryWindow) params.set('expiryWindow', expiryWindow)\n if (warehouseId) params.set('warehouseId', warehouseId)\n const call = await apiCall<PagedResponse<InventoryLotRow>>(`/api/wms/lots?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.lots.errors.load', 'Failed to load lots.'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const nowMs = Date.now()\n\n const openChangeStatus = React.useCallback((row: InventoryLotRow) => {\n setActiveLot(row)\n setChangeStatusOpen(true)\n }, [])\n\n const openMove = React.useCallback((row: InventoryLotRow) => {\n setActiveLot(row)\n setMoveOpen(true)\n }, [])\n\n const columns = React.useMemo<ColumnDef<InventoryLotRow>[]>(\n () => [\n {\n accessorKey: 'lot_number',\n id: 'lotNumber',\n header: t('wms.backend.lots.columns.lotNumber', 'Lot number'),\n enableSorting: true,\n cell: ({ row }) => {\n const lotId = row.original.id\n const label = row.original.lot_number?.trim() || lotId\n return (\n <Link\n href={`/backend/wms/lot/${encodeURIComponent(lotId)}`}\n className=\"font-medium text-primary hover:underline\"\n >\n {label}\n </Link>\n )\n },\n },\n {\n accessorKey: 'sku',\n id: 'sku',\n header: t('wms.backend.lots.columns.sku', 'SKU'),\n enableSorting: true,\n cell: ({ row }) => row.original.sku?.trim() || '\u2014',\n },\n {\n accessorKey: 'expires_at',\n id: 'expiresAt',\n header: t('wms.backend.lots.columns.expiresAt', 'Expires'),\n enableSorting: true,\n cell: ({ row }) => {\n const expiresAt = row.original.expires_at\n if (!expiresAt) return '\u2014'\n const variant = resolveLotStatusVariant(expiresAt, row.original.status, nowMs)\n return (\n <StatusBadge variant={variant} dot>\n {expiryFormatter.format(new Date(expiresAt))}\n </StatusBadge>\n )\n },\n },\n {\n accessorKey: 'status',\n id: 'status',\n header: t('wms.backend.lots.columns.status', 'Status'),\n enableSorting: true,\n cell: ({ row }) => {\n const status = row.original.status?.trim()\n if (!status) return '\u2014'\n const variant = resolveLotStatusVariant(row.original.expires_at, status, nowMs)\n return (\n <StatusBadge variant={variant} dot>\n {t(`wms.backend.lot.status.${status}`, status)}\n </StatusBadge>\n )\n },\n },\n ],\n [expiryFormatter, nowMs, t],\n )\n\n const rowActions = React.useCallback(\n (row: InventoryLotRow) => {\n const items = []\n if (access.canAdjust) {\n items.push({\n id: 'change-status',\n label: t('wms.backend.lots.actions.changeStatus', 'Change status'),\n onSelect: () => openChangeStatus(row),\n })\n items.push({\n id: 'move',\n label: t('wms.backend.lots.actions.move', 'Move'),\n onSelect: () => openMove(row),\n })\n }\n if (items.length === 0) return null\n return <RowActions items={items} />\n },\n [access.canAdjust, openChangeStatus, openMove, t],\n )\n\n const title = expiryWindow === 'pastDue'\n ? t('wms.backend.lots.title.pastDue', 'Past-due lots')\n : expiryWindow === 'expiringSoon'\n ? t('wms.backend.lots.title.expiringSoon', 'Lots expiring soon')\n : t('wms.backend.lots.title.all', 'Inventory lots')\n\n const description = expiryWindow === 'pastDue'\n ? t(\n 'wms.backend.lots.description.pastDue',\n 'Lots past their expiry date with on-hand stock in the selected warehouse scope.',\n )\n : expiryWindow === 'expiringSoon'\n ? t(\n 'wms.backend.lots.description.expiringSoon',\n 'Lots expiring within the next 30 days in the selected warehouse scope.',\n )\n : t('wms.backend.lots.description.all', 'Browse lot and batch records with expiry context.')\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <PageHeader title={title} description={description} />\n\n {lotsQuery.isLoading ? (\n <LoadingMessage label={t('wms.backend.lots.loading', 'Loading lots\u2026')} />\n ) : null}\n\n {lotsQuery.isError ? (\n <ErrorMessage label={t('wms.backend.lots.errors.load', 'Failed to load lots.')} />\n ) : null}\n\n {lotsQuery.data ? (\n <section className=\"rounded-lg border bg-card p-5 text-card-foreground shadow-sm\">\n <div className=\"mb-4 flex items-start gap-3\">\n <div className=\"rounded-md border bg-muted/40 p-2 text-muted-foreground\">\n <Layers className=\"size-5\" />\n </div>\n <div className=\"space-y-1\">\n <h2 className=\"text-xl font-semibold\">{title}</h2>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n </div>\n <DataTable\n embedded\n title={title}\n columns={columns}\n data={lotsQuery.data.items}\n isLoading={lotsQuery.isFetching}\n entityId={E.wms.inventory_lot}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={t('wms.backend.lots.search', 'Search lots')}\n sortable\n manualSorting\n sorting={sorting}\n onSortingChange={handleSortingChange}\n rowActions={rowActions}\n pagination={{\n page,\n pageSize: 25,\n total: lotsQuery.data.total,\n totalPages: lotsQuery.data.totalPages,\n onPageChange: setPage,\n }}\n perspective={{ tableId: extensionPoints.hosts.lotsTable.tableId }}\n emptyState={\n <EmptyState\n title={t('wms.backend.lots.empty.title', 'No lots found')}\n description={t(\n 'wms.backend.lots.empty.description',\n 'Adjust filters or create lots through inventory operations.',\n )}\n actions={\n access.canReceive || access.canImport ? (\n <div className=\"flex flex-wrap items-center justify-center gap-2\">\n {access.canReceive ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setReceiveOpen(true)}\n >\n {t('wms.backend.lots.empty.receive', 'Receive stock')}\n </Button>\n ) : null}\n {access.canImport ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setImportOpen(true)}\n >\n {t('wms.backend.lots.empty.importCsv', 'Import CSV')}\n </Button>\n ) : null}\n </div>\n ) : null\n }\n />\n }\n />\n </section>\n ) : null}\n </PageBody>\n\n {access.canReceive ? (\n <ReceiveInventoryDialog\n open={receiveOpen}\n onOpenChange={setReceiveOpen}\n access={access}\n />\n ) : null}\n {access.canImport ? (\n <ImportInventoryDialog open={importOpen} onOpenChange={setImportOpen} access={access} />\n ) : null}\n {access.canAdjust && activeLot ? (\n <>\n <ChangeLotStatusDialog\n open={changeStatusOpen}\n onOpenChange={setChangeStatusOpen}\n access={access}\n lotId={activeLot.id}\n currentStatus={activeLot.status}\n lotUpdatedAt={activeLot.updated_at}\n onSuccess={() => {\n void lotsQuery.refetch()\n }}\n />\n <MoveInventoryDialog\n open={moveOpen}\n onOpenChange={setMoveOpen}\n access={access}\n initialCatalogVariantId={activeLot.catalog_variant_id ?? undefined}\n initialWarehouseId={warehouseId ?? undefined}\n initialLotId={activeLot.id}\n />\n </>\n ) : null}\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|