@argusoft/medplat-product-management 1.0.4 → 1.0.6

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 (68) hide show
  1. package/dist/{BrandsPage-BE3kn-8b.js → BrandsPage-DM5ZRz8L.js} +7 -6
  2. package/dist/{BrandsPage-BE3kn-8b.js.map → BrandsPage-DM5ZRz8L.js.map} +1 -1
  3. package/dist/{CategoriesPage-COqi3pMi.js → CategoriesPage-v7vz_i2N.js} +7 -6
  4. package/dist/{CategoriesPage-COqi3pMi.js.map → CategoriesPage-v7vz_i2N.js.map} +1 -1
  5. package/dist/{DashboardPage-C_H1Jrey.js → DashboardPage-CW77OzUg.js} +3 -3
  6. package/dist/{DashboardPage-C_H1Jrey.js.map → DashboardPage-CW77OzUg.js.map} +1 -1
  7. package/dist/{FacilityPicker-Diq2hnYg.js → FacilityPicker-C2drWyqo.js} +2 -2
  8. package/dist/{FacilityPicker-Diq2hnYg.js.map → FacilityPicker-C2drWyqo.js.map} +1 -1
  9. package/dist/InventoryPage-_VWlqD2e.js +686 -0
  10. package/dist/InventoryPage-_VWlqD2e.js.map +1 -0
  11. package/dist/{MemberProfileCard-BLUVRILO.js → MemberProfileCard-D9jepThR.js} +3 -3
  12. package/dist/{MemberProfileCard-BLUVRILO.js.map → MemberProfileCard-D9jepThR.js.map} +1 -1
  13. package/dist/{PaymentCreatePage-B5VOgTx9.js → PaymentCreatePage-DAUWv5RO.js} +7 -7
  14. package/dist/{PaymentCreatePage-B5VOgTx9.js.map → PaymentCreatePage-DAUWv5RO.js.map} +1 -1
  15. package/dist/{PaymentsPage-DY6BclkN.js → PaymentsPage-B6Fs2xey.js} +4 -4
  16. package/dist/{PaymentsPage-DY6BclkN.js.map → PaymentsPage-B6Fs2xey.js.map} +1 -1
  17. package/dist/{PickupCreatePage-CCSiyFQF.js → PickupCreatePage-4ffB1_yq.js} +4 -4
  18. package/dist/{PickupCreatePage-CCSiyFQF.js.map → PickupCreatePage-4ffB1_yq.js.map} +1 -1
  19. package/dist/{PickupsPage-DGi6T5Of.js → PickupsPage-C-21LwTf.js} +7 -7
  20. package/dist/{PickupsPage-DGi6T5Of.js.map → PickupsPage-C-21LwTf.js.map} +1 -1
  21. package/dist/{PmGuard-C90yhpsN.js → PmGuard-DrbbysnW.js} +3 -3
  22. package/dist/{PmGuard-C90yhpsN.js.map → PmGuard-DrbbysnW.js.map} +1 -1
  23. package/dist/{PmTable-BkK65bJQ.js → PmTable-BMFiuHdG.js} +2 -2
  24. package/dist/{PmTable-BkK65bJQ.js.map → PmTable-BMFiuHdG.js.map} +1 -1
  25. package/dist/{ProductEditPage-CQj9egzW.js → ProductEditPage-BinNxjlB.js} +174 -116
  26. package/dist/ProductEditPage-BinNxjlB.js.map +1 -0
  27. package/dist/{ProductsPage-i8QVw_Jm.js → ProductsPage-DFgK9-13.js} +5 -5
  28. package/dist/{ProductsPage-i8QVw_Jm.js.map → ProductsPage-DFgK9-13.js.map} +1 -1
  29. package/dist/{PurchaseCreatePage-Bp6bl9n8.js → PurchaseCreatePage-B1eV15aS.js} +9 -9
  30. package/dist/{PurchaseCreatePage-Bp6bl9n8.js.map → PurchaseCreatePage-B1eV15aS.js.map} +1 -1
  31. package/dist/{PurchaseRequestDetailPage-CNAqIFef.js → PurchaseRequestDetailPage-52S6Nf7a.js} +6 -5
  32. package/dist/{PurchaseRequestDetailPage-CNAqIFef.js.map → PurchaseRequestDetailPage-52S6Nf7a.js.map} +1 -1
  33. package/dist/{PurchaseRequestsPage-BJqDlNrc.js → PurchaseRequestsPage-DNNUnXy4.js} +4 -4
  34. package/dist/{PurchaseRequestsPage-BJqDlNrc.js.map → PurchaseRequestsPage-DNNUnXy4.js.map} +1 -1
  35. package/dist/{PurchasesPage-BQEToymQ.js → PurchasesPage-Bsrqxvb1.js} +4 -4
  36. package/dist/{PurchasesPage-BQEToymQ.js.map → PurchasesPage-Bsrqxvb1.js.map} +1 -1
  37. package/dist/SaleCreatePage-CPyVj3Dh.js +544 -0
  38. package/dist/SaleCreatePage-CPyVj3Dh.js.map +1 -0
  39. package/dist/{SalesPage-CTBJMxZe.js → SalesPage-BWpgPKz_.js} +4 -4
  40. package/dist/{SalesPage-CTBJMxZe.js.map → SalesPage-BWpgPKz_.js.map} +1 -1
  41. package/dist/{SettingsPage-CiwVmzM3.js → SettingsPage-Ch-37E5R.js} +4 -4
  42. package/dist/{SettingsPage-CiwVmzM3.js.map → SettingsPage-Ch-37E5R.js.map} +1 -1
  43. package/dist/{StorageUnitsPage-BV7nK_iE.js → StorageUnitsPage-DF0MC356.js} +7 -7
  44. package/dist/{StorageUnitsPage-BV7nK_iE.js.map → StorageUnitsPage-DF0MC356.js.map} +1 -1
  45. package/dist/{TransactionsPage-Cocn4901.js → TransactionsPage-DQBjCHoN.js} +18 -18
  46. package/dist/{TransactionsPage-Cocn4901.js.map → TransactionsPage-DQBjCHoN.js.map} +1 -1
  47. package/dist/{TransferCreatePage-DbjcjuOg.js → TransferCreatePage-DdVExUif.js} +4 -4
  48. package/dist/{TransferCreatePage-DbjcjuOg.js.map → TransferCreatePage-DdVExUif.js.map} +1 -1
  49. package/dist/{TransfersPage-BFVF7Xvc.js → TransfersPage-C1qS9kNL.js} +5 -5
  50. package/dist/{TransfersPage-BFVF7Xvc.js.map → TransfersPage-C1qS9kNL.js.map} +1 -1
  51. package/dist/{VialsPage-BNzYTLVd.js → VialsPage-rTAYlXGZ.js} +8 -8
  52. package/dist/{VialsPage-BNzYTLVd.js.map → VialsPage-rTAYlXGZ.js.map} +1 -1
  53. package/dist/{constants-5ZCZo-Zm.js → constants-CdzV0PxZ.js} +25 -12
  54. package/dist/constants-CdzV0PxZ.js.map +1 -0
  55. package/dist/format-UOyOu75i.js.map +1 -1
  56. package/dist/index.js +5 -5
  57. package/dist/index.js.map +1 -1
  58. package/dist/{settingsStore-KW217unV.js → settingsStore-GLCtI-tv.js} +17 -14
  59. package/dist/{settingsStore-KW217unV.js.map → settingsStore-GLCtI-tv.js.map} +1 -1
  60. package/dist/{validation-Ba7puFIO.js → validation-BkUPqrdA.js} +11 -12
  61. package/dist/{validation-Ba7puFIO.js.map → validation-BkUPqrdA.js.map} +1 -1
  62. package/package.json +1 -1
  63. package/dist/InventoryPage-Bjpw6vTz.js +0 -508
  64. package/dist/InventoryPage-Bjpw6vTz.js.map +0 -1
  65. package/dist/ProductEditPage-CQj9egzW.js.map +0 -1
  66. package/dist/SaleCreatePage-DbfBK4PH.js +0 -523
  67. package/dist/SaleCreatePage-DbfBK4PH.js.map +0 -1
  68. package/dist/constants-5ZCZo-Zm.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductEditPage-BinNxjlB.js","names":[],"sources":["../src/views/ProductEditPage.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { usePermissionedEffect } from '@pm/utils/permissions';\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Checkbox,\n Divider,\n FormControlLabel,\n Grid,\n Stack,\n TextField,\n Typography,\n} from '@mui/material';\nimport SaveRoundedIcon from '@mui/icons-material/SaveRounded';\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 {\n getBrands,\n getCategories,\n getImmunizationMaster,\n getProduct,\n upsertProduct,\n} from '@pm/services/pmApi';\nimport { PRODUCT_UNITS, SALE_PRICE_MODES, VACCINES_CATEGORY_CODE, VACCINE_ROUTES } from '@pm/utils/constants';\nimport {\n isClean,\n validateDose,\n validateMaxConcurrentVials,\n validateOpenVialValidity,\n validateReconstitutedValidity,\n validateRequired,\n validateTempRange,\n validateVialVolume,\n} from '@pm/utils/validation';\nimport { useFieldErrors } from '@pm/utils/useFieldErrors';\nimport { usePmSettings } from '@pm/utils/settingsStore';\nimport { PmFieldHelp, PmSelect } from '@pm/components/PmTable';\n\nconst EMPTY_VACCINE = {\n vaccineCode: '',\n vialVolumeMl: '',\n adultDoseMl: '',\n childDoseMl: '',\n openVialValidityHours: 6,\n maxConcurrentOpenVials: 2,\n requiresReconstitution: false,\n reconstitutedValidityHours: '',\n route: '',\n storageTempMinCelsius: '',\n storageTempMaxCelsius: '',\n comments: '',\n};\n\nexport default function ProductEditPage() {\n const { productId } = useParams();\n const navigate = useNavigate();\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n // Whether this page is creating a brand-new product (no existing row) vs\n // editing an existing one. Drives the \"Add\" vs \"Edit\" header. The route\n // always carries a product id (even for new), so we can't infer it from\n // the id alone — it's resolved by whether getProduct found a record.\n const [isNew, setIsNew] = useState(false);\n const [categories, setCategories] = useState([]);\n const [brands, setBrands] = useState([]);\n const [vaccineMaster, setVaccineMaster] = useState([]);\n const fieldErrors = useFieldErrors();\n const settings = usePmSettings();\n const manageSales = settings?.manageSales !== false;\n const [form, setForm] = useState({\n id: productId,\n name: '',\n categoryId: '',\n brandId: '',\n description: '',\n unit: '',\n unitOther: '',\n hasExpiry: true,\n needsStorageUnit: false,\n notForSale: false,\n canBeQuarantined: true,\n salePriceMode: 'FREE',\n salePrice: '',\n isActive: true,\n imageUuids: [],\n vaccineConfig: null,\n });\n\n const selectedCategory = useMemo(\n () => categories.find((c) => c.id === form.categoryId),\n [categories, form.categoryId]\n );\n const isVaccine = selectedCategory?.code?.toUpperCase() === VACCINES_CATEGORY_CODE;\n\n usePermissionedEffect('update.manage.productManagement.product', () => {\n async function load() {\n setLoading(true);\n try {\n const [cats, brandList, vaccines] = await Promise.all([\n getCategories(),\n getBrands(),\n getImmunizationMaster().catch(() => []),\n ]);\n setCategories(cats);\n setBrands(brandList);\n setVaccineMaster(vaccines);\n\n // Try fetching existing product; if it returns 404 we treat it as new.\n try {\n const existing = await getProduct(productId);\n setIsNew(false);\n setForm({\n id: existing.id,\n name: existing.name ?? '',\n categoryId: existing.categoryId ?? '',\n brandId: existing.brandId ?? '',\n description: existing.description ?? '',\n unit: existing.unit ?? 'PIECE',\n unitOther: existing.unitOther ?? '',\n hasExpiry: existing.hasExpiry ?? false,\n needsStorageUnit: existing.needsStorageUnit ?? false,\n notForSale: existing.notForSale ?? false,\n canBeQuarantined: existing.canBeQuarantined ?? true,\n salePriceMode: existing.salePriceMode ?? 'FREE',\n salePrice: existing.salePrice ?? '',\n isActive: existing.isActive ?? true,\n imageUuids: existing.imageUuids ?? [],\n vaccineConfig: existing.vaccineConfig\n ? {\n vaccineCode: existing.vaccineConfig.vaccineCode ?? '',\n vialVolumeMl: existing.vaccineConfig.vialVolumeMl ?? '',\n adultDoseMl: existing.vaccineConfig.adultDoseMl ?? '',\n childDoseMl: existing.vaccineConfig.childDoseMl ?? '',\n openVialValidityHours: existing.vaccineConfig.openVialValidityHours ?? 6,\n maxConcurrentOpenVials: existing.vaccineConfig.maxConcurrentOpenVials ?? 2,\n requiresReconstitution: existing.vaccineConfig.requiresReconstitution ?? false,\n reconstitutedValidityHours: existing.vaccineConfig.reconstitutedValidityHours ?? '',\n route: existing.vaccineConfig.route ?? '',\n storageTempMinCelsius: existing.vaccineConfig.storageTempMinCelsius ?? '',\n storageTempMaxCelsius: existing.vaccineConfig.storageTempMaxCelsius ?? '',\n comments: existing.vaccineConfig.comments ?? '',\n }\n : null,\n });\n } catch {\n // new product – fall through with default form\n setIsNew(true);\n }\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to load product');\n } finally {\n setLoading(false);\n }\n }\n void load();\n }, [productId]);\n\n useEffect(() => {\n if (isVaccine) {\n // Vaccines are dispensed per vial and live in cold-chain storage — pin both\n // the unit and needsStorageUnit so they can't drift away from those rules.\n setForm((f) => ({\n ...f,\n unit: 'VIAL',\n needsStorageUnit: true,\n vaccineConfig: f.vaccineConfig ?? { ...EMPTY_VACCINE },\n }));\n } else {\n // Switched to (or loaded as) a non-vaccine category — drop any vaccine\n // config so it isn't sent on save. Persisting half-filled config for a\n // non-vaccine product would error on the backend.\n setForm((f) => (f.vaccineConfig ? { ...f, vaccineConfig: null } : f));\n }\n }, [isVaccine]);\n\n function patch(k, v) {\n setForm((f) => ({ ...f, [k]: v }));\n }\n\n function patchVaccine(k, v) {\n setForm((f) => ({ ...f, vaccineConfig: { ...(f.vaccineConfig ?? EMPTY_VACCINE), [k]: v } }));\n }\n\n // -------------------- Field-level error map --------------------\n const errors = useMemo(() => {\n const base = {\n name: validateRequired(form.name, 'Product name'),\n categoryId: validateRequired(form.categoryId, 'Category'),\n brandId: validateRequired(form.brandId, 'Brand'),\n unit: validateRequired(form.unit, 'Unit'),\n // Custom unit label is required only when unit is OTHER.\n unitOther: form.unit === 'OTHER' ? validateRequired(form.unitOther, 'Custom unit') : '',\n // Sale price required (>0) when a fixed/capped mode is chosen on a sellable product.\n salePrice: (manageSales && !form.notForSale && form.salePriceMode !== 'FREE')\n ? (validateRequired(form.salePrice, 'Sale price')\n || (Number(form.salePrice) > 0 ? '' : 'Sale price must be greater than 0.'))\n : '',\n };\n if (isVaccine && form.vaccineConfig) {\n const v = form.vaccineConfig;\n const temps = validateTempRange(v.storageTempMinCelsius, v.storageTempMaxCelsius);\n Object.assign(base, {\n vaccineCode: validateRequired(v.vaccineCode, 'Vaccine'),\n vialVolumeMl: validateVialVolume(v.vialVolumeMl),\n adultDoseMl: validateDose(v.adultDoseMl, 'Adult dose', v.vialVolumeMl),\n childDoseMl: validateDose(v.childDoseMl, 'Child dose', v.vialVolumeMl),\n openVialValidityHours: validateOpenVialValidity(v.openVialValidityHours),\n maxConcurrentOpenVials: validateMaxConcurrentVials(v.maxConcurrentOpenVials),\n reconstitutedValidityHours: validateReconstitutedValidity(\n v.reconstitutedValidityHours,\n v.requiresReconstitution\n ),\n storageTempMinCelsius: temps.min,\n storageTempMaxCelsius: temps.max,\n });\n // Either an adult or a child dose must be configured.\n if (!v.adultDoseMl && !v.childDoseMl) {\n base.adultDoseMl = base.adultDoseMl || 'Provide at least one dose (adult or child)';\n base.childDoseMl = base.childDoseMl || 'Provide at least one dose (adult or child)';\n }\n }\n return base;\n }, [form, isVaccine, manageSales]);\n\n const formIsValid = isClean(errors);\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError('Please fix the highlighted fields and try again.');\n return;\n }\n try {\n const payload = {\n id: form.id,\n name: form.name.trim(),\n categoryId: form.categoryId,\n brandId: form.brandId,\n description: form.description || null,\n unit: form.unit,\n unitOther: form.unit === 'OTHER' ? (form.unitOther?.trim() || null) : null,\n hasExpiry: !!form.hasExpiry,\n needsStorageUnit: !!form.needsStorageUnit,\n notForSale: manageSales ? !!form.notForSale : false,\n canBeQuarantined: !!form.canBeQuarantined,\n // Sale pricing only applies to sellable products in a money-managed\n // deployment; otherwise force FREE with no price.\n salePriceMode: (manageSales && !form.notForSale) ? form.salePriceMode : 'FREE',\n salePrice: (manageSales && !form.notForSale && form.salePriceMode !== 'FREE' && form.salePrice !== '')\n ? Number(form.salePrice) : null,\n isActive: !!form.isActive,\n imageUuids: form.imageUuids ?? [],\n vaccineConfig: form.vaccineConfig\n ? {\n vaccineCode: form.vaccineConfig.vaccineCode?.trim().toUpperCase(),\n vialVolumeMl: Number(form.vaccineConfig.vialVolumeMl),\n adultDoseMl: form.vaccineConfig.adultDoseMl === '' ? null : Number(form.vaccineConfig.adultDoseMl),\n childDoseMl: form.vaccineConfig.childDoseMl === '' ? null : Number(form.vaccineConfig.childDoseMl),\n openVialValidityHours: Number(form.vaccineConfig.openVialValidityHours ?? 6),\n maxConcurrentOpenVials: Number(form.vaccineConfig.maxConcurrentOpenVials ?? 2),\n requiresReconstitution: !!form.vaccineConfig.requiresReconstitution,\n reconstitutedValidityHours: form.vaccineConfig.reconstitutedValidityHours === ''\n ? null\n : Number(form.vaccineConfig.reconstitutedValidityHours),\n route: form.vaccineConfig.route || null,\n storageTempMinCelsius: form.vaccineConfig.storageTempMinCelsius === ''\n ? null\n : Number(form.vaccineConfig.storageTempMinCelsius),\n storageTempMaxCelsius: form.vaccineConfig.storageTempMaxCelsius === ''\n ? null\n : Number(form.vaccineConfig.storageTempMaxCelsius),\n comments: form.vaccineConfig.comments || null,\n }\n : null,\n };\n await upsertProduct(payload);\n showToast({ message: 'Product saved.', type: 'success' });\n navigate('/ui/medplat/manage/pm/products');\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to save product');\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'update.manage.productManagement.product'} title=\"You don’t have permission to create or edit products\">\n <PmPageShell\n title={isNew ? 'Add Product' : 'Edit Product'}\n subtitle={`Category: ${selectedCategory?.code ?? '—'}`}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\" onClick={() => navigate('/ui/medplat/manage/pm/products')}>Cancel</Button>\n <Button variant=\"contained\" startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}>Save</Button>\n </Stack>\n }\n >\n {loading ? <Alert severity=\"info\">Loading…</Alert> : null}\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n <Card sx={{ borderRadius: 1.5, mb: 2 }}>\n <CardContent>\n <Typography variant=\"h6\" sx={{ fontWeight: 800, mb: 2 }}>Basic Details</Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <TextField\n required\n fullWidth size=\"small\" label=\"Product Name\"\n value={form.name}\n inputProps={{ maxLength: 255 }}\n onChange={(e) => patch('name', e.target.value)}\n onBlur={fieldErrors.onBlur('name')}\n error={fieldErrors.show('name') && !!errors.name}\n FormHelperTextProps={{ component: 'div' }}\n helperText={<PmFieldHelp message={fieldErrors.show('name') ? errors.name : ''} value={form.name} max={255} />}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Category\"\n required\n value={form.categoryId}\n onChange={(v) => patch('categoryId', v)}\n onBlur={fieldErrors.onBlur('categoryId')}\n error={fieldErrors.show('categoryId') && !!errors.categoryId}\n helperText={fieldErrors.show('categoryId') ? errors.categoryId : ''}\n options={categories.map((c) => ({ value: c.id, label: `${c.name} (${c.code})` }))}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Brand\"\n required\n value={form.brandId}\n onChange={(v) => patch('brandId', v)}\n onBlur={fieldErrors.onBlur('brandId')}\n error={fieldErrors.show('brandId') && !!errors.brandId}\n helperText={fieldErrors.show('brandId') ? errors.brandId : ''}\n options={brands.map((b) => ({ value: b.id, label: b.name }))}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Unit\"\n required\n disabled={isVaccine}\n value={form.unit}\n onChange={(v) => patch('unit', v)}\n onBlur={fieldErrors.onBlur('unit')}\n error={fieldErrors.show('unit') && !!errors.unit}\n helperText={fieldErrors.show('unit') && errors.unit\n ? errors.unit\n : isVaccine ? 'Vaccines must use VIAL' : ''}\n options={PRODUCT_UNITS}\n />\n </Grid>\n {form.unit === 'OTHER' ? (\n <Grid item xs={12} md={3}>\n <TextField\n required fullWidth size=\"small\" label=\"Custom unit name\"\n value={form.unitOther}\n inputProps={{ maxLength: 50 }}\n onChange={(e) => patch('unitOther', e.target.value)}\n onBlur={fieldErrors.onBlur('unitOther')}\n error={fieldErrors.show('unitOther') && !!errors.unitOther}\n FormHelperTextProps={{ component: 'div' }}\n helperText={<PmFieldHelp\n message={fieldErrors.show('unitOther') && errors.unitOther ? errors.unitOther : 'e.g. Strip, Ampoule, Sachet'}\n value={form.unitOther} max={50} />}\n />\n </Grid>\n ) : null}\n <Grid item xs={6} md={3}>\n <FormControlLabel\n control={<Checkbox\n checked={!!form.hasExpiry}\n disabled={isVaccine}\n onChange={(e) => patch('hasExpiry', e.target.checked)} />}\n label={isVaccine ? 'Has expiry (vaccine)' : 'Has expiry'} />\n </Grid>\n <Grid item xs={6} md={3}>\n <FormControlLabel\n control={<Checkbox\n checked={!!form.needsStorageUnit}\n disabled={isVaccine}\n onChange={(e) => patch('needsStorageUnit', e.target.checked)} />}\n label={isVaccine ? 'Needs storage unit (vaccine)' : 'Needs storage unit'} />\n </Grid>\n {manageSales ? (\n <Grid item xs={6} md={3}>\n <FormControlLabel control={<Checkbox checked={!!form.notForSale}\n onChange={(e) => patch('notForSale', e.target.checked)} />} label=\"Not for sale\" />\n </Grid>\n ) : null}\n <Grid item xs={6} md={3}>\n <FormControlLabel control={<Checkbox checked={!!form.canBeQuarantined}\n onChange={(e) => patch('canBeQuarantined', e.target.checked)} />} label=\"Can be quarantined\" />\n </Grid>\n <Grid item xs={6} md={3}>\n <FormControlLabel control={<Checkbox checked={!!form.isActive}\n onChange={(e) => patch('isActive', e.target.checked)} />} label=\"Active\" />\n </Grid>\n\n {/* Sale pricing — only for sellable products in a money-managed deployment. */}\n {manageSales && !form.notForSale ? (\n <>\n <Grid item xs={12} md={4}>\n <PmSelect\n label=\"Sale price mode\"\n value={form.salePriceMode}\n onChange={(v) => patch('salePriceMode', v)}\n options={SALE_PRICE_MODES}\n helperText=\"Fixed shows a locked price at sale time; MRP caps the entered price.\"\n />\n </Grid>\n {form.salePriceMode !== 'FREE' ? (\n <Grid item xs={12} md={4}>\n <TextField\n required fullWidth size=\"small\" type=\"number\"\n inputProps={{ step: '0.01', min: 0 }}\n label={form.salePriceMode === 'FIXED' ? 'Fixed sale price' : 'Price cap (MRP)'}\n value={form.salePrice}\n onChange={(e) => patch('salePrice', e.target.value)}\n onBlur={fieldErrors.onBlur('salePrice')}\n error={fieldErrors.show('salePrice') && !!errors.salePrice}\n helperText={fieldErrors.show('salePrice') ? errors.salePrice : ''}\n />\n </Grid>\n ) : null}\n </>\n ) : null}\n\n <Grid item xs={12}>\n <TextField fullWidth size=\"small\" multiline minRows={2} label=\"Description\" value={form.description}\n onChange={(e) => patch('description', e.target.value)} />\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n\n {isVaccine ? (\n <Card sx={{ borderRadius: 1.5 }}>\n <CardContent>\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"center\" sx={{ mb: 2 }}>\n <Typography variant=\"h6\" sx={{ fontWeight: 800 }}>Vaccine Configuration</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Required for vaccine products. Controls vial size, dosing, and shelf life rules.\n </Typography>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Grid container spacing={2}>\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Vaccine\"\n required\n value={form.vaccineConfig?.vaccineCode || ''}\n onChange={(v) => patchVaccine('vaccineCode', v)}\n onBlur={fieldErrors.onBlur('vaccineCode')}\n error={fieldErrors.show('vaccineCode') && !!errors.vaccineCode}\n helperText={fieldErrors.show('vaccineCode') && errors.vaccineCode\n ? errors.vaccineCode\n : 'Sourced from medplat_immunization_master'}\n noOptionsText=\"No vaccines configured in immunization master\"\n options={vaccineMaster.map((v) => ({ value: v.vaccineCode, label: `${v.vaccineName} (${v.vaccineCode})` }))}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField required fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Vial Volume (ml)\"\n value={form.vaccineConfig?.vialVolumeMl ?? ''}\n onChange={(e) => patchVaccine('vialVolumeMl', e.target.value)}\n onBlur={fieldErrors.onBlur('vialVolumeMl')}\n error={fieldErrors.show('vialVolumeMl') && !!errors.vialVolumeMl}\n helperText={fieldErrors.show('vialVolumeMl') ? errors.vialVolumeMl : ''}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Adult Dose (ml)\"\n value={form.vaccineConfig?.adultDoseMl ?? ''}\n onChange={(e) => patchVaccine('adultDoseMl', e.target.value)}\n onBlur={fieldErrors.onBlur('adultDoseMl')}\n error={fieldErrors.show('adultDoseMl') && !!errors.adultDoseMl}\n helperText={fieldErrors.show('adultDoseMl') ? errors.adultDoseMl : ''}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Child Dose (ml)\"\n value={form.vaccineConfig?.childDoseMl ?? ''}\n onChange={(e) => patchVaccine('childDoseMl', e.target.value)}\n onBlur={fieldErrors.onBlur('childDoseMl')}\n error={fieldErrors.show('childDoseMl') && !!errors.childDoseMl}\n helperText={fieldErrors.show('childDoseMl') ? errors.childDoseMl : ''}\n />\n </Grid>\n\n <Grid item xs={12} md={3}>\n <TextField required fullWidth size=\"small\" type=\"number\" inputProps={{ min: 0, max: 720 }}\n label=\"Open vial validity (hours)\"\n value={form.vaccineConfig?.openVialValidityHours ?? ''}\n onChange={(e) => patchVaccine('openVialValidityHours', e.target.value)}\n onBlur={fieldErrors.onBlur('openVialValidityHours')}\n error={fieldErrors.show('openVialValidityHours') && !!errors.openVialValidityHours}\n helperText={\n fieldErrors.show('openVialValidityHours') && errors.openVialValidityHours\n ? errors.openVialValidityHours\n : '0 = single-use; max 720 (~30 days)'\n }\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField required fullWidth size=\"small\" type=\"number\" inputProps={{ min: 1, max: 20 }}\n label=\"Max concurrent open vials\"\n value={form.vaccineConfig?.maxConcurrentOpenVials ?? ''}\n onChange={(e) => patchVaccine('maxConcurrentOpenVials', e.target.value)}\n onBlur={fieldErrors.onBlur('maxConcurrentOpenVials')}\n error={fieldErrors.show('maxConcurrentOpenVials') && !!errors.maxConcurrentOpenVials}\n helperText={fieldErrors.show('maxConcurrentOpenVials') ? errors.maxConcurrentOpenVials : ''}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <FormControlLabel control={<Checkbox checked={!!form.vaccineConfig?.requiresReconstitution}\n onChange={(e) => patchVaccine('requiresReconstitution', e.target.checked)} />} label=\"Requires reconstitution\" />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField fullWidth size=\"small\" type=\"number\" inputProps={{ min: 1, max: 720 }}\n required={!!form.vaccineConfig?.requiresReconstitution}\n disabled={!form.vaccineConfig?.requiresReconstitution}\n label=\"Reconstituted validity (hours)\"\n value={form.vaccineConfig?.reconstitutedValidityHours ?? ''}\n onChange={(e) => patchVaccine('reconstitutedValidityHours', e.target.value)}\n onBlur={fieldErrors.onBlur('reconstitutedValidityHours')}\n error={fieldErrors.show('reconstitutedValidityHours') && !!errors.reconstitutedValidityHours}\n helperText={fieldErrors.show('reconstitutedValidityHours') ? errors.reconstitutedValidityHours : ''}\n />\n </Grid>\n\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Route of Administration\"\n value={form.vaccineConfig?.route || ''}\n onChange={(v) => patchVaccine('route', v)}\n options={VACCINE_ROUTES}\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.1', min: -90, max: 30 }}\n label=\"Storage temp min (°C)\"\n value={form.vaccineConfig?.storageTempMinCelsius ?? ''}\n onChange={(e) => patchVaccine('storageTempMinCelsius', e.target.value)}\n onBlur={fieldErrors.onBlur('storageTempMinCelsius')}\n error={fieldErrors.show('storageTempMinCelsius') && !!errors.storageTempMinCelsius}\n helperText={\n fieldErrors.show('storageTempMinCelsius') && errors.storageTempMinCelsius\n ? errors.storageTempMinCelsius\n : 'Allowed range: −90°C to +30°C'\n }\n />\n </Grid>\n <Grid item xs={12} md={3}>\n <TextField fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.1', min: -90, max: 30 }}\n label=\"Storage temp max (°C)\"\n value={form.vaccineConfig?.storageTempMaxCelsius ?? ''}\n onChange={(e) => patchVaccine('storageTempMaxCelsius', e.target.value)}\n onBlur={fieldErrors.onBlur('storageTempMaxCelsius')}\n error={fieldErrors.show('storageTempMaxCelsius') && !!errors.storageTempMaxCelsius}\n helperText={\n fieldErrors.show('storageTempMaxCelsius') && errors.storageTempMaxCelsius\n ? errors.storageTempMaxCelsius\n : 'Allowed range: −90°C to +30°C'\n }\n />\n </Grid>\n\n <Grid item xs={12}>\n <TextField fullWidth size=\"small\" multiline minRows={2}\n label=\"Comments\" value={form.vaccineConfig?.comments || ''}\n onChange={(e) => patchVaccine('comments', e.target.value)} />\n </Grid>\n </Grid>\n </CardContent>\n </Card>\n ) : null}\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { S as e } from "./settingsStore-KW217unV.js";
2
- import { c as t, s as n, t as r } from "./PmTable-BkK65bJQ.js";
3
- import { m as i } from "./constants-5ZCZo-Zm.js";
4
- import { a, i as o, n as s, r as c, t as l } from "./PmGuard-C90yhpsN.js";
1
+ import { S as e } from "./settingsStore-GLCtI-tv.js";
2
+ import { c as t, s as n, t as r } from "./PmTable-BMFiuHdG.js";
3
+ import { h as i } from "./constants-CdzV0PxZ.js";
4
+ import { a, i as o, n as s, r as c, t as l } from "./PmGuard-DrbbysnW.js";
5
5
  import { useState as u } from "react";
6
6
  import { jsx as d, jsxs as f } from "react/jsx-runtime";
7
7
  import { Alert as p, Box as m, Button as h, Chip as g, FormControlLabel as _, InputAdornment as v, Skeleton as y, Stack as b, TableBody as x, TableCell as S, TableHead as C, TableRow as w, TextField as T, Typography as E } from "@mui/material";
@@ -252,4 +252,4 @@ function I() {
252
252
  //#endregion
253
253
  export { I as default };
254
254
 
255
- //# sourceMappingURL=ProductsPage-i8QVw_Jm.js.map
255
+ //# sourceMappingURL=ProductsPage-DFgK9-13.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsPage-i8QVw_Jm.js","names":[],"sources":["../src/views/ProductsPage.jsx"],"sourcesContent":["import { useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Alert,\n Box,\n Button,\n Chip,\n FormControlLabel,\n InputAdornment,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\nimport SearchRoundedIcon from '@mui/icons-material/SearchRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\nimport VaccinesRoundedIcon from '@mui/icons-material/VaccinesRounded';\nimport { showToast } from '@/common/toaster/toaster';\nimport CustomSwitch from '@/views/custom-components/CustomSwitch';\nimport PmPageShell from '@pm/components/PmPageShell';\nimport PmNav from '@pm/components/PmNav';\nimport PmGuard from '@pm/components/PmGuard';\nimport { PmEmptyRow, PmTable, PmTableCard } from '@pm/components/PmTable';\nimport { getProducts } from '@pm/services/pmApi';\nimport { unitLabel } from '@pm/utils/constants';\nimport { usePermissionedEffect, usePmPermissions } from '@pm/utils/permissions';\n\nexport default function ProductsPage() {\n const navigate = useNavigate();\n const perms = usePmPermissions();\n const canWrite = perms.has('update.manage.productManagement.product');\n const [items, setItems] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [search, setSearch] = useState('');\n // Default to active-only; the toggle above the table reveals inactive rows.\n const [showInactive, setShowInactive] = useState(false);\n\n async function load() {\n setLoading(true);\n setError('');\n try {\n setItems(await getProducts(undefined, true));\n } catch (err) {\n const message = err?.response?.data?.message ?? err?.message ?? 'Failed to load products';\n setError(message);\n showToast({ message, type: 'error' });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect('view.manage.productManagement.product', () => {\n void load();\n }, []);\n\n const filtered = items.filter((p) => {\n if (!showInactive && p.isActive === false) return false;\n if (!search.trim()) return true;\n const q = search.toLowerCase();\n return (p.name?.toLowerCase().includes(q))\n || (p.categoryName?.toLowerCase().includes(q))\n || (p.brandName?.toLowerCase().includes(q))\n || (p.vaccineConfig?.vaccineCode?.toLowerCase().includes(q));\n });\n\n function createNew() {\n navigate(`/ui/medplat/manage/pm/products/${uuidv4()}`);\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'view.manage.productManagement.product'}>\n <PmPageShell\n title=\"Products\"\n subtitle=\"Each product is tracked separately. Vaccines need additional setup for vial size and dosing.\"\n chips={['Catalog']}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\" startIcon={<RefreshRoundedIcon />} onClick={() => void load()}>Refresh</Button>\n {canWrite ? (\n <Button variant=\"contained\" startIcon={<AddRoundedIcon />} onClick={createNew}>New Product</Button>\n ) : null}\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n <PmTableCard\n eyebrow=\"Catalog\"\n title=\"Products\"\n subtitle={loading\n ? 'Loading…'\n : `${filtered.length} of ${items.length} ${items.length === 1 ? 'product' : 'products'}${search.trim() ? ` matching \"${search.trim()}\"` : ''}`}\n actions={\n <FormControlLabel\n control={<CustomSwitch checked={showInactive}\n onChange={(e) => setShowInactive(e.target.checked)} />}\n label={<Typography variant=\"body2\" sx={{ fontWeight: 600 }}>Show inactive</Typography>}\n labelPlacement=\"start\"\n sx={{ mr: 0.5, ml: 0 }}\n />\n }\n toolbar={\n <TextField\n size=\"small\"\n fullWidth\n placeholder=\"Search by name, category, brand, or vaccine code\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchRoundedIcon fontSize=\"small\" color=\"action\" />\n </InputAdornment>\n ),\n }}\n sx={{ maxWidth: 540 }}\n />\n }\n >\n <PmTable>\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Category</TableCell>\n <TableCell>Brand</TableCell>\n <TableCell>Unit</TableCell>\n <TableCell>Expiry</TableCell>\n <TableCell>Vaccine config</TableCell>\n <TableCell>Status</TableCell>\n <TableCell align=\"right\">Action</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n Array.from({ length: 5 }).map((_, i) => (\n <TableRow key={i}>\n {Array.from({ length: 8 }).map((__, j) => (\n <TableCell key={j}><Skeleton height={22} /></TableCell>\n ))}\n </TableRow>\n ))\n ) : filtered.length === 0 ? (\n <PmEmptyRow\n colSpan={8}\n icon={Inventory2RoundedIcon}\n title={items.length === 0 ? 'No products yet' : 'No matches'}\n hint={items.length === 0\n ? 'Create categories and brands first, then add products.'\n : 'Try a different search term.'}\n action={items.length === 0 && canWrite ? (\n <Button variant=\"contained\" size=\"small\" startIcon={<AddRoundedIcon />} onClick={createNew}>\n Create first product\n </Button>\n ) : null}\n />\n ) : filtered.map((p) => (\n <TableRow key={p.id} hover>\n <TableCell sx={{ maxWidth: 240 }}>\n <Typography variant=\"body2\" sx={{ fontWeight: 700, wordBreak: 'break-word' }}>{p.name}</Typography>\n </TableCell>\n <TableCell sx={{ maxWidth: 160 }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ wordBreak: 'break-word' }}>{p.categoryName}</Typography>\n </TableCell>\n <TableCell sx={{ maxWidth: 160 }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ wordBreak: 'break-word' }}>{p.brandName}</Typography>\n </TableCell>\n <TableCell>\n <Chip size=\"small\" label={unitLabel(p.unit, p.unitOther)} variant=\"outlined\" sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }} />\n </TableCell>\n <TableCell>\n {p.hasExpiry\n ? <Chip size=\"small\" label=\"Yes\" color=\"warning\" variant=\"outlined\" sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }} />\n : <Chip size=\"small\" label=\"No\" variant=\"outlined\" sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }} />}\n </TableCell>\n <TableCell>\n {p.vaccineConfig ? (\n <Stack direction=\"row\" alignItems=\"center\" spacing={1}>\n <VaccinesRoundedIcon sx={{ fontSize: 18, color: 'info.main' }} />\n <Box>\n <Typography variant=\"caption\" sx={{ fontFamily: 'monospace', fontWeight: 800, display: 'block', lineHeight: 1.2 }}>\n {p.vaccineConfig.vaccineCode}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {p.vaccineConfig.vialVolumeMl}ml · {p.vaccineConfig.adultDoseMl ?? '—'}/{p.vaccineConfig.childDoseMl ?? '—'} ml\n </Typography>\n </Box>\n </Stack>\n ) : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n label={p.isActive ? 'Active' : 'Inactive'}\n color={p.isActive ? 'success' : 'default'}\n variant={p.isActive ? 'filled' : 'outlined'}\n sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }}\n />\n </TableCell>\n <TableCell align=\"right\">\n {canWrite ? (\n <Button size=\"small\" variant=\"outlined\"\n onClick={() => navigate(`/ui/medplat/manage/pm/products/${p.id}`)}>Edit</Button>\n ) : null}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkCA,SAAwB,IAAe;CACrC,IAAM,IAAW,EAAY,GAEvB,IADQ,EACG,EAAM,IAAI,yCAAyC,GAC9D,CAAC,GAAO,KAAY,EAAS,CAAC,CAAC,GAC/B,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAO,KAAY,EAAS,EAAE,GAC/B,CAAC,GAAQ,KAAa,EAAS,EAAE,GAEjC,CAAC,GAAc,KAAmB,EAAS,EAAK;CAEtD,eAAe,IAAO;EAEpB,AADA,EAAW,EAAI,GACf,EAAS,EAAE;EACX,IAAI;GACF,EAAS,MAAM,EAAY,KAAA,GAAW,EAAI,CAAC;EAC7C,SAAS,GAAK;GACZ,IAAM,IAAU,GAAK,UAAU,MAAM,WAAW,GAAK,WAAW;GAEhE,AADA,EAAS,CAAO,GAChB,EAAU;IAAE;IAAS,MAAM;GAAQ,CAAC;EACtC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,EAAsB,+CAA+C;EACnE,EAAU;CACZ,GAAG,CAAC,CAAC;CAEL,IAAM,IAAW,EAAM,QAAQ,MAAM;EACnC,IAAI,CAAC,KAAgB,EAAE,aAAa,IAAO,OAAO;EAClD,IAAI,CAAC,EAAO,KAAK,GAAG,OAAO;EAC3B,IAAM,IAAI,EAAO,YAAY;EAC7B,OAAQ,EAAE,MAAM,YAAY,EAAE,SAAS,CAAC,KAClC,EAAE,cAAc,YAAY,EAAE,SAAS,CAAC,KACxC,EAAE,WAAW,YAAY,EAAE,SAAS,CAAC,KACrC,EAAE,eAAe,aAAa,YAAY,EAAE,SAAS,CAAC;CAC9D,CAAC;CAED,SAAS,IAAY;EACnB,EAAS,kCAAkC,EAAO,GAAG;CACvD;CAEA,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,CAAQ,CAAA,GACR,kBAAC,GAAD;EAAS,MAAM;YACf,kBAAC,GAAD;GACE,OAAM;GACN,UAAS;GACT,OAAO,CAAC,SAAS;GACjB,SACE,kBAAC,GAAD;IAAO,WAAU;IAAM,SAAS;cAAhC,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAW,WAAW,kBAAC,GAAD,CAAqB,CAAA;KAAG,eAAe,KAAK,EAAK;eAAG;IAAe,CAAA,GACxG,IACC,kBAAC,GAAD;KAAQ,SAAQ;KAAY,WAAW,kBAAC,GAAD,CAAiB,CAAA;KAAG,SAAS;eAAW;IAAmB,CAAA,IAChG,IACC;;aAVX,CAaG,IAAQ,kBAAC,GAAD;IAAO,UAAS;IAAQ,IAAI,EAAE,IAAI,EAAE;cAAI;GAAa,CAAA,IAAI,MAElE,kBAAC,GAAD;IACE,SAAQ;IACR,OAAM;IACN,UAAU,IACN,aACA,GAAG,EAAS,OAAO,MAAM,EAAM,OAAO,GAAG,EAAM,WAAW,IAAI,YAAY,aAAa,EAAO,KAAK,IAAI,cAAc,EAAO,KAAK,EAAE,KAAK;IAC5I,SACE,kBAAC,GAAD;KACE,SAAS,kBAAC,GAAD;MAAc,SAAS;MAC9B,WAAW,MAAM,EAAgB,EAAE,OAAO,OAAO;KAAI,CAAA;KACvD,OAAO,kBAAC,GAAD;MAAY,SAAQ;MAAQ,IAAI,EAAE,YAAY,IAAI;gBAAG;KAAyB,CAAA;KACrF,gBAAe;KACf,IAAI;MAAE,IAAI;MAAK,IAAI;KAAE;IACtB,CAAA;IAEH,SACE,kBAAC,GAAD;KACE,MAAK;KACL,WAAA;KACA,aAAY;KACZ,OAAO;KACP,WAAW,MAAM,EAAU,EAAE,OAAO,KAAK;KACzC,YAAY,EACV,gBACE,kBAAC,GAAD;MAAgB,UAAS;gBACvB,kBAAC,GAAD;OAAmB,UAAS;OAAQ,OAAM;MAAU,CAAA;KACtC,CAAA,EAEpB;KACA,IAAI,EAAE,UAAU,IAAI;IACrB,CAAA;cAGH,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD,EAAA,UAAW,UAAkB,CAAA;KAC7B,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA;KAC9B,kBAAC,GAAD,EAAA,UAAW,QAAgB,CAAA;KAC3B,kBAAC,GAAD,EAAA,UAAW,OAAe,CAAA;KAC1B,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;KAC5B,kBAAC,GAAD,EAAA,UAAW,iBAAyB,CAAA;KACpC,kBAAC,GAAD,EAAA,UAAW,SAAiB,CAAA;KAC5B,kBAAC,GAAD;MAAW,OAAM;gBAAQ;KAAiB,CAAA;IAClC,EAAA,CAAA,EACD,CAAA,GACX,kBAAC,GAAD,EAAA,UACG,IACC,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MAChC,kBAAC,GAAD,EAAA,UACG,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAI,MAClC,kBAAC,GAAD,EAAA,UAAmB,kBAAC,GAAD,EAAU,QAAQ,GAAK,CAAA,EAAY,GAAtC,CAAsC,CACvD,EACO,GAJK,CAIL,CACX,IACC,EAAS,WAAW,IACtB,kBAAC,GAAD;KACE,SAAS;KACT,MAAM;KACN,OAAO,EAAM,WAAW,IAAI,oBAAoB;KAChD,MAAM,EAAM,WAAW,IACnB,2DACA;KACJ,QAAQ,EAAM,WAAW,KAAK,IAC5B,kBAAC,GAAD;MAAQ,SAAQ;MAAY,MAAK;MAAQ,WAAW,kBAAC,GAAD,CAAiB,CAAA;MAAG,SAAS;gBAAW;KAEpF,CAAA,IACN;IACL,CAAA,IACC,EAAS,KAAK,MAChB,kBAAC,GAAD;KAAqB,OAAA;eAArB;MACE,kBAAC,GAAD;OAAW,IAAI,EAAE,UAAU,IAAI;iBAC7B,kBAAC,GAAD;QAAY,SAAQ;QAAQ,IAAI;SAAE,YAAY;SAAK,WAAW;QAAa;kBAAI,EAAE;OAAiB,CAAA;MACzF,CAAA;MACX,kBAAC,GAAD;OAAW,IAAI,EAAE,UAAU,IAAI;iBAC7B,kBAAC,GAAD;QAAY,SAAQ;QAAU,OAAM;QAAiB,IAAI,EAAE,WAAW,aAAa;kBAAI,EAAE;OAAyB,CAAA;MACzG,CAAA;MACX,kBAAC,GAAD;OAAW,IAAI,EAAE,UAAU,IAAI;iBAC7B,kBAAC,GAAD;QAAY,SAAQ;QAAU,OAAM;QAAiB,IAAI,EAAE,WAAW,aAAa;kBAAI,EAAE;OAAsB,CAAA;MACtG,CAAA;MACX,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;OAAM,MAAK;OAAQ,OAAO,EAAU,EAAE,MAAM,EAAE,SAAS;OAAG,SAAQ;OAAW,IAAI;QAAE,YAAY;QAAK,cAAc;QAAK,QAAQ;OAAG;MAAI,CAAA,EAC7H,CAAA;MACX,kBAAC,GAAD,EAAA,UACG,EAAE,YACC,kBAAC,GAAD;OAAM,MAAK;OAAQ,OAAM;OAAM,OAAM;OAAU,SAAQ;OAAW,IAAI;QAAE,YAAY;QAAK,cAAc;QAAK,QAAQ;OAAG;MAAI,CAAA,IAC3H,kBAAC,GAAD;OAAM,MAAK;OAAQ,OAAM;OAAK,SAAQ;OAAW,IAAI;QAAE,YAAY;QAAK,cAAc;QAAK,QAAQ;OAAG;MAAI,CAAA,EACrG,CAAA;MACX,kBAAC,GAAD,EAAA,UACG,EAAE,gBACD,kBAAC,GAAD;OAAO,WAAU;OAAM,YAAW;OAAS,SAAS;iBAApD,CACE,kBAAC,GAAD,EAAqB,IAAI;QAAE,UAAU;QAAI,OAAO;OAAY,EAAI,CAAA,GAChE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAY,SAAQ;QAAU,IAAI;SAAE,YAAY;SAAa,YAAY;SAAK,SAAS;SAAS,YAAY;QAAI;kBAC7G,EAAE,cAAc;OACP,CAAA,GACZ,kBAAC,GAAD;QAAY,SAAQ;QAAU,OAAM;QAAiB,IAAI,EAAE,oBAAoB,eAAe;kBAA9F;SACG,EAAE,cAAc;SAAa;SAAM,EAAE,cAAc,eAAe;SAAI;SAAE,EAAE,cAAc,eAAe;SAAI;QAClG;SACT,EAAA,CAAA,CACA;WACL,kBAAC,GAAD;OAAY,SAAQ;OAAU,OAAM;iBAAgB;MAAa,CAAA,EAC5D,CAAA;MACX,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;OACE,MAAK;OACL,OAAO,EAAE,WAAW,WAAW;OAC/B,OAAO,EAAE,WAAW,YAAY;OAChC,SAAS,EAAE,WAAW,WAAW;OACjC,IAAI;QAAE,YAAY;QAAK,cAAc;QAAK,QAAQ;OAAG;MACtD,CAAA,EACQ,CAAA;MACX,kBAAC,GAAD;OAAW,OAAM;iBACd,IACC,kBAAC,GAAD;QAAQ,MAAK;QAAQ,SAAQ;QAC3B,eAAe,EAAS,kCAAkC,EAAE,IAAI;kBAAG;OAAY,CAAA,IAC/E;MACK,CAAA;KACH;OAhDK,EAAE,EAgDP,CACX,EACQ,CAAA,CACJ,EAAA,CAAA;GACE,CAAA,CACF;;CACJ,CAAA,CACN,EAAA,CAAA;AAET"}
1
+ {"version":3,"file":"ProductsPage-DFgK9-13.js","names":[],"sources":["../src/views/ProductsPage.jsx"],"sourcesContent":["import { useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Alert,\n Box,\n Button,\n Chip,\n FormControlLabel,\n InputAdornment,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\nimport SearchRoundedIcon from '@mui/icons-material/SearchRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\nimport VaccinesRoundedIcon from '@mui/icons-material/VaccinesRounded';\nimport { showToast } from '@/common/toaster/toaster';\nimport CustomSwitch from '@/views/custom-components/CustomSwitch';\nimport PmPageShell from '@pm/components/PmPageShell';\nimport PmNav from '@pm/components/PmNav';\nimport PmGuard from '@pm/components/PmGuard';\nimport { PmEmptyRow, PmTable, PmTableCard } from '@pm/components/PmTable';\nimport { getProducts } from '@pm/services/pmApi';\nimport { unitLabel } from '@pm/utils/constants';\nimport { usePermissionedEffect, usePmPermissions } from '@pm/utils/permissions';\n\nexport default function ProductsPage() {\n const navigate = useNavigate();\n const perms = usePmPermissions();\n const canWrite = perms.has('update.manage.productManagement.product');\n const [items, setItems] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n const [search, setSearch] = useState('');\n // Default to active-only; the toggle above the table reveals inactive rows.\n const [showInactive, setShowInactive] = useState(false);\n\n async function load() {\n setLoading(true);\n setError('');\n try {\n setItems(await getProducts(undefined, true));\n } catch (err) {\n const message = err?.response?.data?.message ?? err?.message ?? 'Failed to load products';\n setError(message);\n showToast({ message, type: 'error' });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect('view.manage.productManagement.product', () => {\n void load();\n }, []);\n\n const filtered = items.filter((p) => {\n if (!showInactive && p.isActive === false) return false;\n if (!search.trim()) return true;\n const q = search.toLowerCase();\n return (p.name?.toLowerCase().includes(q))\n || (p.categoryName?.toLowerCase().includes(q))\n || (p.brandName?.toLowerCase().includes(q))\n || (p.vaccineConfig?.vaccineCode?.toLowerCase().includes(q));\n });\n\n function createNew() {\n navigate(`/ui/medplat/manage/pm/products/${uuidv4()}`);\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'view.manage.productManagement.product'}>\n <PmPageShell\n title=\"Products\"\n subtitle=\"Each product is tracked separately. Vaccines need additional setup for vial size and dosing.\"\n chips={['Catalog']}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\" startIcon={<RefreshRoundedIcon />} onClick={() => void load()}>Refresh</Button>\n {canWrite ? (\n <Button variant=\"contained\" startIcon={<AddRoundedIcon />} onClick={createNew}>New Product</Button>\n ) : null}\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n <PmTableCard\n eyebrow=\"Catalog\"\n title=\"Products\"\n subtitle={loading\n ? 'Loading…'\n : `${filtered.length} of ${items.length} ${items.length === 1 ? 'product' : 'products'}${search.trim() ? ` matching \"${search.trim()}\"` : ''}`}\n actions={\n <FormControlLabel\n control={<CustomSwitch checked={showInactive}\n onChange={(e) => setShowInactive(e.target.checked)} />}\n label={<Typography variant=\"body2\" sx={{ fontWeight: 600 }}>Show inactive</Typography>}\n labelPlacement=\"start\"\n sx={{ mr: 0.5, ml: 0 }}\n />\n }\n toolbar={\n <TextField\n size=\"small\"\n fullWidth\n placeholder=\"Search by name, category, brand, or vaccine code\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchRoundedIcon fontSize=\"small\" color=\"action\" />\n </InputAdornment>\n ),\n }}\n sx={{ maxWidth: 540 }}\n />\n }\n >\n <PmTable>\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Category</TableCell>\n <TableCell>Brand</TableCell>\n <TableCell>Unit</TableCell>\n <TableCell>Expiry</TableCell>\n <TableCell>Vaccine config</TableCell>\n <TableCell>Status</TableCell>\n <TableCell align=\"right\">Action</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n Array.from({ length: 5 }).map((_, i) => (\n <TableRow key={i}>\n {Array.from({ length: 8 }).map((__, j) => (\n <TableCell key={j}><Skeleton height={22} /></TableCell>\n ))}\n </TableRow>\n ))\n ) : filtered.length === 0 ? (\n <PmEmptyRow\n colSpan={8}\n icon={Inventory2RoundedIcon}\n title={items.length === 0 ? 'No products yet' : 'No matches'}\n hint={items.length === 0\n ? 'Create categories and brands first, then add products.'\n : 'Try a different search term.'}\n action={items.length === 0 && canWrite ? (\n <Button variant=\"contained\" size=\"small\" startIcon={<AddRoundedIcon />} onClick={createNew}>\n Create first product\n </Button>\n ) : null}\n />\n ) : filtered.map((p) => (\n <TableRow key={p.id} hover>\n <TableCell sx={{ maxWidth: 240 }}>\n <Typography variant=\"body2\" sx={{ fontWeight: 700, wordBreak: 'break-word' }}>{p.name}</Typography>\n </TableCell>\n <TableCell sx={{ maxWidth: 160 }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ wordBreak: 'break-word' }}>{p.categoryName}</Typography>\n </TableCell>\n <TableCell sx={{ maxWidth: 160 }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ wordBreak: 'break-word' }}>{p.brandName}</Typography>\n </TableCell>\n <TableCell>\n <Chip size=\"small\" label={unitLabel(p.unit, p.unitOther)} variant=\"outlined\" sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }} />\n </TableCell>\n <TableCell>\n {p.hasExpiry\n ? <Chip size=\"small\" label=\"Yes\" color=\"warning\" variant=\"outlined\" sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }} />\n : <Chip size=\"small\" label=\"No\" variant=\"outlined\" sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }} />}\n </TableCell>\n <TableCell>\n {p.vaccineConfig ? (\n <Stack direction=\"row\" alignItems=\"center\" spacing={1}>\n <VaccinesRoundedIcon sx={{ fontSize: 18, color: 'info.main' }} />\n <Box>\n <Typography variant=\"caption\" sx={{ fontFamily: 'monospace', fontWeight: 800, display: 'block', lineHeight: 1.2 }}>\n {p.vaccineConfig.vaccineCode}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ fontVariantNumeric: 'tabular-nums' }}>\n {p.vaccineConfig.vialVolumeMl}ml · {p.vaccineConfig.adultDoseMl ?? '—'}/{p.vaccineConfig.childDoseMl ?? '—'} ml\n </Typography>\n </Box>\n </Stack>\n ) : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n <TableCell>\n <Chip\n size=\"small\"\n label={p.isActive ? 'Active' : 'Inactive'}\n color={p.isActive ? 'success' : 'default'}\n variant={p.isActive ? 'filled' : 'outlined'}\n sx={{ fontWeight: 700, borderRadius: 1.5, height: 22 }}\n />\n </TableCell>\n <TableCell align=\"right\">\n {canWrite ? (\n <Button size=\"small\" variant=\"outlined\"\n onClick={() => navigate(`/ui/medplat/manage/pm/products/${p.id}`)}>Edit</Button>\n ) : null}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { E as e, S as t, T as n, Y as r, r as i, u as a } from "./settingsStore-KW217unV.js";
2
- import { o, r as s } from "./PmTable-BkK65bJQ.js";
3
- import { n as ee, p as te, t as ne } from "./constants-5ZCZo-Zm.js";
1
+ import { E as e, S as t, T as n, Y as r, r as i, u as a } from "./settingsStore-GLCtI-tv.js";
2
+ import { o, r as s } from "./PmTable-BMFiuHdG.js";
3
+ import { m as ee, n as te, t as ne } from "./constants-CdzV0PxZ.js";
4
4
  import { a as re, r as ie, t as ae } from "./format-UOyOu75i.js";
5
- import { a as oe, d as c, f as l, l as se, m as u, t as d } from "./validation-Ba7puFIO.js";
5
+ import { a as oe, d as c, f as l, l as se, m as u, t as d } from "./validation-BkUPqrdA.js";
6
6
  import { t as ce } from "./useFieldErrors-hDG8ox_x.js";
7
- import { a as le, n as ue, r as de, t as fe } from "./PmGuard-C90yhpsN.js";
8
- import { t as pe } from "./FacilityPicker-Diq2hnYg.js";
7
+ import { a as le, n as ue, r as de, t as fe } from "./PmGuard-DrbbysnW.js";
8
+ import { t as pe } from "./FacilityPicker-C2drWyqo.js";
9
9
  import { useEffect as me, useMemo as f, useState as p } from "react";
10
10
  import { Fragment as he, jsx as m, jsxs as h } from "react/jsx-runtime";
11
11
  import { Alert as ge, Box as _e, Button as g, Card as _, CardContent as v, Chip as ve, Divider as y, Grid as b, IconButton as ye, Stack as x, Table as be, TableBody as xe, TableCell as S, TableHead as Se, TableRow as C, TextField as w, Typography as T } from "@mui/material";
@@ -317,7 +317,7 @@ function P() {
317
317
  K.ownerType === "FACILITY" ? /* @__PURE__ */ m(S, { children: e.storageUnitName || "—" }) : null,
318
318
  /* @__PURE__ */ m(S, { children: /* @__PURE__ */ m(ve, {
319
319
  size: "small",
320
- label: te(ne, e.status),
320
+ label: ee(ne, e.status),
321
321
  color: re(e.status)
322
322
  }) })
323
323
  ] }, e.id)) })]
@@ -524,7 +524,7 @@ function P() {
524
524
  ...K,
525
525
  paymentMode: e
526
526
  }),
527
- options: ee
527
+ options: te
528
528
  })
529
529
  }) : null, /* @__PURE__ */ m(b, {
530
530
  item: !0,
@@ -588,4 +588,4 @@ function P() {
588
588
  //#endregion
589
589
  export { P as default };
590
590
 
591
- //# sourceMappingURL=PurchaseCreatePage-Bp6bl9n8.js.map
591
+ //# sourceMappingURL=PurchaseCreatePage-B1eV15aS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PurchaseCreatePage-Bp6bl9n8.js","names":[],"sources":["../src/views/PurchaseCreatePage.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Chip,\n Divider,\n Grid,\n IconButton,\n Stack,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport DeleteOutlineRoundedIcon from '@mui/icons-material/DeleteOutlineRounded';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport SaveRoundedIcon from '@mui/icons-material/SaveRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\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 {\n createPurchase,\n getProducts,\n getStorageUnits,\n listBatches,\n submitPurchaseRequest,\n} from '@pm/services/pmApi';\nimport FacilityPicker from '@pm/components/FacilityPicker';\nimport { BATCH_STATUSES, PAYMENT_MODES, labelOf } from '@pm/utils/constants';\nimport {\n isClean,\n validateFutureDate,\n validatePastOrTodayDate,\n validatePrice,\n validateQuantity,\n validateRequired,\n} from '@pm/utils/validation';\nimport { formatDate, formatNumber, statusColor } from '@pm/utils/format';\nimport { PmFormSection, PmSelect } from '@pm/components/PmTable';\nimport { useFieldErrors } from '@pm/utils/useFieldErrors';\nimport { usePermissionedEffect } from '@pm/utils/permissions';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\n// Purchase type → backend owner_type mapping. \"Personal Use\" puts the stock\n// against the acting user; \"Facility Use\" puts it against a facility the user\n// has access to. The Personal option is filtered out when the deployment-level\n// allowPersonalPurchase setting is off (see PURCHASE_TYPES filter below).\nconst ALL_PURCHASE_TYPES = [\n { value: 'USER', label: 'Personal Use' },\n { value: 'FACILITY', label: 'Facility Use' },\n];\n\nfunction emptyLine() {\n return {\n key: uuidv4(),\n batchId: uuidv4(),\n productId: '',\n quantity: 1,\n buyPrice: '',\n expiryDate: '',\n lotNumber: '',\n manufactureDate: '',\n storageUnitId: '',\n };\n}\n\nexport default function PurchaseCreatePage() {\n const navigate = useNavigate();\n const settings = usePmSettings();\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n const allowPersonalPurchase = settings?.allowPersonalPurchase !== false;\n // When confirmation is on, the form submits to the request endpoint and\n // the user lands on the request detail page instead of the purchases list.\n const requireConfirmation = settings?.requirePurchaseConfirmation === true;\n // Personal Use is hidden entirely when the deployment doesn't allow it; the\n // dropdown then has a single option and the form defaults to FACILITY.\n const PURCHASE_TYPES = useMemo(\n () => (allowPersonalPurchase ? ALL_PURCHASE_TYPES : ALL_PURCHASE_TYPES.filter((t) => t.value !== 'USER')),\n [allowPersonalPurchase],\n );\n const [products, setProducts] = useState([]);\n const [storageUnits, setStorageUnits] = useState([]);\n const [batches, setBatches] = useState([]);\n const [error, setError] = useState('');\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n ownerType: allowPersonalPurchase ? 'USER' : 'FACILITY',\n ownerId: '',\n purchaseDate: today,\n paymentMode: 'CASH',\n notes: '',\n items: [emptyLine()],\n });\n\n // When the deployment setting flips from allowing personal purchases to not\n // allowing them (or vice-versa) after the form has rendered, snap the\n // ownerType back to a valid choice so the user can't end up submitting USER\n // against a deployment that doesn't accept it.\n useEffect(() => {\n if (!allowPersonalPurchase && form.ownerType === 'USER') {\n setForm((f) => ({ ...f, ownerType: 'FACILITY', ownerId: '' }));\n }\n }, [allowPersonalPurchase, form.ownerType]);\n\n usePermissionedEffect('update.manage.productManagement.purchase', () => {\n async function load() {\n try {\n const [p, su, b] = await Promise.all([\n getProducts(),\n getStorageUnits({ activeOnly: true }),\n // Pulled here so the existing-inventory panel renders alongside the\n // purchase form — the operator can see what they / the facility\n // already hold before deciding what to buy.\n listBatches().catch(() => []),\n ]);\n setProducts(p);\n setStorageUnits(su);\n setBatches(b);\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to load reference data');\n }\n }\n void load();\n }, []);\n\n // Existing-stock view for the currently-selected owner. Personal purchases\n // show the operator's USER-owned batches; facility purchases show batches\n // owned by the picked facility. Exhausted/expired rows are skipped to keep\n // the panel decision-relevant.\n const existingInventory = useMemo(() => {\n if (form.ownerType === 'USER') {\n return batches.filter((b) => b.ownerType === 'USER' && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n if (form.ownerType === 'FACILITY' && form.ownerId) {\n return batches.filter((b) => b.ownerType === 'FACILITY' && b.ownerId === form.ownerId\n && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n return [];\n }, [batches, form.ownerType, form.ownerId]);\n\n // Storage units filtered by the selected facility (when applicable).\n // Personal purchases don't reference a storage unit at all.\n const eligibleStorageUnits = useMemo(() => {\n if (form.ownerType !== 'FACILITY' || !form.ownerId) return [];\n return storageUnits.filter((u) => u.ownerId === form.ownerId);\n }, [storageUnits, form.ownerType, form.ownerId]);\n\n function patchLine(idx, k, v) {\n setForm((f) => {\n const items = f.items.slice();\n items[idx] = { ...items[idx], [k]: v };\n return { ...f, items };\n });\n }\n\n function removeLine(idx) {\n setForm((f) => ({ ...f, items: f.items.filter((_, i) => i !== idx) }));\n }\n\n function addLine() {\n setForm((f) => ({ ...f, items: [...f.items, emptyLine()] }));\n }\n\n // Sum of (qty * buy price) for lines that have a price. Used to validate\n // the optional Total Amount header field.\n const computedLineTotal = useMemo(() => {\n let priced = false;\n const sum = form.items.reduce((acc, line) => {\n if (line.buyPrice === '' || line.quantity === '' || line.quantity == null) return acc;\n const qty = Number(line.quantity);\n const price = Number(line.buyPrice);\n if (Number.isNaN(qty) || Number.isNaN(price)) return acc;\n priced = true;\n return acc + qty * price;\n }, 0);\n return { sum, priced };\n }, [form.items]);\n\n // Header + per-line errors. The total is auto-derived from the line items so it\n // never needs its own validation message.\n const headerErrors = {\n ownerType: validateRequired(form.ownerType, 'Purchase type'),\n ownerId: form.ownerType === 'FACILITY' ? validateRequired(form.ownerId, 'Facility') : '',\n purchaseDate: validateRequired(form.purchaseDate, 'Purchase date'),\n };\n const lineErrors = form.items.map((line) => {\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n // Storage units only apply to facility-owned stock — personal purchases keep\n // the field disabled, so the requirement is only enforced for facility flows.\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n let storageErr = storageRequired ? validateRequired(line.storageUnitId, 'Storage unit') : '';\n // Cold-chain check: if the product has a vaccine config with a required\n // storage range, the selected storage unit's operating range must fit\n // inside it. Mirror the backend STORAGE_UNIT_TEMP_MISMATCH rule.\n if (!storageErr && line.storageUnitId && product?.vaccineConfig) {\n const cfg = product.vaccineConfig;\n const unit = storageUnits.find((u) => u.id === line.storageUnitId);\n if (cfg.storageTempMinCelsius != null && cfg.storageTempMaxCelsius != null && unit) {\n const uMin = unit.operatingTempMinCelsius;\n const uMax = unit.operatingTempMaxCelsius;\n // Unit must be able to reach every temp the vaccine tolerates: its\n // operating range must contain the vaccine's required range.\n if (uMin == null || uMax == null\n || Number(uMin) > Number(cfg.storageTempMinCelsius)\n || Number(uMax) < Number(cfg.storageTempMaxCelsius)) {\n storageErr = `Unit operates ${uMin ?? '?'}°C → ${uMax ?? '?'}°C, which cannot cover this product's required ${cfg.storageTempMinCelsius}°C → ${cfg.storageTempMaxCelsius}°C.`;\n }\n }\n }\n return {\n productId: validateRequired(line.productId, 'Product'),\n quantity: validateQuantity(line.quantity),\n // When purchase amounts are managed, a positive buy price is mandatory;\n // otherwise it stays optional. Mirrors the backend enforcement.\n buyPrice: manageAmounts\n ? (validateRequired(line.buyPrice, 'Buy price')\n || validatePrice(line.buyPrice)\n || (Number(line.buyPrice) > 0 ? '' : 'Buy price must be greater than 0.'))\n : validatePrice(line.buyPrice),\n // In request mode the approver fills expiry at confirmation, so we\n // don't require it from the requester even when the product carries\n // hasExpiry. The approver gets to enter the actual expiry off the\n // packaging on PurchaseRequestDetailPage.\n expiryDate: requireConfirmation\n ? ''\n : expiryRequired\n ? (validateRequired(line.expiryDate, 'Expiry date')\n || validateFutureDate(line.expiryDate, { label: 'Expiry' }))\n : validateFutureDate(line.expiryDate, { label: 'Expiry' }),\n manufactureDate: validatePastOrTodayDate(line.manufactureDate, { label: 'Manufacture date' }),\n storageUnitId: storageErr,\n };\n });\n const formIsValid = isClean(headerErrors) && lineErrors.every(isClean) && form.items.length > 0;\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError('Please fix the highlighted fields and try again.');\n return;\n }\n // Payload shape is the same for direct purchase and request submission —\n // the difference is the endpoint and what we navigate to afterwards.\n const payload = {\n id: form.id,\n ownerType: form.ownerType,\n ownerId: form.ownerType === 'FACILITY' ? form.ownerId : null,\n purchaseDate: form.purchaseDate,\n paymentMode: manageAmounts ? (form.paymentMode || null) : null,\n // Amount tracking is a per-deployment toggle. When it's off, scrub money\n // values to null so the backend's validatePurchaseTotal stays a no-op\n // and inventory rows don't carry stale prices that aren't surfaced anywhere.\n totalAmount: manageAmounts && computedLineTotal.priced ? Number(computedLineTotal.sum.toFixed(2)) : null,\n notes: form.notes || null,\n items: form.items.map((line) => ({\n batchId: line.batchId,\n productId: line.productId,\n quantity: Number(line.quantity),\n buyPrice: manageAmounts && line.buyPrice !== '' ? Number(line.buyPrice) : null,\n expiryDate: line.expiryDate || null,\n lotNumber: line.lotNumber || null,\n manufactureDate: line.manufactureDate || null,\n storageUnitId: line.storageUnitId || null,\n })),\n };\n try {\n if (requireConfirmation) {\n const submitted = await submitPurchaseRequest(payload);\n showToast({ message: 'Purchase request submitted for approval.', type: 'success' });\n navigate(`/ui/medplat/manage/pm/purchase-requests/${submitted?.id ?? form.id}`);\n } else {\n await createPurchase(payload);\n showToast({ message: 'Purchase recorded.', type: 'success' });\n navigate('/ui/medplat/manage/pm/purchases');\n }\n } catch (err) {\n setError(err?.response?.data?.message\n ?? err?.message\n ?? (requireConfirmation ? 'Failed to submit purchase request' : 'Failed to record purchase'));\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={'update.manage.productManagement.purchase'}\n title={requireConfirmation\n ? \"You don’t have permission to submit purchase requests\"\n : \"You don’t have permission to record purchases\"}>\n <PmPageShell\n title={requireConfirmation ? 'Submit Purchase Request' : 'Record Purchase'}\n subtitle={requireConfirmation\n ? 'Purchases need approval before stock is added. Submit the request and wait for a manager to confirm.'\n : 'Each item creates a batch that you can track and a record in your ledger.'}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\"\n onClick={() => navigate(requireConfirmation\n ? '/ui/medplat/manage/pm/purchase-requests'\n : '/ui/medplat/manage/pm/purchases')}>Cancel</Button>\n <Button variant=\"contained\" startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}>{requireConfirmation ? 'Submit Request' : 'Save'}</Button>\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n {/* ---------- Ownership ---------- */}\n <Card sx={{ borderRadius: 1.5, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Purchase for\"\n hint=\"Choose who will own this stock: yourself or a facility.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <PmSelect\n label=\"Purchase Type\"\n required\n value={form.ownerType}\n onChange={(v) => setForm({ ...form, ownerType: v, ownerId: '' })}\n onBlur={fieldErrors.onBlur('ownerType')}\n error={fieldErrors.show('ownerType') && !!headerErrors.ownerType}\n options={PURCHASE_TYPES}\n helperText={fieldErrors.show('ownerType') && headerErrors.ownerType\n ? headerErrors.ownerType\n : 'Personal stock vs. facility stock'}\n />\n </Grid>\n {form.ownerType === 'FACILITY' ? (\n <Grid item xs={12} md={4}>\n <FacilityPicker\n required\n value={form.ownerId}\n onChange={(id) => setForm({ ...form, ownerId: id })}\n onBlur={fieldErrors.onBlur('ownerId')}\n error={fieldErrors.show('ownerId') && !!headerErrors.ownerId}\n helperText={\n fieldErrors.show('ownerId') && headerErrors.ownerId\n ? headerErrors.ownerId\n : 'Search for the facility.'\n }\n />\n </Grid>\n ) : null}\n </Grid>\n </CardContent>\n </Card>\n\n {/* ---------- Existing inventory for the picked owner ---------- */}\n {(form.ownerType === 'USER' || (form.ownerType === 'FACILITY' && form.ownerId)) ? (\n <Card sx={{ borderRadius: 1.5, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Context\"\n title=\"Existing inventory\"\n hint={form.ownerType === 'USER'\n ? 'Your current stock.'\n : 'What this facility already has, including where it is stored.'}\n sx={{ mb: 1.5 }}\n />\n {existingInventory.length === 0 ? (\n <Stack direction=\"row\" spacing={1.25} alignItems=\"center\" sx={{ py: 1.5, color: 'text.secondary' }}>\n <Inventory2RoundedIcon fontSize=\"small\" />\n <Typography variant=\"body2\">No active stock yet for this owner.</Typography>\n </Stack>\n ) : (\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell align=\"right\">Remaining</TableCell>\n <TableCell>Expiry</TableCell>\n {form.ownerType === 'FACILITY' ? <TableCell>Storage</TableCell> : null}\n <TableCell>Status</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {existingInventory.map((b) => (\n <TableRow key={b.id}>\n <TableCell sx={{ fontWeight: 700 }}>{b.productName}</TableCell>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: '0.78rem' }}>{b.lotNumber || '—'}</TableCell>\n <TableCell align=\"right\" sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 700 }}>\n {b.qtyRemaining}\n </TableCell>\n <TableCell>{b.expiryDate ? formatDate(b.expiryDate) : '—'}</TableCell>\n {form.ownerType === 'FACILITY' ? (\n <TableCell>{b.storageUnitName || '—'}</TableCell>\n ) : null}\n <TableCell>\n <Chip size=\"small\" label={labelOf(BATCH_STATUSES, b.status)} color={statusColor(b.status)} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n ) : null}\n\n {/* ---------- Lines ---------- */}\n <Card sx={{ borderRadius: 1.5, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" sx={{ mb: 2 }}>\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item creates one batch in your inventory and a receipt record.\"\n />\n <Button variant=\"outlined\" startIcon={<AddRoundedIcon />} onClick={addLine}>Add Line</Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const lerr = lineErrors[idx] || {};\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n return (\n <Card key={line.key} variant=\"outlined\" sx={{ p: 2, borderRadius: 1.5 }}>\n <Grid container spacing={2}>\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Product\"\n required\n value={line.productId}\n onChange={(v) => patchLine(idx, 'productId', v)}\n onBlur={fieldErrors.onBlur(`items.${idx}.productId`)}\n error={fieldErrors.show(`items.${idx}.productId`) && !!lerr.productId}\n helperText={fieldErrors.show(`items.${idx}.productId`) ? lerr.productId : ''}\n options={products.map((p) => ({ value: p.id, label: p.name }))}\n />\n </Grid>\n <Grid item xs={6} md={1}>\n <TextField required fullWidth size=\"small\" type=\"number\" label=\"Qty\"\n inputProps={{ min: 1 }}\n value={line.quantity}\n onChange={(e) => patchLine(idx, 'quantity', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={fieldErrors.show(`items.${idx}.quantity`) && !!lerr.quantity}\n helperText={fieldErrors.show(`items.${idx}.quantity`) ? lerr.quantity : ''} />\n </Grid>\n {manageAmounts ? (\n <Grid item xs={6} md={2}>\n <TextField required fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Buy Price\"\n value={line.buyPrice}\n onChange={(e) => patchLine(idx, 'buyPrice', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.buyPrice`)}\n error={fieldErrors.show(`items.${idx}.buyPrice`) && !!lerr.buyPrice}\n helperText={fieldErrors.show(`items.${idx}.buyPrice`) ? lerr.buyPrice : ''} />\n </Grid>\n ) : null}\n {/* Expiry is captured at confirmation time alongside Lot —\n the requester is asking for stock they don't yet have,\n so they can't read the expiry off the packaging. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField required={expiryRequired} fullWidth size=\"small\" type=\"date\" label=\"Expiry\"\n InputLabelProps={{ shrink: true }}\n inputProps={{ min: today }}\n value={line.expiryDate}\n onChange={(e) => patchLine(idx, 'expiryDate', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.expiryDate`)}\n error={fieldErrors.show(`items.${idx}.expiryDate`) && !!lerr.expiryDate}\n helperText={\n fieldErrors.show(`items.${idx}.expiryDate`) && lerr.expiryDate\n ? lerr.expiryDate\n : (expiryRequired ? 'Required for this product' : '')\n } />\n </Grid>\n )}\n {/* Lot # is captured at confirmation time by the approver,\n who physically receives the goods. Hidden when this\n form is in request-mode so the requester doesn't waste\n time guessing a lot they don't have yet. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField fullWidth size=\"small\" label=\"Lot #\"\n value={line.lotNumber}\n onChange={(e) => patchLine(idx, 'lotNumber', e.target.value)} />\n </Grid>\n )}\n <Grid item xs={10} md={1}>\n <PmSelect\n label=\"Storage\"\n required={storageRequired}\n disabled={form.ownerType !== 'FACILITY' || !form.ownerId}\n value={line.storageUnitId || ''}\n onChange={(v) => patchLine(idx, 'storageUnitId', v)}\n onBlur={fieldErrors.onBlur(`items.${idx}.storageUnitId`)}\n error={fieldErrors.show(`items.${idx}.storageUnitId`) && !!lerr.storageUnitId}\n options={eligibleStorageUnits.map((u) => ({ value: u.id, label: u.unitName }))}\n helperText={fieldErrors.show(`items.${idx}.storageUnitId`) && lerr.storageUnitId\n ? lerr.storageUnitId\n : storageRequired ? 'Required for this product' : ''}\n />\n </Grid>\n <Grid item xs={2} md={1} sx={{ textAlign: 'right' }}>\n <IconButton color=\"error\" onClick={() => removeLine(idx)} disabled={form.items.length === 1}>\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Summary footer (like a bill) ---------- */}\n <Card sx={{\n borderRadius: 1.5,\n bgcolor: 'grey.50',\n border: '1px solid',\n borderColor: 'divider',\n boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)',\n }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title={manageAmounts ? 'Bill summary' : 'Receipt details'}\n hint={manageAmounts\n ? 'The total is calculated from items. Add payment mode and notes. The date is set automatically.'\n : 'Add notes about this receipt. The date is set automatically.'}\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n {manageAmounts ? (\n <Grid item xs={12} md={6}>\n <PmSelect\n label=\"Payment Mode\"\n value={form.paymentMode || ''}\n onChange={(v) => setForm({ ...form, paymentMode: v })}\n options={PAYMENT_MODES}\n />\n </Grid>\n ) : null}\n <Grid item xs={12} md={manageAmounts ? 6 : 12}>\n <TextField fullWidth size=\"small\" label=\"Notes\" value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })} />\n </Grid>\n </Grid>\n\n {manageAmounts ? (\n <>\n <Divider sx={{ my: 2 }} />\n\n <Stack direction=\"row\" justifyContent=\"flex-end\" alignItems=\"baseline\" spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Total ({form.items.length} {form.items.length === 1 ? 'line' : 'lines'})\n </Typography>\n <Typography variant=\"h4\" sx={{ fontWeight: 800, fontVariantNumeric: 'tabular-nums' }}>\n {computedLineTotal.priced ? formatNumber(computedLineTotal.sum) : '—'}\n </Typography>\n </Stack>\n {!computedLineTotal.priced ? (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', textAlign: 'right' }}>\n Add a Buy Price on at least one line to compute the total.\n </Typography>\n ) : null}\n </>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PurchaseCreatePage-B1eV15aS.js","names":[],"sources":["../src/views/PurchaseCreatePage.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Chip,\n Divider,\n Grid,\n IconButton,\n Stack,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport DeleteOutlineRoundedIcon from '@mui/icons-material/DeleteOutlineRounded';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport SaveRoundedIcon from '@mui/icons-material/SaveRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\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 {\n createPurchase,\n getProducts,\n getStorageUnits,\n listBatches,\n submitPurchaseRequest,\n} from '@pm/services/pmApi';\nimport FacilityPicker from '@pm/components/FacilityPicker';\nimport { BATCH_STATUSES, PAYMENT_MODES, labelOf } from '@pm/utils/constants';\nimport {\n isClean,\n validateFutureDate,\n validatePastOrTodayDate,\n validatePrice,\n validateQuantity,\n validateRequired,\n} from '@pm/utils/validation';\nimport { formatDate, formatNumber, statusColor } from '@pm/utils/format';\nimport { PmFormSection, PmSelect } from '@pm/components/PmTable';\nimport { useFieldErrors } from '@pm/utils/useFieldErrors';\nimport { usePermissionedEffect } from '@pm/utils/permissions';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\n// Purchase type → backend owner_type mapping. \"Personal Use\" puts the stock\n// against the acting user; \"Facility Use\" puts it against a facility the user\n// has access to. The Personal option is filtered out when the deployment-level\n// allowPersonalPurchase setting is off (see PURCHASE_TYPES filter below).\nconst ALL_PURCHASE_TYPES = [\n { value: 'USER', label: 'Personal Use' },\n { value: 'FACILITY', label: 'Facility Use' },\n];\n\nfunction emptyLine() {\n return {\n key: uuidv4(),\n batchId: uuidv4(),\n productId: '',\n quantity: 1,\n buyPrice: '',\n expiryDate: '',\n lotNumber: '',\n manufactureDate: '',\n storageUnitId: '',\n };\n}\n\nexport default function PurchaseCreatePage() {\n const navigate = useNavigate();\n const settings = usePmSettings();\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n const allowPersonalPurchase = settings?.allowPersonalPurchase !== false;\n // When confirmation is on, the form submits to the request endpoint and\n // the user lands on the request detail page instead of the purchases list.\n const requireConfirmation = settings?.requirePurchaseConfirmation === true;\n // Personal Use is hidden entirely when the deployment doesn't allow it; the\n // dropdown then has a single option and the form defaults to FACILITY.\n const PURCHASE_TYPES = useMemo(\n () => (allowPersonalPurchase ? ALL_PURCHASE_TYPES : ALL_PURCHASE_TYPES.filter((t) => t.value !== 'USER')),\n [allowPersonalPurchase],\n );\n const [products, setProducts] = useState([]);\n const [storageUnits, setStorageUnits] = useState([]);\n const [batches, setBatches] = useState([]);\n const [error, setError] = useState('');\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n ownerType: allowPersonalPurchase ? 'USER' : 'FACILITY',\n ownerId: '',\n purchaseDate: today,\n paymentMode: 'CASH',\n notes: '',\n items: [emptyLine()],\n });\n\n // When the deployment setting flips from allowing personal purchases to not\n // allowing them (or vice-versa) after the form has rendered, snap the\n // ownerType back to a valid choice so the user can't end up submitting USER\n // against a deployment that doesn't accept it.\n useEffect(() => {\n if (!allowPersonalPurchase && form.ownerType === 'USER') {\n setForm((f) => ({ ...f, ownerType: 'FACILITY', ownerId: '' }));\n }\n }, [allowPersonalPurchase, form.ownerType]);\n\n usePermissionedEffect('update.manage.productManagement.purchase', () => {\n async function load() {\n try {\n const [p, su, b] = await Promise.all([\n getProducts(),\n getStorageUnits({ activeOnly: true }),\n // Pulled here so the existing-inventory panel renders alongside the\n // purchase form — the operator can see what they / the facility\n // already hold before deciding what to buy.\n listBatches().catch(() => []),\n ]);\n setProducts(p);\n setStorageUnits(su);\n setBatches(b);\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to load reference data');\n }\n }\n void load();\n }, []);\n\n // Existing-stock view for the currently-selected owner. Personal purchases\n // show the operator's USER-owned batches; facility purchases show batches\n // owned by the picked facility. Exhausted/expired rows are skipped to keep\n // the panel decision-relevant.\n const existingInventory = useMemo(() => {\n if (form.ownerType === 'USER') {\n return batches.filter((b) => b.ownerType === 'USER' && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n if (form.ownerType === 'FACILITY' && form.ownerId) {\n return batches.filter((b) => b.ownerType === 'FACILITY' && b.ownerId === form.ownerId\n && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n return [];\n }, [batches, form.ownerType, form.ownerId]);\n\n // Storage units filtered by the selected facility (when applicable).\n // Personal purchases don't reference a storage unit at all.\n const eligibleStorageUnits = useMemo(() => {\n if (form.ownerType !== 'FACILITY' || !form.ownerId) return [];\n return storageUnits.filter((u) => u.ownerId === form.ownerId);\n }, [storageUnits, form.ownerType, form.ownerId]);\n\n function patchLine(idx, k, v) {\n setForm((f) => {\n const items = f.items.slice();\n items[idx] = { ...items[idx], [k]: v };\n return { ...f, items };\n });\n }\n\n function removeLine(idx) {\n setForm((f) => ({ ...f, items: f.items.filter((_, i) => i !== idx) }));\n }\n\n function addLine() {\n setForm((f) => ({ ...f, items: [...f.items, emptyLine()] }));\n }\n\n // Sum of (qty * buy price) for lines that have a price. Used to validate\n // the optional Total Amount header field.\n const computedLineTotal = useMemo(() => {\n let priced = false;\n const sum = form.items.reduce((acc, line) => {\n if (line.buyPrice === '' || line.quantity === '' || line.quantity == null) return acc;\n const qty = Number(line.quantity);\n const price = Number(line.buyPrice);\n if (Number.isNaN(qty) || Number.isNaN(price)) return acc;\n priced = true;\n return acc + qty * price;\n }, 0);\n return { sum, priced };\n }, [form.items]);\n\n // Header + per-line errors. The total is auto-derived from the line items so it\n // never needs its own validation message.\n const headerErrors = {\n ownerType: validateRequired(form.ownerType, 'Purchase type'),\n ownerId: form.ownerType === 'FACILITY' ? validateRequired(form.ownerId, 'Facility') : '',\n purchaseDate: validateRequired(form.purchaseDate, 'Purchase date'),\n };\n const lineErrors = form.items.map((line) => {\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n // Storage units only apply to facility-owned stock — personal purchases keep\n // the field disabled, so the requirement is only enforced for facility flows.\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n let storageErr = storageRequired ? validateRequired(line.storageUnitId, 'Storage unit') : '';\n // Cold-chain check: if the product has a vaccine config with a required\n // storage range, the selected storage unit's operating range must fit\n // inside it. Mirror the backend STORAGE_UNIT_TEMP_MISMATCH rule.\n if (!storageErr && line.storageUnitId && product?.vaccineConfig) {\n const cfg = product.vaccineConfig;\n const unit = storageUnits.find((u) => u.id === line.storageUnitId);\n if (cfg.storageTempMinCelsius != null && cfg.storageTempMaxCelsius != null && unit) {\n const uMin = unit.operatingTempMinCelsius;\n const uMax = unit.operatingTempMaxCelsius;\n // Unit must be able to reach every temp the vaccine tolerates: its\n // operating range must contain the vaccine's required range.\n if (uMin == null || uMax == null\n || Number(uMin) > Number(cfg.storageTempMinCelsius)\n || Number(uMax) < Number(cfg.storageTempMaxCelsius)) {\n storageErr = `Unit operates ${uMin ?? '?'}°C → ${uMax ?? '?'}°C, which cannot cover this product's required ${cfg.storageTempMinCelsius}°C → ${cfg.storageTempMaxCelsius}°C.`;\n }\n }\n }\n return {\n productId: validateRequired(line.productId, 'Product'),\n quantity: validateQuantity(line.quantity),\n // When purchase amounts are managed, a positive buy price is mandatory;\n // otherwise it stays optional. Mirrors the backend enforcement.\n buyPrice: manageAmounts\n ? (validateRequired(line.buyPrice, 'Buy price')\n || validatePrice(line.buyPrice)\n || (Number(line.buyPrice) > 0 ? '' : 'Buy price must be greater than 0.'))\n : validatePrice(line.buyPrice),\n // In request mode the approver fills expiry at confirmation, so we\n // don't require it from the requester even when the product carries\n // hasExpiry. The approver gets to enter the actual expiry off the\n // packaging on PurchaseRequestDetailPage.\n expiryDate: requireConfirmation\n ? ''\n : expiryRequired\n ? (validateRequired(line.expiryDate, 'Expiry date')\n || validateFutureDate(line.expiryDate, { label: 'Expiry' }))\n : validateFutureDate(line.expiryDate, { label: 'Expiry' }),\n manufactureDate: validatePastOrTodayDate(line.manufactureDate, { label: 'Manufacture date' }),\n storageUnitId: storageErr,\n };\n });\n const formIsValid = isClean(headerErrors) && lineErrors.every(isClean) && form.items.length > 0;\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError('Please fix the highlighted fields and try again.');\n return;\n }\n // Payload shape is the same for direct purchase and request submission —\n // the difference is the endpoint and what we navigate to afterwards.\n const payload = {\n id: form.id,\n ownerType: form.ownerType,\n ownerId: form.ownerType === 'FACILITY' ? form.ownerId : null,\n purchaseDate: form.purchaseDate,\n paymentMode: manageAmounts ? (form.paymentMode || null) : null,\n // Amount tracking is a per-deployment toggle. When it's off, scrub money\n // values to null so the backend's validatePurchaseTotal stays a no-op\n // and inventory rows don't carry stale prices that aren't surfaced anywhere.\n totalAmount: manageAmounts && computedLineTotal.priced ? Number(computedLineTotal.sum.toFixed(2)) : null,\n notes: form.notes || null,\n items: form.items.map((line) => ({\n batchId: line.batchId,\n productId: line.productId,\n quantity: Number(line.quantity),\n buyPrice: manageAmounts && line.buyPrice !== '' ? Number(line.buyPrice) : null,\n expiryDate: line.expiryDate || null,\n lotNumber: line.lotNumber || null,\n manufactureDate: line.manufactureDate || null,\n storageUnitId: line.storageUnitId || null,\n })),\n };\n try {\n if (requireConfirmation) {\n const submitted = await submitPurchaseRequest(payload);\n showToast({ message: 'Purchase request submitted for approval.', type: 'success' });\n navigate(`/ui/medplat/manage/pm/purchase-requests/${submitted?.id ?? form.id}`);\n } else {\n await createPurchase(payload);\n showToast({ message: 'Purchase recorded.', type: 'success' });\n navigate('/ui/medplat/manage/pm/purchases');\n }\n } catch (err) {\n setError(err?.response?.data?.message\n ?? err?.message\n ?? (requireConfirmation ? 'Failed to submit purchase request' : 'Failed to record purchase'));\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={'update.manage.productManagement.purchase'}\n title={requireConfirmation\n ? \"You don’t have permission to submit purchase requests\"\n : \"You don’t have permission to record purchases\"}>\n <PmPageShell\n title={requireConfirmation ? 'Submit Purchase Request' : 'Record Purchase'}\n subtitle={requireConfirmation\n ? 'Purchases need approval before stock is added. Submit the request and wait for a manager to confirm.'\n : 'Each item creates a batch that you can track and a record in your ledger.'}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\"\n onClick={() => navigate(requireConfirmation\n ? '/ui/medplat/manage/pm/purchase-requests'\n : '/ui/medplat/manage/pm/purchases')}>Cancel</Button>\n <Button variant=\"contained\" startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}>{requireConfirmation ? 'Submit Request' : 'Save'}</Button>\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n {/* ---------- Ownership ---------- */}\n <Card sx={{ borderRadius: 1.5, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Purchase for\"\n hint=\"Choose who will own this stock: yourself or a facility.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <PmSelect\n label=\"Purchase Type\"\n required\n value={form.ownerType}\n onChange={(v) => setForm({ ...form, ownerType: v, ownerId: '' })}\n onBlur={fieldErrors.onBlur('ownerType')}\n error={fieldErrors.show('ownerType') && !!headerErrors.ownerType}\n options={PURCHASE_TYPES}\n helperText={fieldErrors.show('ownerType') && headerErrors.ownerType\n ? headerErrors.ownerType\n : 'Personal stock vs. facility stock'}\n />\n </Grid>\n {form.ownerType === 'FACILITY' ? (\n <Grid item xs={12} md={4}>\n <FacilityPicker\n required\n value={form.ownerId}\n onChange={(id) => setForm({ ...form, ownerId: id })}\n onBlur={fieldErrors.onBlur('ownerId')}\n error={fieldErrors.show('ownerId') && !!headerErrors.ownerId}\n helperText={\n fieldErrors.show('ownerId') && headerErrors.ownerId\n ? headerErrors.ownerId\n : 'Search for the facility.'\n }\n />\n </Grid>\n ) : null}\n </Grid>\n </CardContent>\n </Card>\n\n {/* ---------- Existing inventory for the picked owner ---------- */}\n {(form.ownerType === 'USER' || (form.ownerType === 'FACILITY' && form.ownerId)) ? (\n <Card sx={{ borderRadius: 1.5, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Context\"\n title=\"Existing inventory\"\n hint={form.ownerType === 'USER'\n ? 'Your current stock.'\n : 'What this facility already has, including where it is stored.'}\n sx={{ mb: 1.5 }}\n />\n {existingInventory.length === 0 ? (\n <Stack direction=\"row\" spacing={1.25} alignItems=\"center\" sx={{ py: 1.5, color: 'text.secondary' }}>\n <Inventory2RoundedIcon fontSize=\"small\" />\n <Typography variant=\"body2\">No active stock yet for this owner.</Typography>\n </Stack>\n ) : (\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell align=\"right\">Remaining</TableCell>\n <TableCell>Expiry</TableCell>\n {form.ownerType === 'FACILITY' ? <TableCell>Storage</TableCell> : null}\n <TableCell>Status</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {existingInventory.map((b) => (\n <TableRow key={b.id}>\n <TableCell sx={{ fontWeight: 700 }}>{b.productName}</TableCell>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: '0.78rem' }}>{b.lotNumber || '—'}</TableCell>\n <TableCell align=\"right\" sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 700 }}>\n {b.qtyRemaining}\n </TableCell>\n <TableCell>{b.expiryDate ? formatDate(b.expiryDate) : '—'}</TableCell>\n {form.ownerType === 'FACILITY' ? (\n <TableCell>{b.storageUnitName || '—'}</TableCell>\n ) : null}\n <TableCell>\n <Chip size=\"small\" label={labelOf(BATCH_STATUSES, b.status)} color={statusColor(b.status)} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n ) : null}\n\n {/* ---------- Lines ---------- */}\n <Card sx={{ borderRadius: 1.5, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" sx={{ mb: 2 }}>\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item creates one batch in your inventory and a receipt record.\"\n />\n <Button variant=\"outlined\" startIcon={<AddRoundedIcon />} onClick={addLine}>Add Line</Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const lerr = lineErrors[idx] || {};\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n return (\n <Card key={line.key} variant=\"outlined\" sx={{ p: 2, borderRadius: 1.5 }}>\n <Grid container spacing={2}>\n <Grid item xs={12} md={3}>\n <PmSelect\n label=\"Product\"\n required\n value={line.productId}\n onChange={(v) => patchLine(idx, 'productId', v)}\n onBlur={fieldErrors.onBlur(`items.${idx}.productId`)}\n error={fieldErrors.show(`items.${idx}.productId`) && !!lerr.productId}\n helperText={fieldErrors.show(`items.${idx}.productId`) ? lerr.productId : ''}\n options={products.map((p) => ({ value: p.id, label: p.name }))}\n />\n </Grid>\n <Grid item xs={6} md={1}>\n <TextField required fullWidth size=\"small\" type=\"number\" label=\"Qty\"\n inputProps={{ min: 1 }}\n value={line.quantity}\n onChange={(e) => patchLine(idx, 'quantity', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={fieldErrors.show(`items.${idx}.quantity`) && !!lerr.quantity}\n helperText={fieldErrors.show(`items.${idx}.quantity`) ? lerr.quantity : ''} />\n </Grid>\n {manageAmounts ? (\n <Grid item xs={6} md={2}>\n <TextField required fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Buy Price\"\n value={line.buyPrice}\n onChange={(e) => patchLine(idx, 'buyPrice', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.buyPrice`)}\n error={fieldErrors.show(`items.${idx}.buyPrice`) && !!lerr.buyPrice}\n helperText={fieldErrors.show(`items.${idx}.buyPrice`) ? lerr.buyPrice : ''} />\n </Grid>\n ) : null}\n {/* Expiry is captured at confirmation time alongside Lot —\n the requester is asking for stock they don't yet have,\n so they can't read the expiry off the packaging. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField required={expiryRequired} fullWidth size=\"small\" type=\"date\" label=\"Expiry\"\n InputLabelProps={{ shrink: true }}\n inputProps={{ min: today }}\n value={line.expiryDate}\n onChange={(e) => patchLine(idx, 'expiryDate', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.expiryDate`)}\n error={fieldErrors.show(`items.${idx}.expiryDate`) && !!lerr.expiryDate}\n helperText={\n fieldErrors.show(`items.${idx}.expiryDate`) && lerr.expiryDate\n ? lerr.expiryDate\n : (expiryRequired ? 'Required for this product' : '')\n } />\n </Grid>\n )}\n {/* Lot # is captured at confirmation time by the approver,\n who physically receives the goods. Hidden when this\n form is in request-mode so the requester doesn't waste\n time guessing a lot they don't have yet. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField fullWidth size=\"small\" label=\"Lot #\"\n value={line.lotNumber}\n onChange={(e) => patchLine(idx, 'lotNumber', e.target.value)} />\n </Grid>\n )}\n <Grid item xs={10} md={1}>\n <PmSelect\n label=\"Storage\"\n required={storageRequired}\n disabled={form.ownerType !== 'FACILITY' || !form.ownerId}\n value={line.storageUnitId || ''}\n onChange={(v) => patchLine(idx, 'storageUnitId', v)}\n onBlur={fieldErrors.onBlur(`items.${idx}.storageUnitId`)}\n error={fieldErrors.show(`items.${idx}.storageUnitId`) && !!lerr.storageUnitId}\n options={eligibleStorageUnits.map((u) => ({ value: u.id, label: u.unitName }))}\n helperText={fieldErrors.show(`items.${idx}.storageUnitId`) && lerr.storageUnitId\n ? lerr.storageUnitId\n : storageRequired ? 'Required for this product' : ''}\n />\n </Grid>\n <Grid item xs={2} md={1} sx={{ textAlign: 'right' }}>\n <IconButton color=\"error\" onClick={() => removeLine(idx)} disabled={form.items.length === 1}>\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Summary footer (like a bill) ---------- */}\n <Card sx={{\n borderRadius: 1.5,\n bgcolor: 'grey.50',\n border: '1px solid',\n borderColor: 'divider',\n boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)',\n }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title={manageAmounts ? 'Bill summary' : 'Receipt details'}\n hint={manageAmounts\n ? 'The total is calculated from items. Add payment mode and notes. The date is set automatically.'\n : 'Add notes about this receipt. The date is set automatically.'}\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n {manageAmounts ? (\n <Grid item xs={12} md={6}>\n <PmSelect\n label=\"Payment Mode\"\n value={form.paymentMode || ''}\n onChange={(v) => setForm({ ...form, paymentMode: v })}\n options={PAYMENT_MODES}\n />\n </Grid>\n ) : null}\n <Grid item xs={12} md={manageAmounts ? 6 : 12}>\n <TextField fullWidth size=\"small\" label=\"Notes\" value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })} />\n </Grid>\n </Grid>\n\n {manageAmounts ? (\n <>\n <Divider sx={{ my: 2 }} />\n\n <Stack direction=\"row\" justifyContent=\"flex-end\" alignItems=\"baseline\" spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Total ({form.items.length} {form.items.length === 1 ? 'line' : 'lines'})\n </Typography>\n <Typography variant=\"h4\" sx={{ fontWeight: 800, fontVariantNumeric: 'tabular-nums' }}>\n {computedLineTotal.priced ? formatNumber(computedLineTotal.sum) : '—'}\n </Typography>\n </Stack>\n {!computedLineTotal.priced ? (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', textAlign: 'right' }}>\n Add a Buy Price on at least one line to compute the total.\n </Typography>\n ) : null}\n </>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { C as e, a as t, f as n, r } from "./settingsStore-KW217unV.js";
2
- import { a as i, i as a, r as o } from "./PmTable-BkK65bJQ.js";
1
+ import { C as e, a as t, f as n, r } from "./settingsStore-GLCtI-tv.js";
2
+ import { a as i, i as a, r as o } from "./PmTable-BMFiuHdG.js";
3
3
  import { n as s, t as ee } from "./format-UOyOu75i.js";
4
- import { a as te, i as ne, n as re, r as ie, t as ae } from "./PmGuard-C90yhpsN.js";
4
+ import { a as te, i as ne, n as re, r as ie, t as ae } from "./PmGuard-DrbbysnW.js";
5
5
  import { useMemo as c, useState as l } from "react";
6
6
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
7
7
  import { Alert as p, Box as m, Button as h, Card as g, CardContent as _, Chip as oe, Divider as v, Stack as y, Table as se, TableBody as ce, TableCell as b, TableHead as le, TableRow as x, TextField as S, Typography as C } from "@mui/material";
@@ -159,7 +159,8 @@ function E() {
159
159
  if (j) {
160
160
  F(!0), L("");
161
161
  try {
162
- M(await t(j.id)), w({
162
+ let e = await t(j.id);
163
+ M(e), w({
163
164
  message: "Request cancelled.",
164
165
  type: "success"
165
166
  });
@@ -584,4 +585,4 @@ function D({ label: e, children: t }) {
584
585
  //#endregion
585
586
  export { E as default };
586
587
 
587
- //# sourceMappingURL=PurchaseRequestDetailPage-CNAqIFef.js.map
588
+ //# sourceMappingURL=PurchaseRequestDetailPage-52S6Nf7a.js.map