@open-mercato/core 0.6.8-develop.6910.1.560e304de2 → 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.
Files changed (140) hide show
  1. package/dist/modules/api_keys/backend/api-keys/page.js.map +1 -1
  2. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  3. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +1 -1
  4. package/dist/modules/audit_logs/components/AccessLogsTable.js.map +1 -1
  5. package/dist/modules/audit_logs/components/AuditLogsActions.js.map +1 -1
  6. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  7. package/dist/modules/auth/backend/users/page.js.map +2 -2
  8. package/dist/modules/business_rules/backend/logs/page.js.map +1 -1
  9. package/dist/modules/business_rules/backend/rules/page.js.map +1 -1
  10. package/dist/modules/business_rules/backend/sets/page.js.map +1 -1
  11. package/dist/modules/catalog/components/PriceKindSettings.js.map +1 -1
  12. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +1 -1
  13. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  14. package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +1 -1
  15. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +1 -1
  16. package/dist/modules/currencies/backend/currencies/page.js.map +1 -1
  17. package/dist/modules/currencies/backend/exchange-rates/page.js.map +1 -1
  18. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +1 -1
  19. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +1 -1
  20. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  21. package/dist/modules/customers/backend/customers/deals/page.js.map +1 -1
  22. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  23. package/dist/modules/customers/components/CustomerTodosTable.js.map +1 -1
  24. package/dist/modules/data_sync/backend/data-sync/page.js.map +1 -1
  25. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  26. package/dist/modules/directory/backend/directory/organizations/page.js.map +1 -1
  27. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  28. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  29. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  30. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  31. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  32. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  33. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +1 -1
  34. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +1 -1
  35. package/dist/modules/messages/components/MessagesInboxPageClient.js.map +1 -1
  36. package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +1 -1
  37. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  38. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  39. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  40. package/dist/modules/resources/backend/resources/resources/page.js.map +1 -1
  41. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  42. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  43. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +1 -1
  44. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +1 -1
  45. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +1 -1
  46. package/dist/modules/sales/components/TaxRatesSettings.js.map +1 -1
  47. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  48. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +1 -1
  49. package/dist/modules/sales/components/documents/PaymentsSection.js.map +1 -1
  50. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  51. package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +1 -1
  52. package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
  53. package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
  54. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  55. package/dist/modules/staff/backend/staff/team-roles/page.js.map +1 -1
  56. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  57. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  58. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  59. package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +2 -2
  60. package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
  61. package/dist/modules/wms/components/backend/WmsLocationDetailPage.js.map +1 -1
  62. package/dist/modules/wms/components/backend/WmsLotDetailPage.js.map +1 -1
  63. package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
  64. package/dist/modules/wms/components/backend/WmsOperationalDashboardPage.js.map +1 -1
  65. package/dist/modules/wms/components/backend/WmsSkuDetailPage.js.map +1 -1
  66. package/dist/modules/workflows/backend/definitions/page.js.map +1 -1
  67. package/dist/modules/workflows/backend/events/page.js.map +1 -1
  68. package/dist/modules/workflows/backend/instances/page.js.map +1 -1
  69. package/dist/modules/workflows/backend/tasks/page.js.map +1 -1
  70. package/jest.config.cjs +1 -1
  71. package/package.json +7 -7
  72. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  73. package/src/modules/attachments/components/AttachmentLibrary.tsx +2 -1
  74. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +1 -1
  75. package/src/modules/audit_logs/components/AccessLogsTable.tsx +1 -1
  76. package/src/modules/audit_logs/components/AuditLogsActions.tsx +1 -1
  77. package/src/modules/auth/backend/roles/page.tsx +2 -1
  78. package/src/modules/auth/backend/users/page.tsx +2 -1
  79. package/src/modules/business_rules/backend/logs/page.tsx +1 -1
  80. package/src/modules/business_rules/backend/rules/page.tsx +1 -1
  81. package/src/modules/business_rules/backend/sets/page.tsx +1 -1
  82. package/src/modules/catalog/components/PriceKindSettings.tsx +1 -1
  83. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -1
  84. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -1
  85. package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +1 -1
  86. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +1 -1
  87. package/src/modules/currencies/backend/currencies/page.tsx +1 -1
  88. package/src/modules/currencies/backend/exchange-rates/page.tsx +1 -1
  89. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +1 -1
  90. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +1 -1
  91. package/src/modules/customers/backend/customers/companies/page.tsx +2 -1
  92. package/src/modules/customers/backend/customers/deals/page.tsx +1 -1
  93. package/src/modules/customers/backend/customers/people/page.tsx +2 -1
  94. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -1
  95. package/src/modules/data_sync/backend/data-sync/page.tsx +1 -1
  96. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -1
  97. package/src/modules/directory/backend/directory/organizations/page.tsx +1 -1
  98. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -1
  99. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -1
  100. package/src/modules/entities/components/SystemEntitiesTable.tsx +2 -1
  101. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -1
  102. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
  103. package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
  104. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +1 -1
  105. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +1 -1
  106. package/src/modules/messages/components/MessagesInboxPageClient.tsx +1 -1
  107. package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +1 -1
  108. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -1
  109. package/src/modules/query_index/components/QueryIndexesTable.tsx +2 -1
  110. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -1
  111. package/src/modules/resources/backend/resources/resources/page.tsx +1 -1
  112. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -1
  113. package/src/modules/sales/backend/sales/channels/page.tsx +2 -1
  114. package/src/modules/sales/components/AdjustmentKindSettings.tsx +1 -1
  115. package/src/modules/sales/components/PaymentMethodsSettings.tsx +1 -1
  116. package/src/modules/sales/components/ShippingMethodsSettings.tsx +1 -1
  117. package/src/modules/sales/components/TaxRatesSettings.tsx +1 -1
  118. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -1
  119. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +1 -1
  120. package/src/modules/sales/components/documents/PaymentsSection.tsx +1 -1
  121. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
  122. package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +1 -1
  123. package/src/modules/staff/backend/staff/leave-requests/page.tsx +2 -1
  124. package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +2 -1
  125. package/src/modules/staff/backend/staff/team-members/page.tsx +3 -2
  126. package/src/modules/staff/backend/staff/team-roles/page.tsx +1 -1
  127. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -1
  128. package/src/modules/staff/backend/staff/teams/page.tsx +2 -1
  129. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +2 -1
  130. package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +2 -1
  131. package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +4 -3
  132. package/src/modules/wms/components/backend/WmsLocationDetailPage.tsx +1 -1
  133. package/src/modules/wms/components/backend/WmsLotDetailPage.tsx +1 -1
  134. package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -1
  135. package/src/modules/wms/components/backend/WmsOperationalDashboardPage.tsx +1 -1
  136. package/src/modules/wms/components/backend/WmsSkuDetailPage.tsx +1 -1
  137. package/src/modules/workflows/backend/definitions/page.tsx +1 -1
  138. package/src/modules/workflows/backend/events/page.tsx +1 -1
  139. package/src/modules/workflows/backend/instances/page.tsx +1 -1
  140. 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/WmsOperationalDashboardPage.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport {\n ArrowDown,\n ClipboardList,\n ExternalLink,\n MapPin,\n Package,\n RefreshCw,\n SlidersHorizontal,\n Warehouse as WarehouseIcon2,\n} from 'lucide-react'\nimport { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'\nimport { BarChart } from '@open-mercato/ui/backend/charts'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\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 type {\n OperationalDashboardActivityRow,\n OperationalDashboardExpiryLotRow,\n OperationalDashboardKpi,\n OperationalDashboardPayload,\n} from '../../lib/loadOperationalDashboard'\nimport {\n inventoryMovementReasonLabel,\n type InventoryDisplayTranslator,\n} from '../../lib/inventoryDisplayUi'\nimport { AdjustInventoryDialog } from './AdjustInventoryDialog'\nimport { ChangeLotStatusDialog } from './ChangeLotStatusDialog'\nimport { CycleCountWizardDialog } from './CycleCountWizardDialog'\nimport { MoveInventoryDialog } from './MoveInventoryDialog'\nimport { ReceiveInventoryDialog } from './ReceiveInventoryDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\ntype WarehouseOption = {\n id: string\n name?: string | null\n code?: string | null\n}\n\ntype PagedWarehouses = {\n items: WarehouseOption[]\n}\n\nconst AUTO_REFRESH_MS = 60_000\n\nfunction Sparkline({ values, className }: { values: number[]; className?: string }) {\n const width = 160\n const height = 36\n const max = Math.max(...values, 1)\n const points = values.map((value, index) => {\n const x = values.length <= 1 ? width / 2 : (index / (values.length - 1)) * width\n const y = height - (value / max) * (height - 4) - 2\n return `${x},${y}`\n })\n\n return (\n <svg\n viewBox={`0 0 ${width} ${height}`}\n className={className}\n aria-hidden=\"true\"\n preserveAspectRatio=\"none\"\n >\n <polyline\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n points={points.join(' ')}\n className=\"text-status-info-icon\"\n />\n </svg>\n )\n}\n\nfunction formatActivityTitle(\n row: OperationalDashboardActivityRow,\n t: ReturnType<typeof useT>,\n): string {\n const sku = row.variantSku?.trim() || row.variantId\n const quantity = Math.abs(row.quantity)\n const signedQuantity = row.quantity\n switch (row.movementType) {\n case 'receipt':\n case 'return_receive':\n return t('wms.backend.dashboard.activity.titles.received', 'Received {quantity}\u00D7 {sku}', {\n quantity,\n sku,\n })\n case 'adjust':\n return t('wms.backend.dashboard.activity.titles.adjusted', 'Adjusted {quantity}\u00D7 {sku}', {\n quantity: `${signedQuantity >= 0 ? '+' : ''}${signedQuantity}`,\n sku,\n })\n case 'transfer':\n return t('wms.backend.dashboard.activity.titles.moved', 'Moved {quantity}\u00D7 {sku}', {\n quantity,\n sku,\n })\n case 'pick':\n case 'pack':\n return t('wms.backend.dashboard.activity.titles.allocated', 'Allocated {quantity}\u00D7 {sku}', {\n quantity,\n sku,\n })\n case 'cycle_count':\n return t('wms.backend.dashboard.activity.titles.reconciled', 'Inventory reconciled \u2014 {sku}', {\n sku,\n })\n default:\n return t('wms.backend.dashboard.activity.titles.generic', '{type} {quantity}\u00D7 {sku}', {\n type: row.movementType,\n quantity,\n sku,\n })\n }\n}\n\nfunction formatActivitySubtitle(\n row: OperationalDashboardActivityRow,\n t: InventoryDisplayTranslator,\n): string | null {\n const reasonLabel = inventoryMovementReasonLabel(\n {\n reasonCode: row.reasonCode,\n reason: row.reason,\n movementType: row.movementType,\n },\n t,\n )\n if (reasonLabel) return reasonLabel\n if (row.referenceType && row.referenceId) return `${row.referenceType} \u00B7 ${row.referenceId}`\n return null\n}\n\nfunction createDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n return new Intl.DateTimeFormat(locale, options)\n}\n\nexport function resolveDashboardFirstRunMode(input: {\n warehousesLoading: boolean\n warehousesError: boolean\n hasWarehouses: boolean\n locationsLoading: boolean\n locationsError: boolean\n locationsCount: number\n}): 'no-warehouses' | 'no-locations' | null {\n if (input.warehousesLoading || input.warehousesError) return null\n if (!input.hasWarehouses) return 'no-warehouses'\n if (input.locationsLoading || input.locationsError) return null\n if (input.locationsCount === 0) return 'no-locations'\n return null\n}\n\ntype DashboardKpiCardProps = {\n kpi: OperationalDashboardKpi\n title: string\n caption: string\n badgeLabel: string | null\n badgeVariant: StatusBadgeVariant\n ctaLabel: string\n href: string\n}\n\nfunction DashboardKpiCard({\n kpi,\n title,\n caption,\n badgeLabel,\n badgeVariant,\n ctaLabel,\n href,\n}: DashboardKpiCardProps) {\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\">{kpi.count}</p>\n {badgeLabel ? (\n <StatusBadge variant={badgeVariant} dot>\n {badgeLabel}\n </StatusBadge>\n ) : null}\n </div>\n <Sparkline values={kpi.sparkline} className=\"mt-3 h-9 w-full max-w-40\" />\n <LinkButton asChild variant=\"primary\" size=\"sm\" className=\"mt-4 w-fit\">\n <Link href={href}>{ctaLabel}</Link>\n </LinkButton>\n </section>\n )\n}\n\ntype ExpiryLotListProps = {\n title: string\n emptyLabel: string\n viewAllLabel: string\n viewAllHref: string\n rows: OperationalDashboardExpiryLotRow[]\n expiryFormatter: Intl.DateTimeFormat\n quantityFormatter: Intl.NumberFormat\n canAdjust: boolean\n onChangeStatus: (row: OperationalDashboardExpiryLotRow) => void\n onMove: (row: OperationalDashboardExpiryLotRow) => void\n t: ReturnType<typeof useT>\n}\n\nfunction ExpiryLotList({\n title,\n emptyLabel,\n viewAllLabel,\n viewAllHref,\n rows,\n expiryFormatter,\n quantityFormatter,\n canAdjust,\n onChangeStatus,\n onMove,\n t,\n}: ExpiryLotListProps) {\n return (\n <div className=\"min-w-0\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold\">{title}</h3>\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"h-8 px-2\">\n <Link href={viewAllHref}>{viewAllLabel}</Link>\n </LinkButton>\n </div>\n {rows.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{emptyLabel}</p>\n ) : (\n <ul className=\"divide-y divide-border\">\n {rows.map((row) => {\n const badgeVariant: StatusBadgeVariant = row.category === 'pastDue' ? 'error' : 'warning'\n return (\n <li key={row.id} className=\"flex items-start justify-between gap-3 py-3 first:pt-0 last:pb-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{row.lotNumber}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{row.sku}</p>\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <StatusBadge variant={badgeVariant} dot>\n {expiryFormatter.format(new Date(row.expiresAt))}\n </StatusBadge>\n <span className=\"text-xs text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.expiry.available',\n '{quantity} available',\n { quantity: quantityFormatter.format(row.availableQuantity) },\n )}\n </span>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {canAdjust ? (\n <RowActions\n items={[\n {\n id: 'change-status',\n label: t('wms.backend.lots.actions.changeStatus', 'Change status'),\n onSelect: () => onChangeStatus(row),\n },\n {\n id: 'move',\n label: t('wms.backend.lots.actions.move', 'Move'),\n onSelect: () => onMove(row),\n },\n ]}\n />\n ) : null}\n <Button asChild type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7 shrink-0\">\n <Link\n href={`/backend/wms/lot/${row.id}`}\n aria-label={t('wms.backend.dashboard.expiry.openLot', 'Open lot')}\n >\n <ExternalLink className=\"size-3.5\" />\n </Link>\n </Button>\n </div>\n </li>\n )\n })}\n </ul>\n )}\n </div>\n )\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\nexport default function WmsOperationalDashboardPage() {\n const t = useT()\n const locale = useLocale()\n const access = useWmsInventoryMutationAccess()\n const [warehouseId, setWarehouseId] = React.useState<string>('all')\n const [adjustOpen, setAdjustOpen] = React.useState(false)\n const [cycleOpen, setCycleOpen] = React.useState(false)\n const [changeStatusOpen, setChangeStatusOpen] = React.useState(false)\n const [moveOpen, setMoveOpen] = React.useState(false)\n const [receiveOpen, setReceiveOpen] = React.useState(false)\n const [activeExpiryLot, setActiveExpiryLot] = React.useState<OperationalDashboardExpiryLotRow | null>(null)\n\n const openExpiryChangeStatus = React.useCallback((row: OperationalDashboardExpiryLotRow) => {\n setActiveExpiryLot(row)\n setChangeStatusOpen(true)\n }, [])\n\n const openExpiryMove = React.useCallback((row: OperationalDashboardExpiryLotRow) => {\n setActiveExpiryLot(row)\n setMoveOpen(true)\n }, [])\n\n const activityTimeFormatter = React.useMemo(\n () =>\n createDateTimeFormatter(locale, {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }),\n [locale],\n )\n\n const lastUpdatedFormatter = React.useMemo(\n () =>\n createDateTimeFormatter(locale, {\n hour: '2-digit',\n minute: '2-digit',\n }),\n [locale],\n )\n\n const expiryDateFormatter = React.useMemo(\n () =>\n createDateTimeFormatter(locale, {\n month: '2-digit',\n day: '2-digit',\n year: '2-digit',\n }),\n [locale],\n )\n\n const quantityFormatter = React.useMemo(\n () => new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),\n [locale],\n )\n\n const warehousesQuery = useQuery({\n queryKey: ['wms-dashboard', 'warehouses'],\n queryFn: async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '100', sortField: 'name', sortDir: 'asc' })\n const call = await apiCall<PagedWarehouses>(`/api/wms/warehouses?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.dashboard.errors.warehouses', 'Failed to load warehouses.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const hasWarehouses = (warehousesQuery.data ?? []).length > 0\n\n const locationsCountQuery = useQuery({\n queryKey: ['wms-dashboard', 'locations-count'],\n queryFn: async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '1' })\n const call = await apiCall<{ total?: number }>(`/api/wms/locations?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.dashboard.errors.locations', 'Failed to load locations.'))\n }\n return call.result?.total ?? 0\n },\n enabled: hasWarehouses,\n })\n\n const dashboardQuery = useQuery({\n queryKey: ['wms-dashboard', 'operational', warehouseId],\n queryFn: async () => {\n const params = new URLSearchParams()\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const call = await apiCall<OperationalDashboardPayload>(\n `/api/wms/dashboard/operational?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.dashboard.errors.load', 'Failed to load dashboard.'))\n }\n return call.result as OperationalDashboardPayload\n },\n refetchInterval: AUTO_REFRESH_MS,\n })\n\n const selectedWarehouse = warehousesQuery.data?.find((warehouse) => warehouse.id === warehouseId)\n const warehouseLabel =\n warehouseId === 'all'\n ? t('wms.backend.dashboard.filters.allWarehouses', 'All warehouses')\n : selectedWarehouse?.name || selectedWarehouse?.code || warehouseId\n\n const firstRunMode = React.useMemo<'no-warehouses' | 'no-locations' | null>(\n () =>\n resolveDashboardFirstRunMode({\n warehousesLoading: warehousesQuery.isLoading,\n warehousesError: warehousesQuery.isError,\n hasWarehouses,\n locationsLoading: locationsCountQuery.isLoading,\n locationsError: locationsCountQuery.isError,\n locationsCount: locationsCountQuery.data ?? 0,\n }),\n [\n warehousesQuery.isLoading,\n warehousesQuery.isError,\n hasWarehouses,\n locationsCountQuery.isLoading,\n locationsCountQuery.isError,\n locationsCountQuery.data,\n ],\n )\n\n const kpiConfig = React.useMemo(() => {\n const buildLotsHref = (expiryWindow: 'expiringSoon' | 'pastDue') => {\n const params = new URLSearchParams({ expiryWindow })\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n return `/backend/wms/lots?${params.toString()}`\n }\n const buildInventoryHref = (lowStock?: 'belowReorder' | 'belowSafety') => {\n const params = new URLSearchParams()\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n if (lowStock) params.set('lowStock', lowStock)\n const query = params.toString()\n return query ? `/backend/wms/inventory?${query}` : '/backend/wms/inventory'\n }\n const inventoryHref = buildInventoryHref()\n const reservationsHref = warehouseId === 'all'\n ? '/backend/wms/reservations'\n : `/backend/wms/reservations?warehouseId=${encodeURIComponent(warehouseId)}`\n const movementsHref = warehouseId === 'all'\n ? '/backend/wms/movements'\n : `/backend/wms/movements?warehouseId=${encodeURIComponent(warehouseId)}`\n\n return {\n lowStock: {\n title: t('wms.backend.dashboard.kpis.lowStock.title', 'Low stock'),\n caption: t('wms.backend.dashboard.kpis.lowStock.caption', 'Below reorder point'),\n ctaLabel: t('wms.backend.dashboard.kpis.lowStock.cta', 'View low stock'),\n href: buildInventoryHref('belowReorder'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'warning' as const,\n label: t('wms.backend.dashboard.kpis.lowStock.badgeActive', '{count} active', { count: kpi.count }),\n }),\n },\n reorderCritical: {\n title: t('wms.backend.dashboard.kpis.reorderCritical.title', 'Reorder critical'),\n caption: t('wms.backend.dashboard.kpis.reorderCritical.caption', 'Below safety stock'),\n ctaLabel: t('wms.backend.dashboard.kpis.reorderCritical.cta', 'View critical'),\n href: buildInventoryHref('belowSafety'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'error' as const,\n label: t('wms.backend.dashboard.kpis.reorderCritical.badge', '{count} critical', { count: kpi.count }),\n }),\n },\n expiringSoon: {\n title: t('wms.backend.dashboard.kpis.expiringSoon.title', 'Expiring soon'),\n caption: t('wms.backend.dashboard.kpis.expiringSoon.caption', 'Lots expiring in 30 days'),\n ctaLabel: t('wms.backend.dashboard.kpis.expiringSoon.cta', 'View expiry'),\n href: buildLotsHref('expiringSoon'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'warning' as const,\n label: t('wms.backend.dashboard.kpis.expiringSoon.badge', '{count} lots', { count: kpi.count }),\n }),\n },\n pastDue: {\n title: t('wms.backend.dashboard.kpis.pastDue.title', 'Past due'),\n caption: t('wms.backend.dashboard.kpis.pastDue.caption', 'Expired lots with on-hand stock'),\n ctaLabel: t('wms.backend.dashboard.kpis.pastDue.cta', 'View past due'),\n href: buildLotsHref('pastDue'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'error' as const,\n label: t('wms.backend.dashboard.kpis.pastDue.badge', '{count} lots', { count: kpi.count }),\n }),\n },\n agingReservations: {\n title: t('wms.backend.dashboard.kpis.agingReservations.title', 'Aging reservations'),\n caption: t('wms.backend.dashboard.kpis.agingReservations.caption', 'Active holds older than 7 days'),\n ctaLabel: t('wms.backend.dashboard.kpis.agingReservations.cta', 'View reservations'),\n href: reservationsHref,\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'warning' as const,\n label: t('wms.backend.dashboard.kpis.agingReservations.badge', '{count} aging', { count: kpi.count }),\n }),\n },\n todaysMoves: {\n title: t('wms.backend.dashboard.kpis.todaysMoves.title', \"Today's moves\"),\n caption: t('wms.backend.dashboard.kpis.todaysMoves.caption', 'Movements posted today'),\n ctaLabel: t('wms.backend.dashboard.kpis.todaysMoves.cta', 'View ledger'),\n href: movementsHref,\n resolveBadge: (kpi: OperationalDashboardKpi) => {\n if (kpi.deltaSinceYesterday === null) {\n return { variant: 'neutral' as const, label: null }\n }\n return {\n variant: kpi.deltaSinceYesterday >= 0 ? ('success' as const) : ('neutral' as const),\n label:\n kpi.deltaSinceYesterday >= 0\n ? t('wms.backend.dashboard.kpis.todaysMoves.badgeUp', '+{count} vs yesterday', {\n count: kpi.deltaSinceYesterday,\n })\n : t('wms.backend.dashboard.kpis.todaysMoves.badgeDown', '{count} vs yesterday', {\n count: kpi.deltaSinceYesterday,\n }),\n }\n },\n },\n }\n }, [t, warehouseId])\n\n const monthlyTrendData = React.useMemo(\n () =>\n (dashboardQuery.data?.monthlyTrends ?? []).map((point) => ({\n month: point.month,\n receive: point.receive,\n allocate: point.allocate,\n })),\n [dashboardQuery.data?.monthlyTrends],\n )\n\n const expiryLotsByCategory = React.useMemo(() => {\n const lots = dashboardQuery.data?.expiryLots ?? []\n return {\n expiringSoon: lots.filter((lot) => lot.category === 'expiringSoon'),\n pastDue: lots.filter((lot) => lot.category === 'pastDue'),\n }\n }, [dashboardQuery.data?.expiryLots])\n\n const movementsHref = kpiConfig.todaysMoves.href\n\n const activityColumns = React.useMemo<ColumnDef<OperationalDashboardActivityRow>[]>(\n () => [\n {\n id: 'event',\n header: t('wms.backend.dashboard.activity.columns.event', 'Event'),\n accessorKey: 'movementType',\n cell: ({ row }) => {\n const movementLabel = t(\n `wms.backend.dashboard.activity.types.${row.original.movementType}`,\n row.original.movementType,\n )\n const badgeVariant = movementStatusMap[row.original.movementType] ?? 'neutral'\n return (\n <StatusBadge variant={badgeVariant} dot>\n {movementLabel}\n </StatusBadge>\n )\n },\n meta: { maxWidth: '7rem' },\n },\n {\n id: 'details',\n header: t('wms.backend.dashboard.activity.columns.details', 'Details'),\n cell: ({ row }) => {\n const subtitle = formatActivitySubtitle(row.original, t)\n return (\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{formatActivityTitle(row.original, t)}</p>\n {subtitle ? (\n <p className=\"truncate text-sm text-muted-foreground\">{subtitle}</p>\n ) : null}\n </div>\n )\n },\n },\n {\n id: 'location',\n header: t('wms.backend.dashboard.activity.columns.location', 'Location'),\n accessorKey: 'locationLabel',\n cell: ({ row }) => (\n <p className=\"truncate text-sm text-muted-foreground\">{row.original.locationLabel}</p>\n ),\n meta: { maxWidth: '11rem', truncate: true },\n },\n {\n id: 'time',\n header: t('wms.backend.dashboard.activity.columns.time', 'Time'),\n accessorKey: 'performedAt',\n cell: ({ row }) => (\n <p className=\"text-xs leading-4 text-muted-foreground\">\n {activityTimeFormatter.format(new Date(row.original.performedAt))}\n </p>\n ),\n meta: { maxWidth: '5rem' },\n },\n {\n id: 'actions',\n header: t('wms.backend.dashboard.activity.columns.actions', 'Actions'),\n cell: ({ row }) => (\n <Button asChild type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7\">\n <Link\n href={`${movementsHref}#${row.original.id}`}\n aria-label={t('wms.backend.dashboard.activity.open', 'Open movement')}\n >\n <ExternalLink className=\"size-3.5\" />\n </Link>\n </Button>\n ),\n meta: { maxWidth: '2.5rem' },\n },\n ],\n [activityTimeFormatter, movementsHref, t],\n )\n\n const subtitle = dashboardQuery.data?.lastUpdatedAt\n ? t('wms.backend.dashboard.subtitle', 'Live activity for today \u00B7 auto-refresh 60s \u00B7 last update {time}', {\n time: lastUpdatedFormatter.format(new Date(dashboardQuery.data.lastUpdatedAt)),\n })\n : t('wms.backend.dashboard.subtitleLoading', 'Live activity for today \u00B7 auto-refresh 60s')\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <PageHeader\n title={t('wms.backend.dashboard.title', 'Operational dashboard')}\n description={subtitle}\n actions={(\n <>\n <Select value={warehouseId} onValueChange={setWarehouseId}>\n <SelectTrigger className=\"w-full max-w-xs sm:w-56\">\n <SelectValue placeholder={warehouseLabel} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {t('wms.backend.dashboard.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 <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => { void dashboardQuery.refetch() }}\n disabled={dashboardQuery.isFetching}\n >\n <RefreshCw className={`size-4 ${dashboardQuery.isFetching ? 'animate-spin' : ''}`} />\n {t('wms.backend.dashboard.actions.refresh', 'Refresh')}\n </Button>\n </>\n )}\n />\n\n {dashboardQuery.isLoading ? (\n <LoadingMessage label={t('wms.backend.dashboard.loading', 'Loading dashboard\u2026')} />\n ) : null}\n\n {dashboardQuery.isError ? (\n <ErrorMessage\n label={t('wms.backend.dashboard.errors.load', 'Failed to load dashboard.')}\n action={(\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => { void dashboardQuery.refetch() }}>\n {t('wms.backend.dashboard.actions.refresh', 'Refresh')}\n </Button>\n )}\n />\n ) : null}\n\n {firstRunMode === 'no-warehouses' ? (\n <section className=\"rounded-lg border-2 border-dashed border-border bg-card p-8 text-center shadow-sm\">\n <div className=\"mx-auto max-w-sm\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-muted\">\n <WarehouseIcon2 className=\"size-7 text-muted-foreground\" />\n </div>\n <h2 className=\"text-lg font-semibold\">\n {t('wms.backend.dashboard.firstRun.title', 'Set up your warehouse')}\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.firstRun.description',\n 'Create a warehouse, add storage locations, then receive your first stock to start tracking inventory.',\n )}\n </p>\n <div className=\"mt-6 flex flex-col items-center gap-3 sm:flex-row sm:justify-center\">\n <LinkButton asChild variant=\"primary\" size=\"sm\">\n <Link href=\"/backend/config/wms\">\n <WarehouseIcon2 className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.createWarehouse', 'Create warehouse')}\n </Link>\n </LinkButton>\n <LinkButton asChild variant=\"gray\" size=\"sm\">\n <Link href=\"/backend/config/wms\">\n <MapPin className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.addLocations', 'Add locations')}\n </Link>\n </LinkButton>\n {access.canReceive ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setReceiveOpen(true)}>\n <ArrowDown className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.receiveStock', 'Receive first stock')}\n </Button>\n ) : null}\n </div>\n </div>\n </section>\n ) : null}\n\n {firstRunMode === 'no-locations' ? (\n <section className=\"rounded-lg border-2 border-dashed border-border bg-card p-8 text-center shadow-sm\">\n <div className=\"mx-auto max-w-sm\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-muted\">\n <MapPin className=\"size-7 text-muted-foreground\" />\n </div>\n <h2 className=\"text-lg font-semibold\">\n {t('wms.backend.dashboard.firstRun.noLocations.title', 'Add storage locations')}\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.firstRun.noLocations.description',\n 'Your warehouse is set up \u2014 add at least one storage location before you can receive stock.',\n )}\n </p>\n <div className=\"mt-6 flex flex-col items-center gap-3 sm:flex-row sm:justify-center\">\n <LinkButton asChild variant=\"primary\" size=\"sm\">\n <Link href=\"/backend/config/wms\">\n <MapPin className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.addLocations', 'Add locations')}\n </Link>\n </LinkButton>\n </div>\n </div>\n </section>\n ) : null}\n\n {dashboardQuery.data ? (\n <>\n <section className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\">\n {dashboardQuery.data.kpis.map((kpi) => {\n const config = kpiConfig[kpi.id]\n const badge = config.resolveBadge(kpi)\n return (\n <DashboardKpiCard\n key={kpi.id}\n kpi={kpi}\n title={config.title}\n caption={config.caption}\n badgeLabel={badge.label}\n badgeVariant={badge.variant}\n ctaLabel={config.ctaLabel}\n href={config.href}\n />\n )\n })}\n </section>\n\n <section className=\"rounded-lg border bg-card shadow-sm\">\n <div className=\"border-b px-5 py-4\">\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.dashboard.expiry.title', 'Expiry watch')}\n </h2>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.expiry.description',\n 'Upcoming and overdue lots with on-hand stock in the selected warehouse scope.',\n )}\n </p>\n </div>\n <div className=\"grid gap-6 p-5 md:grid-cols-2\">\n <ExpiryLotList\n title={t('wms.backend.dashboard.expiry.expiringSoon.title', 'Expiring soon')}\n emptyLabel={t(\n 'wms.backend.dashboard.expiry.expiringSoon.empty',\n 'No lots expiring within the next 30 days.',\n )}\n viewAllLabel={t(\n 'wms.backend.dashboard.expiry.expiringSoon.viewAll',\n 'View all expiring \u2192',\n )}\n viewAllHref={kpiConfig.expiringSoon.href}\n rows={expiryLotsByCategory.expiringSoon}\n expiryFormatter={expiryDateFormatter}\n quantityFormatter={quantityFormatter}\n canAdjust={access.canAdjust}\n onChangeStatus={openExpiryChangeStatus}\n onMove={openExpiryMove}\n t={t}\n />\n <ExpiryLotList\n title={t('wms.backend.dashboard.expiry.pastDue.title', 'Past due')}\n emptyLabel={t(\n 'wms.backend.dashboard.expiry.pastDue.empty',\n 'No expired lots with available stock.',\n )}\n viewAllLabel={t(\n 'wms.backend.dashboard.expiry.pastDue.viewAll',\n 'View all past due \u2192',\n )}\n viewAllHref={kpiConfig.pastDue.href}\n rows={expiryLotsByCategory.pastDue}\n expiryFormatter={expiryDateFormatter}\n quantityFormatter={quantityFormatter}\n canAdjust={access.canAdjust}\n onChangeStatus={openExpiryChangeStatus}\n onMove={openExpiryMove}\n t={t}\n />\n </div>\n </section>\n\n <section className=\"rounded-lg border bg-card shadow-sm\">\n <div className=\"flex items-center justify-between gap-3 border-b px-5 py-4\">\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.dashboard.trends.title', 'Monthly trends')}\n </h2>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span className=\"inline-flex items-center gap-2\">\n <span className=\"size-2.5 rounded-sm bg-chart-1\" aria-hidden=\"true\" />\n {t('wms.backend.dashboard.trends.receive', 'Receive')}\n </span>\n <span className=\"inline-flex items-center gap-2\">\n <span className=\"size-2.5 rounded-sm bg-chart-2\" aria-hidden=\"true\" />\n {t('wms.backend.dashboard.trends.allocate', 'Allocate')}\n </span>\n </div>\n </div>\n <div className=\"p-5\">\n <BarChart\n data={monthlyTrendData}\n index=\"month\"\n categories={['receive', 'allocate']}\n showLegend={false}\n categoryLabels={{\n receive: t('wms.backend.dashboard.trends.receive', 'Receive'),\n allocate: t('wms.backend.dashboard.trends.allocate', 'Allocate'),\n }}\n emptyMessage={t('wms.backend.dashboard.trends.empty', 'No movement trends yet.')}\n className=\"border-0 bg-transparent p-0 shadow-none\"\n />\n </div>\n </section>\n\n <DataTable<OperationalDashboardActivityRow>\n title={t('wms.backend.dashboard.activity.title', 'Recent activity')}\n columns={activityColumns}\n data={dashboardQuery.data.recentActivity}\n disableRowClick\n emptyState={t('wms.backend.dashboard.activity.empty', 'No recent movements yet.')}\n actions={(\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href={movementsHref}>\n {t('wms.backend.dashboard.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.dashboard.quickActions.title', 'Quick actions')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.quickActions.description',\n 'Open a drawer without leaving the dashboard',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={kpiConfig.lowStock.href}>\n <Package className=\"size-4\" />\n {t('wms.backend.dashboard.quickActions.inventory', 'Open inventory console')}\n </Link>\n </Button>\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => setAdjustOpen(true)}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.dashboard.quickActions.adjust', 'Adjust inventory')}\n </Button>\n ) : null}\n {access.canCycleCount ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => setCycleOpen(true)}>\n <ClipboardList className=\"size-4\" />\n {t('wms.backend.dashboard.quickActions.cycleCount', 'Cycle count')}\n </Button>\n ) : null}\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={movementsHref}>\n {t('wms.backend.dashboard.quickActions.movements', 'View movements')}\n </Link>\n </Button>\n </div>\n </section>\n </>\n ) : null}\n </PageBody>\n\n {access.canReceive ? (\n <ReceiveInventoryDialog open={receiveOpen} onOpenChange={setReceiveOpen} access={access} />\n ) : null}\n {access.canAdjust ? (\n <AdjustInventoryDialog open={adjustOpen} onOpenChange={setAdjustOpen} access={access} />\n ) : null}\n {access.canCycleCount ? (\n <CycleCountWizardDialog open={cycleOpen} onOpenChange={setCycleOpen} access={access} />\n ) : null}\n {access.canAdjust && activeExpiryLot ? (\n <>\n <ChangeLotStatusDialog\n open={changeStatusOpen}\n onOpenChange={setChangeStatusOpen}\n access={access}\n lotId={activeExpiryLot.id}\n currentStatus={activeExpiryLot.status}\n lotUpdatedAt={activeExpiryLot.updatedAt}\n onSuccess={() => {\n void dashboardQuery.refetch()\n }}\n />\n <MoveInventoryDialog\n open={moveOpen}\n onOpenChange={setMoveOpen}\n access={access}\n initialCatalogVariantId={activeExpiryLot.catalogVariantId}\n initialWarehouseId={warehouseId === 'all' ? undefined : warehouseId}\n initialLotId={activeExpiryLot.id}\n initialAvailable={activeExpiryLot.availableQuantity}\n lockSourceContext\n />\n </>\n ) : null}\n </Page>\n )\n}\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useQuery } from '@tanstack/react-query'\nimport type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'\nimport {\n ArrowDown,\n ClipboardList,\n ExternalLink,\n MapPin,\n Package,\n RefreshCw,\n SlidersHorizontal,\n Warehouse as WarehouseIcon2,\n} from 'lucide-react'\nimport { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'\nimport { BarChart } from '@open-mercato/ui/backend/charts'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\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 type {\n OperationalDashboardActivityRow,\n OperationalDashboardExpiryLotRow,\n OperationalDashboardKpi,\n OperationalDashboardPayload,\n} from '../../lib/loadOperationalDashboard'\nimport {\n inventoryMovementReasonLabel,\n type InventoryDisplayTranslator,\n} from '../../lib/inventoryDisplayUi'\nimport { AdjustInventoryDialog } from './AdjustInventoryDialog'\nimport { ChangeLotStatusDialog } from './ChangeLotStatusDialog'\nimport { CycleCountWizardDialog } from './CycleCountWizardDialog'\nimport { MoveInventoryDialog } from './MoveInventoryDialog'\nimport { ReceiveInventoryDialog } from './ReceiveInventoryDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\ntype WarehouseOption = {\n id: string\n name?: string | null\n code?: string | null\n}\n\ntype PagedWarehouses = {\n items: WarehouseOption[]\n}\n\nconst AUTO_REFRESH_MS = 60_000\n\nfunction Sparkline({ values, className }: { values: number[]; className?: string }) {\n const width = 160\n const height = 36\n const max = Math.max(...values, 1)\n const points = values.map((value, index) => {\n const x = values.length <= 1 ? width / 2 : (index / (values.length - 1)) * width\n const y = height - (value / max) * (height - 4) - 2\n return `${x},${y}`\n })\n\n return (\n <svg\n viewBox={`0 0 ${width} ${height}`}\n className={className}\n aria-hidden=\"true\"\n preserveAspectRatio=\"none\"\n >\n <polyline\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n points={points.join(' ')}\n className=\"text-status-info-icon\"\n />\n </svg>\n )\n}\n\nfunction formatActivityTitle(\n row: OperationalDashboardActivityRow,\n t: ReturnType<typeof useT>,\n): string {\n const sku = row.variantSku?.trim() || row.variantId\n const quantity = Math.abs(row.quantity)\n const signedQuantity = row.quantity\n switch (row.movementType) {\n case 'receipt':\n case 'return_receive':\n return t('wms.backend.dashboard.activity.titles.received', 'Received {quantity}\u00D7 {sku}', {\n quantity,\n sku,\n })\n case 'adjust':\n return t('wms.backend.dashboard.activity.titles.adjusted', 'Adjusted {quantity}\u00D7 {sku}', {\n quantity: `${signedQuantity >= 0 ? '+' : ''}${signedQuantity}`,\n sku,\n })\n case 'transfer':\n return t('wms.backend.dashboard.activity.titles.moved', 'Moved {quantity}\u00D7 {sku}', {\n quantity,\n sku,\n })\n case 'pick':\n case 'pack':\n return t('wms.backend.dashboard.activity.titles.allocated', 'Allocated {quantity}\u00D7 {sku}', {\n quantity,\n sku,\n })\n case 'cycle_count':\n return t('wms.backend.dashboard.activity.titles.reconciled', 'Inventory reconciled \u2014 {sku}', {\n sku,\n })\n default:\n return t('wms.backend.dashboard.activity.titles.generic', '{type} {quantity}\u00D7 {sku}', {\n type: row.movementType,\n quantity,\n sku,\n })\n }\n}\n\nfunction formatActivitySubtitle(\n row: OperationalDashboardActivityRow,\n t: InventoryDisplayTranslator,\n): string | null {\n const reasonLabel = inventoryMovementReasonLabel(\n {\n reasonCode: row.reasonCode,\n reason: row.reason,\n movementType: row.movementType,\n },\n t,\n )\n if (reasonLabel) return reasonLabel\n if (row.referenceType && row.referenceId) return `${row.referenceType} \u00B7 ${row.referenceId}`\n return null\n}\n\nfunction createDateTimeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n return new Intl.DateTimeFormat(locale, options)\n}\n\nexport function resolveDashboardFirstRunMode(input: {\n warehousesLoading: boolean\n warehousesError: boolean\n hasWarehouses: boolean\n locationsLoading: boolean\n locationsError: boolean\n locationsCount: number\n}): 'no-warehouses' | 'no-locations' | null {\n if (input.warehousesLoading || input.warehousesError) return null\n if (!input.hasWarehouses) return 'no-warehouses'\n if (input.locationsLoading || input.locationsError) return null\n if (input.locationsCount === 0) return 'no-locations'\n return null\n}\n\ntype DashboardKpiCardProps = {\n kpi: OperationalDashboardKpi\n title: string\n caption: string\n badgeLabel: string | null\n badgeVariant: StatusBadgeVariant\n ctaLabel: string\n href: string\n}\n\nfunction DashboardKpiCard({\n kpi,\n title,\n caption,\n badgeLabel,\n badgeVariant,\n ctaLabel,\n href,\n}: DashboardKpiCardProps) {\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\">{kpi.count}</p>\n {badgeLabel ? (\n <StatusBadge variant={badgeVariant} dot>\n {badgeLabel}\n </StatusBadge>\n ) : null}\n </div>\n <Sparkline values={kpi.sparkline} className=\"mt-3 h-9 w-full max-w-40\" />\n <LinkButton asChild variant=\"primary\" size=\"sm\" className=\"mt-4 w-fit\">\n <Link href={href}>{ctaLabel}</Link>\n </LinkButton>\n </section>\n )\n}\n\ntype ExpiryLotListProps = {\n title: string\n emptyLabel: string\n viewAllLabel: string\n viewAllHref: string\n rows: OperationalDashboardExpiryLotRow[]\n expiryFormatter: Intl.DateTimeFormat\n quantityFormatter: Intl.NumberFormat\n canAdjust: boolean\n onChangeStatus: (row: OperationalDashboardExpiryLotRow) => void\n onMove: (row: OperationalDashboardExpiryLotRow) => void\n t: ReturnType<typeof useT>\n}\n\nfunction ExpiryLotList({\n title,\n emptyLabel,\n viewAllLabel,\n viewAllHref,\n rows,\n expiryFormatter,\n quantityFormatter,\n canAdjust,\n onChangeStatus,\n onMove,\n t,\n}: ExpiryLotListProps) {\n return (\n <div className=\"min-w-0\">\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold\">{title}</h3>\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"h-8 px-2\">\n <Link href={viewAllHref}>{viewAllLabel}</Link>\n </LinkButton>\n </div>\n {rows.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{emptyLabel}</p>\n ) : (\n <ul className=\"divide-y divide-border\">\n {rows.map((row) => {\n const badgeVariant: StatusBadgeVariant = row.category === 'pastDue' ? 'error' : 'warning'\n return (\n <li key={row.id} className=\"flex items-start justify-between gap-3 py-3 first:pt-0 last:pb-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{row.lotNumber}</p>\n <p className=\"truncate text-xs text-muted-foreground\">{row.sku}</p>\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <StatusBadge variant={badgeVariant} dot>\n {expiryFormatter.format(new Date(row.expiresAt))}\n </StatusBadge>\n <span className=\"text-xs text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.expiry.available',\n '{quantity} available',\n { quantity: quantityFormatter.format(row.availableQuantity) },\n )}\n </span>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {canAdjust ? (\n <RowActions\n items={[\n {\n id: 'change-status',\n label: t('wms.backend.lots.actions.changeStatus', 'Change status'),\n onSelect: () => onChangeStatus(row),\n },\n {\n id: 'move',\n label: t('wms.backend.lots.actions.move', 'Move'),\n onSelect: () => onMove(row),\n },\n ]}\n />\n ) : null}\n <Button asChild type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7 shrink-0\">\n <Link\n href={`/backend/wms/lot/${row.id}`}\n aria-label={t('wms.backend.dashboard.expiry.openLot', 'Open lot')}\n >\n <ExternalLink className=\"size-3.5\" />\n </Link>\n </Button>\n </div>\n </li>\n )\n })}\n </ul>\n )}\n </div>\n )\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\nexport default function WmsOperationalDashboardPage() {\n const t = useT()\n const locale = useLocale()\n const access = useWmsInventoryMutationAccess()\n const [warehouseId, setWarehouseId] = React.useState<string>('all')\n const [adjustOpen, setAdjustOpen] = React.useState(false)\n const [cycleOpen, setCycleOpen] = React.useState(false)\n const [changeStatusOpen, setChangeStatusOpen] = React.useState(false)\n const [moveOpen, setMoveOpen] = React.useState(false)\n const [receiveOpen, setReceiveOpen] = React.useState(false)\n const [activeExpiryLot, setActiveExpiryLot] = React.useState<OperationalDashboardExpiryLotRow | null>(null)\n\n const openExpiryChangeStatus = React.useCallback((row: OperationalDashboardExpiryLotRow) => {\n setActiveExpiryLot(row)\n setChangeStatusOpen(true)\n }, [])\n\n const openExpiryMove = React.useCallback((row: OperationalDashboardExpiryLotRow) => {\n setActiveExpiryLot(row)\n setMoveOpen(true)\n }, [])\n\n const activityTimeFormatter = React.useMemo(\n () =>\n createDateTimeFormatter(locale, {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n }),\n [locale],\n )\n\n const lastUpdatedFormatter = React.useMemo(\n () =>\n createDateTimeFormatter(locale, {\n hour: '2-digit',\n minute: '2-digit',\n }),\n [locale],\n )\n\n const expiryDateFormatter = React.useMemo(\n () =>\n createDateTimeFormatter(locale, {\n month: '2-digit',\n day: '2-digit',\n year: '2-digit',\n }),\n [locale],\n )\n\n const quantityFormatter = React.useMemo(\n () => new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }),\n [locale],\n )\n\n const warehousesQuery = useQuery({\n queryKey: ['wms-dashboard', 'warehouses'],\n queryFn: async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '100', sortField: 'name', sortDir: 'asc' })\n const call = await apiCall<PagedWarehouses>(`/api/wms/warehouses?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.dashboard.errors.warehouses', 'Failed to load warehouses.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const hasWarehouses = (warehousesQuery.data ?? []).length > 0\n\n const locationsCountQuery = useQuery({\n queryKey: ['wms-dashboard', 'locations-count'],\n queryFn: async () => {\n const params = new URLSearchParams({ page: '1', pageSize: '1' })\n const call = await apiCall<{ total?: number }>(`/api/wms/locations?${params.toString()}`)\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.dashboard.errors.locations', 'Failed to load locations.'))\n }\n return call.result?.total ?? 0\n },\n enabled: hasWarehouses,\n })\n\n const dashboardQuery = useQuery({\n queryKey: ['wms-dashboard', 'operational', warehouseId],\n queryFn: async () => {\n const params = new URLSearchParams()\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const call = await apiCall<OperationalDashboardPayload>(\n `/api/wms/dashboard/operational?${params.toString()}`,\n )\n if (!call.ok) {\n await raiseCrudError(call.response, t('wms.backend.dashboard.errors.load', 'Failed to load dashboard.'))\n }\n return call.result as OperationalDashboardPayload\n },\n refetchInterval: AUTO_REFRESH_MS,\n })\n\n const selectedWarehouse = warehousesQuery.data?.find((warehouse) => warehouse.id === warehouseId)\n const warehouseLabel =\n warehouseId === 'all'\n ? t('wms.backend.dashboard.filters.allWarehouses', 'All warehouses')\n : selectedWarehouse?.name || selectedWarehouse?.code || warehouseId\n\n const firstRunMode = React.useMemo<'no-warehouses' | 'no-locations' | null>(\n () =>\n resolveDashboardFirstRunMode({\n warehousesLoading: warehousesQuery.isLoading,\n warehousesError: warehousesQuery.isError,\n hasWarehouses,\n locationsLoading: locationsCountQuery.isLoading,\n locationsError: locationsCountQuery.isError,\n locationsCount: locationsCountQuery.data ?? 0,\n }),\n [\n warehousesQuery.isLoading,\n warehousesQuery.isError,\n hasWarehouses,\n locationsCountQuery.isLoading,\n locationsCountQuery.isError,\n locationsCountQuery.data,\n ],\n )\n\n const kpiConfig = React.useMemo(() => {\n const buildLotsHref = (expiryWindow: 'expiringSoon' | 'pastDue') => {\n const params = new URLSearchParams({ expiryWindow })\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n return `/backend/wms/lots?${params.toString()}`\n }\n const buildInventoryHref = (lowStock?: 'belowReorder' | 'belowSafety') => {\n const params = new URLSearchParams()\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n if (lowStock) params.set('lowStock', lowStock)\n const query = params.toString()\n return query ? `/backend/wms/inventory?${query}` : '/backend/wms/inventory'\n }\n const inventoryHref = buildInventoryHref()\n const reservationsHref = warehouseId === 'all'\n ? '/backend/wms/reservations'\n : `/backend/wms/reservations?warehouseId=${encodeURIComponent(warehouseId)}`\n const movementsHref = warehouseId === 'all'\n ? '/backend/wms/movements'\n : `/backend/wms/movements?warehouseId=${encodeURIComponent(warehouseId)}`\n\n return {\n lowStock: {\n title: t('wms.backend.dashboard.kpis.lowStock.title', 'Low stock'),\n caption: t('wms.backend.dashboard.kpis.lowStock.caption', 'Below reorder point'),\n ctaLabel: t('wms.backend.dashboard.kpis.lowStock.cta', 'View low stock'),\n href: buildInventoryHref('belowReorder'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'warning' as const,\n label: t('wms.backend.dashboard.kpis.lowStock.badgeActive', '{count} active', { count: kpi.count }),\n }),\n },\n reorderCritical: {\n title: t('wms.backend.dashboard.kpis.reorderCritical.title', 'Reorder critical'),\n caption: t('wms.backend.dashboard.kpis.reorderCritical.caption', 'Below safety stock'),\n ctaLabel: t('wms.backend.dashboard.kpis.reorderCritical.cta', 'View critical'),\n href: buildInventoryHref('belowSafety'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'error' as const,\n label: t('wms.backend.dashboard.kpis.reorderCritical.badge', '{count} critical', { count: kpi.count }),\n }),\n },\n expiringSoon: {\n title: t('wms.backend.dashboard.kpis.expiringSoon.title', 'Expiring soon'),\n caption: t('wms.backend.dashboard.kpis.expiringSoon.caption', 'Lots expiring in 30 days'),\n ctaLabel: t('wms.backend.dashboard.kpis.expiringSoon.cta', 'View expiry'),\n href: buildLotsHref('expiringSoon'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'warning' as const,\n label: t('wms.backend.dashboard.kpis.expiringSoon.badge', '{count} lots', { count: kpi.count }),\n }),\n },\n pastDue: {\n title: t('wms.backend.dashboard.kpis.pastDue.title', 'Past due'),\n caption: t('wms.backend.dashboard.kpis.pastDue.caption', 'Expired lots with on-hand stock'),\n ctaLabel: t('wms.backend.dashboard.kpis.pastDue.cta', 'View past due'),\n href: buildLotsHref('pastDue'),\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'error' as const,\n label: t('wms.backend.dashboard.kpis.pastDue.badge', '{count} lots', { count: kpi.count }),\n }),\n },\n agingReservations: {\n title: t('wms.backend.dashboard.kpis.agingReservations.title', 'Aging reservations'),\n caption: t('wms.backend.dashboard.kpis.agingReservations.caption', 'Active holds older than 7 days'),\n ctaLabel: t('wms.backend.dashboard.kpis.agingReservations.cta', 'View reservations'),\n href: reservationsHref,\n resolveBadge: (kpi: OperationalDashboardKpi) => ({\n variant: 'warning' as const,\n label: t('wms.backend.dashboard.kpis.agingReservations.badge', '{count} aging', { count: kpi.count }),\n }),\n },\n todaysMoves: {\n title: t('wms.backend.dashboard.kpis.todaysMoves.title', \"Today's moves\"),\n caption: t('wms.backend.dashboard.kpis.todaysMoves.caption', 'Movements posted today'),\n ctaLabel: t('wms.backend.dashboard.kpis.todaysMoves.cta', 'View ledger'),\n href: movementsHref,\n resolveBadge: (kpi: OperationalDashboardKpi) => {\n if (kpi.deltaSinceYesterday === null) {\n return { variant: 'neutral' as const, label: null }\n }\n return {\n variant: kpi.deltaSinceYesterday >= 0 ? ('success' as const) : ('neutral' as const),\n label:\n kpi.deltaSinceYesterday >= 0\n ? t('wms.backend.dashboard.kpis.todaysMoves.badgeUp', '+{count} vs yesterday', {\n count: kpi.deltaSinceYesterday,\n })\n : t('wms.backend.dashboard.kpis.todaysMoves.badgeDown', '{count} vs yesterday', {\n count: kpi.deltaSinceYesterday,\n }),\n }\n },\n },\n }\n }, [t, warehouseId])\n\n const monthlyTrendData = React.useMemo(\n () =>\n (dashboardQuery.data?.monthlyTrends ?? []).map((point) => ({\n month: point.month,\n receive: point.receive,\n allocate: point.allocate,\n })),\n [dashboardQuery.data?.monthlyTrends],\n )\n\n const expiryLotsByCategory = React.useMemo(() => {\n const lots = dashboardQuery.data?.expiryLots ?? []\n return {\n expiringSoon: lots.filter((lot) => lot.category === 'expiringSoon'),\n pastDue: lots.filter((lot) => lot.category === 'pastDue'),\n }\n }, [dashboardQuery.data?.expiryLots])\n\n const movementsHref = kpiConfig.todaysMoves.href\n\n const activityColumns = React.useMemo<ColumnDef<OperationalDashboardActivityRow>[]>(\n () => [\n {\n id: 'event',\n header: t('wms.backend.dashboard.activity.columns.event', 'Event'),\n accessorKey: 'movementType',\n cell: ({ row }) => {\n const movementLabel = t(\n `wms.backend.dashboard.activity.types.${row.original.movementType}`,\n row.original.movementType,\n )\n const badgeVariant = movementStatusMap[row.original.movementType] ?? 'neutral'\n return (\n <StatusBadge variant={badgeVariant} dot>\n {movementLabel}\n </StatusBadge>\n )\n },\n meta: { maxWidth: '7rem' },\n },\n {\n id: 'details',\n header: t('wms.backend.dashboard.activity.columns.details', 'Details'),\n cell: ({ row }) => {\n const subtitle = formatActivitySubtitle(row.original, t)\n return (\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{formatActivityTitle(row.original, t)}</p>\n {subtitle ? (\n <p className=\"truncate text-sm text-muted-foreground\">{subtitle}</p>\n ) : null}\n </div>\n )\n },\n },\n {\n id: 'location',\n header: t('wms.backend.dashboard.activity.columns.location', 'Location'),\n accessorKey: 'locationLabel',\n cell: ({ row }) => (\n <p className=\"truncate text-sm text-muted-foreground\">{row.original.locationLabel}</p>\n ),\n meta: { maxWidth: '11rem', truncate: true },\n },\n {\n id: 'time',\n header: t('wms.backend.dashboard.activity.columns.time', 'Time'),\n accessorKey: 'performedAt',\n cell: ({ row }) => (\n <p className=\"text-xs leading-4 text-muted-foreground\">\n {activityTimeFormatter.format(new Date(row.original.performedAt))}\n </p>\n ),\n meta: { maxWidth: '5rem' },\n },\n {\n id: 'actions',\n header: t('wms.backend.dashboard.activity.columns.actions', 'Actions'),\n cell: ({ row }) => (\n <Button asChild type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7\">\n <Link\n href={`${movementsHref}#${row.original.id}`}\n aria-label={t('wms.backend.dashboard.activity.open', 'Open movement')}\n >\n <ExternalLink className=\"size-3.5\" />\n </Link>\n </Button>\n ),\n meta: { maxWidth: '2.5rem' },\n },\n ],\n [activityTimeFormatter, movementsHref, t],\n )\n\n const subtitle = dashboardQuery.data?.lastUpdatedAt\n ? t('wms.backend.dashboard.subtitle', 'Live activity for today \u00B7 auto-refresh 60s \u00B7 last update {time}', {\n time: lastUpdatedFormatter.format(new Date(dashboardQuery.data.lastUpdatedAt)),\n })\n : t('wms.backend.dashboard.subtitleLoading', 'Live activity for today \u00B7 auto-refresh 60s')\n\n return (\n <Page>\n <PageBody className=\"space-y-6\">\n <PageHeader\n title={t('wms.backend.dashboard.title', 'Operational dashboard')}\n description={subtitle}\n actions={(\n <>\n <Select value={warehouseId} onValueChange={setWarehouseId}>\n <SelectTrigger className=\"w-full max-w-xs sm:w-56\">\n <SelectValue placeholder={warehouseLabel} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {t('wms.backend.dashboard.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 <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => { void dashboardQuery.refetch() }}\n disabled={dashboardQuery.isFetching}\n >\n <RefreshCw className={`size-4 ${dashboardQuery.isFetching ? 'animate-spin' : ''}`} />\n {t('wms.backend.dashboard.actions.refresh', 'Refresh')}\n </Button>\n </>\n )}\n />\n\n {dashboardQuery.isLoading ? (\n <LoadingMessage label={t('wms.backend.dashboard.loading', 'Loading dashboard\u2026')} />\n ) : null}\n\n {dashboardQuery.isError ? (\n <ErrorMessage\n label={t('wms.backend.dashboard.errors.load', 'Failed to load dashboard.')}\n action={(\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => { void dashboardQuery.refetch() }}>\n {t('wms.backend.dashboard.actions.refresh', 'Refresh')}\n </Button>\n )}\n />\n ) : null}\n\n {firstRunMode === 'no-warehouses' ? (\n <section className=\"rounded-lg border-2 border-dashed border-border bg-card p-8 text-center shadow-sm\">\n <div className=\"mx-auto max-w-sm\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-muted\">\n <WarehouseIcon2 className=\"size-7 text-muted-foreground\" />\n </div>\n <h2 className=\"text-lg font-semibold\">\n {t('wms.backend.dashboard.firstRun.title', 'Set up your warehouse')}\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.firstRun.description',\n 'Create a warehouse, add storage locations, then receive your first stock to start tracking inventory.',\n )}\n </p>\n <div className=\"mt-6 flex flex-col items-center gap-3 sm:flex-row sm:justify-center\">\n <LinkButton asChild variant=\"primary\" size=\"sm\">\n <Link href=\"/backend/config/wms\">\n <WarehouseIcon2 className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.createWarehouse', 'Create warehouse')}\n </Link>\n </LinkButton>\n <LinkButton asChild variant=\"gray\" size=\"sm\">\n <Link href=\"/backend/config/wms\">\n <MapPin className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.addLocations', 'Add locations')}\n </Link>\n </LinkButton>\n {access.canReceive ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setReceiveOpen(true)}>\n <ArrowDown className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.receiveStock', 'Receive first stock')}\n </Button>\n ) : null}\n </div>\n </div>\n </section>\n ) : null}\n\n {firstRunMode === 'no-locations' ? (\n <section className=\"rounded-lg border-2 border-dashed border-border bg-card p-8 text-center shadow-sm\">\n <div className=\"mx-auto max-w-sm\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-muted\">\n <MapPin className=\"size-7 text-muted-foreground\" />\n </div>\n <h2 className=\"text-lg font-semibold\">\n {t('wms.backend.dashboard.firstRun.noLocations.title', 'Add storage locations')}\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.firstRun.noLocations.description',\n 'Your warehouse is set up \u2014 add at least one storage location before you can receive stock.',\n )}\n </p>\n <div className=\"mt-6 flex flex-col items-center gap-3 sm:flex-row sm:justify-center\">\n <LinkButton asChild variant=\"primary\" size=\"sm\">\n <Link href=\"/backend/config/wms\">\n <MapPin className=\"size-4\" />\n {t('wms.backend.dashboard.firstRun.actions.addLocations', 'Add locations')}\n </Link>\n </LinkButton>\n </div>\n </div>\n </section>\n ) : null}\n\n {dashboardQuery.data ? (\n <>\n <section className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6\">\n {dashboardQuery.data.kpis.map((kpi) => {\n const config = kpiConfig[kpi.id]\n const badge = config.resolveBadge(kpi)\n return (\n <DashboardKpiCard\n key={kpi.id}\n kpi={kpi}\n title={config.title}\n caption={config.caption}\n badgeLabel={badge.label}\n badgeVariant={badge.variant}\n ctaLabel={config.ctaLabel}\n href={config.href}\n />\n )\n })}\n </section>\n\n <section className=\"rounded-lg border bg-card shadow-sm\">\n <div className=\"border-b px-5 py-4\">\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.dashboard.expiry.title', 'Expiry watch')}\n </h2>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.expiry.description',\n 'Upcoming and overdue lots with on-hand stock in the selected warehouse scope.',\n )}\n </p>\n </div>\n <div className=\"grid gap-6 p-5 md:grid-cols-2\">\n <ExpiryLotList\n title={t('wms.backend.dashboard.expiry.expiringSoon.title', 'Expiring soon')}\n emptyLabel={t(\n 'wms.backend.dashboard.expiry.expiringSoon.empty',\n 'No lots expiring within the next 30 days.',\n )}\n viewAllLabel={t(\n 'wms.backend.dashboard.expiry.expiringSoon.viewAll',\n 'View all expiring \u2192',\n )}\n viewAllHref={kpiConfig.expiringSoon.href}\n rows={expiryLotsByCategory.expiringSoon}\n expiryFormatter={expiryDateFormatter}\n quantityFormatter={quantityFormatter}\n canAdjust={access.canAdjust}\n onChangeStatus={openExpiryChangeStatus}\n onMove={openExpiryMove}\n t={t}\n />\n <ExpiryLotList\n title={t('wms.backend.dashboard.expiry.pastDue.title', 'Past due')}\n emptyLabel={t(\n 'wms.backend.dashboard.expiry.pastDue.empty',\n 'No expired lots with available stock.',\n )}\n viewAllLabel={t(\n 'wms.backend.dashboard.expiry.pastDue.viewAll',\n 'View all past due \u2192',\n )}\n viewAllHref={kpiConfig.pastDue.href}\n rows={expiryLotsByCategory.pastDue}\n expiryFormatter={expiryDateFormatter}\n quantityFormatter={quantityFormatter}\n canAdjust={access.canAdjust}\n onChangeStatus={openExpiryChangeStatus}\n onMove={openExpiryMove}\n t={t}\n />\n </div>\n </section>\n\n <section className=\"rounded-lg border bg-card shadow-sm\">\n <div className=\"flex items-center justify-between gap-3 border-b px-5 py-4\">\n <h2 className=\"text-base font-semibold\">\n {t('wms.backend.dashboard.trends.title', 'Monthly trends')}\n </h2>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span className=\"inline-flex items-center gap-2\">\n <span className=\"size-2.5 rounded-sm bg-chart-1\" aria-hidden=\"true\" />\n {t('wms.backend.dashboard.trends.receive', 'Receive')}\n </span>\n <span className=\"inline-flex items-center gap-2\">\n <span className=\"size-2.5 rounded-sm bg-chart-2\" aria-hidden=\"true\" />\n {t('wms.backend.dashboard.trends.allocate', 'Allocate')}\n </span>\n </div>\n </div>\n <div className=\"p-5\">\n <BarChart\n data={monthlyTrendData}\n index=\"month\"\n categories={['receive', 'allocate']}\n showLegend={false}\n categoryLabels={{\n receive: t('wms.backend.dashboard.trends.receive', 'Receive'),\n allocate: t('wms.backend.dashboard.trends.allocate', 'Allocate'),\n }}\n emptyMessage={t('wms.backend.dashboard.trends.empty', 'No movement trends yet.')}\n className=\"border-0 bg-transparent p-0 shadow-none\"\n />\n </div>\n </section>\n\n <DataTable<OperationalDashboardActivityRow>\n title={t('wms.backend.dashboard.activity.title', 'Recent activity')}\n columns={activityColumns}\n data={dashboardQuery.data.recentActivity}\n disableRowClick\n emptyState={t('wms.backend.dashboard.activity.empty', 'No recent movements yet.')}\n actions={(\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href={movementsHref}>\n {t('wms.backend.dashboard.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.dashboard.quickActions.title', 'Quick actions')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'wms.backend.dashboard.quickActions.description',\n 'Open a drawer without leaving the dashboard',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={kpiConfig.lowStock.href}>\n <Package className=\"size-4\" />\n {t('wms.backend.dashboard.quickActions.inventory', 'Open inventory console')}\n </Link>\n </Button>\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => setAdjustOpen(true)}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.dashboard.quickActions.adjust', 'Adjust inventory')}\n </Button>\n ) : null}\n {access.canCycleCount ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => setCycleOpen(true)}>\n <ClipboardList className=\"size-4\" />\n {t('wms.backend.dashboard.quickActions.cycleCount', 'Cycle count')}\n </Button>\n ) : null}\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={movementsHref}>\n {t('wms.backend.dashboard.quickActions.movements', 'View movements')}\n </Link>\n </Button>\n </div>\n </section>\n </>\n ) : null}\n </PageBody>\n\n {access.canReceive ? (\n <ReceiveInventoryDialog open={receiveOpen} onOpenChange={setReceiveOpen} access={access} />\n ) : null}\n {access.canAdjust ? (\n <AdjustInventoryDialog open={adjustOpen} onOpenChange={setAdjustOpen} access={access} />\n ) : null}\n {access.canCycleCount ? (\n <CycleCountWizardDialog open={cycleOpen} onOpenChange={setCycleOpen} access={access} />\n ) : null}\n {access.canAdjust && activeExpiryLot ? (\n <>\n <ChangeLotStatusDialog\n open={changeStatusOpen}\n onOpenChange={setChangeStatusOpen}\n access={access}\n lotId={activeExpiryLot.id}\n currentStatus={activeExpiryLot.status}\n lotUpdatedAt={activeExpiryLot.updatedAt}\n onSuccess={() => {\n void dashboardQuery.refetch()\n }}\n />\n <MoveInventoryDialog\n open={moveOpen}\n onOpenChange={setMoveOpen}\n access={access}\n initialCatalogVariantId={activeExpiryLot.catalogVariantId}\n initialWarehouseId={warehouseId === 'all' ? undefined : warehouseId}\n initialLotId={activeExpiryLot.id}\n initialAvailable={activeExpiryLot.availableQuantity}\n lockSourceContext\n />\n </>\n ) : null}\n </Page>\n )\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["movementsHref", "subtitle"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/wms/components/backend/WmsSkuDetailPage.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 ArrowDown,\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 { CycleCountWizardDialog } from './CycleCountWizardDialog'\nimport { ReceiveInventoryDialog } from './ReceiveInventoryDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\nconst variantIdSchema = 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 is_active?: boolean | null\n created_at?: string | null\n}\n\ntype InventoryProfileRow = {\n reorder_point?: string | number | null\n safety_stock?: 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 lot_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 InventoryLotRow = {\n id: string\n lot_number?: string | null\n expires_at?: string | null\n status?: string | 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 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 lotId?: string\n}\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}\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 USAGE_WINDOW_DAYS = 14\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 formatLotLabel(\n lot: InventoryLotRow | undefined,\n locale: string,\n): string {\n if (!lot) return '\u2014'\n const number = (lot.lot_number ?? '').trim() || lot.id\n if (!lot.expires_at) return number\n const expires = new Date(lot.expires_at)\n if (Number.isNaN(expires.getTime())) return number\n const expLabel = new Intl.DateTimeFormat(locale, { month: '2-digit', year: '2-digit' }).format(expires)\n return `${number} \u00B7 exp ${expLabel}`\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 resolveBalanceStatus(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | 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.sku.distribution.status.expired',\n labelFallback: 'Expired',\n }\n }\n if (isNearExpiry(lot?.expires_at, nowMs)) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.sku.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.sku.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.sku.distribution.status.reserved',\n labelFallback: 'Reserved',\n }\n }\n return {\n variant: 'success',\n labelKey: 'wms.backend.sku.distribution.status.available',\n labelFallback: 'Available',\n }\n}\n\nfunction matchesDistributionFilter(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | 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)\n case 'picking':\n return (\n PICKING_LOCATION_TYPES.has(locationType) ||\n locationCode.includes('pick') ||\n locationCode.includes('staging')\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.sku.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 skuLabel: 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.dashboard.activity.titles.received', 'Received {quantity}\u00D7 {sku}', {\n quantity,\n sku: skuLabel,\n })\n case 'adjust':\n return t('wms.backend.dashboard.activity.titles.adjusted', 'Adjusted {quantity}\u00D7 {sku}', {\n quantity: `${signedQuantity >= 0 ? '+' : ''}${signedQuantity}`,\n sku: skuLabel,\n })\n case 'transfer':\n return t('wms.backend.dashboard.activity.titles.moved', 'Moved {quantity}\u00D7 {sku}', {\n quantity,\n sku: skuLabel,\n })\n case 'pick':\n case 'pack':\n return t('wms.backend.dashboard.activity.titles.allocated', 'Allocated {quantity}\u00D7 {sku}', {\n quantity,\n sku: skuLabel,\n })\n case 'cycle_count':\n return t('wms.backend.dashboard.activity.titles.reconciled', 'Inventory reconciled \u2014 {sku}', {\n sku: skuLabel,\n })\n default:\n return t('wms.backend.dashboard.activity.titles.generic', '{type} {quantity}\u00D7 {sku}', {\n type: row.type ?? 'movement',\n quantity,\n sku: skuLabel,\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\ntype SkuKpiCardProps = {\n title: string\n caption: string\n value: string\n badgeLabel: string | null\n badgeVariant: StatusBadgeVariant\n ctaLabel: string\n ctaHref: string\n}\n\nfunction SkuKpiCard({\n title,\n caption,\n value,\n badgeLabel,\n badgeVariant,\n ctaLabel,\n ctaHref,\n}: SkuKpiCardProps) {\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 <LinkButton asChild variant=\"primary\" size=\"sm\" className=\"mt-auto pt-4 w-fit\">\n <Link href={ctaHref}>{ctaLabel}</Link>\n </LinkButton>\n </section>\n )\n}\n\ntype WmsSkuDetailPageProps = {\n variantId: string\n}\n\nexport default function WmsSkuDetailPage({ variantId }: WmsSkuDetailPageProps) {\n const t = useT()\n const locale = useLocale()\n const router = useRouter()\n const access = useWmsInventoryMutationAccess()\n const parsedVariantId = variantIdSchema.safeParse(variantId.trim())\n const catalogVariantId = parsedVariantId.success ? parsedVariantId.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 [receiveOpen, setReceiveOpen] = React.useState(false)\n const [cycleOpen, setCycleOpen] = React.useState(false)\n const [cyclePreset, setCyclePreset] = React.useState<Pick<InventoryMutationPreset, 'warehouseId' | 'locationId'>>({})\n\n const distributionPageSize = 20\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 variantQuery = useQuery({\n queryKey: ['wms-sku-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.sku.errors.variant', 'Failed to load SKU.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const profileQuery = useQuery({\n queryKey: ['wms-sku-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.sku.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-sku-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.sku.errors.warehouses', 'Failed to load warehouses.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const balancesQuery = useQuery({\n queryKey: ['wms-sku-detail', 'balances', catalogVariantId, warehouseId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\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.sku.errors.balances', 'Failed to load stock distribution.'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const lotsQuery = useQuery({\n queryKey: ['wms-sku-detail', 'lots', catalogVariantId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\n page: '1',\n pageSize: '100',\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.sku.errors.lots', 'Failed to load lots.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const movementsQuery = useQuery({\n queryKey: ['wms-sku-detail', 'movements', catalogVariantId, warehouseId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\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.sku.errors.movements', 'Failed to load recent activity.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const lotById = React.useMemo(() => {\n const map = new Map<string, InventoryLotRow>()\n for (const lot of lotsQuery.data ?? []) {\n if (lot.id) map.set(lot.id, lot)\n }\n return map\n }, [lotsQuery.data])\n\n const reorderPoint = toNumber(profileQuery.data?.reorder_point)\n const safetyStock = toNumber(profileQuery.data?.safety_stock)\n const skuLabel =\n (variantQuery.data?.sku ?? '').trim() ||\n (variantQuery.data?.name ?? '').trim() ||\n catalogVariantId ||\n '\u2014'\n const variantName = (variantQuery.data?.name ?? '').trim()\n const pageTitle = (variantQuery.data?.sku ?? '').trim() || skuLabel\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 daysOfSupply = React.useMemo(() => {\n const movements = movementsQuery.data ?? []\n const windowStart = Date.now() - USAGE_WINDOW_DAYS * 24 * 60 * 60 * 1000\n let outbound = 0\n for (const row of movements) {\n const at = row.performed_at ?? row.received_at\n if (!at) continue\n const ts = new Date(at).getTime()\n if (Number.isNaN(ts) || ts < windowStart) continue\n if (row.type === 'pick' || row.type === 'pack' || row.type === 'ship') {\n outbound += Math.abs(toNumber(row.quantity))\n }\n }\n const daily = outbound / USAGE_WINDOW_DAYS\n if (daily <= 0 || totals.available <= 0) return null\n return Math.round(totals.available / daily)\n }, [movementsQuery.data, totals.available])\n\n const nowMs = React.useMemo(() => Date.now(), [])\n\n const filteredBalances = React.useMemo(() => {\n const items = balancesQuery.data?.items ?? []\n return items.filter((row) =>\n matchesDistributionFilter(row, row.lot_id ? lotById.get(row.lot_id) : undefined, distributionFilter, nowMs),\n )\n }, [balancesQuery.data?.items, distributionFilter, lotById, 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 (catalogVariantId) params.set('catalogVariantId', catalogVariantId)\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const query = params.toString()\n return query ? `/backend/wms/movements?${query}` : '/backend/wms/movements'\n }, [catalogVariantId, warehouseId])\n\n const inventoryConsoleHref = React.useMemo(() => {\n if (!catalogVariantId) return '/backend/wms/inventory'\n return `/backend/wms/inventory?catalogVariantId=${encodeURIComponent(catalogVariantId)}`\n }, [catalogVariantId])\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 const lotId =\n selectedBalances.length === 1 ? firstSelected?.lot_id?.trim() || undefined : undefined\n return {\n warehouseId: scopedWarehouseId,\n locationId,\n lotId,\n }\n }, [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.sku.distribution.columns.warehouse', 'Warehouse'),\n t('wms.backend.sku.distribution.columns.location', 'Location'),\n t('wms.backend.sku.distribution.columns.lot', 'Lot'),\n t('wms.backend.sku.distribution.columns.onHand', 'On hand'),\n t('wms.backend.sku.distribution.columns.reserved', 'Reserved'),\n t('wms.backend.sku.distribution.columns.status', 'Status'),\n ]\n const rows = filteredBalances.map((row) => {\n const lot = row.lot_id ? lotById.get(row.lot_id) : undefined\n const status = resolveBalanceStatus(row, lot, reorderPoint, nowMs)\n return [\n formatWarehouseLabel(row),\n formatLocationLabel(row.location_code, row.location_id),\n formatLotLabel(lot, locale),\n String(toNumber(row.quantity_on_hand)),\n String(toNumber(row.quantity_reserved)),\n t(status.labelKey, status.labelFallback),\n ]\n })\n const safeSku = pageTitle.replace(/[^\\w.-]+/g, '-').replace(/^-+|-+$/g, '') || 'sku'\n downloadCsvFile(`${safeSku}-distribution.csv`, [headers, ...rows])\n }, [filteredBalances, locale, lotById, nowMs, pageTitle, reorderPoint, t])\n\n const inventoryHref = '/backend/wms/inventory'\n\n const selectedWarehouse = warehousesQuery.data?.find((warehouse) => warehouse.id === warehouseId)\n const warehouseLabel =\n warehouseId === 'all'\n ? t('wms.backend.sku.filters.allWarehouses', 'All warehouses')\n : selectedWarehouse?.name || selectedWarehouse?.code || warehouseId\n\n const subtitleParts = [\n variantName || null,\n variantQuery.data?.is_active === false\n ? t('wms.backend.sku.header.inactive', 'Inactive')\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.sku.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.sku.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.sku.distribution.columns.warehouse', 'Warehouse'),\n cell: ({ row }) => formatWarehouseLabel(row.original),\n },\n {\n accessorKey: 'location_id',\n header: t('wms.backend.sku.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 accessorKey: 'lot_id',\n header: t('wms.backend.sku.distribution.columns.lot', 'Lot'),\n cell: ({ row }) => {\n const lotId = row.original.lot_id?.trim()\n const lot = lotId ? lotById.get(lotId) : undefined\n const label = formatLotLabel(lot, locale)\n if (!lotId || label === '\u2014') return label\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: 'quantity_on_hand',\n header: t('wms.backend.sku.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.sku.distribution.columns.reserved', 'Reserved'),\n cell: ({ row }) => String(toNumber(row.original.quantity_reserved)),\n },\n {\n id: 'status',\n header: t('wms.backend.sku.distribution.columns.status', 'Status'),\n cell: ({ row }) => {\n const lot = row.original.lot_id ? lotById.get(row.original.lot_id) : undefined\n const status = resolveBalanceStatus(row.original, lot, reorderPoint, nowMs)\n return (\n <StatusBadge variant={status.variant} dot>\n {t(status.labelKey, status.labelFallback)}\n </StatusBadge>\n )\n },\n },\n ],\n [\n locale,\n lotById,\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.sku.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.sku.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, skuLabel, 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.sku.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.sku.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, skuLabel, t],\n )\n\n const filterChips: Array<{ id: DistributionFilter; label: string }> = [\n { id: 'all', label: t('wms.backend.sku.distribution.filters.all', 'All bins') },\n { id: 'sellable', label: t('wms.backend.sku.distribution.filters.sellable', 'Sellable') },\n { id: 'picking', label: t('wms.backend.sku.distribution.filters.picking', 'Picking') },\n { id: 'nearExpiry', label: t('wms.backend.sku.distribution.filters.nearExpiry', 'Near expiry') },\n ]\n\n const isLoading =\n variantQuery.isLoading ||\n profileQuery.isLoading ||\n balancesQuery.isLoading ||\n lotsQuery.isLoading ||\n movementsQuery.isLoading\n\n const hasError =\n variantQuery.isError ||\n profileQuery.isError ||\n balancesQuery.isError ||\n lotsQuery.isError ||\n movementsQuery.isError\n\n if (!catalogVariantId) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={t('wms.backend.sku.errors.invalidId', 'Invalid SKU identifier.')}\n action={(\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => router.push(inventoryHref)}>\n {t('wms.backend.sku.actions.backToInventory', 'Back to inventory')}\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={inventoryHref}>\n <ArrowLeft className=\"size-4\" />\n {t('wms.backend.sku.actions.backToInventory', 'Back to inventory')}\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.sku.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()}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.sku.actions.adjust', 'Adjust stock')}\n </Button>\n ) : null}\n </>\n )}\n />\n\n {isLoading ? (\n <LoadingMessage label={t('wms.backend.sku.loading', 'Loading SKU view\u2026')} />\n ) : null}\n\n {hasError ? (\n <ErrorMessage label={t('wms.backend.sku.errors.load', 'Failed to load SKU view.')} />\n ) : null}\n\n {!isLoading && !hasError && variantQuery.data ? (\n <>\n <section className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.onHand.title', 'On hand')}\n caption={t('wms.backend.sku.kpis.onHand.caption', 'Total physical quantity')}\n value={String(totals.onHand)}\n badgeLabel={\n reorderPoint > 0 && totals.onHand <= reorderPoint\n ? t('wms.backend.sku.kpis.onHand.badgeLow', 'Below reorder')\n : null\n }\n badgeVariant=\"warning\"\n ctaLabel={t('wms.backend.sku.kpis.onHand.cta', 'View distribution')}\n ctaHref=\"#stock-distribution\"\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.reserved.title', 'Reserved')}\n caption={t('wms.backend.sku.kpis.reserved.caption', 'Committed to orders')}\n value={String(totals.reserved)}\n badgeLabel={\n totals.reserved > 0\n ? t('wms.backend.sku.kpis.reserved.badge', '{count} units held', { count: totals.reserved })\n : null\n }\n badgeVariant=\"info\"\n ctaLabel={t('wms.backend.sku.kpis.reserved.cta', 'View reservations')}\n ctaHref=\"/backend/wms/reservations\"\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.available.title', 'Available')}\n caption={t('wms.backend.sku.kpis.available.caption', 'Ready to allocate')}\n value={String(totals.available)}\n badgeLabel={\n totals.available > 0\n ? t('wms.backend.sku.kpis.available.badge', 'Sellable')\n : t('wms.backend.sku.kpis.available.badgeEmpty', 'None')\n }\n badgeVariant={totals.available > 0 ? 'success' : 'neutral'}\n ctaLabel={t('wms.backend.sku.kpis.available.cta', 'Adjust stock')}\n ctaHref={access.canAdjust ? '#stock-distribution' : inventoryHref}\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.reorderPoint.title', 'Reorder point')}\n caption={t('wms.backend.sku.kpis.reorderPoint.caption', 'From inventory profile')}\n value={String(reorderPoint)}\n badgeLabel={\n safetyStock > 0\n ? t('wms.backend.sku.kpis.reorderPoint.badgeSafety', 'Safety {count}', { count: safetyStock })\n : null\n }\n badgeVariant={totals.available <= reorderPoint && reorderPoint > 0 ? 'error' : 'neutral'}\n ctaLabel={t('wms.backend.sku.kpis.reorderPoint.cta', 'Edit profile')}\n ctaHref=\"/backend/config/wms\"\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.daysOfSupply.title', 'Days of supply')}\n caption={t('wms.backend.sku.kpis.daysOfSupply.caption', 'Based on last {days} days outbound', {\n days: USAGE_WINDOW_DAYS,\n })}\n value={daysOfSupply === null ? '\u2014' : String(daysOfSupply)}\n badgeLabel={\n daysOfSupply !== null && daysOfSupply <= 7\n ? t('wms.backend.sku.kpis.daysOfSupply.badgeShort', 'Short runway')\n : null\n }\n badgeVariant=\"warning\"\n ctaLabel={t('wms.backend.sku.kpis.daysOfSupply.cta', 'View movements')}\n ctaHref={movementsHref}\n />\n </section>\n\n <section\n id=\"stock-distribution\"\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.sku.distribution.title', 'Stock distribution')}\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-2\">\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\" onClick={handleExportDistributionCsv}>\n <Download className=\"size-4\" />\n {t('wms.backend.sku.distribution.actions.exportCsv', 'Export CSV')}\n </Button>\n {access.canCycleCount ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => openCycleCountDialog(resolveMutationContext())}\n >\n {t('wms.backend.sku.distribution.actions.cycleCountZone', 'Cycle count zone')}\n </Button>\n ) : null}\n {access.canAdjust && selectedBalances.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"default\"\n onClick={() => openAdjustDialog(resolveMutationContext())}\n >\n {t('wms.backend.sku.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.skuDistributionTable.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.sku.distribution.empty.title', 'No locations in this view')}\n description={t(\n 'wms.backend.sku.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.sku.distribution.footer.showing', 'Showing {shown} of {total} locations', {\n shown: pagedBalances.length,\n total: distributionTotal,\n })}\n </span>\n {balancesQuery.data && balancesQuery.data.total > distributionTotal ? (\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"h-auto px-0\">\n <Link href={inventoryHref}>\n {t('wms.backend.sku.distribution.footer.viewAll', 'View all locations \u2192')}\n </Link>\n </LinkButton>\n ) : null}\n </div>\n ) : null}\n </section>\n\n <DataTable<InventoryMovementRow>\n title={t('wms.backend.sku.activity.title', 'Recent activity')}\n columns={activityColumns}\n data={movementsQuery.data ?? []}\n disableRowClick\n entityId={E.wms.inventory_movement}\n perspective={{ tableId: extensionPoints.hosts.skuActivityTable.tableId }}\n emptyState={t('wms.backend.sku.activity.empty', 'No recent movements for this SKU.')}\n actions={(\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href={movementsHref}>\n {t('wms.backend.sku.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.sku.quickActions.title', 'Quick actions')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'wms.backend.sku.quickActions.description',\n 'Run common inventory actions without leaving this SKU view',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {access.canReceive ? (\n <Button type=\"button\" variant=\"default\" onClick={() => setReceiveOpen(true)}>\n <ArrowDown className=\"size-4\" />\n {t('wms.backend.sku.quickActions.receive', 'Receive stock')}\n </Button>\n ) : null}\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => openAdjustDialog()}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.sku.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.sku.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.sku.quickActions.cycleCount', 'Cycle count')}\n </Button>\n ) : null}\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={movementsHref}>\n {t('wms.backend.sku.quickActions.openLedger', 'Open ledger')}\n <ExternalLink className=\"size-4\" />\n </Link>\n </Button>\n </div>\n </section>\n </>\n ) : null}\n\n {!isLoading && !hasError && !variantQuery.data ? (\n <RecordNotFoundState\n label={t('wms.backend.sku.errors.notFound', 'SKU not found.')}\n backHref={inventoryHref}\n backLabel={t('wms.backend.sku.actions.backToInventory', 'Back to inventory')}\n />\n ) : null}\n </PageBody>\n\n {access.canReceive && catalogVariantId ? (\n <ReceiveInventoryDialog\n open={receiveOpen}\n onOpenChange={setReceiveOpen}\n access={access}\n initialCatalogVariantId={catalogVariantId}\n />\n ) : null}\n {access.canAdjust && catalogVariantId ? (\n <AdjustInventoryDialog\n open={adjustOpen}\n onOpenChange={setAdjustOpen}\n access={access}\n initialCatalogVariantId={catalogVariantId}\n initialWarehouseId={adjustPreset.warehouseId}\n initialLocationId={adjustPreset.locationId}\n initialLotId={adjustPreset.lotId}\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}\n initialLocationId={cyclePreset.locationId}\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 ArrowDown,\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 { CycleCountWizardDialog } from './CycleCountWizardDialog'\nimport { ReceiveInventoryDialog } from './ReceiveInventoryDialog'\nimport { useWmsInventoryMutationAccess } from './useWmsInventoryMutationAccess'\n\nconst variantIdSchema = 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 is_active?: boolean | null\n created_at?: string | null\n}\n\ntype InventoryProfileRow = {\n reorder_point?: string | number | null\n safety_stock?: 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 lot_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 InventoryLotRow = {\n id: string\n lot_number?: string | null\n expires_at?: string | null\n status?: string | 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 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 lotId?: string\n}\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}\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 USAGE_WINDOW_DAYS = 14\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 formatLotLabel(\n lot: InventoryLotRow | undefined,\n locale: string,\n): string {\n if (!lot) return '\u2014'\n const number = (lot.lot_number ?? '').trim() || lot.id\n if (!lot.expires_at) return number\n const expires = new Date(lot.expires_at)\n if (Number.isNaN(expires.getTime())) return number\n const expLabel = new Intl.DateTimeFormat(locale, { month: '2-digit', year: '2-digit' }).format(expires)\n return `${number} \u00B7 exp ${expLabel}`\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 resolveBalanceStatus(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | 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.sku.distribution.status.expired',\n labelFallback: 'Expired',\n }\n }\n if (isNearExpiry(lot?.expires_at, nowMs)) {\n return {\n variant: 'warning',\n labelKey: 'wms.backend.sku.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.sku.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.sku.distribution.status.reserved',\n labelFallback: 'Reserved',\n }\n }\n return {\n variant: 'success',\n labelKey: 'wms.backend.sku.distribution.status.available',\n labelFallback: 'Available',\n }\n}\n\nfunction matchesDistributionFilter(\n row: InventoryBalanceRow,\n lot: InventoryLotRow | 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)\n case 'picking':\n return (\n PICKING_LOCATION_TYPES.has(locationType) ||\n locationCode.includes('pick') ||\n locationCode.includes('staging')\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.sku.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 skuLabel: 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.dashboard.activity.titles.received', 'Received {quantity}\u00D7 {sku}', {\n quantity,\n sku: skuLabel,\n })\n case 'adjust':\n return t('wms.backend.dashboard.activity.titles.adjusted', 'Adjusted {quantity}\u00D7 {sku}', {\n quantity: `${signedQuantity >= 0 ? '+' : ''}${signedQuantity}`,\n sku: skuLabel,\n })\n case 'transfer':\n return t('wms.backend.dashboard.activity.titles.moved', 'Moved {quantity}\u00D7 {sku}', {\n quantity,\n sku: skuLabel,\n })\n case 'pick':\n case 'pack':\n return t('wms.backend.dashboard.activity.titles.allocated', 'Allocated {quantity}\u00D7 {sku}', {\n quantity,\n sku: skuLabel,\n })\n case 'cycle_count':\n return t('wms.backend.dashboard.activity.titles.reconciled', 'Inventory reconciled \u2014 {sku}', {\n sku: skuLabel,\n })\n default:\n return t('wms.backend.dashboard.activity.titles.generic', '{type} {quantity}\u00D7 {sku}', {\n type: row.type ?? 'movement',\n quantity,\n sku: skuLabel,\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\ntype SkuKpiCardProps = {\n title: string\n caption: string\n value: string\n badgeLabel: string | null\n badgeVariant: StatusBadgeVariant\n ctaLabel: string\n ctaHref: string\n}\n\nfunction SkuKpiCard({\n title,\n caption,\n value,\n badgeLabel,\n badgeVariant,\n ctaLabel,\n ctaHref,\n}: SkuKpiCardProps) {\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 <LinkButton asChild variant=\"primary\" size=\"sm\" className=\"mt-auto pt-4 w-fit\">\n <Link href={ctaHref}>{ctaLabel}</Link>\n </LinkButton>\n </section>\n )\n}\n\ntype WmsSkuDetailPageProps = {\n variantId: string\n}\n\nexport default function WmsSkuDetailPage({ variantId }: WmsSkuDetailPageProps) {\n const t = useT()\n const locale = useLocale()\n const router = useRouter()\n const access = useWmsInventoryMutationAccess()\n const parsedVariantId = variantIdSchema.safeParse(variantId.trim())\n const catalogVariantId = parsedVariantId.success ? parsedVariantId.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 [receiveOpen, setReceiveOpen] = React.useState(false)\n const [cycleOpen, setCycleOpen] = React.useState(false)\n const [cyclePreset, setCyclePreset] = React.useState<Pick<InventoryMutationPreset, 'warehouseId' | 'locationId'>>({})\n\n const distributionPageSize = 20\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 variantQuery = useQuery({\n queryKey: ['wms-sku-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.sku.errors.variant', 'Failed to load SKU.'))\n }\n return call.result?.items?.[0] ?? null\n },\n })\n\n const profileQuery = useQuery({\n queryKey: ['wms-sku-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.sku.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-sku-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.sku.errors.warehouses', 'Failed to load warehouses.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const balancesQuery = useQuery({\n queryKey: ['wms-sku-detail', 'balances', catalogVariantId, warehouseId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\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.sku.errors.balances', 'Failed to load stock distribution.'))\n }\n return call.result ?? { items: [], total: 0, totalPages: 1 }\n },\n })\n\n const lotsQuery = useQuery({\n queryKey: ['wms-sku-detail', 'lots', catalogVariantId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\n page: '1',\n pageSize: '100',\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.sku.errors.lots', 'Failed to load lots.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const movementsQuery = useQuery({\n queryKey: ['wms-sku-detail', 'movements', catalogVariantId, warehouseId],\n enabled: Boolean(catalogVariantId),\n queryFn: async () => {\n const params = new URLSearchParams({\n catalogVariantId: catalogVariantId!,\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.sku.errors.movements', 'Failed to load recent activity.'))\n }\n return call.result?.items ?? []\n },\n })\n\n const lotById = React.useMemo(() => {\n const map = new Map<string, InventoryLotRow>()\n for (const lot of lotsQuery.data ?? []) {\n if (lot.id) map.set(lot.id, lot)\n }\n return map\n }, [lotsQuery.data])\n\n const reorderPoint = toNumber(profileQuery.data?.reorder_point)\n const safetyStock = toNumber(profileQuery.data?.safety_stock)\n const skuLabel =\n (variantQuery.data?.sku ?? '').trim() ||\n (variantQuery.data?.name ?? '').trim() ||\n catalogVariantId ||\n '\u2014'\n const variantName = (variantQuery.data?.name ?? '').trim()\n const pageTitle = (variantQuery.data?.sku ?? '').trim() || skuLabel\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 daysOfSupply = React.useMemo(() => {\n const movements = movementsQuery.data ?? []\n const windowStart = Date.now() - USAGE_WINDOW_DAYS * 24 * 60 * 60 * 1000\n let outbound = 0\n for (const row of movements) {\n const at = row.performed_at ?? row.received_at\n if (!at) continue\n const ts = new Date(at).getTime()\n if (Number.isNaN(ts) || ts < windowStart) continue\n if (row.type === 'pick' || row.type === 'pack' || row.type === 'ship') {\n outbound += Math.abs(toNumber(row.quantity))\n }\n }\n const daily = outbound / USAGE_WINDOW_DAYS\n if (daily <= 0 || totals.available <= 0) return null\n return Math.round(totals.available / daily)\n }, [movementsQuery.data, totals.available])\n\n const nowMs = React.useMemo(() => Date.now(), [])\n\n const filteredBalances = React.useMemo(() => {\n const items = balancesQuery.data?.items ?? []\n return items.filter((row) =>\n matchesDistributionFilter(row, row.lot_id ? lotById.get(row.lot_id) : undefined, distributionFilter, nowMs),\n )\n }, [balancesQuery.data?.items, distributionFilter, lotById, 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 (catalogVariantId) params.set('catalogVariantId', catalogVariantId)\n if (warehouseId !== 'all') params.set('warehouseId', warehouseId)\n const query = params.toString()\n return query ? `/backend/wms/movements?${query}` : '/backend/wms/movements'\n }, [catalogVariantId, warehouseId])\n\n const inventoryConsoleHref = React.useMemo(() => {\n if (!catalogVariantId) return '/backend/wms/inventory'\n return `/backend/wms/inventory?catalogVariantId=${encodeURIComponent(catalogVariantId)}`\n }, [catalogVariantId])\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 const lotId =\n selectedBalances.length === 1 ? firstSelected?.lot_id?.trim() || undefined : undefined\n return {\n warehouseId: scopedWarehouseId,\n locationId,\n lotId,\n }\n }, [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.sku.distribution.columns.warehouse', 'Warehouse'),\n t('wms.backend.sku.distribution.columns.location', 'Location'),\n t('wms.backend.sku.distribution.columns.lot', 'Lot'),\n t('wms.backend.sku.distribution.columns.onHand', 'On hand'),\n t('wms.backend.sku.distribution.columns.reserved', 'Reserved'),\n t('wms.backend.sku.distribution.columns.status', 'Status'),\n ]\n const rows = filteredBalances.map((row) => {\n const lot = row.lot_id ? lotById.get(row.lot_id) : undefined\n const status = resolveBalanceStatus(row, lot, reorderPoint, nowMs)\n return [\n formatWarehouseLabel(row),\n formatLocationLabel(row.location_code, row.location_id),\n formatLotLabel(lot, locale),\n String(toNumber(row.quantity_on_hand)),\n String(toNumber(row.quantity_reserved)),\n t(status.labelKey, status.labelFallback),\n ]\n })\n const safeSku = pageTitle.replace(/[^\\w.-]+/g, '-').replace(/^-+|-+$/g, '') || 'sku'\n downloadCsvFile(`${safeSku}-distribution.csv`, [headers, ...rows])\n }, [filteredBalances, locale, lotById, nowMs, pageTitle, reorderPoint, t])\n\n const inventoryHref = '/backend/wms/inventory'\n\n const selectedWarehouse = warehousesQuery.data?.find((warehouse) => warehouse.id === warehouseId)\n const warehouseLabel =\n warehouseId === 'all'\n ? t('wms.backend.sku.filters.allWarehouses', 'All warehouses')\n : selectedWarehouse?.name || selectedWarehouse?.code || warehouseId\n\n const subtitleParts = [\n variantName || null,\n variantQuery.data?.is_active === false\n ? t('wms.backend.sku.header.inactive', 'Inactive')\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.sku.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.sku.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.sku.distribution.columns.warehouse', 'Warehouse'),\n cell: ({ row }) => formatWarehouseLabel(row.original),\n },\n {\n accessorKey: 'location_id',\n header: t('wms.backend.sku.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 accessorKey: 'lot_id',\n header: t('wms.backend.sku.distribution.columns.lot', 'Lot'),\n cell: ({ row }) => {\n const lotId = row.original.lot_id?.trim()\n const lot = lotId ? lotById.get(lotId) : undefined\n const label = formatLotLabel(lot, locale)\n if (!lotId || label === '\u2014') return label\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: 'quantity_on_hand',\n header: t('wms.backend.sku.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.sku.distribution.columns.reserved', 'Reserved'),\n cell: ({ row }) => String(toNumber(row.original.quantity_reserved)),\n },\n {\n id: 'status',\n header: t('wms.backend.sku.distribution.columns.status', 'Status'),\n cell: ({ row }) => {\n const lot = row.original.lot_id ? lotById.get(row.original.lot_id) : undefined\n const status = resolveBalanceStatus(row.original, lot, reorderPoint, nowMs)\n return (\n <StatusBadge variant={status.variant} dot>\n {t(status.labelKey, status.labelFallback)}\n </StatusBadge>\n )\n },\n },\n ],\n [\n locale,\n lotById,\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.sku.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.sku.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, skuLabel, 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.sku.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.sku.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, skuLabel, t],\n )\n\n const filterChips: Array<{ id: DistributionFilter; label: string }> = [\n { id: 'all', label: t('wms.backend.sku.distribution.filters.all', 'All bins') },\n { id: 'sellable', label: t('wms.backend.sku.distribution.filters.sellable', 'Sellable') },\n { id: 'picking', label: t('wms.backend.sku.distribution.filters.picking', 'Picking') },\n { id: 'nearExpiry', label: t('wms.backend.sku.distribution.filters.nearExpiry', 'Near expiry') },\n ]\n\n const isLoading =\n variantQuery.isLoading ||\n profileQuery.isLoading ||\n balancesQuery.isLoading ||\n lotsQuery.isLoading ||\n movementsQuery.isLoading\n\n const hasError =\n variantQuery.isError ||\n profileQuery.isError ||\n balancesQuery.isError ||\n lotsQuery.isError ||\n movementsQuery.isError\n\n if (!catalogVariantId) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={t('wms.backend.sku.errors.invalidId', 'Invalid SKU identifier.')}\n action={(\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => router.push(inventoryHref)}>\n {t('wms.backend.sku.actions.backToInventory', 'Back to inventory')}\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={inventoryHref}>\n <ArrowLeft className=\"size-4\" />\n {t('wms.backend.sku.actions.backToInventory', 'Back to inventory')}\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.sku.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()}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.sku.actions.adjust', 'Adjust stock')}\n </Button>\n ) : null}\n </>\n )}\n />\n\n {isLoading ? (\n <LoadingMessage label={t('wms.backend.sku.loading', 'Loading SKU view\u2026')} />\n ) : null}\n\n {hasError ? (\n <ErrorMessage label={t('wms.backend.sku.errors.load', 'Failed to load SKU view.')} />\n ) : null}\n\n {!isLoading && !hasError && variantQuery.data ? (\n <>\n <section className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.onHand.title', 'On hand')}\n caption={t('wms.backend.sku.kpis.onHand.caption', 'Total physical quantity')}\n value={String(totals.onHand)}\n badgeLabel={\n reorderPoint > 0 && totals.onHand <= reorderPoint\n ? t('wms.backend.sku.kpis.onHand.badgeLow', 'Below reorder')\n : null\n }\n badgeVariant=\"warning\"\n ctaLabel={t('wms.backend.sku.kpis.onHand.cta', 'View distribution')}\n ctaHref=\"#stock-distribution\"\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.reserved.title', 'Reserved')}\n caption={t('wms.backend.sku.kpis.reserved.caption', 'Committed to orders')}\n value={String(totals.reserved)}\n badgeLabel={\n totals.reserved > 0\n ? t('wms.backend.sku.kpis.reserved.badge', '{count} units held', { count: totals.reserved })\n : null\n }\n badgeVariant=\"info\"\n ctaLabel={t('wms.backend.sku.kpis.reserved.cta', 'View reservations')}\n ctaHref=\"/backend/wms/reservations\"\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.available.title', 'Available')}\n caption={t('wms.backend.sku.kpis.available.caption', 'Ready to allocate')}\n value={String(totals.available)}\n badgeLabel={\n totals.available > 0\n ? t('wms.backend.sku.kpis.available.badge', 'Sellable')\n : t('wms.backend.sku.kpis.available.badgeEmpty', 'None')\n }\n badgeVariant={totals.available > 0 ? 'success' : 'neutral'}\n ctaLabel={t('wms.backend.sku.kpis.available.cta', 'Adjust stock')}\n ctaHref={access.canAdjust ? '#stock-distribution' : inventoryHref}\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.reorderPoint.title', 'Reorder point')}\n caption={t('wms.backend.sku.kpis.reorderPoint.caption', 'From inventory profile')}\n value={String(reorderPoint)}\n badgeLabel={\n safetyStock > 0\n ? t('wms.backend.sku.kpis.reorderPoint.badgeSafety', 'Safety {count}', { count: safetyStock })\n : null\n }\n badgeVariant={totals.available <= reorderPoint && reorderPoint > 0 ? 'error' : 'neutral'}\n ctaLabel={t('wms.backend.sku.kpis.reorderPoint.cta', 'Edit profile')}\n ctaHref=\"/backend/config/wms\"\n />\n <SkuKpiCard\n title={t('wms.backend.sku.kpis.daysOfSupply.title', 'Days of supply')}\n caption={t('wms.backend.sku.kpis.daysOfSupply.caption', 'Based on last {days} days outbound', {\n days: USAGE_WINDOW_DAYS,\n })}\n value={daysOfSupply === null ? '\u2014' : String(daysOfSupply)}\n badgeLabel={\n daysOfSupply !== null && daysOfSupply <= 7\n ? t('wms.backend.sku.kpis.daysOfSupply.badgeShort', 'Short runway')\n : null\n }\n badgeVariant=\"warning\"\n ctaLabel={t('wms.backend.sku.kpis.daysOfSupply.cta', 'View movements')}\n ctaHref={movementsHref}\n />\n </section>\n\n <section\n id=\"stock-distribution\"\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.sku.distribution.title', 'Stock distribution')}\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-2\">\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\" onClick={handleExportDistributionCsv}>\n <Download className=\"size-4\" />\n {t('wms.backend.sku.distribution.actions.exportCsv', 'Export CSV')}\n </Button>\n {access.canCycleCount ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => openCycleCountDialog(resolveMutationContext())}\n >\n {t('wms.backend.sku.distribution.actions.cycleCountZone', 'Cycle count zone')}\n </Button>\n ) : null}\n {access.canAdjust && selectedBalances.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"default\"\n onClick={() => openAdjustDialog(resolveMutationContext())}\n >\n {t('wms.backend.sku.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.skuDistributionTable.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.sku.distribution.empty.title', 'No locations in this view')}\n description={t(\n 'wms.backend.sku.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.sku.distribution.footer.showing', 'Showing {shown} of {total} locations', {\n shown: pagedBalances.length,\n total: distributionTotal,\n })}\n </span>\n {balancesQuery.data && balancesQuery.data.total > distributionTotal ? (\n <LinkButton asChild variant=\"gray\" size=\"sm\" className=\"h-auto px-0\">\n <Link href={inventoryHref}>\n {t('wms.backend.sku.distribution.footer.viewAll', 'View all locations \u2192')}\n </Link>\n </LinkButton>\n ) : null}\n </div>\n ) : null}\n </section>\n\n <DataTable<InventoryMovementRow>\n title={t('wms.backend.sku.activity.title', 'Recent activity')}\n columns={activityColumns}\n data={movementsQuery.data ?? []}\n disableRowClick\n entityId={E.wms.inventory_movement}\n perspective={{ tableId: extensionPoints.hosts.skuActivityTable.tableId }}\n emptyState={t('wms.backend.sku.activity.empty', 'No recent movements for this SKU.')}\n actions={(\n <Button asChild type=\"button\" variant=\"ghost\" size=\"sm\">\n <Link href={movementsHref}>\n {t('wms.backend.sku.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.sku.quickActions.title', 'Quick actions')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\n 'wms.backend.sku.quickActions.description',\n 'Run common inventory actions without leaving this SKU view',\n )}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {access.canReceive ? (\n <Button type=\"button\" variant=\"default\" onClick={() => setReceiveOpen(true)}>\n <ArrowDown className=\"size-4\" />\n {t('wms.backend.sku.quickActions.receive', 'Receive stock')}\n </Button>\n ) : null}\n {access.canAdjust ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => openAdjustDialog()}>\n <SlidersHorizontal className=\"size-4\" />\n {t('wms.backend.sku.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.sku.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.sku.quickActions.cycleCount', 'Cycle count')}\n </Button>\n ) : null}\n <Button asChild type=\"button\" variant=\"outline\">\n <Link href={movementsHref}>\n {t('wms.backend.sku.quickActions.openLedger', 'Open ledger')}\n <ExternalLink className=\"size-4\" />\n </Link>\n </Button>\n </div>\n </section>\n </>\n ) : null}\n\n {!isLoading && !hasError && !variantQuery.data ? (\n <RecordNotFoundState\n label={t('wms.backend.sku.errors.notFound', 'SKU not found.')}\n backHref={inventoryHref}\n backLabel={t('wms.backend.sku.actions.backToInventory', 'Back to inventory')}\n />\n ) : null}\n </PageBody>\n\n {access.canReceive && catalogVariantId ? (\n <ReceiveInventoryDialog\n open={receiveOpen}\n onOpenChange={setReceiveOpen}\n access={access}\n initialCatalogVariantId={catalogVariantId}\n />\n ) : null}\n {access.canAdjust && catalogVariantId ? (\n <AdjustInventoryDialog\n open={adjustOpen}\n onOpenChange={setAdjustOpen}\n access={access}\n initialCatalogVariantId={catalogVariantId}\n initialWarehouseId={adjustPreset.warehouseId}\n initialLocationId={adjustPreset.locationId}\n initialLotId={adjustPreset.lotId}\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}\n initialLocationId={cyclePreset.locationId}\n />\n ) : null}\n </Page>\n )\n}\n"],
5
5
  "mappings": ";AAgYM,SAuiBM,UAviBN,KAEA,YAFA;AA9XN,YAAY,WAAW;AACvB,SAAS,uBAAuB;AAChC,OAAO,UAAU;AACjB,SAAS,iBAAiB;AAC1B,SAAS,SAAS;AAClB,SAAS,gBAAgB;AAEzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,MAAM,UAAU,kBAAkB;AAC3C,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,cAAc,gBAAgB,2BAA2B;AAClE,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,WAAW,YAAY;AAChC,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAA4C;AACrD,SAAS,gBAAgB;AACzB,SAAS,UAAU;AACnB,SAAS,SAAS;AAClB;AAAA,EACE;AAAA,OAEK;AACP,SAAS,6BAA6B;AACtC,SAAS,8BAA8B;AACvC,SAAS,8BAA8B;AACvC,SAAS,qCAAqC;AAE9C,MAAM,kBAAkB,EAAE,OAAO,EAAE,KAAK;AAsExC,SAAS,cAAc,OAAgC;AACrD,QAAM,MAAM,OAAO,SAAS,EAAE;AAC9B,MAAI,SAAS,KAAK,GAAG,EAAG,QAAO,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC;AAC1D,SAAO;AACT;AAEA,SAAS,gBAAgB,UAAkB,MAAkB;AAC3D,QAAM,MAAM,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,aAAa,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI;AACzE,QAAM,OAAO,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAChE,QAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,OAAO;AACd,SAAO,WAAW;AAClB,SAAO,MAAM;AACb,MAAI,gBAAgB,GAAG;AACzB;AASA,MAAM,8BAA8B,oBAAI,IAAI,CAAC,WAAW,MAAM,CAAC;AAC/D,MAAM,yBAAyB,oBAAI,IAAI,CAAC,WAAW,OAAO,MAAM,CAAC;AACjE,MAAM,iBAAiB,KAAK,KAAK,KAAK,KAAK;AAC3C,MAAM,oBAAoB;AAE1B,SAAS,SAAS,OAAmD;AACnE,QAAM,SAAS,OAAO,SAAS,CAAC;AAChC,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAEA,SAAS,qBAAqB,KAInB;AACT,QAAM,QAAQ,IAAI,kBAAkB,IAAI,KAAK;AAC7C,QAAM,QAAQ,IAAI,kBAAkB,IAAI,KAAK;AAC7C,MAAI,QAAQ,KAAM,QAAO,GAAG,IAAI,SAAM,IAAI;AAC1C,SAAO,QAAQ,QAAQ,IAAI,gBAAgB;AAC7C;AAEA,SAAS,oBAAoB,MAAiC,IAAuC;AACnG,QAAM,WAAW,QAAQ,IAAI,KAAK;AAClC,MAAI,QAAS,QAAO;AACpB,SAAO,MAAM;AACf;AAEA,SAAS,eACP,KACA,QACQ;AACR,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,UAAU,IAAI,cAAc,IAAI,KAAK,KAAK,IAAI;AACpD,MAAI,CAAC,IAAI,WAAY,QAAO;AAC5B,QAAM,UAAU,IAAI,KAAK,IAAI,UAAU;AACvC,MAAI,OAAO,MAAM,QAAQ,QAAQ,CAAC,EAAG,QAAO;AAC5C,QAAM,WAAW,IAAI,KAAK,eAAe,QAAQ,EAAE,OAAO,WAAW,MAAM,UAAU,CAAC,EAAE,OAAO,OAAO;AACtG,SAAO,GAAG,MAAM,aAAU,QAAQ;AACpC;AAEA,SAAS,aAAa,WAAsC,OAAwB;AAClF,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,UAAU,IAAI,KAAK,SAAS,EAAE,QAAQ;AAC5C,MAAI,OAAO,MAAM,OAAO,EAAG,QAAO;AAClC,SAAO,UAAU,SAAS,UAAU,SAAS;AAC/C;AAEA,SAAS,UAAU,WAAsC,OAAwB;AAC/E,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,UAAU,IAAI,KAAK,SAAS,EAAE,QAAQ;AAC5C,SAAO,CAAC,OAAO,MAAM,OAAO,KAAK,WAAW;AAC9C;AAEA,SAAS,qBACP,KACA,KACA,cACA,OAC0E;AAC1E,MAAI,KAAK,WAAW,aAAa,UAAU,KAAK,YAAY,KAAK,GAAG;AAClE,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,eAAe;AAAA,IACjB;AAAA,EACF;AACA,MAAI,aAAa,KAAK,YAAY,KAAK,GAAG;AACxC,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,eAAe;AAAA,IACjB;AAAA,EACF;AACA,QAAM,YAAY,IAAI,sBAAsB;AAC5C,MAAI,eAAe,KAAK,aAAa,cAAc;AACjD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,eAAe;AAAA,IACjB;AAAA,EACF;AACA,QAAM,WAAW,SAAS,IAAI,iBAAiB;AAC/C,QAAM,SAAS,SAAS,IAAI,gBAAgB;AAC5C,MAAI,WAAW,KAAK,SAAS,KAAK,YAAY,QAAQ;AACpD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,UAAU;AAAA,MACV,eAAe;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,eAAe;AAAA,EACjB;AACF;AAEA,SAAS,0BACP,KACA,KACA,QACA,OACS;AACT,QAAM,gBAAgB,IAAI,iBAAiB,IAAI,KAAK,EAAE,YAAY;AAClE,QAAM,gBAAgB,IAAI,iBAAiB,IAAI,KAAK,EAAE,YAAY;AAClE,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,CAAC,4BAA4B,IAAI,YAAY;AAAA,IACtD,KAAK;AACH,aACE,uBAAuB,IAAI,YAAY,KACvC,aAAa,SAAS,MAAM,KAC5B,aAAa,SAAS,SAAS;AAAA,IAEnC,KAAK;AACH,aAAO,aAAa,KAAK,YAAY,KAAK,KAAK,KAAK,WAAW;AAAA,IACjE;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,kBAAkB,MAAc,GAAoC;AAC3E,QAAM,MAAM,kCAAkC,IAAI;AAClD,QAAM,YAAoC;AAAA,IACxC,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,IACb,SAAS;AAAA,IACT,MAAM;AAAA,EACR;AACA,SAAO,EAAE,KAAK,UAAU,IAAI,KAAK,IAAI;AACvC;AAEA,MAAM,oBAAwD;AAAA,EAC5D,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AAAA,EACb,SAAS;AAAA,EACT,MAAM;AACR;AAEA,SAAS,oBACP,KACA,UACA,GACQ;AACR,QAAM,WAAW,KAAK,IAAI,SAAS,IAAI,QAAQ,CAAC;AAChD,QAAM,iBAAiB,SAAS,IAAI,QAAQ;AAC5C,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,kDAAkD,iCAA8B;AAAA,QACvF;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,IACH,KAAK;AACH,aAAO,EAAE,kDAAkD,iCAA8B;AAAA,QACvF,UAAU,GAAG,kBAAkB,IAAI,MAAM,EAAE,GAAG,cAAc;AAAA,QAC5D,KAAK;AAAA,MACP,CAAC;AAAA,IACH,KAAK;AACH,aAAO,EAAE,+CAA+C,8BAA2B;AAAA,QACjF;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,IACH,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,mDAAmD,kCAA+B;AAAA,QACzF;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,IACH,KAAK;AACH,aAAO,EAAE,oDAAoD,qCAAgC;AAAA,QAC3F,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AACE,aAAO,EAAE,iDAAiD,+BAA4B;AAAA,QACpF,MAAM,IAAI,QAAQ;AAAA,QAClB;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,EACL;AACF;AAEA,SAAS,uBACP,KACA,GACe;AACf,QAAM,cAAc;AAAA,IAClB;AAAA,MACE,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI;AAAA,MACZ,cAAc,IAAI;AAAA,IACpB;AAAA,IACA;AAAA,EACF;AACA,MAAI,YAAa,QAAO;AACxB,MAAI,IAAI,kBAAkB,IAAI,aAAc,QAAO,GAAG,IAAI,cAAc,SAAM,IAAI,YAAY;AAC9F,SAAO;AACT;AAEA,SAAS,uBAAuB,KAAmC;AACjE,QAAM,YAAY,qBAAqB,GAAG;AAC1C,QAAM,OAAO,oBAAoB,IAAI,oBAAoB,IAAI,gBAAgB;AAC7E,QAAM,KAAK,oBAAoB,IAAI,kBAAkB,IAAI,cAAc;AACvE,MAAI,IAAI,SAAS,cAAc,SAAS,YAAO,OAAO,SAAK,QAAO,GAAG,IAAI,WAAM,EAAE;AACjF,QAAM,WAAW,OAAO,WAAM,KAAK;AACnC,MAAI,aAAa,SAAK,QAAO,GAAG,SAAS,SAAM,QAAQ;AACvD,SAAO;AACT;AAYA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,SACE,qBAAC,aAAQ,WAAU,uFACjB;AAAA,wBAAC,OAAE,WAAU,uBAAuB,iBAAM;AAAA,IAC1C,oBAAC,OAAE,WAAU,sCAAsC,mBAAQ;AAAA,IAC3D,qBAAC,SAAI,WAAU,6BACb;AAAA,0BAAC,OAAE,WAAU,yCAAyC,iBAAM;AAAA,MAC3D,aACC,oBAAC,eAAY,SAAS,cAAc,KAAG,MACpC,sBACH,IACE;AAAA,OACN;AAAA,IACA,oBAAC,cAAW,SAAO,MAAC,SAAQ,WAAU,MAAK,MAAK,WAAU,sBACxD,8BAAC,QAAK,MAAM,SAAU,oBAAS,GACjC;AAAA,KACF;AAEJ;AAMe,SAAR,iBAAkC,EAAE,UAAU,GAA0B;AAC7E,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,8BAA8B;AAC7C,QAAM,kBAAkB,gBAAgB,UAAU,UAAU,KAAK,CAAC;AAClE,QAAM,mBAAmB,gBAAgB,UAAU,gBAAgB,OAAO;AAE1E,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAiB,KAAK;AAClE,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAA6B,KAAK;AAC5F,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,MAAM,SAAS,CAAC;AAChE,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAsB,MAAM,oBAAI,IAAI,CAAC;AAC/F,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAkC,CAAC,CAAC;AAClF,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,KAAK;AAC1D,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,KAAK;AACtD,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAsE,CAAC,CAAC;AAEpH,QAAM,uBAAuB;AAE7B,QAAM,wBAAwB,MAAM;AAAA,IAClC,MACE,IAAI,KAAK,eAAe,QAAQ;AAAA,MAC9B,OAAO;AAAA,MACP,KAAK;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,IACV,CAAC;AAAA,IACH,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,eAAe,SAAS;AAAA,IAC5B,UAAU,CAAC,kBAAkB,WAAW,gBAAgB;AAAA,IACxD,SAAS,QAAQ,gBAAgB;AAAA,IACjC,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU;AAAA,MACZ,CAAC;AACD,YAAM,OAAO,MAAM;AAAA,QACjB,yBAAyB,OAAO,SAAS,CAAC;AAAA,MAC5C;AACA,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,EAAE,kCAAkC,qBAAqB,CAAC;AAAA,MAChG;AACA,aAAO,KAAK,QAAQ,QAAQ,CAAC,KAAK;AAAA,IACpC;AAAA,EACF,CAAC;AAED,QAAM,eAAe,SAAS;AAAA,IAC5B,UAAU,CAAC,kBAAkB,WAAW,gBAAgB;AAAA,IACxD,SAAS,QAAQ,gBAAgB;AAAA,IACjC,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC;AAAA,QACA,MAAM;AAAA,QACN,UAAU;AAAA,MACZ,CAAC;AACD,YAAM,OAAO,MAAM;AAAA,QACjB,+BAA+B,OAAO,SAAS,CAAC;AAAA,MAClD;AACA,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,EAAE,kCAAkC,mCAAmC,CAAC;AAAA,MAC9G;AACA,aAAO,KAAK,QAAQ,QAAQ,CAAC,KAAK;AAAA,IACpC;AAAA,EACF,CAAC;AAED,QAAM,kBAAkB,SAAS;AAAA,IAC/B,UAAU,CAAC,kBAAkB,YAAY;AAAA,IACzC,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,OAAO,WAAW,QAAQ,SAAS,MAAM,CAAC;AACpG,YAAM,OAAO,MAAM,QAAwC,uBAAuB,OAAO,SAAS,CAAC,EAAE;AACrG,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,EAAE,qCAAqC,4BAA4B,CAAC;AAAA,MAC1G;AACA,aAAO,KAAK,QAAQ,SAAS,CAAC;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,gBAAgB,SAAS;AAAA,IAC7B,UAAU,CAAC,kBAAkB,YAAY,kBAAkB,WAAW;AAAA,IACtE,SAAS,QAAQ,gBAAgB;AAAA,IACjC,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC;AAAA,QACA,MAAM;AAAA,QACN,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AACD,UAAI,gBAAgB,MAAO,QAAO,IAAI,eAAe,WAAW;AAChE,YAAM,OAAO,MAAM;AAAA,QACjB,+BAA+B,OAAO,SAAS,CAAC;AAAA,MAClD;AACA,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,EAAE,mCAAmC,oCAAoC,CAAC;AAAA,MAChH;AACA,aAAO,KAAK,UAAU,EAAE,OAAO,CAAC,GAAG,OAAO,GAAG,YAAY,EAAE;AAAA,IAC7D;AAAA,EACF,CAAC;AAED,QAAM,YAAY,SAAS;AAAA,IACzB,UAAU,CAAC,kBAAkB,QAAQ,gBAAgB;AAAA,IACrD,SAAS,QAAQ,gBAAgB;AAAA,IACjC,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC;AAAA,QACA,MAAM;AAAA,QACN,UAAU;AAAA,MACZ,CAAC;AACD,YAAM,OAAO,MAAM,QAAwC,iBAAiB,OAAO,SAAS,CAAC,EAAE;AAC/F,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,EAAE,+BAA+B,sBAAsB,CAAC;AAAA,MAC9F;AACA,aAAO,KAAK,QAAQ,SAAS,CAAC;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,iBAAiB,SAAS;AAAA,IAC9B,UAAU,CAAC,kBAAkB,aAAa,kBAAkB,WAAW;AAAA,IACvE,SAAS,QAAQ,gBAAgB;AAAA,IACjC,SAAS,YAAY;AACnB,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC;AAAA,QACA,MAAM;AAAA,QACN,UAAU;AAAA,QACV,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AACD,UAAI,gBAAgB,MAAO,QAAO,IAAI,eAAe,WAAW;AAChE,YAAM,OAAO,MAAM;AAAA,QACjB,gCAAgC,OAAO,SAAS,CAAC;AAAA,MACnD;AACA,UAAI,CAAC,KAAK,IAAI;AACZ,cAAM,eAAe,KAAK,UAAU,EAAE,oCAAoC,iCAAiC,CAAC;AAAA,MAC9G;AACA,aAAO,KAAK,QAAQ,SAAS,CAAC;AAAA,IAChC;AAAA,EACF,CAAC;AAED,QAAM,UAAU,MAAM,QAAQ,MAAM;AAClC,UAAM,MAAM,oBAAI,IAA6B;AAC7C,eAAW,OAAO,UAAU,QAAQ,CAAC,GAAG;AACtC,UAAI,IAAI,GAAI,KAAI,IAAI,IAAI,IAAI,GAAG;AAAA,IACjC;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,IAAI,CAAC;AAEnB,QAAM,eAAe,SAAS,aAAa,MAAM,aAAa;AAC9D,QAAM,cAAc,SAAS,aAAa,MAAM,YAAY;AAC5D,QAAM,YACH,aAAa,MAAM,OAAO,IAAI,KAAK,MACnC,aAAa,MAAM,QAAQ,IAAI,KAAK,KACrC,oBACA;AACF,QAAM,eAAe,aAAa,MAAM,QAAQ,IAAI,KAAK;AACzD,QAAM,aAAa,aAAa,MAAM,OAAO,IAAI,KAAK,KAAK;AAE3D,QAAM,SAAS,MAAM,QAAQ,MAAM;AACjC,UAAM,QAAQ,cAAc,MAAM,SAAS,CAAC;AAC5C,QAAI,SAAS;AACb,QAAI,WAAW;AACf,QAAI,YAAY;AAChB,eAAW,OAAO,OAAO;AACvB,gBAAU,SAAS,IAAI,gBAAgB;AACvC,kBAAY,SAAS,IAAI,iBAAiB;AAC1C,mBAAa,IAAI,sBAAsB;AAAA,IACzC;AACA,WAAO,EAAE,QAAQ,UAAU,UAAU;AAAA,EACvC,GAAG,CAAC,cAAc,MAAM,KAAK,CAAC;AAE9B,QAAM,eAAe,MAAM,QAAQ,MAAM;AACvC,UAAM,YAAY,eAAe,QAAQ,CAAC;AAC1C,UAAM,cAAc,KAAK,IAAI,IAAI,oBAAoB,KAAK,KAAK,KAAK;AACpE,QAAI,WAAW;AACf,eAAW,OAAO,WAAW;AAC3B,YAAM,KAAK,IAAI,gBAAgB,IAAI;AACnC,UAAI,CAAC,GAAI;AACT,YAAM,KAAK,IAAI,KAAK,EAAE,EAAE,QAAQ;AAChC,UAAI,OAAO,MAAM,EAAE,KAAK,KAAK,YAAa;AAC1C,UAAI,IAAI,SAAS,UAAU,IAAI,SAAS,UAAU,IAAI,SAAS,QAAQ;AACrE,oBAAY,KAAK,IAAI,SAAS,IAAI,QAAQ,CAAC;AAAA,MAC7C;AAAA,IACF;AACA,UAAM,QAAQ,WAAW;AACzB,QAAI,SAAS,KAAK,OAAO,aAAa,EAAG,QAAO;AAChD,WAAO,KAAK,MAAM,OAAO,YAAY,KAAK;AAAA,EAC5C,GAAG,CAAC,eAAe,MAAM,OAAO,SAAS,CAAC;AAE1C,QAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,GAAG,CAAC,CAAC;AAEhD,QAAM,mBAAmB,MAAM,QAAQ,MAAM;AAC3C,UAAM,QAAQ,cAAc,MAAM,SAAS,CAAC;AAC5C,WAAO,MAAM;AAAA,MAAO,CAAC,QACnB,0BAA0B,KAAK,IAAI,SAAS,QAAQ,IAAI,IAAI,MAAM,IAAI,QAAW,oBAAoB,KAAK;AAAA,IAC5G;AAAA,EACF,GAAG,CAAC,cAAc,MAAM,OAAO,oBAAoB,SAAS,KAAK,CAAC;AAElE,QAAM,oBAAoB,iBAAiB;AAC3C,QAAM,yBAAyB,KAAK,IAAI,GAAG,KAAK,KAAK,oBAAoB,oBAAoB,CAAC;AAC9F,QAAM,gBAAgB,MAAM,QAAQ,MAAM;AACxC,UAAM,SAAS,mBAAmB,KAAK;AACvC,WAAO,iBAAiB,MAAM,OAAO,QAAQ,oBAAoB;AAAA,EACnE,GAAG,CAAC,kBAAkB,sBAAsB,gBAAgB,CAAC;AAE7D,QAAM,UAAU,MAAM;AACpB,wBAAoB,CAAC;AACrB,0BAAsB,oBAAI,IAAI,CAAC;AAAA,EACjC,GAAG,CAAC,oBAAoB,WAAW,CAAC;AAEpC,QAAM,mBAAmB,MAAM;AAAA,IAC7B,MAAM,iBAAiB,OAAO,CAAC,QAAQ,mBAAmB,IAAI,IAAI,EAAE,CAAC;AAAA,IACrE,CAAC,kBAAkB,kBAAkB;AAAA,EACvC;AAEA,QAAM,gBAAgB,MAAM,QAAQ,MAAM;AACxC,UAAM,SAAS,IAAI,gBAAgB;AACnC,QAAI,iBAAkB,QAAO,IAAI,oBAAoB,gBAAgB;AACrE,QAAI,gBAAgB,MAAO,QAAO,IAAI,eAAe,WAAW;AAChE,UAAM,QAAQ,OAAO,SAAS;AAC9B,WAAO,QAAQ,0BAA0B,KAAK,KAAK;AAAA,EACrD,GAAG,CAAC,kBAAkB,WAAW,CAAC;AAElC,QAAM,uBAAuB,MAAM,QAAQ,MAAM;AAC/C,QAAI,CAAC,iBAAkB,QAAO;AAC9B,WAAO,2CAA2C,mBAAmB,gBAAgB,CAAC;AAAA,EACxF,GAAG,CAAC,gBAAgB,CAAC;AAErB,QAAM,mBAAmB,MAAM,YAAY,CAAC,SAAkC,CAAC,MAAM;AACnF,oBAAgB,MAAM;AACtB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM;AAAA,IACjC,CAAC,SAAsE,CAAC,MAAM;AAC5E,qBAAe,MAAM;AACrB,mBAAa,IAAI;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,yBAAyB,MAAM,YAAY,MAA+B;AAC9E,UAAM,gBAAgB,iBAAiB,CAAC;AACxC,UAAM,oBACJ,eAAe,cAAc,KAAK,MACjC,gBAAgB,QAAQ,cAAc;AACzC,UAAM,aACJ,iBAAiB,WAAW,IAAI,eAAe,aAAa,KAAK,KAAK,SAAY;AACpF,UAAM,QACJ,iBAAiB,WAAW,IAAI,eAAe,QAAQ,KAAK,KAAK,SAAY;AAC/E,WAAO;AAAA,MACL,aAAa;AAAA,MACb;AAAA,MACA;AAAA,IACF;AAAA,EACF,GAAG,CAAC,kBAAkB,WAAW,CAAC;AAElC,QAAM,yBAAyB,MAAM,YAAY,CAAC,WAAmB,aAAsB;AACzF,0BAAsB,CAAC,YAAY;AACjC,YAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,UAAI,SAAU,MAAK,IAAI,SAAS;AAAA,UAC3B,MAAK,OAAO,SAAS;AAC1B,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,sBAAsB,MAAM,YAAY,CAAC,aAAsB;AACnE,0BAAsB,CAAC,YAAY;AACjC,YAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,iBAAW,OAAO,eAAe;AAC/B,YAAI,SAAU,MAAK,IAAI,IAAI,EAAE;AAAA,YACxB,MAAK,OAAO,IAAI,EAAE;AAAA,MACzB;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,CAAC;AAElB,QAAM,qBAAqB,MAAM,QAAQ,MAAM;AAC7C,QAAI,cAAc,WAAW,GAAG;AAC9B,aAAO,EAAE,SAAS,OAAO,eAAe,MAAM;AAAA,IAChD;AACA,UAAM,iBAAiB,cAAc,OAAO,CAAC,QAAQ,mBAAmB,IAAI,IAAI,EAAE,CAAC,EAAE;AACrF,WAAO;AAAA,MACL,SAAS,mBAAmB,cAAc;AAAA,MAC1C,eAAe,iBAAiB,KAAK,iBAAiB,cAAc;AAAA,IACtE;AAAA,EACF,GAAG,CAAC,eAAe,kBAAkB,CAAC;AAEtC,QAAM,8BAA8B,MAAM,YAAY,MAAM;AAC1D,UAAM,UAAU;AAAA,MACd,EAAE,kDAAkD,WAAW;AAAA,MAC/D,EAAE,iDAAiD,UAAU;AAAA,MAC7D,EAAE,4CAA4C,KAAK;AAAA,MACnD,EAAE,+CAA+C,SAAS;AAAA,MAC1D,EAAE,iDAAiD,UAAU;AAAA,MAC7D,EAAE,+CAA+C,QAAQ;AAAA,IAC3D;AACA,UAAM,OAAO,iBAAiB,IAAI,CAAC,QAAQ;AACzC,YAAM,MAAM,IAAI,SAAS,QAAQ,IAAI,IAAI,MAAM,IAAI;AACnD,YAAM,SAAS,qBAAqB,KAAK,KAAK,cAAc,KAAK;AACjE,aAAO;AAAA,QACL,qBAAqB,GAAG;AAAA,QACxB,oBAAoB,IAAI,eAAe,IAAI,WAAW;AAAA,QACtD,eAAe,KAAK,MAAM;AAAA,QAC1B,OAAO,SAAS,IAAI,gBAAgB,CAAC;AAAA,QACrC,OAAO,SAAS,IAAI,iBAAiB,CAAC;AAAA,QACtC,EAAE,OAAO,UAAU,OAAO,aAAa;AAAA,MACzC;AAAA,IACF,CAAC;AACD,UAAM,UAAU,UAAU,QAAQ,aAAa,GAAG,EAAE,QAAQ,YAAY,EAAE,KAAK;AAC/E,oBAAgB,GAAG,OAAO,qBAAqB,CAAC,SAAS,GAAG,IAAI,CAAC;AAAA,EACnE,GAAG,CAAC,kBAAkB,QAAQ,SAAS,OAAO,WAAW,cAAc,CAAC,CAAC;AAEzE,QAAM,gBAAgB;AAEtB,QAAM,oBAAoB,gBAAgB,MAAM,KAAK,CAAC,cAAc,UAAU,OAAO,WAAW;AAChG,QAAM,iBACJ,gBAAgB,QACZ,EAAE,yCAAyC,gBAAgB,IAC3D,mBAAmB,QAAQ,mBAAmB,QAAQ;AAE5D,QAAM,gBAAgB;AAAA,IACpB,eAAe;AAAA,IACf,aAAa,MAAM,cAAc,QAC7B,EAAE,mCAAmC,UAAU,IAC/C;AAAA,EACN,EAAE,OAAO,OAAO;AAEhB,QAAM,sBAAsB,MAAM;AAAA,IAChC,MAAM;AAAA,MACJ;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,MACN;AAAA,UAAC;AAAA;AAAA,YACC,cAAY,EAAE,+CAA+C,QAAQ;AAAA,YACrE,SAAS,mBAAmB,gBAAgB,kBAAkB,mBAAmB;AAAA,YACjF,iBAAiB,CAAC,YAAY,oBAAoB,YAAY,IAAI;AAAA;AAAA,QACpE;AAAA,QAEF,MAAM,CAAC,EAAE,IAAI,MACX;AAAA,UAAC;AAAA;AAAA,YACC,cAAY,EAAE,+CAA+C,QAAQ;AAAA,YACrE,SAAS,mBAAmB,IAAI,IAAI,SAAS,EAAE;AAAA,YAC/C,iBAAiB,CAAC,YAAY,uBAAuB,IAAI,SAAS,IAAI,YAAY,IAAI;AAAA;AAAA,QACxF;AAAA,QAEF,MAAM,EAAE,UAAU,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,kDAAkD,WAAW;AAAA,QACvE,MAAM,CAAC,EAAE,IAAI,MAAM,qBAAqB,IAAI,QAAQ;AAAA,MACtD;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,iDAAiD,UAAU;AAAA,QACrE,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,aAAa,IAAI,SAAS,aAAa,KAAK;AAClD,gBAAM,QAAQ,oBAAoB,IAAI,SAAS,eAAe,IAAI,SAAS,WAAW;AACtF,cAAI,CAAC,cAAc,UAAU,SAAK,QAAO;AACzC,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,yBAAyB,mBAAmB,UAAU,CAAC;AAAA,cAC7D,WAAU;AAAA,cAET;AAAA;AAAA,UACH;AAAA,QAEJ;AAAA,MACF;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,4CAA4C,KAAK;AAAA,QAC3D,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,QAAQ,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAM,MAAM,QAAQ,QAAQ,IAAI,KAAK,IAAI;AACzC,gBAAM,QAAQ,eAAe,KAAK,MAAM;AACxC,cAAI,CAAC,SAAS,UAAU,SAAK,QAAO;AACpC,iBACE;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,oBAAoB,mBAAmB,KAAK,CAAC;AAAA,cACnD,WAAU;AAAA,cAET;AAAA;AAAA,UACH;AAAA,QAEJ;AAAA,MACF;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,+CAA+C,SAAS;AAAA,QAClE,MAAM,CAAC,EAAE,IAAI,MAAM,OAAO,SAAS,IAAI,SAAS,gBAAgB,CAAC;AAAA,MACnE;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,iDAAiD,UAAU;AAAA,QACrE,MAAM,CAAC,EAAE,IAAI,MAAM,OAAO,SAAS,IAAI,SAAS,iBAAiB,CAAC;AAAA,MACpE;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,+CAA+C,QAAQ;AAAA,QACjE,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,MAAM,IAAI,SAAS,SAAS,QAAQ,IAAI,IAAI,SAAS,MAAM,IAAI;AACrE,gBAAM,SAAS,qBAAqB,IAAI,UAAU,KAAK,cAAc,KAAK;AAC1E,iBACE,oBAAC,eAAY,SAAS,OAAO,SAAS,KAAG,MACtC,YAAE,OAAO,UAAU,OAAO,aAAa,GAC1C;AAAA,QAEJ;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAM;AAAA,MACJ;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,0CAA0C,OAAO;AAAA,QAC3D,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,OAAO,IAAI,SAAS,QAAQ;AAClC,iBACE,oBAAC,eAAY,SAAS,kBAAkB,IAAI,KAAK,WAC9C,4BAAkB,MAAM,CAAC,GAC5B;AAAA,QAEJ;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,4CAA4C,SAAS;AAAA,QAC/D,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,WAAW,uBAAuB,IAAI,UAAU,CAAC;AACvD,iBACE,qBAAC,SAAI,WAAU,eACb;AAAA,gCAAC,OAAE,WAAU,uBAAuB,8BAAoB,IAAI,UAAU,UAAU,CAAC,GAAE;AAAA,YAClF,WAAW,oBAAC,OAAE,WAAU,iCAAiC,oBAAS,IAAO;AAAA,aAC5E;AAAA,QAEJ;AAAA,MACF;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,6CAA6C,UAAU;AAAA,QACjE,MAAM,CAAC,EAAE,IAAI,MACX,oBAAC,UAAK,WAAU,iCAAiC,iCAAuB,IAAI,QAAQ,GAAE;AAAA,MAE1F;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,QAAQ,EAAE,yCAAyC,MAAM;AAAA,QACzD,MAAM,CAAC,EAAE,IAAI,MAAM;AACjB,gBAAM,MAAM,IAAI,SAAS,gBAAgB,IAAI,SAAS;AACtD,cAAI,CAAC,IAAK,QAAO;AACjB,gBAAM,OAAO,IAAI,KAAK,GAAG;AACzB,cAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,iBAAO,sBAAsB,OAAO,IAAI;AAAA,QAC1C;AAAA,QACA,MAAM,EAAE,UAAU,OAAO;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,CAAC,uBAAuB,UAAU,CAAC;AAAA,EACrC;AAEA,QAAM,cAAgE;AAAA,IACpE,EAAE,IAAI,OAAO,OAAO,EAAE,4CAA4C,UAAU,EAAE;AAAA,IAC9E,EAAE,IAAI,YAAY,OAAO,EAAE,iDAAiD,UAAU,EAAE;AAAA,IACxF,EAAE,IAAI,WAAW,OAAO,EAAE,gDAAgD,SAAS,EAAE;AAAA,IACrF,EAAE,IAAI,cAAc,OAAO,EAAE,mDAAmD,aAAa,EAAE;AAAA,EACjG;AAEA,QAAM,YACJ,aAAa,aACb,aAAa,aACb,cAAc,aACd,UAAU,aACV,eAAe;AAEjB,QAAM,WACJ,aAAa,WACb,aAAa,WACb,cAAc,WACd,UAAU,WACV,eAAe;AAEjB,MAAI,CAAC,kBAAkB;AACrB,WACE,oBAAC,QACC,8BAAC,YACC;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,oCAAoC,yBAAyB;AAAA,QACtE,QACE,oBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,MAAK,SAAS,MAAM,OAAO,KAAK,aAAa,GACvF,YAAE,2CAA2C,mBAAmB,GACnE;AAAA;AAAA,IAEJ,GACF,GACF;AAAA,EAEJ;AAEA,SACE,qBAAC,QACC;AAAA,yBAAC,YAAS,WAAU,aAClB;AAAA,0BAAC,cAAW,SAAO,MAAC,SAAQ,QAAO,MAAK,MAAK,WAAU,0DACrD,+BAAC,QAAK,MAAM,eACV;AAAA,4BAAC,aAAU,WAAU,UAAS;AAAA,QAC7B,EAAE,2CAA2C,mBAAmB;AAAA,SACnE,GACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,aAAa,cAAc,KAAK,QAAK;AAAA,UACrC,SACE,iCACE;AAAA,iCAAC,UAAO,OAAO,aAAa,eAAe,gBACzC;AAAA,mCAAC,iBAAc,WAAU,2BACvB;AAAA,oCAAC,aAAU,WAAU,8CAA6C;AAAA,gBAClE,oBAAC,eAAY,aAAa,gBAAgB;AAAA,iBAC5C;AAAA,cACA,qBAAC,iBACC;AAAA,oCAAC,cAAW,OAAM,OACf,YAAE,yCAAyC,gBAAgB,GAC9D;AAAA,iBACE,gBAAgB,QAAQ,CAAC,GAAG,IAAI,CAAC,cACjC,oBAAC,cAA8B,OAAO,UAAU,IAC7C,oBAAU,QAAQ,UAAU,QAAQ,UAAU,MADhC,UAAU,EAE3B,CACD;AAAA,iBACH;AAAA,eACF;AAAA,YACC,OAAO,YACN,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,MAAM,iBAAiB,GACtE;AAAA,kCAAC,qBAAkB,WAAU,UAAS;AAAA,cACrC,EAAE,kCAAkC,cAAc;AAAA,eACrD,IACE;AAAA,aACN;AAAA;AAAA,MAEJ;AAAA,MAEC,YACC,oBAAC,kBAAe,OAAO,EAAE,2BAA2B,wBAAmB,GAAG,IACxE;AAAA,MAEH,WACC,oBAAC,gBAAa,OAAO,EAAE,+BAA+B,0BAA0B,GAAG,IACjF;AAAA,MAEH,CAAC,aAAa,CAAC,YAAY,aAAa,OACvC,iCACE;AAAA,6BAAC,aAAQ,WAAU,2DACjB;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,qCAAqC,SAAS;AAAA,cACvD,SAAS,EAAE,uCAAuC,yBAAyB;AAAA,cAC3E,OAAO,OAAO,OAAO,MAAM;AAAA,cAC3B,YACE,eAAe,KAAK,OAAO,UAAU,eACjC,EAAE,wCAAwC,eAAe,IACzD;AAAA,cAEN,cAAa;AAAA,cACb,UAAU,EAAE,mCAAmC,mBAAmB;AAAA,cAClE,SAAQ;AAAA;AAAA,UACV;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,uCAAuC,UAAU;AAAA,cAC1D,SAAS,EAAE,yCAAyC,qBAAqB;AAAA,cACzE,OAAO,OAAO,OAAO,QAAQ;AAAA,cAC7B,YACE,OAAO,WAAW,IACd,EAAE,uCAAuC,sBAAsB,EAAE,OAAO,OAAO,SAAS,CAAC,IACzF;AAAA,cAEN,cAAa;AAAA,cACb,UAAU,EAAE,qCAAqC,mBAAmB;AAAA,cACpE,SAAQ;AAAA;AAAA,UACV;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,wCAAwC,WAAW;AAAA,cAC5D,SAAS,EAAE,0CAA0C,mBAAmB;AAAA,cACxE,OAAO,OAAO,OAAO,SAAS;AAAA,cAC9B,YACE,OAAO,YAAY,IACf,EAAE,wCAAwC,UAAU,IACpD,EAAE,6CAA6C,MAAM;AAAA,cAE3D,cAAc,OAAO,YAAY,IAAI,YAAY;AAAA,cACjD,UAAU,EAAE,sCAAsC,cAAc;AAAA,cAChE,SAAS,OAAO,YAAY,wBAAwB;AAAA;AAAA,UACtD;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,2CAA2C,eAAe;AAAA,cACnE,SAAS,EAAE,6CAA6C,wBAAwB;AAAA,cAChF,OAAO,OAAO,YAAY;AAAA,cAC1B,YACE,cAAc,IACV,EAAE,iDAAiD,kBAAkB,EAAE,OAAO,YAAY,CAAC,IAC3F;AAAA,cAEN,cAAc,OAAO,aAAa,gBAAgB,eAAe,IAAI,UAAU;AAAA,cAC/E,UAAU,EAAE,yCAAyC,cAAc;AAAA,cACnE,SAAQ;AAAA;AAAA,UACV;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,EAAE,2CAA2C,gBAAgB;AAAA,cACpE,SAAS,EAAE,6CAA6C,sCAAsC;AAAA,gBAC5F,MAAM;AAAA,cACR,CAAC;AAAA,cACD,OAAO,iBAAiB,OAAO,WAAM,OAAO,YAAY;AAAA,cACxD,YACE,iBAAiB,QAAQ,gBAAgB,IACrC,EAAE,gDAAgD,cAAc,IAChE;AAAA,cAEN,cAAa;AAAA,cACb,UAAU,EAAE,yCAAyC,gBAAgB;AAAA,cACrE,SAAS;AAAA;AAAA,UACX;AAAA,WACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,WAAU;AAAA,YAEV;AAAA,kCAAC,SAAI,WAAU,sBACb,8BAAC,QAAG,WAAU,2BACX,YAAE,sCAAsC,oBAAoB,GAC/D,GACF;AAAA,cACA,qBAAC,SAAI,WAAU,yFACb;AAAA,oCAAC,SAAI,WAAU,wBACZ,sBAAY,IAAI,CAAC,SAChB;AAAA,kBAAC;AAAA;AAAA,oBAEC,MAAK;AAAA,oBACL,MAAK;AAAA,oBACL,SAAS,uBAAuB,KAAK,KAAK,YAAY;AAAA,oBACtD,WAAW,GAAG,gBAAgB,uBAAuB,KAAK,MAAM,WAAW;AAAA,oBAC3E,SAAS,MAAM,sBAAsB,KAAK,EAAE;AAAA,oBAE3C,eAAK;AAAA;AAAA,kBAPD,KAAK;AAAA,gBAQZ,CACD,GACH;AAAA,gBACA,qBAAC,SAAI,WAAU,qCACb;AAAA,uCAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,6BAC/C;AAAA,wCAAC,YAAS,WAAU,UAAS;AAAA,oBAC5B,EAAE,kDAAkD,YAAY;AAAA,qBACnE;AAAA,kBACC,OAAO,gBACN;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,SAAS,MAAM,qBAAqB,uBAAuB,CAAC;AAAA,sBAE3D,YAAE,uDAAuD,kBAAkB;AAAA;AAAA,kBAC9E,IACE;AAAA,kBACH,OAAO,aAAa,iBAAiB,SAAS,IAC7C;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAQ;AAAA,sBACR,SAAS,MAAM,iBAAiB,uBAAuB,CAAC;AAAA,sBAEvD,YAAE,uDAAuD,6BAA6B,EAAE,OAAO,iBAAiB,OAAO,CAAC;AAAA;AAAA,kBAC3H,IACE;AAAA,mBACN;AAAA,iBACF;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,UAAQ;AAAA,kBACR,SAAS;AAAA,kBACT,MAAM;AAAA,kBACN,iBAAe;AAAA,kBACf,UAAU,EAAE,IAAI;AAAA,kBAChB,aAAa,EAAE,SAAS,gBAAgB,MAAM,qBAAqB,QAAQ;AAAA,kBAC3E,YAAY;AAAA,oBACV,MAAM;AAAA,oBACN,UAAU;AAAA,oBACV,OAAO;AAAA,oBACP,YAAY;AAAA,oBACZ,cAAc;AAAA,kBAChB;AAAA,kBACA,YACE;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,EAAE,4CAA4C,2BAA2B;AAAA,sBAChF,aAAa;AAAA,wBACX;AAAA,wBACA;AAAA,sBACF;AAAA;AAAA,kBACF;AAAA;AAAA,cAEJ;AAAA,cACC,oBAAoB,IACnB,qBAAC,SAAI,WAAU,uHACb;AAAA,oCAAC,UACE,YAAE,+CAA+C,wCAAwC;AAAA,kBACxF,OAAO,cAAc;AAAA,kBACrB,OAAO;AAAA,gBACT,CAAC,GACH;AAAA,gBACC,cAAc,QAAQ,cAAc,KAAK,QAAQ,oBAChD,oBAAC,cAAW,SAAO,MAAC,SAAQ,QAAO,MAAK,MAAK,WAAU,eACrD,8BAAC,QAAK,MAAM,eACT,YAAE,+CAA+C,2BAAsB,GAC1E,GACF,IACE;AAAA,iBACN,IACE;AAAA;AAAA;AAAA,QACN;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,EAAE,kCAAkC,iBAAiB;AAAA,YAC5D,SAAS;AAAA,YACT,MAAM,eAAe,QAAQ,CAAC;AAAA,YAC9B,iBAAe;AAAA,YACf,UAAU,EAAE,IAAI;AAAA,YAChB,aAAa,EAAE,SAAS,gBAAgB,MAAM,iBAAiB,QAAQ;AAAA,YACvE,YAAY,EAAE,kCAAkC,mCAAmC;AAAA,YACnF,SACE,oBAAC,UAAO,SAAO,MAAC,MAAK,UAAS,SAAQ,SAAQ,MAAK,MACjD,8BAAC,QAAK,MAAM,eACT,YAAE,oCAAoC,2BAAsB,GAC/D,GACF;AAAA;AAAA,QAEJ;AAAA,QAEA,qBAAC,aAAQ,WAAU,oHACjB;AAAA,+BAAC,SACC;AAAA,gCAAC,QAAG,WAAU,2BACX,YAAE,sCAAsC,eAAe,GAC1D;AAAA,YACA,oBAAC,OAAE,WAAU,iCACV;AAAA,cACC;AAAA,cACA;AAAA,YACF,GACF;AAAA,aACF;AAAA,UACA,qBAAC,SAAI,WAAU,wBACZ;AAAA,mBAAO,aACN,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,MAAM,eAAe,IAAI,GACxE;AAAA,kCAAC,aAAU,WAAU,UAAS;AAAA,cAC7B,EAAE,wCAAwC,eAAe;AAAA,eAC5D,IACE;AAAA,YACH,OAAO,YACN,qBAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,SAAS,MAAM,iBAAiB,GACtE;AAAA,kCAAC,qBAAkB,WAAU,UAAS;AAAA,cACrC,EAAE,uCAAuC,kBAAkB;AAAA,eAC9D,IACE;AAAA,YACH,OAAO,YACN,oBAAC,UAAO,SAAO,MAAC,MAAK,UAAS,SAAQ,WACpC,+BAAC,QAAK,MAAM,sBACV;AAAA,kCAAC,kBAAe,WAAU,UAAS;AAAA,cAClC,EAAE,qCAAqC,YAAY;AAAA,eACtD,GACF,IACE;AAAA,YACH,OAAO,gBACN;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,SAAS,MAAM,qBAAqB,uBAAuB,CAAC;AAAA,gBAE5D;AAAA,sCAAC,iBAAc,WAAU,UAAS;AAAA,kBACjC,EAAE,2CAA2C,aAAa;AAAA;AAAA;AAAA,YAC7D,IACE;AAAA,YACJ,oBAAC,UAAO,SAAO,MAAC,MAAK,UAAS,SAAQ,WACpC,+BAAC,QAAK,MAAM,eACT;AAAA,gBAAE,2CAA2C,aAAa;AAAA,cAC3D,oBAAC,gBAAa,WAAU,UAAS;AAAA,eACnC,GACF;AAAA,aACF;AAAA,WACF;AAAA,SACF,IACE;AAAA,MAEH,CAAC,aAAa,CAAC,YAAY,CAAC,aAAa,OACxC;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,EAAE,mCAAmC,gBAAgB;AAAA,UAC5D,UAAU;AAAA,UACV,WAAW,EAAE,2CAA2C,mBAAmB;AAAA;AAAA,MAC7E,IACE;AAAA,OACN;AAAA,IAEC,OAAO,cAAc,mBACpB;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,yBAAyB;AAAA;AAAA,IAC3B,IACE;AAAA,IACH,OAAO,aAAa,mBACnB;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,yBAAyB;AAAA,QACzB,oBAAoB,aAAa;AAAA,QACjC,mBAAmB,aAAa;AAAA,QAChC,cAAc,aAAa;AAAA;AAAA,IAC7B,IACE;AAAA,IACH,OAAO,gBACN;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,yBAAyB,oBAAoB;AAAA,QAC7C,oBAAoB,YAAY;AAAA,QAChC,mBAAmB,YAAY;AAAA;AAAA,IACjC,IACE;AAAA,KACN;AAEJ;",
6
6
  "names": []
7
7
  }