@argusoft/medplat-product-management 1.0.2 → 1.0.3

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 (79) hide show
  1. package/dist/{BrandsPage-CSA3meke.js → BrandsPage-NSvaMXWg.js} +76 -75
  2. package/dist/BrandsPage-NSvaMXWg.js.map +1 -0
  3. package/dist/{CategoriesPage-BrspzkJL.js → CategoriesPage-aZBZrDOi.js} +8 -8
  4. package/dist/{CategoriesPage-BrspzkJL.js.map → CategoriesPage-aZBZrDOi.js.map} +1 -1
  5. package/dist/{DashboardPage-CqaquOs2.js → DashboardPage-DjwlOhrd.js} +22 -22
  6. package/dist/DashboardPage-DjwlOhrd.js.map +1 -0
  7. package/dist/{FacilityPicker-Dv_H95WW.js → FacilityPicker-Chh-ZaR0.js} +2 -2
  8. package/dist/{FacilityPicker-Dv_H95WW.js.map → FacilityPicker-Chh-ZaR0.js.map} +1 -1
  9. package/dist/{InventoryPage-CPbzfDMR.js → InventoryPage-cy76UsMp.js} +109 -119
  10. package/dist/InventoryPage-cy76UsMp.js.map +1 -0
  11. package/dist/{MemberProfileCard-0vjO3Gzd.js → MemberProfileCard-CinPt09F.js} +6 -6
  12. package/dist/MemberProfileCard-CinPt09F.js.map +1 -0
  13. package/dist/{PaymentCreatePage-BYmDZAIB.js → PaymentCreatePage-BJe3LyFF.js} +148 -153
  14. package/dist/PaymentCreatePage-BJe3LyFF.js.map +1 -0
  15. package/dist/{PaymentsPage-BXPvKon-.js → PaymentsPage-DxHoKksN.js} +11 -11
  16. package/dist/{PaymentsPage-BXPvKon-.js.map → PaymentsPage-DxHoKksN.js.map} +1 -1
  17. package/dist/PickupCreatePage-Cq8zJ62Y.js +705 -0
  18. package/dist/PickupCreatePage-Cq8zJ62Y.js.map +1 -0
  19. package/dist/{PickupsPage-D66_Wta4.js → PickupsPage-BOygBQAb.js} +66 -74
  20. package/dist/PickupsPage-BOygBQAb.js.map +1 -0
  21. package/dist/{PmTable-B9970kPY.js → PmTable-CCpaJEA_.js} +4 -4
  22. package/dist/PmTable-CCpaJEA_.js.map +1 -0
  23. package/dist/{ProductEditPage-B8qFTCW9.js → ProductEditPage-DmdHO-fS.js} +109 -109
  24. package/dist/ProductEditPage-DmdHO-fS.js.map +1 -0
  25. package/dist/{ProductsPage-C7_r9cKn.js → ProductsPage-BglMe8EM.js} +6 -6
  26. package/dist/{ProductsPage-C7_r9cKn.js.map → ProductsPage-BglMe8EM.js.map} +1 -1
  27. package/dist/PurchaseCreatePage-1tYk41RS.js +590 -0
  28. package/dist/PurchaseCreatePage-1tYk41RS.js.map +1 -0
  29. package/dist/{PurchaseRequestDetailPage-C7aAwEqw.js → PurchaseRequestDetailPage-BYLx3aBI.js} +14 -14
  30. package/dist/PurchaseRequestDetailPage-BYLx3aBI.js.map +1 -0
  31. package/dist/{PurchaseRequestsPage-BwCkbpaX.js → PurchaseRequestsPage-D2yorc4s.js} +4 -4
  32. package/dist/{PurchaseRequestsPage-BwCkbpaX.js.map → PurchaseRequestsPage-D2yorc4s.js.map} +1 -1
  33. package/dist/{PurchasesPage-CvaZouVy.js → PurchasesPage-CanQTjlw.js} +3 -3
  34. package/dist/{PurchasesPage-CvaZouVy.js.map → PurchasesPage-CanQTjlw.js.map} +1 -1
  35. package/dist/{SaleCreatePage-DP1Qmltu.js → SaleCreatePage-DxFvPvju.js} +145 -170
  36. package/dist/SaleCreatePage-DxFvPvju.js.map +1 -0
  37. package/dist/{SalesPage-CO3cdYX5.js → SalesPage-CPMrdr5-.js} +12 -12
  38. package/dist/SalesPage-CPMrdr5-.js.map +1 -0
  39. package/dist/SettingsPage-CEy922rW.js +662 -0
  40. package/dist/SettingsPage-CEy922rW.js.map +1 -0
  41. package/dist/{StorageUnitsPage-B9Hr_RkW.js → StorageUnitsPage-DfLT6xC_.js} +13 -13
  42. package/dist/{StorageUnitsPage-B9Hr_RkW.js.map → StorageUnitsPage-DfLT6xC_.js.map} +1 -1
  43. package/dist/{TransactionsPage-HKIGwEZN.js → TransactionsPage-KFUJ4O5g.js} +4 -4
  44. package/dist/TransactionsPage-KFUJ4O5g.js.map +1 -0
  45. package/dist/TransferCreatePage-k1tsRGB3.js +315 -0
  46. package/dist/TransferCreatePage-k1tsRGB3.js.map +1 -0
  47. package/dist/TransfersPage-CDafSVO9.js +470 -0
  48. package/dist/TransfersPage-CDafSVO9.js.map +1 -0
  49. package/dist/VialsPage-DPsYFgaQ.js +897 -0
  50. package/dist/VialsPage-DPsYFgaQ.js.map +1 -0
  51. package/dist/{currency-ClRLIEg2.js → currency-bVQ7aEk2.js} +141 -506
  52. package/dist/currency-bVQ7aEk2.js.map +1 -0
  53. package/dist/index.js +1 -1
  54. package/package.json +1 -1
  55. package/dist/BrandsPage-CSA3meke.js.map +0 -1
  56. package/dist/DashboardPage-CqaquOs2.js.map +0 -1
  57. package/dist/InventoryPage-CPbzfDMR.js.map +0 -1
  58. package/dist/MemberProfileCard-0vjO3Gzd.js.map +0 -1
  59. package/dist/PaymentCreatePage-BYmDZAIB.js.map +0 -1
  60. package/dist/PickupCreatePage-CMGwoAXj.js +0 -426
  61. package/dist/PickupCreatePage-CMGwoAXj.js.map +0 -1
  62. package/dist/PickupsPage-D66_Wta4.js.map +0 -1
  63. package/dist/PmTable-B9970kPY.js.map +0 -1
  64. package/dist/ProductEditPage-B8qFTCW9.js.map +0 -1
  65. package/dist/PurchaseCreatePage-CDfNDcKG.js +0 -627
  66. package/dist/PurchaseCreatePage-CDfNDcKG.js.map +0 -1
  67. package/dist/PurchaseRequestDetailPage-C7aAwEqw.js.map +0 -1
  68. package/dist/SaleCreatePage-DP1Qmltu.js.map +0 -1
  69. package/dist/SalesPage-CO3cdYX5.js.map +0 -1
  70. package/dist/SettingsPage-DvVRwSFw.js +0 -711
  71. package/dist/SettingsPage-DvVRwSFw.js.map +0 -1
  72. package/dist/TransactionsPage-HKIGwEZN.js.map +0 -1
  73. package/dist/TransferCreatePage-DMuZqQ3c.js +0 -363
  74. package/dist/TransferCreatePage-DMuZqQ3c.js.map +0 -1
  75. package/dist/TransfersPage-COIyouL4.js +0 -480
  76. package/dist/TransfersPage-COIyouL4.js.map +0 -1
  77. package/dist/VialsPage-BNPUwGCU.js +0 -922
  78. package/dist/VialsPage-BNPUwGCU.js.map +0 -1
  79. package/dist/currency-ClRLIEg2.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"VialsPage-BNPUwGCU.js","names":[],"sources":["../src/views/VialsPage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControl,\n FormHelperText,\n InputLabel,\n LinearProgress,\n MenuItem,\n Radio,\n Select,\n Stack,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n TextField,\n Tooltip,\n Typography,\n} from \"@mui/material\";\nimport RefreshRoundedIcon from \"@mui/icons-material/RefreshRounded\";\nimport VaccinesRoundedIcon from \"@mui/icons-material/VaccinesRounded\";\nimport { showToast } from \"@/common/toaster/toaster\";\nimport PmPageShell from \"@pm/components/PmPageShell\";\nimport PmNav from \"@pm/components/PmNav\";\nimport PmGuard from \"@pm/components/PmGuard\";\nimport { usePermissionedEffect, usePmPermissions } from \"@pm/utils/permissions\";\nimport { PmEmptyRow, PmTable, PmTableCard } from \"@pm/components/PmTable\";\nimport {\n administerDose,\n closeVial,\n getProducts,\n listFacilities,\n listOpenVials,\n listVialsForProduct,\n listBatches,\n listMyOpenPickups,\n} from \"@pm/services/pmApi\";\nimport {\n VACCINES_CATEGORY_CODE,\n VIAL_STATUSES,\n WASTAGE_REASONS,\n labelOf,\n} from \"@pm/utils/constants\";\nimport { formatDate, formatDateTime, formatNumber } from \"@pm/utils/format\";\nimport {\n isClean,\n validatePositiveNumber,\n validateRequired,\n} from \"@pm/utils/validation\";\nimport { useFieldErrors } from \"@pm/utils/useFieldErrors\";\nimport { usePmSettings } from \"@pm/utils/settingsStore\";\n\n// Earliest-expiry-first: a vial is unusable past whichever date arrives first —\n// its own open-vial validity (expiresAt) or its parent batch's printed expiry.\n//\n// The batch's expiryDate is a date-only field; the batch is valid through the\n// whole of that day, so we shift it to end-of-day before comparing. Without\n// this, a batch printed-expiry of today is treated as \"expired at 00:00\",\n// which incorrectly filters out otherwise-usable open vials mid-day.\nfunction effectiveVialExpiry(v) {\n const vialT = v.expiresAt ? new Date(v.expiresAt).getTime() : Infinity;\n let batchT = Infinity;\n if (v.batchExpiryDate) {\n const d = new Date(v.batchExpiryDate);\n d.setHours(23, 59, 59, 999);\n batchT = d.getTime();\n }\n return Math.min(vialT, batchT);\n}\n\nfunction batchExpiryMs(b) {\n return b.expiryDate ? new Date(b.expiryDate).getTime() : Infinity;\n}\n\nconst EMPTY_VACCINE_DIALOG = {\n open: false,\n step: \"product\", // 'product' | 'select'\n mode: \"use-existing\", // 'use-existing' (open vials available) | 'open-new' (need to open from a batch)\n productId: \"\",\n // Source = administration context. USER (outreach, consume from personal\n // pickup stock) or FACILITY (consume from a facility the user is assigned\n // to). Each source has an isolated open-vial pool — a facility vial cannot\n // satisfy an outreach administration and vice-versa.\n ownerType: \"\", // 'USER' | 'FACILITY'\n ownerId: \"\", // facility uuid when ownerType=FACILITY; empty for USER\n // For ownerType=USER only: which personal pool — 'OUTREACH' (pickup) or\n // 'PURCHASE' (personal-purchase stock). Empty for FACILITY.\n personalSource: \"\",\n // Unfiltered candidate pools (pre-source-filter). Used to recompute the\n // displayed vialCandidates / batchCandidates when the user changes source.\n allVials: [],\n allBatches: [],\n // The per-source available source list, computed in proceedFromProductStep\n // so the source picker only shows owners with actual stock for this product.\n sources: [],\n vialCandidates: [],\n batchCandidates: [],\n selectedVialId: \"\",\n selectedBatchId: \"\",\n volumeMl: \"\",\n loading: false,\n};\n\nexport default function VialsPage() {\n const perms = usePmPermissions();\n const canWrite = perms.has(\"update.manage.productManagement.vial\");\n const [vials, setVials] = useState([]);\n const [batches, setBatches] = useState([]);\n const [products, setProducts] = useState([]);\n // Assigned facilities — used to label the source picker on the administer\n // dialog (\"Facility — <name>\"). The user can only administer from\n // facilities they're assigned to.\n const [facilities, setFacilities] = useState([]);\n // The user's active outreach session(s) — the OPEN pickup. Shown on the\n // administer screen when the chosen source is Personal (Outreach).\n const [openSessions, setOpenSessions] = useState([]);\n const settings = usePmSettings();\n const allowPersonalPurchase = settings?.allowPersonalPurchase !== false;\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [closeDialog, setCloseDialog] = useState({\n open: false,\n vial: null,\n reason: \"TIME_EXPIRATION\",\n comments: \"\",\n });\n // Single consolidated \"Administer Vaccine\" flow: pick vaccine → pick open vial OR\n // pick a sealed batch and open-and-administer in one call.\n const [vaccineDialog, setVaccineDialog] = useState(EMPTY_VACCINE_DIALOG);\n const closeFieldErrors = useFieldErrors();\n const vaccineFieldErrors = useFieldErrors();\n\n async function load() {\n setLoading(true);\n setError(\"\");\n try {\n const [v, b, p, f, sessions] = await Promise.all([\n listOpenVials(),\n listBatches(),\n getProducts(),\n // Empty array if perm-gated — the dialog falls back to USER-only sources.\n listFacilities().catch(() => []),\n listMyOpenPickups().catch(() => []),\n ]);\n setVials(v);\n setBatches(b);\n setProducts(p);\n setFacilities(f);\n setOpenSessions(sessions);\n } catch (err) {\n setError(\n err?.response?.data?.message ?? err?.message ?? \"Failed to load\",\n );\n showToast({ message: err?.message || \"Failed to load\", type: \"error\" });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect(\n \"view.manage.productManagement.vial\",\n () => {\n void load();\n },\n [],\n );\n\n // Vaccine products with a vaccine config — used by the administer dialog.\n const vaccineProducts = useMemo(\n () =>\n products\n .filter((p) => p.categoryCode === VACCINES_CATEGORY_CODE && p.isActive)\n .sort((a, b) => a.name.localeCompare(b.name)),\n [products],\n );\n\n const closeErrors = {\n wastageReason: validateRequired(closeDialog.reason, \"Reason\"),\n };\n const closeIsValid = isClean(closeErrors);\n\n async function doClose() {\n if (!closeDialog.vial) return;\n if (!closeIsValid) {\n closeFieldErrors.markAllTouched();\n return;\n }\n try {\n await closeVial({\n id: uuidv4(),\n openVialId: closeDialog.vial.id,\n wastageReason: closeDialog.reason,\n comments: closeDialog.comments || null,\n });\n showToast({ message: \"Vial closed.\", type: \"success\" });\n setCloseDialog({\n open: false,\n vial: null,\n reason: \"TIME_EXPIRATION\",\n comments: \"\",\n });\n await load();\n } catch (err) {\n showToast({\n message: err?.response?.data?.message ?? err?.message ?? \"Failed\",\n type: \"error\",\n });\n }\n }\n\n // -------- Administer flow --------\n function closeVaccineDialog() {\n vaccineFieldErrors.reset();\n setVaccineDialog(EMPTY_VACCINE_DIALOG);\n }\n\n // Takes the batch pool to filter as an arg so callers can use either the\n // cached `batches` state or a freshly-fetched list (e.g. when the dialog\n // refreshes mid-flow after the user did a pickup elsewhere).\n function fefoBatchesForProduct(productId, pool = batches) {\n const todayMs = new Date().setHours(0, 0, 0, 0);\n return pool\n .filter(\n (b) =>\n b.productId === productId &&\n b.qtyRemaining > 0 &&\n !b.isQuarantined &&\n (!b.expiryDate || new Date(b.expiryDate).getTime() >= todayMs),\n )\n .sort((a, b) => batchExpiryMs(a) - batchExpiryMs(b));\n }\n\n async function proceedFromProductStep() {\n if (!vaccineDialog.productId) {\n vaccineFieldErrors.markAllTouched();\n return;\n }\n setVaccineDialog((d) => ({ ...d, loading: true }));\n try {\n // Re-fetch live batches + facilities here too. The page-mount load only\n // runs once, so a pickup or new facility assignment made elsewhere in\n // the session would otherwise leave the source picker missing options.\n // Vials are already fetched per-product below, so we co-load all three.\n const [list, freshBatches, freshFacilities] = await Promise.all([\n listVialsForProduct(vaccineDialog.productId),\n listBatches().catch(() => batches),\n listFacilities().catch(() => facilities),\n ]);\n setBatches(freshBatches);\n setFacilities(freshFacilities);\n const now = Date.now();\n const usableVials = list\n .filter(\n (v) =>\n v.status === \"OPEN\" &&\n !v.expired &&\n effectiveVialExpiry(v) > now &&\n Number(v.volumeRemainingMl) > 0,\n )\n .sort((a, b) => effectiveVialExpiry(a) - effectiveVialExpiry(b));\n\n // Per-batch OPEN-vial count — used to filter out batches that have already hit\n // the configured maxConcurrentOpenVials cap. Backend will reject those anyway,\n // we just don't want to offer them.\n const openCountByBatch = list.reduce((acc, v) => {\n if (v.status === \"OPEN\") acc[v.batchId] = (acc[v.batchId] || 0) + 1;\n return acc;\n }, {});\n\n const product = products.find((p) => p.id === vaccineDialog.productId);\n const cfg = product?.vaccineConfig;\n const maxConcurrent = cfg?.maxConcurrentOpenVials ?? 2;\n const fefo = fefoBatchesForProduct(\n vaccineDialog.productId,\n freshBatches,\n ).filter((b) => (openCountByBatch[b.id] || 0) < maxConcurrent);\n\n // Derive the source picker options. A source is \"available\" iff the\n // user has either an open usable vial OR a fresh-vialable batch for\n // this product at that owner. Drop sources with no stock so the\n // picker never offers a dead-end.\n const sourceKeys = new Set();\n const sources = [];\n function pushSource(ownerType, ownerId, label, personalSource = \"\") {\n const key = `${ownerType}::${ownerId ?? \"\"}::${personalSource ?? \"\"}`;\n if (sourceKeys.has(key)) return;\n sourceKeys.add(key);\n sources.push({ key, ownerType, ownerId: ownerId ?? null, personalSource: personalSource || null, label });\n }\n // Personal (Outreach) — USER stock minted by a pickup (batch source PICKUP).\n const hasOutreach =\n usableVials.some((v) => v.ownerType === \"USER\" && v.batchSourceType === \"PICKUP\") ||\n fefo.some((b) => b.ownerType === \"USER\" && b.sourceType === \"PICKUP\");\n if (hasOutreach) pushSource(\"USER\", null, \"Personal (Outreach)\", \"OUTREACH\");\n // Personal (Purchase) — USER stock from a personal-use purchase. Only\n // offered when the deployment allows personal purchases AND such stock\n // actually exists for this product.\n const hasPurchase =\n usableVials.some((v) => v.ownerType === \"USER\" && v.batchSourceType === \"PURCHASE\") ||\n fefo.some((b) => b.ownerType === \"USER\" && b.sourceType === \"PURCHASE\");\n if (allowPersonalPurchase && hasPurchase) pushSource(\"USER\", null, \"Personal (Purchase)\", \"PURCHASE\");\n // Facility sources — one per facility owner with stock for this product.\n const facilityIds = new Set();\n usableVials.forEach((v) => {\n if (v.ownerType === \"FACILITY\" && v.ownerId) facilityIds.add(v.ownerId);\n });\n fefo.forEach((b) => {\n if (b.ownerType === \"FACILITY\" && b.ownerId) facilityIds.add(b.ownerId);\n });\n facilityIds.forEach((fid) => {\n // Use the freshly-fetched facility list, not the cached state —\n // setFacilities() above hasn't committed yet at this point in the\n // render cycle.\n const fac = freshFacilities.find((x) => x.id === fid);\n pushSource(\"FACILITY\", fid, `Facility — ${fac?.name ?? fid}`);\n });\n\n // Default source: single available → autopick; multiple → require the\n // operator to commit (no preselection so the picker is the first thing\n // they interact with).\n const defaultSource = sources.length === 1 ? sources[0] : null;\n const defaultOwnerType = defaultSource?.ownerType ?? \"\";\n const defaultOwnerId = defaultSource?.ownerId ?? \"\";\n const defaultPersonalSource = defaultSource?.personalSource ?? \"\";\n\n // Filter candidates by the (possibly-empty) default source. When no\n // default is picked, show empty lists; the operator picks a source first.\n const filtered = filterCandidatesByOwner(\n usableVials,\n fefo,\n defaultOwnerType,\n defaultOwnerId,\n defaultPersonalSource,\n );\n\n const defaultMode =\n filtered.vials.length > 0 ? \"use-existing\" : \"open-new\";\n setVaccineDialog((d) => ({\n ...d,\n loading: false,\n step: \"select\",\n mode: defaultMode,\n ownerType: defaultOwnerType,\n ownerId: defaultOwnerId,\n personalSource: defaultPersonalSource,\n allVials: usableVials,\n allBatches: fefo,\n sources,\n vialCandidates: filtered.vials,\n batchCandidates: filtered.batches,\n selectedVialId: filtered.vials[0]?.id ?? \"\",\n selectedBatchId: filtered.batches[0]?.id ?? \"\",\n volumeMl:\n defaultMode === \"use-existing\"\n ? (filtered.vials[0]?.adultDoseMl ?? cfg?.adultDoseMl ?? \"\")\n : (cfg?.adultDoseMl ?? \"\"),\n }));\n } catch (err) {\n setVaccineDialog((d) => ({ ...d, loading: false }));\n showToast({\n message:\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to load vials\",\n type: \"error\",\n });\n }\n }\n\n // Slice the full vial / batch pools down to just the rows matching the\n // selected administration source. Pulled out so the source-change handler\n // can reuse it without duplicating the predicate.\n function filterCandidatesByOwner(allVials, allBatches, ownerType, ownerId, personalSource) {\n if (!ownerType) return { vials: [], batches: [] };\n // For USER stock, the personal source selects which batch provenance to\n // draw from: OUTREACH → PICKUP batches, PURCHASE → PURCHASE batches.\n const wantSource = ownerType === \"USER\" && personalSource\n ? (personalSource === \"OUTREACH\" ? \"PICKUP\" : \"PURCHASE\")\n : null;\n const matchVial = (v) =>\n v.ownerType === ownerType &&\n (ownerType === \"USER\"\n ? (!wantSource || v.batchSourceType === wantSource)\n : v.ownerId === ownerId);\n const matchBatch = (b) =>\n b.ownerType === ownerType &&\n (ownerType === \"USER\"\n ? (!wantSource || b.sourceType === wantSource)\n : b.ownerId === ownerId);\n return {\n vials: allVials.filter(matchVial),\n batches: allBatches.filter(matchBatch),\n };\n }\n\n function pickSource(sourceKey) {\n const next = vaccineDialog.sources.find((s) => s.key === sourceKey);\n if (!next) return;\n const filtered = filterCandidatesByOwner(\n vaccineDialog.allVials,\n vaccineDialog.allBatches,\n next.ownerType,\n next.ownerId,\n next.personalSource,\n );\n const product = products.find((p) => p.id === vaccineDialog.productId);\n const cfg = product?.vaccineConfig;\n // Pick the same mode-default logic as proceedFromProductStep — favour\n // use-existing when a vial is available, fall back to open-new otherwise.\n const nextMode = filtered.vials.length > 0 ? \"use-existing\" : \"open-new\";\n setVaccineDialog((d) => ({\n ...d,\n ownerType: next.ownerType,\n ownerId: next.ownerId ?? \"\",\n personalSource: next.personalSource ?? \"\",\n mode: nextMode,\n vialCandidates: filtered.vials,\n batchCandidates: filtered.batches,\n selectedVialId: filtered.vials[0]?.id ?? \"\",\n selectedBatchId: filtered.batches[0]?.id ?? \"\",\n // Reset volume so the operator doesn't carry stale dose from a\n // different source — the new vial's adult dose drives it.\n volumeMl:\n nextMode === \"use-existing\"\n ? (filtered.vials[0]?.adultDoseMl ?? cfg?.adultDoseMl ?? \"\")\n : (cfg?.adultDoseMl ?? \"\"),\n }));\n }\n\n function switchMode(nextMode) {\n // Preserve the typed volume across mode flips — the operator is dosing a real patient,\n // the intent doesn't change just because we're picking a different vial/batch.\n setVaccineDialog((d) => ({ ...d, mode: nextMode }));\n }\n\n const vaccineSelectedVial = vaccineDialog.vialCandidates.find(\n (v) => v.id === vaccineDialog.selectedVialId,\n );\n const vaccineSelectedBatch = vaccineDialog.batchCandidates.find(\n (b) => b.id === vaccineDialog.selectedBatchId,\n );\n const vaccineSelectedProduct = products.find(\n (p) => p.id === vaccineDialog.productId,\n );\n const vaccineCfg = vaccineSelectedProduct?.vaccineConfig;\n\n const vaccineErrors = (() => {\n if (vaccineDialog.step === \"product\") {\n return {\n productId: validateRequired(vaccineDialog.productId, \"Vaccine\"),\n };\n }\n const errs = {\n // Source picker is required before the operator can pick a vial /\n // batch — without it the backend can't scope its vial pool.\n ownerType: validateRequired(vaccineDialog.ownerType, \"Source\"),\n volumeMl: validatePositiveNumber(vaccineDialog.volumeMl, \"Volume\", {\n required: true,\n }),\n };\n if (vaccineDialog.mode === \"use-existing\") {\n errs.selectedVialId = validateRequired(\n vaccineDialog.selectedVialId,\n \"Vial\",\n );\n if (!errs.volumeMl && vaccineSelectedVial) {\n const vol = Number(vaccineDialog.volumeMl);\n const remaining = Number(vaccineSelectedVial.volumeRemainingMl);\n if (vol > remaining) {\n errs.volumeMl =\n vaccineDialog.batchCandidates.length > 0\n ? `Only ${remaining.toFixed(2)} ml remaining in this vial — open another vial instead.`\n : `Only ${remaining.toFixed(2)} ml remaining in this vial.`;\n }\n }\n } else {\n errs.selectedBatchId = validateRequired(\n vaccineDialog.selectedBatchId,\n \"Batch\",\n );\n if (!errs.volumeMl && vaccineCfg?.vialVolumeMl) {\n const vol = Number(vaccineDialog.volumeMl);\n const total = Number(vaccineCfg.vialVolumeMl);\n if (vol > total)\n errs.volumeMl = `A fresh vial holds ${total.toFixed(2)} ml`;\n }\n }\n return errs;\n })();\n const vaccineIsValid = isClean(vaccineErrors);\n\n async function doVaccineAdminister() {\n if (!vaccineIsValid) {\n vaccineFieldErrors.markAllTouched();\n return;\n }\n try {\n const payload = {\n id: uuidv4(),\n productId: vaccineDialog.productId,\n volumeMl: Number(vaccineDialog.volumeMl),\n // Owner = administration source. Drives backend's open-vial pool\n // scoping so an outreach administer can't reach into facility stock\n // and vice-versa.\n ownerType: vaccineDialog.ownerType,\n ownerId:\n vaccineDialog.ownerType === \"FACILITY\"\n ? vaccineDialog.ownerId || null\n : null,\n // For USER stock, tell the backend which personal pool to draw from\n // so Outreach and Personal-purchase stay separate.\n personalSource:\n vaccineDialog.ownerType === \"USER\"\n ? vaccineDialog.personalSource || null\n : null,\n };\n if (vaccineDialog.mode === \"use-existing\") {\n payload.openVialId = vaccineSelectedVial.id;\n } else {\n // Backend opens a fresh vial from this batch and administers in the same transaction.\n payload.batchId = vaccineSelectedBatch.id;\n }\n await administerDose(payload);\n showToast({\n message:\n vaccineDialog.mode === \"use-existing\"\n ? \"Dose administered.\"\n : \"New vial opened and dose administered.\",\n type: \"success\",\n });\n closeVaccineDialog();\n await load();\n } catch (err) {\n showToast({\n message: err?.response?.data?.message ?? err?.message ?? \"Failed\",\n type: \"error\",\n });\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={\"view.manage.productManagement.vial\"}>\n <PmPageShell\n title=\"Open Vials\"\n subtitle=\"Open vaccine vials in use. Close them at the end of the day and record any waste.\"\n chips={[\"Vaccine\"]}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n startIcon={<RefreshRoundedIcon />}\n onClick={() => void load()}\n >\n Refresh\n </Button>\n {/* DEMO-ONLY: the Administer Vaccine entry point lives elsewhere\n in production (a vaccinator's flow passes the open vial id in).\n Unhide the button below to dose from this page for demos. */}\n {true && (\n <Button\n variant=\"contained\"\n startIcon={<VaccinesRoundedIcon />}\n onClick={() => {\n vaccineFieldErrors.reset();\n setVaccineDialog({ ...EMPTY_VACCINE_DIALOG, open: true });\n }}\n >\n Administer Vaccine\n </Button>\n )}\n </Stack>\n }\n >\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n {loading ? <LinearProgress sx={{ mb: 2 }} /> : null}\n\n <PmTableCard\n eyebrow=\"In use\"\n title=\"Open vials\"\n subtitle={\n loading\n ? \"Loading…\"\n : `${vials.length} ${vials.length === 1 ? \"vial\" : \"vials\"} currently in use`\n }\n >\n <PmTable dense>\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell>Owner</TableCell>\n <TableCell>Opened</TableCell>\n <TableCell>Expires</TableCell>\n <TableCell>Volume remaining</TableCell>\n <TableCell>Status</TableCell>\n <TableCell align=\"right\">Action</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {vials.length === 0 && !loading ? (\n <PmEmptyRow\n colSpan={8}\n icon={VaccinesRoundedIcon}\n title=\"No open vials\"\n hint=\"Use Administer Vaccine to open and dose a vial.\"\n />\n ) : (\n vials.map((v) => {\n const usage = v.volumeTotalMl\n ? (1 -\n Number(v.volumeRemainingMl) /\n Number(v.volumeTotalMl)) *\n 100\n : 0;\n return (\n <TableRow key={v.id} hover>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {v.productName}\n </Typography>\n </TableCell>\n <TableCell>\n {v.lotNumber ? (\n <Box\n component=\"span\"\n sx={{\n display: \"inline-flex\",\n px: 0.7,\n py: 0.1,\n borderRadius: 1,\n bgcolor: \"grey.100\",\n fontFamily: \"monospace\",\n fontSize: \"0.72rem\",\n fontWeight: 700,\n }}\n >\n {v.lotNumber}\n </Box>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {v.ownerName ||\n (v.ownerType === \"USER\"\n ? \"Personal\"\n : \"Facility\")}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {v.ownerType ?? \"—\"}\n </Typography>\n </TableCell>\n <TableCell>\n <Typography\n variant=\"caption\"\n sx={{ fontWeight: 600 }}\n >\n {formatDateTime(v.openedAt)}\n </Typography>\n </TableCell>\n <TableCell>\n <Tooltip\n title={\n v.expired ? \"Expired — close out immediately\" : \"\"\n }\n >\n <Chip\n size=\"small\"\n color={v.expired ? \"error\" : \"success\"}\n label={formatDateTime(v.expiresAt)}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n </Tooltip>\n </TableCell>\n <TableCell>\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n spacing={1}\n >\n <Typography\n variant=\"body2\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n minWidth: 80,\n }}\n >\n {formatNumber(v.volumeRemainingMl)} /{\" \"}\n {formatNumber(v.volumeTotalMl)} ml\n </Typography>\n <Box sx={{ width: 80 }}>\n <LinearProgress\n variant=\"determinate\"\n value={usage}\n sx={{ height: 4, borderRadius: 2 }}\n color={usage > 75 ? \"warning\" : \"primary\"}\n />\n </Box>\n </Stack>\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n color={v.expired ? \"error\" : \"info\"}\n label={\n v.expired\n ? \"Expired\"\n : labelOf(VIAL_STATUSES, v.status)\n }\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n </TableCell>\n <TableCell align=\"right\">\n {canWrite ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"error\"\n onClick={() => {\n closeFieldErrors.reset();\n setCloseDialog({\n open: true,\n vial: v,\n reason: \"TIME_EXPIRATION\",\n comments: \"\",\n });\n }}\n >\n Close\n </Button>\n ) : null}\n </TableCell>\n </TableRow>\n );\n })\n )}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n\n <Dialog\n open={vaccineDialog.open}\n onClose={closeVaccineDialog}\n fullWidth\n maxWidth=\"md\"\n >\n <DialogTitle>\n {vaccineDialog.step === \"product\"\n ? \"Administer Vaccine — Select Vaccine\"\n : vaccineDialog.mode === \"use-existing\"\n ? \"Administer Vaccine — Select Open Vial\"\n : \"Administer Vaccine — Open & Administer\"}\n </DialogTitle>\n <DialogContent dividers>\n {vaccineDialog.step === \"product\" ? (\n <FormControl\n required\n fullWidth\n size=\"small\"\n sx={{ mt: 1 }}\n error={\n vaccineFieldErrors.show(\"productId\") &&\n !!vaccineErrors.productId\n }\n >\n <InputLabel>Vaccine</InputLabel>\n <Select\n label=\"Vaccine\"\n value={vaccineDialog.productId}\n onChange={(e) =>\n setVaccineDialog({\n ...vaccineDialog,\n productId: e.target.value,\n })\n }\n onBlur={vaccineFieldErrors.onBlur(\"productId\")}\n >\n {vaccineProducts.length === 0 ? (\n <MenuItem value=\"\" disabled>\n <em>No active vaccine products</em>\n </MenuItem>\n ) : null}\n {vaccineProducts.map((p) => (\n <MenuItem key={p.id} value={p.id}>\n {p.name}\n {p.vaccineConfig?.vaccineCode\n ? ` · ${p.vaccineConfig.vaccineCode}`\n : \"\"}\n </MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {vaccineFieldErrors.show(\"productId\") &&\n vaccineErrors.productId\n ? vaccineErrors.productId\n : \"Open vials for this vaccine will be shown next. If none are open, a fresh vial can be opened on the same screen.\"}\n </FormHelperText>\n </FormControl>\n ) : (\n <Stack spacing={2} sx={{ pt: 1 }}>\n {vaccineDialog.loading ? <LinearProgress /> : null}\n\n {/* Source picker — drives which open vials / batches the\n operator can pick from. Hidden when only one source is\n available (autoselected). */}\n {vaccineDialog.sources.length === 0 ? (\n <Alert severity=\"warning\">\n No stock available for this vaccine — neither in your\n personal stock nor at an assigned facility.\n </Alert>\n ) : vaccineDialog.sources.length > 1 ? (\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={\n vaccineFieldErrors.show(\"ownerType\") &&\n !!vaccineErrors.ownerType\n }\n >\n <InputLabel>Administer from</InputLabel>\n <Select\n label=\"Administer from\"\n value={\n vaccineDialog.ownerType\n ? `${vaccineDialog.ownerType}::${vaccineDialog.ownerId ?? \"\"}::${vaccineDialog.personalSource ?? \"\"}`\n : \"\"\n }\n onChange={(e) => pickSource(e.target.value)}\n onBlur={vaccineFieldErrors.onBlur(\"ownerType\")}\n >\n {vaccineDialog.sources.map((s) => (\n <MenuItem key={s.key} value={s.key}>\n {s.label}\n </MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {vaccineFieldErrors.show(\"ownerType\") &&\n vaccineErrors.ownerType\n ? vaccineErrors.ownerType\n : \"Personal stock (outreach) and facility stock are separate vial pools — pick which one this dose comes from.\"}\n </FormHelperText>\n </FormControl>\n ) : (\n <Alert severity=\"info\">\n Administering from{\" \"}\n <strong>{vaccineDialog.sources[0]?.label}</strong>.\n </Alert>\n )}\n\n {/* Outreach session context — shown when drawing from personal\n pickup stock so the operator sees which session this is. */}\n {vaccineDialog.ownerType === \"USER\" &&\n vaccineDialog.personalSource === \"OUTREACH\" &&\n openSessions[0] ? (\n <Box\n sx={{\n p: 1.5,\n borderRadius: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n bgcolor: (t) => t.palette.action.hover,\n }}\n >\n <Typography\n variant=\"overline\"\n sx={{ fontWeight: 800, letterSpacing: 0.6, color: \"text.secondary\" }}\n >\n Outreach session\n </Typography>\n <Typography variant=\"body2\" sx={{ fontWeight: 800 }}>\n {openSessions[0].sessionName || \"—\"}\n </Typography>\n {openSessions[0].sessionSite ? (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: \"block\" }}>\n Site: {openSessions[0].sessionSite}\n </Typography>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: \"block\" }}>\n {openSessions[0].sessionStartTime\n ? formatDateTime(openSessions[0].sessionStartTime)\n : \"—\"}\n {openSessions[0].sessionExpectedEndTime\n ? ` → ${formatDateTime(openSessions[0].sessionExpectedEndTime)}`\n : \"\"}\n </Typography>\n {openSessions[0].sessionDescription ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: \"block\", mt: 0.5, whiteSpace: \"pre-wrap\" }}\n >\n {openSessions[0].sessionDescription}\n </Typography>\n ) : null}\n </Box>\n ) : null}\n\n {vaccineDialog.mode === \"use-existing\" ? (\n <>\n {!vaccineDialog.ownerType ? (\n <Alert severity=\"info\">\n Pick an administration source above to see the available\n open vials.\n </Alert>\n ) : (\n (() => {\n // Only offer \"Open another vial\" when no existing open vial can\n // actually hold the requested dose. This stops the operator from\n // opening a new vial unnecessarily and accumulating leftover\n // partial vials.\n const vol = Number(vaccineDialog.volumeMl);\n const validVol = !Number.isNaN(vol) && vol > 0;\n const someVialFits =\n validVol &&\n vaccineDialog.vialCandidates.some(\n (v) => Number(v.volumeRemainingMl) >= vol,\n );\n return (\n <Alert\n severity=\"info\"\n action={\n someVialFits ? null : (\n <Button\n color=\"inherit\"\n size=\"small\"\n onClick={() => switchMode(\"open-new\")}\n >\n Open another vial instead\n </Button>\n )\n }\n >\n {someVialFits\n ? \"Pick an open vial — earliest-expiry-first is preselected. An existing vial can hold the requested dose, so no new vial is needed.\"\n : \"Pick an open vial — earliest-expiry-first is preselected. The requested dose does not fit in any open vial; you can open another vial from stock.\"}\n </Alert>\n );\n })()\n )}\n <TableContainer>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell />\n <TableCell sx={{ fontWeight: 800 }}>Lot</TableCell>\n <TableCell sx={{ fontWeight: 800 }}>\n Vial expires\n </TableCell>\n <TableCell sx={{ fontWeight: 800 }}>\n Batch expires\n </TableCell>\n <TableCell sx={{ fontWeight: 800 }} align=\"right\">\n Remaining (ml)\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {vaccineDialog.vialCandidates.length === 0 ? (\n <TableRow>\n <TableCell\n colSpan={5}\n sx={{ py: 4, textAlign: \"center\" }}\n >\n <Stack alignItems=\"center\" spacing={1}>\n <VaccinesRoundedIcon\n sx={{\n fontSize: 36,\n color: \"text.disabled\",\n opacity: 0.7,\n }}\n />\n <Typography\n variant=\"body2\"\n sx={{ fontWeight: 700 }}\n >\n No usable open vials for this vaccine\n </Typography>\n {vaccineDialog.batchCandidates.length > 0 ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={() => switchMode(\"open-new\")}\n >\n Open a new vial instead\n </Button>\n ) : (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n >\n No stock available either — receive a\n purchase first.\n </Typography>\n )}\n </Stack>\n </TableCell>\n </TableRow>\n ) : (\n vaccineDialog.vialCandidates.map((v, idx) => {\n const selected =\n vaccineDialog.selectedVialId === v.id;\n return (\n <TableRow\n key={v.id}\n hover\n selected={selected}\n onClick={() =>\n setVaccineDialog({\n ...vaccineDialog,\n selectedVialId: v.id,\n volumeMl:\n v.adultDoseMl ?? vaccineDialog.volumeMl,\n })\n }\n sx={{ cursor: \"pointer\" }}\n >\n <TableCell padding=\"checkbox\">\n <Radio checked={selected} />\n </TableCell>\n <TableCell sx={{ fontFamily: \"monospace\" }}>\n {v.lotNumber || \"—\"}\n {idx === 0 ? (\n <Chip\n size=\"small\"\n color=\"primary\"\n label=\"Use first\"\n sx={{ ml: 1 }}\n />\n ) : null}\n </TableCell>\n <TableCell>\n {formatDateTime(v.expiresAt)}\n </TableCell>\n <TableCell>\n {v.batchExpiryDate\n ? formatDate(v.batchExpiryDate)\n : \"—\"}\n </TableCell>\n <TableCell\n align=\"right\"\n sx={{ fontWeight: 700 }}\n >\n {formatNumber(v.volumeRemainingMl)} /{\" \"}\n {formatNumber(v.volumeTotalMl)}\n </TableCell>\n </TableRow>\n );\n })\n )}\n </TableBody>\n </Table>\n </TableContainer>\n </>\n ) : (\n <>\n {/* Suppress the empty-state warnings until the source has\n been picked — with no source committed the candidate\n list is intentionally empty and the operator just\n needs to choose, not read a \"no stock\" warning. */}\n {!vaccineDialog.ownerType ? (\n <Alert severity=\"info\">\n Pick an administration source above to see the available\n batches.\n </Alert>\n ) : vaccineDialog.batchCandidates.length === 0 ? (\n <Alert\n severity=\"error\"\n action={\n vaccineDialog.vialCandidates.length > 0 ? (\n <Button\n color=\"inherit\"\n size=\"small\"\n onClick={() => switchMode(\"use-existing\")}\n >\n Use an open vial\n </Button>\n ) : null\n }\n >\n No batches available — every batch is either out of\n stock, expired, or already at the maximum concurrent\n open vials. Receive a purchase or finish the open vials\n first.\n </Alert>\n ) : (\n <>\n <Alert\n severity=\"warning\"\n action={\n vaccineDialog.vialCandidates.length > 0 ? (\n <Button\n color=\"inherit\"\n size=\"small\"\n onClick={() => switchMode(\"use-existing\")}\n >\n Use an open vial instead\n </Button>\n ) : null\n }\n >\n {vaccineDialog.vialCandidates.length > 0\n ? \"Opening a new vial from stock. Existing open vials are still available if the dose fits.\"\n : \"No open vials for this vaccine. A new vial will be opened from the selected batch and the dose recorded in one step.\"}\n </Alert>\n <TableContainer>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell />\n <TableCell sx={{ fontWeight: 800 }}>\n Lot\n </TableCell>\n <TableCell sx={{ fontWeight: 800 }}>\n Batch expires\n </TableCell>\n <TableCell\n sx={{ fontWeight: 800 }}\n align=\"right\"\n >\n Qty available\n </TableCell>\n <TableCell sx={{ fontWeight: 800 }}>\n Storage\n </TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {vaccineDialog.batchCandidates.map((b, idx) => {\n const selected =\n vaccineDialog.selectedBatchId === b.id;\n return (\n <TableRow\n key={b.id}\n hover\n selected={selected}\n onClick={() =>\n setVaccineDialog({\n ...vaccineDialog,\n selectedBatchId: b.id,\n })\n }\n sx={{ cursor: \"pointer\" }}\n >\n <TableCell padding=\"checkbox\">\n <Radio checked={selected} />\n </TableCell>\n <TableCell sx={{ fontFamily: \"monospace\" }}>\n {b.lotNumber || \"—\"}\n {idx === 0 ? (\n <Chip\n size=\"small\"\n color=\"primary\"\n label=\"Use first\"\n sx={{ ml: 1 }}\n />\n ) : null}\n </TableCell>\n <TableCell>\n {b.expiryDate\n ? formatDate(b.expiryDate)\n : \"—\"}\n </TableCell>\n <TableCell\n align=\"right\"\n sx={{ fontWeight: 700 }}\n >\n {b.qtyRemaining}\n </TableCell>\n <TableCell>\n {b.storageUnitName || \"—\"}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </TableContainer>\n </>\n )}\n </>\n )}\n\n {(vaccineDialog.mode === \"use-existing\" &&\n vaccineSelectedVial) ||\n (vaccineDialog.mode === \"open-new\" && vaccineSelectedBatch) ? (\n <Stack\n direction=\"row\"\n spacing={1}\n alignItems=\"center\"\n flexWrap=\"wrap\"\n >\n {vaccineDialog.mode === \"use-existing\" ? (\n <>\n {vaccineSelectedVial.adultDoseMl ? (\n <Button\n size=\"small\"\n onClick={() =>\n setVaccineDialog({\n ...vaccineDialog,\n volumeMl: vaccineSelectedVial.adultDoseMl,\n })\n }\n >\n Adult dose ({vaccineSelectedVial.adultDoseMl} ml)\n </Button>\n ) : null}\n {vaccineSelectedVial.childDoseMl ? (\n <Button\n size=\"small\"\n onClick={() =>\n setVaccineDialog({\n ...vaccineDialog,\n volumeMl: vaccineSelectedVial.childDoseMl,\n })\n }\n >\n Child dose ({vaccineSelectedVial.childDoseMl} ml)\n </Button>\n ) : null}\n </>\n ) : (\n <>\n {vaccineCfg?.adultDoseMl ? (\n <Button\n size=\"small\"\n onClick={() =>\n setVaccineDialog({\n ...vaccineDialog,\n volumeMl: vaccineCfg.adultDoseMl,\n })\n }\n >\n Adult dose ({vaccineCfg.adultDoseMl} ml)\n </Button>\n ) : null}\n {vaccineCfg?.childDoseMl ? (\n <Button\n size=\"small\"\n onClick={() =>\n setVaccineDialog({\n ...vaccineDialog,\n volumeMl: vaccineCfg.childDoseMl,\n })\n }\n >\n Child dose ({vaccineCfg.childDoseMl} ml)\n </Button>\n ) : null}\n </>\n )}\n <TextField\n required\n label=\"Volume (ml)\"\n size=\"small\"\n type=\"number\"\n inputProps={{\n step: \"0.01\",\n min: 0,\n max:\n vaccineDialog.mode === \"use-existing\"\n ? vaccineSelectedVial?.volumeRemainingMl\n : vaccineCfg?.vialVolumeMl,\n }}\n value={vaccineDialog.volumeMl}\n onChange={(e) =>\n setVaccineDialog({\n ...vaccineDialog,\n volumeMl: e.target.value,\n })\n }\n onBlur={vaccineFieldErrors.onBlur(\"volumeMl\")}\n error={\n vaccineFieldErrors.show(\"volumeMl\") &&\n !!vaccineErrors.volumeMl\n }\n helperText={\n vaccineFieldErrors.show(\"volumeMl\")\n ? vaccineErrors.volumeMl\n : \"\"\n }\n />\n </Stack>\n ) : null}\n </Stack>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={closeVaccineDialog}>Cancel</Button>\n {vaccineDialog.step === \"select\" ? (\n <Button\n onClick={() =>\n setVaccineDialog({\n ...EMPTY_VACCINE_DIALOG,\n open: true,\n productId: vaccineDialog.productId,\n })\n }\n >\n Back\n </Button>\n ) : null}\n {vaccineDialog.step === \"product\" ? (\n <Button\n variant=\"contained\"\n disabled={!vaccineDialog.productId || vaccineDialog.loading}\n onClick={() => void proceedFromProductStep()}\n >\n Next\n </Button>\n ) : (\n <Button\n variant=\"contained\"\n disabled={!vaccineIsValid}\n onClick={() => void doVaccineAdminister()}\n >\n {vaccineDialog.mode === \"use-existing\"\n ? \"Administer\"\n : \"Open & Administer\"}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n\n <Dialog\n open={closeDialog.open}\n onClose={() =>\n setCloseDialog({\n open: false,\n vial: null,\n reason: \"TIME_EXPIRATION\",\n comments: \"\",\n })\n }\n fullWidth\n maxWidth=\"sm\"\n >\n <DialogTitle>Close Vial (record remaining as wastage)</DialogTitle>\n <DialogContent dividers>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\">\n {closeDialog.vial?.productName} ·{\" \"}\n {formatNumber(closeDialog.vial?.volumeRemainingMl)} ml will be\n marked as wasted.\n </Typography>\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={\n closeFieldErrors.show(\"wastageReason\") &&\n !!closeErrors.wastageReason\n }\n >\n <InputLabel>Reason</InputLabel>\n <Select\n label=\"Reason\"\n value={closeDialog.reason}\n onChange={(e) =>\n setCloseDialog({ ...closeDialog, reason: e.target.value })\n }\n onBlur={closeFieldErrors.onBlur(\"wastageReason\")}\n >\n {WASTAGE_REASONS.map((r) => (\n <MenuItem key={r.value} value={r.value}>\n {r.label}\n </MenuItem>\n ))}\n </Select>\n {closeFieldErrors.show(\"wastageReason\") &&\n closeErrors.wastageReason ? (\n <FormHelperText>{closeErrors.wastageReason}</FormHelperText>\n ) : null}\n </FormControl>\n <TextField\n label=\"Comments\"\n size=\"small\"\n multiline\n minRows={2}\n fullWidth\n value={closeDialog.comments}\n onChange={(e) =>\n setCloseDialog({ ...closeDialog, comments: e.target.value })\n }\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() =>\n setCloseDialog({\n open: false,\n vial: null,\n reason: \"TIME_EXPIRATION\",\n comments: \"\",\n })\n }\n >\n Cancel\n </Button>\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={!closeIsValid}\n onClick={() => void doClose()}\n >\n Close Vial\n </Button>\n </DialogActions>\n </Dialog>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"currency-ClRLIEg2.js","names":[],"sources":["../src/utils/workspaces.js","../src/services/pmApi.js","../src/utils/countries.js","../src/utils/settingsStore.js","../src/components/PmPageShell.jsx","../src/utils/permissions.js","../src/components/PmNav.jsx","../src/components/PmGuard.jsx","../src/utils/currency.js"],"sourcesContent":["// Information architecture: the module is split into three \"workspaces\" that\n// match real-world roles in a clinic / store.\n//\n// Catalog — master-data setup (configured occasionally by an admin)\n// Operations — daily floor work (stock, sales, vials, payments)\n// Ledger — read-only audit trail (every stock + money movement)\n//\n// Each workspace has its own accent hue so the operator knows which\n// \"section\" of the tool they're in without explicit labels everywhere.\n// Dashboard sits outside any workspace — it's the landing page.\n\nimport SpaceDashboardRoundedIcon from '@mui/icons-material/SpaceDashboardRounded';\nimport SettingsRoundedIcon from '@mui/icons-material/SettingsRounded';\nimport LayersRoundedIcon from '@mui/icons-material/LayersRounded';\nimport CategoryRoundedIcon from '@mui/icons-material/CategoryRounded';\nimport SellRoundedIcon from '@mui/icons-material/SellRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\nimport WarehouseRoundedIcon from '@mui/icons-material/WarehouseRounded';\nimport LocalShippingRoundedIcon from '@mui/icons-material/LocalShippingRounded';\nimport WidgetsRoundedIcon from '@mui/icons-material/WidgetsRounded';\nimport ShoppingCartRoundedIcon from '@mui/icons-material/ShoppingCartRounded';\nimport FactCheckRoundedIcon from '@mui/icons-material/FactCheckRounded';\nimport MoveDownRoundedIcon from '@mui/icons-material/MoveDownRounded';\nimport SwapHorizRoundedIcon from '@mui/icons-material/SwapHorizRounded';\nimport PointOfSaleRoundedIcon from '@mui/icons-material/PointOfSaleRounded';\nimport VaccinesRoundedIcon from '@mui/icons-material/VaccinesRounded';\nimport PaymentsRoundedIcon from '@mui/icons-material/PaymentsRounded';\nimport ReceiptLongRoundedIcon from '@mui/icons-material/ReceiptLongRounded';\n\n// Permission ids match the backend's api_permission rows. Treated as plain\n// strings here — there's no parallel constants table; the featureJson at\n// runtime is the source of truth for what each user has been granted.\nexport const DASHBOARD = {\n id: 'dashboard',\n label: 'Dashboard',\n path: '/ui/medplat/manage/pm',\n icon: SpaceDashboardRoundedIcon,\n accent: '#6366f1', // indigo — the \"home\" accent\n accentDark: '#4338ca',\n permissionId: 'view.manage.productManagement',\n};\n\n// Settings sits outside the three workspaces — administrative, rarely used,\n// available as its own pill on the nav row alongside Dashboard.\nexport const SETTINGS = {\n id: 'settings',\n label: 'Settings',\n path: '/ui/medplat/manage/pm/settings',\n icon: SettingsRoundedIcon,\n accent: '#64748b', // slate — quiet, non-competing\n accentDark: '#334155',\n permissionId: 'view.manage.productManagement.settings',\n};\n\nexport const WORKSPACES = [\n {\n id: 'catalog',\n label: 'Catalog',\n description: 'Define what you sell and where you keep it. Master data — configured once, used everywhere.',\n icon: LayersRoundedIcon,\n accent: '#8b5cf6', // violet\n accentDark: '#6d28d9',\n accentSoft: '#ede9fe',\n items: [\n { id: 'categories', label: 'Categories', path: '/ui/medplat/manage/pm/categories', icon: CategoryRoundedIcon, permissionId: 'view.manage.productManagement.category' },\n { id: 'brands', label: 'Brands', path: '/ui/medplat/manage/pm/brands', icon: SellRoundedIcon, permissionId: 'view.manage.productManagement.brand' },\n { id: 'products', label: 'Products', path: '/ui/medplat/manage/pm/products', icon: Inventory2RoundedIcon, permissionId: 'view.manage.productManagement.product' },\n { id: 'storage', label: 'Storage Units', path: '/ui/medplat/manage/pm/storage-units', icon: WarehouseRoundedIcon, permissionId: 'view.manage.productManagement.storageUnit' },\n ],\n },\n {\n id: 'operations',\n label: 'Operations',\n description: 'Run the day — receive stock, sell to customers, administer vaccines, collect payments.',\n icon: LocalShippingRoundedIcon,\n accent: '#10b981', // emerald\n accentDark: '#047857',\n accentSoft: '#d1fae5',\n items: [\n // Purchase tabs are mutually exclusive based on the deployment-level\n // requirePurchaseConfirmation setting:\n // confirmation ON → \"Purchase Requests\" page with My Requests +\n // Pending tabs (per-tab perm-gated). An approver\n // with only the approve perm sees just Pending.\n // confirmation OFF → \"Purchases\" page with the direct-purchase list.\n // Both surface under either view.purchase or approve.purchase — the\n // approver-only role still gets the nav entry to reach their queue.\n { id: 'purchase-requests', label: 'Purchase Requests', path: '/ui/medplat/manage/pm/purchase-requests', icon: FactCheckRoundedIcon,\n anyOfPermissionIds: ['view.manage.productManagement.purchase', 'approve.manage.productManagement.purchase'],\n purchaseApprovalGroup: true },\n { id: 'purchases', label: 'Purchases', path: '/ui/medplat/manage/pm/purchases', icon: ShoppingCartRoundedIcon,\n permissionId: 'view.manage.productManagement.purchase', hiddenWhenConfirmationOn: true },\n { id: 'inventory', label: 'Inventory', path: '/ui/medplat/manage/pm/inventory', icon: WidgetsRoundedIcon, permissionId: 'view.manage.productManagement.inventory' },\n // Pickups — user checks out facility stock onto personal inventory and\n // hands it back later. Wastage gets recorded at return if there's a gap.\n { id: 'pickups', label: 'Pickups', path: '/ui/medplat/manage/pm/pickups', icon: MoveDownRoundedIcon, permissionId: 'view.manage.productManagement.pickup' },\n // Transfers — two-step facility ↔ facility movement (sender ships,\n // receiver confirms). Permission-gated so deployments that don't use\n // transfers can leave the menu out by withholding the perm.\n { id: 'transfers', label: 'Transfers', path: '/ui/medplat/manage/pm/transfers', icon: SwapHorizRoundedIcon, permissionId: 'view.manage.productManagement.transfer' },\n // Sales + Payments items also AND with settings.manageSales; PmNav and\n // DashboardPage hide them when the deployment isn't running sales.\n { id: 'sales', label: 'Sales', path: '/ui/medplat/manage/pm/sales', icon: PointOfSaleRoundedIcon, permissionId: 'view.manage.productManagement.sale', salesGroup: true },\n { id: 'vials', label: 'Vial', path: '/ui/medplat/manage/pm/vials', icon: VaccinesRoundedIcon, permissionId: 'view.manage.productManagement.vial' },\n { id: 'payments', label: 'Payments', path: '/ui/medplat/manage/pm/payments', icon: PaymentsRoundedIcon, permissionId: 'view.manage.productManagement.payment', salesGroup: true },\n ],\n },\n {\n id: 'ledger',\n label: 'Ledger',\n description: 'Every stock and money movement, append-only, with full lot / volume / monetary detail.',\n icon: ReceiptLongRoundedIcon,\n accent: '#f59e0b', // amber\n accentDark: '#b45309',\n accentSoft: '#fef3c7',\n items: [\n { id: 'transactions', label: 'Transactions', path: '/ui/medplat/manage/pm/transactions', icon: ReceiptLongRoundedIcon, permissionId: 'view.manage.productManagement.ledger' },\n ],\n },\n];\n\n/**\n * Filter the workspace tree against (a) the current operator's PM permission\n * set and (b) the manageSales settings toggle. An item is visible iff its\n * permissionId is granted AND (manageSales is on OR it isn't in the sales\n * group). A workspace tab survives iff at least one of its items survives.\n *\n * Both PmNav and DashboardPage consume this so the two stay in lock-step.\n */\nexport function getVisibleWorkspaces(perms, settings) {\n const manageSales = settings?.manageSales !== false;\n const requirePurchaseConfirmation = settings?.requirePurchaseConfirmation === true;\n return WORKSPACES\n .map((ws) => ({\n ...ws,\n items: ws.items.filter((it) => {\n // Items can declare either a single `permissionId` or\n // `anyOfPermissionIds` for the \"user needs any of these perms\" case\n // (used by the Purchase Requests entry — visible to both viewers\n // and approvers).\n const hasPerm = it.anyOfPermissionIds\n ? !!perms?.hasAny?.(it.anyOfPermissionIds)\n : !!perms?.has?.(it.permissionId);\n return hasPerm\n && (manageSales || !it.salesGroup)\n && (requirePurchaseConfirmation || !it.purchaseApprovalGroup)\n && (!requirePurchaseConfirmation || !it.hiddenWhenConfirmationOn);\n }),\n }))\n .filter((ws) => ws.items.length > 0);\n}\n\n// Locate the workspace that owns a given pathname. The Dashboard route is\n// intentionally not in any workspace; we surface it via a dedicated pill.\nexport function findWorkspaceForPath(pathname) {\n if (!pathname) return null;\n for (const ws of WORKSPACES) {\n if (ws.items.some((it) => pathname.startsWith(it.path))) return ws;\n }\n return null;\n}\n\nexport function findItemForPath(pathname) {\n if (!pathname) return null;\n for (const ws of WORKSPACES) {\n const hit = ws.items.find((it) => pathname.startsWith(it.path));\n if (hit) return { workspace: ws, item: hit };\n }\n return null;\n}\n","import { api } from \"@/common/interceptors/AxiosInterceptor\";\nimport { execute } from \"@/common/services/QueryBuilder.js\";\n\nconst BASE = \"/pm\";\n\n// ---------- Module settings (subject mappings + currency) ----------\nexport async function getSettings() {\n const response = await api.get(`${BASE}/settings`);\n return response.data;\n}\n\nexport async function upsertSettings(payload) {\n const response = await api.put(`${BASE}/settings`, payload);\n return response.data;\n}\n\nexport async function listPublicTables() {\n const response = await api.get(`${BASE}/settings/tables`);\n return response.data ?? [];\n}\n\nexport async function listUuidColumns(tableName) {\n const response = await api.get(`${BASE}/settings/tables/${encodeURIComponent(tableName)}/uuid-columns`);\n return response.data ?? [];\n}\n\nexport async function listTextColumns(tableName) {\n const response = await api.get(`${BASE}/settings/tables/${encodeURIComponent(tableName)}/text-columns`);\n return response.data ?? [];\n}\n\nexport async function listDateColumns(tableName) {\n const response = await api.get(`${BASE}/settings/tables/${encodeURIComponent(tableName)}/date-columns`);\n return response.data ?? [];\n}\n\n// ---------- Reference data (from medplat shared query master) ----------\n// Returns rows from medplat_immunization_master via the FETCH_IMMUNISATION_MASTER query.\n// Each row carries id, vaccine_name, vaccine_code, etc. We expose only what the\n// product-management UI needs.\nexport async function getImmunizationMaster() {\n const response = await execute(\n { code: \"FETCH_IMMUNISATION_MASTER\", parameters: {} },\n { showLoader: false }\n );\n const rows = response?.data?.result ?? [];\n return rows.map((row) => ({\n id: row.id,\n vaccineCode: row.vaccine_code,\n vaccineName: row.vaccine_name,\n }));\n}\n\n// ---------- Categories ----------\n// `includeInactive` is for management screens that need to see (and re-activate)\n// deactivated rows; dropdowns/pickers leave it false so they only show active.\nexport async function getCategories(lastModifiedDate, includeInactive = false) {\n const params = {};\n if (lastModifiedDate) params.lastModifiedDate = lastModifiedDate;\n if (includeInactive) params.includeInactive = true;\n const response = await api.get(`${BASE}/categories`, { params });\n return response.data ?? [];\n}\n\nexport async function upsertCategory(payload) {\n const response = await api.post(`${BASE}/categories`, payload);\n return response.data;\n}\n\n// ---------- Brands ----------\nexport async function getBrands(lastModifiedDate, includeInactive = false) {\n const params = {};\n if (lastModifiedDate) params.lastModifiedDate = lastModifiedDate;\n if (includeInactive) params.includeInactive = true;\n const response = await api.get(`${BASE}/brands`, { params });\n return response.data ?? [];\n}\n\nexport async function upsertBrand(payload) {\n const response = await api.post(`${BASE}/brands`, payload);\n return response.data;\n}\n\n// ---------- Products ----------\nexport async function getProducts(lastModifiedDate, includeInactive = false) {\n const params = {};\n if (lastModifiedDate) params.lastModifiedDate = lastModifiedDate;\n if (includeInactive) params.includeInactive = true;\n const response = await api.get(`${BASE}/products`, { params });\n return response.data ?? [];\n}\n\nexport async function getProduct(productId) {\n const response = await api.get(`${BASE}/products/${productId}`);\n return response.data;\n}\n\nexport async function upsertProduct(payload) {\n const response = await api.post(`${BASE}/products`, payload);\n return response.data;\n}\n\n// ---------- Storage Units ----------\nexport async function getStorageUnits({ facilityId, activeOnly = false } = {}) {\n const params = { activeOnly };\n if (facilityId) params.facilityId = facilityId;\n const response = await api.get(`${BASE}/storage-units`, { params });\n return response.data ?? [];\n}\n\nexport async function upsertStorageUnit(payload) {\n const response = await api.post(`${BASE}/storage-units`, payload);\n return response.data;\n}\n\n// ---------- Health facilities ----------\n// Scoped server-side to the current operator's assignments\n// (user_health_infrastructure JOIN health_infrastructure_details). The list\n// is small enough that the picker filters client-side — no q/limit.\nexport async function listFacilities() {\n const response = await api.get(`${BASE}/facilities`);\n return response.data ?? [];\n}\n\n// Bulk-hydrate facility names for a known set of ids. Used by list views\n// that already have saved ownerIds and just need to render display names\n// without pulling the full directory.\nexport async function getFacilitiesByIds(ids) {\n const list = (ids ?? []).filter(Boolean);\n if (list.length === 0) return [];\n const response = await api.get(`${BASE}/facilities/by-ids`, {\n params: { ids: list.join(',') },\n });\n return response.data ?? [];\n}\n\n// ---------- Members ----------\n// Sales/Payments pages receive the memberId via route param, so the API\n// surface is profile-by-id (rich card payload) + bulk-by-ids (display\n// names for list tables). No search endpoint — the picker is upstream.\n\n// Full member profile (identifier, family id, name, dob) for one member.\n// Returns null when the row doesn't exist or the mapping is incomplete.\nexport async function getMemberProfile(memberId) {\n if (!memberId) return null;\n try {\n const response = await api.get(`${BASE}/members/${memberId}`);\n return response.data ?? null;\n } catch (err) {\n if (err?.response?.status === 404) return null;\n throw err;\n }\n}\n\n// Bulk-hydrate display names for a known set of member ids. Used by list\n// views (sales / payments history) that already have memberIds and need\n// names.\nexport async function getMembersByIds(ids) {\n const list = (ids ?? []).filter(Boolean);\n if (list.length === 0) return [];\n const response = await api.get(`${BASE}/members/by-ids`, {\n params: { ids: list.join(',') },\n });\n return response.data ?? [];\n}\n\n// Per-member outstanding dues, aggregated from pm_sale.due_amount on the\n// backend. Returns [{ memberId, totalDue }]; only members with non-zero dues\n// are included.\nexport async function getMemberOutstandingDues() {\n const response = await api.get(`${BASE}/inventory/dues/by-member`);\n return response.data ?? [];\n}\n\n// Full dues details for one member: total, oldestDueDate, lastSaleDate, and\n// the open sales with item-level breakdown. Powers the Collect Payment screen.\nexport async function getMemberDuesDetails(memberId) {\n const response = await api.get(`${BASE}/inventory/dues/by-member/${memberId}`);\n return response.data;\n}\n\n// ---------- Inventory (read) ----------\nexport async function getStockSnapshot(updatedSince) {\n const response = await api.get(`${BASE}/inventory/stock`, {\n params: updatedSince ? { updatedSince } : {},\n });\n return response.data;\n}\n\nexport async function listBatches() {\n const response = await api.get(`${BASE}/inventory/batches`);\n return response.data ?? [];\n}\n\nexport async function getBatch(batchId) {\n const response = await api.get(`${BASE}/inventory/batches/${batchId}`);\n return response.data;\n}\n\nexport async function listOpenVials() {\n const response = await api.get(`${BASE}/inventory/vials/open`);\n return response.data ?? [];\n}\n\nexport async function listVialsForProduct(productId) {\n const response = await api.get(\n `${BASE}/inventory/vials/by-product/${productId}`,\n );\n return response.data ?? [];\n}\n\nexport async function listPurchases(page = 0, size = 25) {\n const response = await api.get(`${BASE}/inventory/purchases`, {\n params: { page, size },\n });\n return response.data ?? [];\n}\n\nexport async function getPurchase(purchaseId) {\n const response = await api.get(`${BASE}/inventory/purchases/${purchaseId}`);\n return response.data;\n}\n\nexport async function listSales(page = 0, size = 25) {\n const response = await api.get(`${BASE}/inventory/sales`, {\n params: { page, size },\n });\n return response.data ?? [];\n}\n\nexport async function getSale(saleId) {\n const response = await api.get(`${BASE}/inventory/sales/${saleId}`);\n return response.data;\n}\n\nexport async function listPayments(page = 0, size = 25) {\n const response = await api.get(`${BASE}/inventory/payments`, {\n params: { page, size },\n });\n return response.data ?? [];\n}\n\nexport async function getPayment(paymentId) {\n const response = await api.get(`${BASE}/inventory/payments/${paymentId}`);\n return response.data;\n}\n\nexport async function listTransactions(page = 0, size = 50) {\n // Back-compat wrapper for places that only need the content array (e.g.\n // the Dashboard). New code should call queryTransactions for the full\n // PageResponse + filter support.\n const response = await api.get(`${BASE}/inventory/transactions`, {\n params: { page, size },\n });\n return response.data?.content ?? [];\n}\n\nexport async function queryTransactions(params = {}) {\n // `params` accepts: page, size, dateFrom, dateTo, types (array → joined),\n // productId, batchId, ownerType, ownerId, search.\n const { types, ...rest } = params;\n const response = await api.get(`${BASE}/inventory/transactions`, {\n params: {\n ...rest,\n // Spring binds repeated query params (types=A&types=B) onto List<String>\n types: Array.isArray(types) ? types : undefined,\n },\n // Repeat the key for each array entry (Spring's default binding).\n paramsSerializer: {\n serialize: (q) => {\n const sp = new URLSearchParams();\n Object.entries(q).forEach(([k, v]) => {\n if (v == null || v === '') return;\n if (Array.isArray(v)) v.forEach((item) => sp.append(k, item));\n else sp.append(k, v);\n });\n return sp.toString();\n },\n },\n });\n return response.data ?? { content: [], totalElements: 0, totalPages: 0, page: 0, size: 50 };\n}\n\nexport async function txnsForBatch(batchId) {\n const response = await api.get(\n `${BASE}/inventory/transactions/by-batch/${batchId}`,\n );\n return response.data ?? [];\n}\n\nexport async function txnsForVial(openVialId) {\n const response = await api.get(\n `${BASE}/inventory/transactions/by-vial/${openVialId}`,\n );\n return response.data ?? [];\n}\n\n// ---------- Inventory (commands) ----------\nexport async function createPurchase(payload) {\n const response = await api.post(`${BASE}/inventory/purchase`, payload);\n return response.data;\n}\n\n// ---------- Stock Transfers (facility → facility, two-step) ----------\nexport async function shipTransfer(payload) {\n const response = await api.post(`${BASE}/inventory/transfer`, payload);\n return response.data;\n}\n\nexport async function confirmTransfer(id, payload) {\n const response = await api.post(`${BASE}/inventory/transfer/${id}/confirm`, payload ?? {});\n return response.data;\n}\n\nexport async function cancelTransfer(id, payload) {\n const response = await api.post(`${BASE}/inventory/transfer/${id}/cancel`, payload ?? {});\n return response.data;\n}\n\nexport async function listTransfers() {\n const response = await api.get(`${BASE}/inventory/transfers`);\n return response.data ?? [];\n}\n\nexport async function getTransfer(id) {\n const response = await api.get(`${BASE}/inventory/transfers/${id}`);\n return response.data;\n}\n\n// ---------- Stock Pickups (user checks out facility stock + returns it) ----------\nexport async function recordPickup(payload) {\n const response = await api.post(`${BASE}/inventory/pickup`, payload);\n return response.data;\n}\n\nexport async function recordPickupReturn(id, payload) {\n const response = await api.post(`${BASE}/inventory/pickup/${id}/return`, payload);\n return response.data;\n}\n\nexport async function recordPickupTopup(id, payload) {\n const response = await api.post(`${BASE}/inventory/pickup/${id}/topup`, payload);\n return response.data;\n}\n\nexport async function cancelPickup(id, payload) {\n const response = await api.post(`${BASE}/inventory/pickup/${id}/cancel`, payload ?? {});\n return response.data;\n}\n\nexport async function listPickups() {\n const response = await api.get(`${BASE}/inventory/pickups`);\n return response.data ?? [];\n}\n\nexport async function listMyOpenPickups() {\n const response = await api.get(`${BASE}/inventory/pickups/mine/open`);\n return response.data ?? [];\n}\n\nexport async function getPickup(id) {\n const response = await api.get(`${BASE}/inventory/pickups/${id}`);\n return response.data;\n}\n\n// ---------- Purchase Requests (approval workflow) ----------\nexport async function submitPurchaseRequest(payload) {\n const response = await api.post(`${BASE}/inventory/purchase-request`, payload);\n return response.data;\n}\n\nexport async function listMyPurchaseRequests() {\n const response = await api.get(`${BASE}/inventory/purchase-requests/mine`);\n return response.data ?? [];\n}\n\nexport async function listPendingPurchaseRequests() {\n const response = await api.get(`${BASE}/inventory/purchase-requests/pending`);\n return response.data ?? [];\n}\n\nexport async function getPurchaseRequest(id) {\n const response = await api.get(`${BASE}/inventory/purchase-requests/${id}`);\n return response.data;\n}\n\nexport async function decidePurchaseRequest(id, payload) {\n const response = await api.post(`${BASE}/inventory/purchase-request/${id}/decision`, payload);\n return response.data;\n}\n\nexport async function cancelPurchaseRequest(id) {\n const response = await api.post(`${BASE}/inventory/purchase-request/${id}/cancel`);\n return response.data;\n}\n\nexport async function createSale(payload) {\n const response = await api.post(`${BASE}/inventory/sale`, payload);\n return response.data;\n}\n\nexport async function createPayment(payload) {\n const response = await api.post(`${BASE}/inventory/payment`, payload);\n return response.data;\n}\n\nexport async function openVial(payload) {\n const response = await api.post(`${BASE}/inventory/vial/open`, payload);\n return response.data;\n}\n\nexport async function administerDose(payload) {\n const response = await api.post(`${BASE}/inventory/vial/administer`, payload);\n return response.data;\n}\n\nexport async function closeVial(payload) {\n const response = await api.post(`${BASE}/inventory/vial/close`, payload);\n return response.data;\n}\n\nexport async function quarantineBatch(payload) {\n const response = await api.post(\n `${BASE}/inventory/batch/quarantine`,\n payload,\n );\n return response.data;\n}\n\nexport async function unquarantineBatch(payload) {\n const response = await api.post(\n `${BASE}/inventory/batch/unquarantine`,\n payload,\n );\n return response.data;\n}\n","// Country master list. Each entry carries everything the module needs derived\n// from the picked deployment country: ISO code, display name, currency it\n// uses, and the local phone format (dial code + min/max digits) for the\n// country-aware phone input on Brands.\n//\n// Phone length is the local part — i.e. digits after the dial code. North\n// American countries share +1 but each is on the 10-digit NANP plan.\n\nexport const COUNTRIES = [\n { code: 'IN', name: 'India', currencyCode: 'INR', currencySymbol: '₹', phoneCode: '+91', phoneMin: 10, phoneMax: 10 },\n { code: 'JM', name: 'Jamaica', currencyCode: 'JMD', currencySymbol: 'J$', phoneCode: '+1', phoneMin: 10, phoneMax: 10 },\n { code: 'US', name: 'United States', currencyCode: 'USD', currencySymbol: '$', phoneCode: '+1', phoneMin: 10, phoneMax: 10 },\n { code: 'CA', name: 'Canada', currencyCode: 'CAD', currencySymbol: 'C$', phoneCode: '+1', phoneMin: 10, phoneMax: 10 },\n { code: 'GB', name: 'United Kingdom', currencyCode: 'GBP', currencySymbol: '£', phoneCode: '+44', phoneMin: 10, phoneMax: 10 },\n { code: 'AU', name: 'Australia', currencyCode: 'AUD', currencySymbol: 'A$', phoneCode: '+61', phoneMin: 9, phoneMax: 9 },\n { code: 'NZ', name: 'New Zealand', currencyCode: 'NZD', currencySymbol: 'NZ$', phoneCode: '+64', phoneMin: 8, phoneMax: 10 },\n { code: 'SG', name: 'Singapore', currencyCode: 'SGD', currencySymbol: 'S$', phoneCode: '+65', phoneMin: 8, phoneMax: 8 },\n { code: 'HK', name: 'Hong Kong', currencyCode: 'HKD', currencySymbol: 'HK$', phoneCode: '+852', phoneMin: 8, phoneMax: 8 },\n { code: 'MY', name: 'Malaysia', currencyCode: 'MYR', currencySymbol: 'RM', phoneCode: '+60', phoneMin: 9, phoneMax: 10 },\n { code: 'TH', name: 'Thailand', currencyCode: 'THB', currencySymbol: '฿', phoneCode: '+66', phoneMin: 9, phoneMax: 9 },\n { code: 'VN', name: 'Vietnam', currencyCode: 'VND', currencySymbol: '₫', phoneCode: '+84', phoneMin: 9, phoneMax: 10 },\n { code: 'PH', name: 'Philippines', currencyCode: 'PHP', currencySymbol: '₱', phoneCode: '+63', phoneMin: 10, phoneMax: 10 },\n { code: 'ID', name: 'Indonesia', currencyCode: 'IDR', currencySymbol: 'Rp', phoneCode: '+62', phoneMin: 9, phoneMax: 12 },\n { code: 'CN', name: 'China', currencyCode: 'CNY', currencySymbol: '¥', phoneCode: '+86', phoneMin: 11, phoneMax: 11 },\n { code: 'JP', name: 'Japan', currencyCode: 'JPY', currencySymbol: '¥', phoneCode: '+81', phoneMin: 10, phoneMax: 11 },\n { code: 'KR', name: 'South Korea', currencyCode: 'KRW', currencySymbol: '₩', phoneCode: '+82', phoneMin: 9, phoneMax: 11 },\n { code: 'PK', name: 'Pakistan', currencyCode: 'PKR', currencySymbol: '₨', phoneCode: '+92', phoneMin: 10, phoneMax: 10 },\n { code: 'BD', name: 'Bangladesh', currencyCode: 'BDT', currencySymbol: '৳', phoneCode: '+880', phoneMin: 10, phoneMax: 10 },\n { code: 'LK', name: 'Sri Lanka', currencyCode: 'LKR', currencySymbol: '₨', phoneCode: '+94', phoneMin: 9, phoneMax: 9 },\n { code: 'NP', name: 'Nepal', currencyCode: 'NPR', currencySymbol: '₨', phoneCode: '+977', phoneMin: 10, phoneMax: 10 },\n { code: 'AE', name: 'United Arab Emirates', currencyCode: 'AED', currencySymbol: 'د.إ', phoneCode: '+971', phoneMin: 9, phoneMax: 9 },\n { code: 'SA', name: 'Saudi Arabia', currencyCode: 'SAR', currencySymbol: '﷼', phoneCode: '+966', phoneMin: 9, phoneMax: 9 },\n { code: 'EG', name: 'Egypt', currencyCode: 'EGP', currencySymbol: 'E£', phoneCode: '+20', phoneMin: 10, phoneMax: 10 },\n { code: 'NG', name: 'Nigeria', currencyCode: 'NGN', currencySymbol: '₦', phoneCode: '+234', phoneMin: 10, phoneMax: 10 },\n { code: 'KE', name: 'Kenya', currencyCode: 'KES', currencySymbol: 'KSh', phoneCode: '+254', phoneMin: 9, phoneMax: 10 },\n { code: 'GH', name: 'Ghana', currencyCode: 'GHS', currencySymbol: 'GH₵', phoneCode: '+233', phoneMin: 9, phoneMax: 10 },\n { code: 'ZA', name: 'South Africa', currencyCode: 'ZAR', currencySymbol: 'R', phoneCode: '+27', phoneMin: 9, phoneMax: 9 },\n { code: 'BR', name: 'Brazil', currencyCode: 'BRL', currencySymbol: 'R$', phoneCode: '+55', phoneMin: 10, phoneMax: 11 },\n { code: 'MX', name: 'Mexico', currencyCode: 'MXN', currencySymbol: 'MX$', phoneCode: '+52', phoneMin: 10, phoneMax: 10 },\n { code: 'AR', name: 'Argentina', currencyCode: 'ARS', currencySymbol: '$', phoneCode: '+54', phoneMin: 10, phoneMax: 11 },\n { code: 'DE', name: 'Germany', currencyCode: 'EUR', currencySymbol: '€', phoneCode: '+49', phoneMin: 10, phoneMax: 11 },\n { code: 'FR', name: 'France', currencyCode: 'EUR', currencySymbol: '€', phoneCode: '+33', phoneMin: 9, phoneMax: 9 },\n { code: 'IT', name: 'Italy', currencyCode: 'EUR', currencySymbol: '€', phoneCode: '+39', phoneMin: 9, phoneMax: 10 },\n { code: 'ES', name: 'Spain', currencyCode: 'EUR', currencySymbol: '€', phoneCode: '+34', phoneMin: 9, phoneMax: 9 },\n { code: 'NL', name: 'Netherlands', currencyCode: 'EUR', currencySymbol: '€', phoneCode: '+31', phoneMin: 9, phoneMax: 9 },\n { code: 'CH', name: 'Switzerland', currencyCode: 'CHF', currencySymbol: 'Fr', phoneCode: '+41', phoneMin: 9, phoneMax: 9 },\n { code: 'TR', name: 'Turkey', currencyCode: 'TRY', currencySymbol: '₺', phoneCode: '+90', phoneMin: 10, phoneMax: 10 },\n { code: 'RU', name: 'Russia', currencyCode: 'RUB', currencySymbol: '₽', phoneCode: '+7', phoneMin: 10, phoneMax: 10 },\n];\n\nconst BY_CODE = Object.fromEntries(COUNTRIES.map((c) => [c.code, c]));\n\nexport function findCountry(code) {\n if (!code) return null;\n return BY_CODE[code] ?? null;\n}\n\n/** Symbol for the country's currency, with sensible fallback. */\nexport function currencySymbolForCountry(countryCode) {\n return findCountry(countryCode)?.currencySymbol ?? '₹';\n}\n\n/** ISO currency code for a country, useful for downstream code that already speaks \"currency\". */\nexport function currencyCodeForCountry(countryCode) {\n return findCountry(countryCode)?.currencyCode ?? 'INR';\n}\n\n/**\n * Validate a phone number against a country's local format. Pure digits only;\n * the dial code is shown as adornment in the UI and not part of the input value.\n * Returns an error string or empty string when the value is acceptable.\n */\nexport function validatePhoneForCountry(value, country) {\n if (!value) return '';\n const digits = String(value).replace(/\\D/g, '');\n if (!country) {\n if (digits.length < 6 || digits.length > 15) return 'Phone number looks invalid.';\n return '';\n }\n if (digits.length < country.phoneMin || digits.length > country.phoneMax) {\n const range = country.phoneMin === country.phoneMax\n ? `${country.phoneMin} digits`\n : `${country.phoneMin}–${country.phoneMax} digits`;\n return `Phone must be ${range} for ${country.name}.`;\n }\n return '';\n}\n","import { useEffect, useState } from 'react';\nimport { getSettings } from '@pm/services/pmApi';\nimport { currencyCodeForCountry } from './countries';\n\n// Module-scoped cache + tiny subscriber model so settings-aware components\n// (PmMoney, PmNav, PurchaseCreatePage, …) can read the same snapshot without\n// re-fetching per page.\n//\n// Lifecycle:\n// - first usePmSettings() call from anywhere triggers the fetch\n// - subsequent renders read from the cache synchronously\n// - SettingsPage calls updateLocalPmSettings() after saving so the change\n// propagates without a round-trip\n\nconst DEFAULT_SETTINGS = {\n memberTableName: null,\n memberIdColumn: null,\n memberIdentifierColumn: null,\n memberFamilyIdColumn: null,\n memberNameColumn: null,\n memberDobColumn: null,\n countryCode: 'IN',\n // Derived from countryCode at hydrate time so PmMoney etc. can keep\n // reading a stable currencyCode field without knowing about countries.\n currencyCode: 'INR',\n managePurchaseAmounts: true,\n manageSales: true,\n allowPersonalPurchase: true,\n requirePurchaseConfirmation: false,\n};\n\nlet _settings = null;\nlet _pending = null;\nconst _listeners = new Set();\n\nfunction hydrate(raw) {\n const base = { ...DEFAULT_SETTINGS, ...(raw ?? {}) };\n // Derive currency from country every load — country is the source of truth.\n base.currencyCode = currencyCodeForCountry(base.countryCode);\n base.managePurchaseAmounts = base.managePurchaseAmounts !== false; // default true\n base.manageSales = base.manageSales !== false; // default true\n base.allowPersonalPurchase = base.allowPersonalPurchase !== false; // default true\n base.requirePurchaseConfirmation = base.requirePurchaseConfirmation === true; // default false\n return base;\n}\n\nfunction notify() {\n _listeners.forEach((cb) => cb(_settings));\n}\n\nexport async function loadPmSettings(force = false) {\n if (!force && _settings) return _settings;\n if (!force && _pending) return _pending;\n _pending = getSettings()\n .then((s) => {\n _settings = hydrate(s);\n _pending = null;\n notify();\n return _settings;\n })\n .catch((err) => {\n _pending = null;\n if (!_settings) {\n _settings = hydrate(null);\n notify();\n }\n throw err;\n });\n return _pending;\n}\n\nexport function updateLocalPmSettings(next) {\n _settings = hydrate(next);\n notify();\n}\n\nexport function getCachedPmSettings() {\n return _settings;\n}\n\nexport function usePmSettings() {\n const [snap, setSnap] = useState(_settings ?? hydrate(null));\n useEffect(() => {\n _listeners.add(setSnap);\n if (!_settings && !_pending) {\n void loadPmSettings().catch(() => {});\n } else if (_settings) {\n setSnap(_settings);\n }\n return () => {\n _listeners.delete(setSnap);\n };\n }, []);\n return snap;\n}\n","import PropTypes from 'prop-types';\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { alpha, useTheme } from '@mui/material/styles';\nimport { Box, Chip, Fade, Stack, Typography } from '@mui/material';\nimport { DASHBOARD, SETTINGS, findItemForPath } from '@pm/utils/workspaces';\nimport { loadPmSettings } from '@pm/utils/settingsStore';\n\n// Page shell shared by every PM screen. Reads the active workspace from the\n// route and tints the header to match — peripheral cue for \"what section of\n// the tool am I in\". Body content fades in to soften route changes.\nexport default function PmPageShell({ title, subtitle, chips = [], actions, children }) {\n const theme = useTheme();\n const { pathname } = useLocation();\n\n // Kick off the settings preload from the shared shell, so every PM page has\n // the currency cached by the time PmMoney renders. usePmSettings inside the\n // money cell will also trigger this; calling here just shaves one render.\n useEffect(() => { void loadPmSettings().catch(() => {}); }, []);\n\n const onDashboard = pathname.startsWith(DASHBOARD.path);\n const onSettings = pathname.startsWith(SETTINGS.path);\n const hit = findItemForPath(pathname);\n const workspace = hit?.workspace;\n\n const accent = onDashboard\n ? DASHBOARD.accent\n : onSettings\n ? SETTINGS.accent\n : (workspace?.accent ?? theme.palette.primary.main);\n const accentDark = onDashboard\n ? DASHBOARD.accentDark\n : onSettings\n ? SETTINGS.accentDark\n : (workspace?.accentDark ?? theme.palette.primary.dark);\n\n return (\n <Box sx={{ position: 'relative', px: { xs: 1, md: 2 }, py: { xs: 1, md: 2 } }}>\n {/* Soft accent glow tied to the active workspace. */}\n <Box sx={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'hidden' }}>\n <Box\n sx={{\n position: 'absolute',\n top: -60,\n right: -40,\n width: 260,\n height: 260,\n borderRadius: '50%',\n background: `radial-gradient(circle, ${alpha(accent, 0.18)} 0%, transparent 70%)`,\n filter: 'blur(2px)',\n }}\n />\n <Box\n sx={{\n position: 'absolute',\n top: 40,\n left: -50,\n width: 200,\n height: 200,\n borderRadius: '50%',\n background: `radial-gradient(circle, ${alpha(theme.palette.info.main, 0.08)} 0%, transparent 72%)`,\n filter: 'blur(2px)',\n }}\n />\n </Box>\n\n {/* Header card — workspace badge + title + actions. */}\n <Box\n sx={{\n position: 'relative',\n zIndex: 1,\n mb: 2,\n p: { xs: 2, md: 2.5 },\n borderRadius: 4,\n border: `1px solid ${alpha(accent, 0.18)}`,\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.98)} 0%, ${alpha(accent, 0.07)} 100%)`,\n boxShadow: `0 18px 44px ${alpha(theme.palette.common.black, 0.06)}`,\n overflow: 'hidden',\n // Thin colored bar at the left edge — instantly tells you which workspace.\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n left: 0,\n top: 0,\n bottom: 0,\n width: 4,\n background: `linear-gradient(180deg, ${accent} 0%, ${accentDark} 100%)`,\n },\n }}\n >\n <Stack\n direction={{ xs: 'column', md: 'row' }}\n justifyContent=\"space-between\"\n alignItems={{ xs: 'flex-start', md: 'center' }}\n spacing={1.5}\n >\n <Box sx={{ minWidth: 0 }}>\n {workspace || onDashboard || onSettings ? (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.75} sx={{ mb: 0.75 }}>\n {(() => {\n const Icon = onDashboard\n ? DASHBOARD.icon\n : onSettings\n ? SETTINGS.icon\n : workspace.icon;\n return Icon ? (\n <Icon sx={{ fontSize: 16, color: accentDark }} />\n ) : null;\n })()}\n <Typography\n variant=\"overline\"\n sx={{\n color: accentDark,\n fontWeight: 800,\n letterSpacing: 1.1,\n lineHeight: 1,\n }}\n >\n {onDashboard ? 'Overview' : onSettings ? 'Configuration' : workspace.label}\n </Typography>\n {hit?.item ? (\n <>\n <Box sx={{ width: 4, height: 4, borderRadius: '50%', bgcolor: alpha(accent, 0.4), mx: 0.5 }} />\n <Typography variant=\"overline\" sx={{ color: theme.palette.text.secondary, fontWeight: 700, letterSpacing: 0.8 }}>\n {hit.item.label}\n </Typography>\n </>\n ) : null}\n </Stack>\n ) : null}\n\n <Typography variant=\"h5\" sx={{ fontWeight: 800, color: theme.palette.text.primary, lineHeight: 1.2 }}>\n {title}\n </Typography>\n {subtitle ? (\n <Typography variant=\"body2\" sx={{ mt: 0.75, maxWidth: 900, color: theme.palette.text.secondary }}>\n {subtitle}\n </Typography>\n ) : null}\n {chips?.length ? (\n <Stack direction=\"row\" spacing={1} useFlexGap flexWrap=\"wrap\" sx={{ mt: 1.5 }}>\n {chips.map((chip) => (\n <Chip\n key={chip}\n label={chip}\n size=\"small\"\n sx={{\n borderRadius: 2,\n backgroundColor: alpha(accent, 0.1),\n color: accentDark,\n fontWeight: 700,\n border: `1px solid ${alpha(accent, 0.2)}`,\n }}\n />\n ))}\n </Stack>\n ) : null}\n </Box>\n {actions ? <Box>{actions}</Box> : null}\n </Stack>\n </Box>\n\n {/* Body — fade in to soften route transitions. Keyed on pathname so each\n navigation triggers a fresh animation rather than reusing prior fade. */}\n <Fade in key={pathname} timeout={360}>\n <Box\n sx={{\n position: 'relative',\n zIndex: 1,\n // Form field labels and helper/hint text default to a small, light\n // grey that's hard to read — bump the size and darken the colour\n // consistently across every PM form.\n '& .MuiInputLabel-root': {\n fontSize: '0.95rem',\n color: theme.palette.text.primary,\n },\n '& .MuiInputLabel-root.Mui-focused': {\n color: theme.palette.primary.main,\n },\n '& .MuiFormHelperText-root': {\n fontSize: '0.78rem',\n color: alpha(theme.palette.text.primary, 0.7),\n },\n }}\n >\n {children}\n </Box>\n </Fade>\n </Box>\n );\n}\n\nPmPageShell.propTypes = {\n title: PropTypes.node,\n subtitle: PropTypes.node,\n chips: PropTypes.arrayOf(PropTypes.string),\n actions: PropTypes.node,\n children: PropTypes.node,\n};\n","import { useEffect, useMemo } from 'react';\nimport { useSelector } from 'react-redux';\n\n// PM module's navigation_state in menu_config. The backend attaches a\n// featureJson onto this node whose keys are the permission_ids the current\n// user has been granted under the module — exactly the same featureJson\n// MedplatForm reads off any other menu node. Nothing about which\n// permissions exist is baked into the frontend; the source of truth is\n// whatever the backend serialised onto this node.\nconst PM_MENU_KEY = 'techo.manage.pm';\n\n/**\n * Hook returning the PM permission set for the current user.\n *\n * Reads `linearMenuItems['techo.manage.pm'].featureJson` from the\n * Authenticate slice — Protected.jsx populates that at login via\n * /api/login/menuV2. The featureJson is a JSON-string-or-object whose keys\n * are permission_ids and whose values are true/false.\n *\n * Returns:\n * has(permId) — true iff the user has been granted that permission_id\n * hasAny(ids[]) — true iff any id in the list is granted\n *\n * **Default-deny**: when the PM menu node is missing or its featureJson is\n * empty, both helpers return false. There's no dev escape hatch — devs go\n * through the same grant flow as everyone else.\n *\n * **What's actually in featureJson**: only the top-level api_permission\n * rows for menu_id 543 (those with `parent_permission_id = NULL`). The\n * `.child.get.X` / `.child.post.X` rows govern API-URL access via the\n * backend's GlobalMenuPermissionFilter but they do NOT surface to the\n * frontend. So every UI check in PM uses the parent perm\n * (`view.manage.productManagement.<page>`). The backend still 403s any\n * write the user lacks the `.child.post.X` row for — that's the actual\n * write gate. To gate buttons in the UI separately from page-visibility\n * later, mint flat write perms (e.g. `write.manage.productManagement.X`\n * with `parent_permission_id = NULL`).\n */\n/**\n * `useEffect` that only runs when the current user has the permission.\n *\n * <p>PM pages historically declared their load `useEffect` in the outer\n * component above {@code <PmGuard>}. That meant the fetch fired on mount\n * regardless of the user's perm — the guard only hid the UI after the\n * network call already went out. Wrapping the effect with this hook makes\n * the fetch wait for (or skip entirely without) the perm, matching what\n * `PmGuard` does for the rendered tree.\n *\n * @param {string|string[]} perm Permission id required to fire the effect. Array means any-of.\n * @param {() => (void|(()=>void))} fn The effect body — receives no args, may return a cleanup.\n * @param {ReadonlyArray<*>} [deps] Extra deps; the perm gate is added implicitly.\n */\nexport function usePermissionedEffect(perm, fn, deps) {\n const perms = usePmPermissions();\n const allowed = Array.isArray(perm)\n ? perms.hasAny(perm)\n : (!!perm && perms.has(perm));\n // We list `allowed` first so a perm flip re-fires the effect (e.g. a user\n // who gains the perm mid-session lands on the page fully loaded). Caller\n // deps come after — same semantics they'd get from a plain useEffect.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => {\n if (!allowed) return undefined;\n return fn();\n }, [allowed, ...(deps ?? [])]);\n}\n\nexport function usePmPermissions() {\n const linearMenuItems = useSelector((state) => state.Authenticate?.linearMenuItems);\n return useMemo(() => {\n const node = linearMenuItems?.[PM_MENU_KEY];\n let grants = node?.featureJson;\n if (typeof grants === 'string') {\n try { grants = JSON.parse(grants); } catch { grants = {}; }\n }\n const grantSet = grants && typeof grants === 'object' ? grants : {};\n const has = (permId) => !!permId && grantSet[permId] === true;\n const hasAny = (perms) => Array.isArray(perms) && perms.some(has);\n return { has, hasAny };\n }, [linearMenuItems]);\n}\n","import { useMemo } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { Box, Stack, Tab, Tabs, Typography } from '@mui/material';\nimport { alpha, useTheme } from '@mui/material/styles';\nimport { DASHBOARD, SETTINGS, findWorkspaceForPath, getVisibleWorkspaces } from '@pm/utils/workspaces';\nimport { usePmSettings } from '@pm/utils/settingsStore';\nimport { usePmPermissions } from '@pm/utils/permissions';\n\n// Two-row navigation:\n// Row 1 — workspace pills: Dashboard · Catalog · Operations · Ledger\n// Row 2 — when inside a workspace, the items in that workspace as tabs,\n// accented with the workspace's hue.\n//\n// The active workspace's accent flows into both rows so the user always\n// has a peripheral cue for \"where am I in the tool\".\n//\n// Visibility is filtered by getVisibleWorkspaces() — items that the user\n// lacks permission for OR that the deployment has turned off (e.g. Sales +\n// Payments when manageSales=false) are dropped, and a workspace tab whose\n// items all got dropped disappears too.\nexport default function PmNav() {\n const navigate = useNavigate();\n const location = useLocation();\n const theme = useTheme();\n const settings = usePmSettings();\n const perms = usePmPermissions();\n\n const visibleWorkspaces = useMemo(\n () => getVisibleWorkspaces(perms, settings),\n [perms, settings],\n );\n\n const showDashboardPill = perms.has(DASHBOARD.permissionId);\n const showSettingsPill = perms.has(SETTINGS.permissionId);\n\n const onDashboard = location.pathname.startsWith(DASHBOARD.path);\n const onSettings = location.pathname.startsWith(SETTINGS.path);\n const activeWorkspace = findWorkspaceForPath(location.pathname);\n // visibleWorkspaces is already filtered; if the user landed on a workspace\n // whose items they no longer have access to (e.g. deep link), it won't be\n // here and the row-2 tab strip stays collapsed.\n const visibleActiveWorkspace = activeWorkspace\n ? visibleWorkspaces.find((ws) => ws.id === activeWorkspace.id) ?? null\n : null;\n const accent = onDashboard\n ? DASHBOARD.accent\n : onSettings\n ? SETTINGS.accent\n : (activeWorkspace?.accent ?? theme.palette.primary.main);\n\n const activeItemIndex = visibleActiveWorkspace\n ? visibleActiveWorkspace.items.findIndex((it) => location.pathname.startsWith(it.path))\n : -1;\n\n function pillSx(active, hue) {\n return {\n px: 2,\n py: 0.9,\n borderRadius: 999,\n cursor: 'pointer',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 1,\n fontWeight: 700,\n fontSize: '0.875rem',\n letterSpacing: 0.2,\n transition: 'all 220ms cubic-bezier(0.4, 0, 0.2, 1)',\n color: active ? '#fff' : theme.palette.text.secondary,\n backgroundColor: active ? hue : 'transparent',\n boxShadow: active ? `0 8px 22px ${alpha(hue, 0.32)}` : 'none',\n '&:hover': {\n color: active ? '#fff' : hue,\n backgroundColor: active ? hue : alpha(hue, 0.08),\n transform: active ? 'none' : 'translateY(-1px)',\n },\n };\n }\n\n function renderPill({ id, label, icon: Icon, active, onClick, hue }) {\n return (\n <Box key={id} role=\"button\" onClick={onClick} sx={pillSx(active, hue)}>\n {Icon ? <Icon sx={{ fontSize: 18 }} /> : null}\n {label}\n </Box>\n );\n }\n\n return (\n <Box sx={{ mb: 2 }}>\n {/* Row 1 — workspace pills */}\n <Box\n sx={{\n position: 'relative',\n borderRadius: 4,\n p: 1,\n background: `linear-gradient(135deg, ${alpha(theme.palette.background.paper, 0.96)} 0%, ${alpha(accent, 0.06)} 100%)`,\n border: `1px solid ${alpha(theme.palette.divider, 0.7)}`,\n boxShadow: `0 10px 28px ${alpha(theme.palette.common.black, 0.04)}`,\n }}\n >\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.75} flexWrap=\"wrap\" useFlexGap>\n {showDashboardPill ? renderPill({\n id: DASHBOARD.id,\n label: DASHBOARD.label,\n icon: DASHBOARD.icon,\n active: onDashboard,\n hue: DASHBOARD.accent,\n onClick: () => navigate(DASHBOARD.path),\n }) : null}\n\n {showDashboardPill && visibleWorkspaces.length > 0 ? (\n <Box sx={{ width: 1, height: 22, mx: 0.5, bgcolor: 'divider', opacity: 0.7 }} />\n ) : null}\n\n {visibleWorkspaces.map((ws) => renderPill({\n id: ws.id,\n label: ws.label,\n icon: ws.icon,\n active: activeWorkspace?.id === ws.id,\n hue: ws.accent,\n // Tapping a workspace jumps to its first item — a deterministic\n // landing, no submenu to fight with.\n onClick: () => ws.items[0] && navigate(ws.items[0].path),\n }))}\n\n {/* Settings sits to the right, separated from the workspaces — it's\n configuration, not a workspace, and shouldn't compete visually. */}\n <Box sx={{ flexGrow: 1 }} />\n {showSettingsPill ? renderPill({\n id: SETTINGS.id,\n label: SETTINGS.label,\n icon: SETTINGS.icon,\n active: onSettings,\n hue: SETTINGS.accent,\n onClick: () => navigate(SETTINGS.path),\n }) : null}\n </Stack>\n </Box>\n\n {/* Row 2 — items within the active workspace */}\n {visibleActiveWorkspace && visibleActiveWorkspace.items.length > 0 ? (\n <Box\n sx={{\n mt: 1,\n borderRadius: 3,\n px: 1,\n bgcolor: alpha(visibleActiveWorkspace.accent, 0.04),\n border: `1px solid ${alpha(visibleActiveWorkspace.accent, 0.12)}`,\n }}\n >\n <Tabs\n value={activeItemIndex === -1 ? false : activeItemIndex}\n onChange={(_, idx) => navigate(visibleActiveWorkspace.items[idx].path)}\n variant=\"scrollable\"\n scrollButtons=\"auto\"\n slotProps={{\n indicator: { sx: { backgroundColor: visibleActiveWorkspace.accent, height: 3, borderRadius: 2 } },\n }}\n sx={{\n minHeight: 44,\n '& .MuiTab-root': {\n minHeight: 44,\n fontWeight: 700,\n textTransform: 'none',\n fontSize: '0.85rem',\n color: theme.palette.text.secondary,\n transition: 'color 180ms ease',\n '&:hover': { color: visibleActiveWorkspace.accentDark },\n '&.Mui-selected': { color: visibleActiveWorkspace.accentDark },\n },\n }}\n >\n {visibleActiveWorkspace.items.map((it) => {\n const Icon = it.icon;\n return (\n <Tab\n key={it.id}\n iconPosition=\"start\"\n icon={Icon ? <Icon sx={{ fontSize: 18 }} /> : undefined}\n label={<Typography component=\"span\" sx={{ fontWeight: 700 }}>{it.label}</Typography>}\n />\n );\n })}\n </Tabs>\n </Box>\n ) : null}\n </Box>\n );\n}\n","import { useNavigate } from 'react-router-dom';\nimport { useSelector } from 'react-redux';\nimport { Alert, Box, Button, LinearProgress, Stack, Typography } from '@mui/material';\nimport LockOutlinedIcon from '@mui/icons-material/LockOutlined';\nimport { usePmPermissions } from '@pm/utils/permissions';\n\n/**\n * Page-level guard. Wrap a PM page in `<PmGuard perm=\"...\">…</PmGuard>` and\n * the children only render when the current user has the perm; otherwise\n * the page shows a friendly \"no access\" panel with a back-to-dashboard\n * button.\n *\n * Default-deny: when the featureJson is missing the guard blocks. While the\n * menu tree hasn't loaded yet (first paint right after login) we show a\n * thin loader so the \"no access\" alert doesn't flash.\n */\nexport default function PmGuard({ perm, anyOf, children, title = 'You don’t have access to this page' }) {\n const perms = usePmPermissions();\n const navigate = useNavigate();\n const linearMenuItems = useSelector((state) => state.Authenticate?.linearMenuItems);\n const menuReady = linearMenuItems !== null && linearMenuItems !== undefined;\n if (!menuReady) {\n return (\n <Box sx={{ p: 3 }}>\n <LinearProgress />\n </Box>\n );\n }\n const allowed = anyOf ? perms.hasAny(anyOf) : perms.has(perm);\n if (allowed) return children;\n return (\n <Box sx={{ p: 3 }}>\n <Alert\n severity=\"warning\"\n icon={<LockOutlinedIcon />}\n sx={{ borderRadius: 3, alignItems: 'flex-start' }}\n >\n <Stack spacing={1.25}>\n <Typography sx={{ fontWeight: 800 }}>{title}</Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Your account doesn’t have the required permission to view this\n section of Product Management. Ask an admin to grant it via the\n role / user permission assignment.\n </Typography>\n <Box>\n <Button\n size=\"small\"\n variant=\"contained\"\n onClick={() => navigate('/ui/medplat/manage/pm')}\n >\n Back to Dashboard\n </Button>\n </Box>\n </Stack>\n </Alert>\n </Box>\n );\n}\n","// Curated list of currencies the operator can pick from when configuring the\n// module per deployment. Codes are ISO-4217; symbols match what shows up on\n// the local cash register / receipt rather than always falling back to the\n// ISO code. New deployments only need a row added here.\n\nexport const CURRENCY_OPTIONS = [\n { code: 'INR', symbol: '₹', name: 'Indian Rupee' },\n { code: 'USD', symbol: '$', name: 'US Dollar' },\n { code: 'EUR', symbol: '€', name: 'Euro' },\n { code: 'GBP', symbol: '£', name: 'British Pound' },\n { code: 'JPY', symbol: '¥', name: 'Japanese Yen' },\n { code: 'CNY', symbol: '¥', name: 'Chinese Yuan' },\n { code: 'KRW', symbol: '₩', name: 'South Korean Won' },\n { code: 'THB', symbol: '฿', name: 'Thai Baht' },\n { code: 'VND', symbol: '₫', name: 'Vietnamese Dong' },\n { code: 'NGN', symbol: '₦', name: 'Nigerian Naira' },\n { code: 'BDT', symbol: '৳', name: 'Bangladeshi Taka' },\n { code: 'PKR', symbol: '₨', name: 'Pakistani Rupee' },\n { code: 'LKR', symbol: '₨', name: 'Sri Lankan Rupee' },\n { code: 'NPR', symbol: '₨', name: 'Nepalese Rupee' },\n { code: 'AUD', symbol: 'A$', name: 'Australian Dollar' },\n { code: 'CAD', symbol: 'C$', name: 'Canadian Dollar' },\n { code: 'CHF', symbol: 'Fr', name: 'Swiss Franc' },\n { code: 'SGD', symbol: 'S$', name: 'Singapore Dollar' },\n { code: 'HKD', symbol: 'HK$', name: 'Hong Kong Dollar' },\n { code: 'MYR', symbol: 'RM', name: 'Malaysian Ringgit' },\n { code: 'AED', symbol: 'د.إ', name: 'UAE Dirham' },\n { code: 'SAR', symbol: '﷼', name: 'Saudi Riyal' },\n { code: 'ZAR', symbol: 'R', name: 'South African Rand' },\n { code: 'BRL', symbol: 'R$', name: 'Brazilian Real' },\n { code: 'MXN', symbol: 'MX$', name: 'Mexican Peso' },\n { code: 'KES', symbol: 'KSh', name: 'Kenyan Shilling' },\n { code: 'GHS', symbol: 'GH₵', name: 'Ghanaian Cedi' },\n { code: 'EGP', symbol: 'E£', name: 'Egyptian Pound' },\n { code: 'PHP', symbol: '₱', name: 'Philippine Peso' },\n { code: 'IDR', symbol: 'Rp', name: 'Indonesian Rupiah' },\n { code: 'TRY', symbol: '₺', name: 'Turkish Lira' },\n { code: 'RUB', symbol: '₽', name: 'Russian Ruble' },\n];\n\nconst CODE_TO_OPTION = Object.fromEntries(CURRENCY_OPTIONS.map((c) => [c.code, c]));\n\n/** Resolve an ISO code to a render symbol; unknown codes fall back to the code itself. */\nexport function currencySymbol(code) {\n if (!code) return '₹';\n return CODE_TO_OPTION[code]?.symbol ?? code;\n}\n\n/** Human label, e.g. \"Indian Rupee (₹)\". */\nexport function currencyLabel(code) {\n const opt = CODE_TO_OPTION[code];\n if (!opt) return code || '';\n return `${opt.name} (${opt.symbol})`;\n}\n"],"mappings":"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"}