@argusoft/medplat-product-management 1.0.0 → 1.0.2

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 (77) hide show
  1. package/dist/BrandsPage-CSA3meke.js +385 -0
  2. package/dist/BrandsPage-CSA3meke.js.map +1 -0
  3. package/dist/CategoriesPage-BrspzkJL.js +340 -0
  4. package/dist/CategoriesPage-BrspzkJL.js.map +1 -0
  5. package/dist/{DashboardPage-BNKUQj46.js → DashboardPage-CqaquOs2.js} +21 -22
  6. package/dist/{DashboardPage-BNKUQj46.js.map → DashboardPage-CqaquOs2.js.map} +1 -1
  7. package/dist/{FacilityPicker-Coi8I3yE.js → FacilityPicker-Dv_H95WW.js} +13 -13
  8. package/dist/{FacilityPicker-Coi8I3yE.js.map → FacilityPicker-Dv_H95WW.js.map} +1 -1
  9. package/dist/{InventoryPage-BR6jOzg2.js → InventoryPage-CPbzfDMR.js} +12 -12
  10. package/dist/{InventoryPage-BR6jOzg2.js.map → InventoryPage-CPbzfDMR.js.map} +1 -1
  11. package/dist/{MemberProfileCard-DbHZCS5n.js → MemberProfileCard-0vjO3Gzd.js} +2 -2
  12. package/dist/{MemberProfileCard-DbHZCS5n.js.map → MemberProfileCard-0vjO3Gzd.js.map} +1 -1
  13. package/dist/{PaymentCreatePage-Ci3ERnby.js → PaymentCreatePage-BYmDZAIB.js} +9 -10
  14. package/dist/{PaymentCreatePage-Ci3ERnby.js.map → PaymentCreatePage-BYmDZAIB.js.map} +1 -1
  15. package/dist/{PaymentsPage-sdg902zX.js → PaymentsPage-BXPvKon-.js} +16 -16
  16. package/dist/{PaymentsPage-sdg902zX.js.map → PaymentsPage-BXPvKon-.js.map} +1 -1
  17. package/dist/PickupCreatePage-CMGwoAXj.js +426 -0
  18. package/dist/PickupCreatePage-CMGwoAXj.js.map +1 -0
  19. package/dist/{PickupsPage-Ci2Ll0Kn.js → PickupsPage-D66_Wta4.js} +84 -39
  20. package/dist/PickupsPage-D66_Wta4.js.map +1 -0
  21. package/dist/PmTable-B9970kPY.js +375 -0
  22. package/dist/PmTable-B9970kPY.js.map +1 -0
  23. package/dist/{ProductEditPage-DL8gy85Y.js → ProductEditPage-B8qFTCW9.js} +226 -243
  24. package/dist/ProductEditPage-B8qFTCW9.js.map +1 -0
  25. package/dist/ProductsPage-C7_r9cKn.js +254 -0
  26. package/dist/ProductsPage-C7_r9cKn.js.map +1 -0
  27. package/dist/{PurchaseCreatePage-C3KMfBxJ.js → PurchaseCreatePage-CDfNDcKG.js} +150 -149
  28. package/dist/PurchaseCreatePage-CDfNDcKG.js.map +1 -0
  29. package/dist/{PurchaseRequestDetailPage-C0GYFJDl.js → PurchaseRequestDetailPage-C7aAwEqw.js} +15 -15
  30. package/dist/{PurchaseRequestDetailPage-C0GYFJDl.js.map → PurchaseRequestDetailPage-C7aAwEqw.js.map} +1 -1
  31. package/dist/{PurchaseRequestsPage-DvFH_tWe.js → PurchaseRequestsPage-BwCkbpaX.js} +14 -14
  32. package/dist/{PurchaseRequestsPage-DvFH_tWe.js.map → PurchaseRequestsPage-BwCkbpaX.js.map} +1 -1
  33. package/dist/{PurchasesPage-BCvLvSp4.js → PurchasesPage-CvaZouVy.js} +11 -12
  34. package/dist/{PurchasesPage-BCvLvSp4.js.map → PurchasesPage-CvaZouVy.js.map} +1 -1
  35. package/dist/{SaleCreatePage-DpkQMoTq.js → SaleCreatePage-DP1Qmltu.js} +30 -31
  36. package/dist/{SaleCreatePage-DpkQMoTq.js.map → SaleCreatePage-DP1Qmltu.js.map} +1 -1
  37. package/dist/{SalesPage-DUwgkEO-.js → SalesPage-CO3cdYX5.js} +18 -19
  38. package/dist/{SalesPage-DUwgkEO-.js.map → SalesPage-CO3cdYX5.js.map} +1 -1
  39. package/dist/{SettingsPage-CusC0jqJ.js → SettingsPage-DvVRwSFw.js} +104 -103
  40. package/dist/SettingsPage-DvVRwSFw.js.map +1 -0
  41. package/dist/StorageUnitsPage-B9Hr_RkW.js +470 -0
  42. package/dist/StorageUnitsPage-B9Hr_RkW.js.map +1 -0
  43. package/dist/{TransactionsPage-DDaK4VyY.js → TransactionsPage-HKIGwEZN.js} +127 -123
  44. package/dist/TransactionsPage-HKIGwEZN.js.map +1 -0
  45. package/dist/{TransferCreatePage-BxudLfhS.js → TransferCreatePage-DMuZqQ3c.js} +9 -9
  46. package/dist/{TransferCreatePage-BxudLfhS.js.map → TransferCreatePage-DMuZqQ3c.js.map} +1 -1
  47. package/dist/{TransfersPage-zgmt7teZ.js → TransfersPage-COIyouL4.js} +16 -16
  48. package/dist/{TransfersPage-zgmt7teZ.js.map → TransfersPage-COIyouL4.js.map} +1 -1
  49. package/dist/{VialsPage-CjqPfgpV.js → VialsPage-BNPUwGCU.js} +291 -224
  50. package/dist/VialsPage-BNPUwGCU.js.map +1 -0
  51. package/dist/{constants-BrA_hfAI.js → constants-5ZCZo-Zm.js} +5 -2
  52. package/dist/{constants-BrA_hfAI.js.map → constants-5ZCZo-Zm.js.map} +1 -1
  53. package/dist/{PmGuard-CjJBisaN.js → currency-ClRLIEg2.js} +186 -9
  54. package/dist/currency-ClRLIEg2.js.map +1 -0
  55. package/dist/index.js +1 -1
  56. package/package.json +1 -1
  57. package/dist/BrandsPage-DCMCVvDY.js +0 -334
  58. package/dist/BrandsPage-DCMCVvDY.js.map +0 -1
  59. package/dist/CategoriesPage-BpRhwfrc.js +0 -284
  60. package/dist/CategoriesPage-BpRhwfrc.js.map +0 -1
  61. package/dist/PickupCreatePage-CieMsgWV.js +0 -310
  62. package/dist/PickupCreatePage-CieMsgWV.js.map +0 -1
  63. package/dist/PickupsPage-Ci2Ll0Kn.js.map +0 -1
  64. package/dist/PmGuard-CjJBisaN.js.map +0 -1
  65. package/dist/PmTable-BuXZQZCU.js +0 -302
  66. package/dist/PmTable-BuXZQZCU.js.map +0 -1
  67. package/dist/ProductEditPage-DL8gy85Y.js.map +0 -1
  68. package/dist/ProductsPage-BANTKxLb.js +0 -221
  69. package/dist/ProductsPage-BANTKxLb.js.map +0 -1
  70. package/dist/PurchaseCreatePage-C3KMfBxJ.js.map +0 -1
  71. package/dist/SettingsPage-CusC0jqJ.js.map +0 -1
  72. package/dist/StorageUnitsPage-BlC2AgrJ.js +0 -469
  73. package/dist/StorageUnitsPage-BlC2AgrJ.js.map +0 -1
  74. package/dist/TransactionsPage-DDaK4VyY.js.map +0 -1
  75. package/dist/VialsPage-CjqPfgpV.js.map +0 -1
  76. package/dist/currency-BDDCDgeE.js +0 -169
  77. package/dist/currency-BDDCDgeE.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductEditPage-B8qFTCW9.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, 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 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 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 };\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]);\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 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: 4, 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.isActive}\n onChange={(e) => patch('isActive', e.target.checked)} />} label=\"Active\" />\n </Grid>\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: 4 }}>\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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"}
@@ -0,0 +1,254 @@
1
+ import { a as e, i as t, j as n, n as r, o as i, r as a } from "./currency-ClRLIEg2.js";
2
+ import { c as o, s, t as c } from "./PmTable-B9970kPY.js";
3
+ import { m as l } from "./constants-5ZCZo-Zm.js";
4
+ import { useState as u } from "react";
5
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
6
+ import { v4 as p } from "uuid";
7
+ import { Alert as m, Box as h, Button as g, Chip as _, FormControlLabel as v, InputAdornment as y, Skeleton as b, Stack as x, TableBody as S, TableCell as C, TableHead as w, TableRow as T, TextField as E, Typography as D } from "@mui/material";
8
+ import O from "@mui/icons-material/AddRounded";
9
+ import k from "@mui/icons-material/RefreshRounded";
10
+ import { showToast as A } from "@/common/toaster/toaster";
11
+ import j from "@/views/custom-components/CustomSwitch";
12
+ import { useNavigate as M } from "react-router-dom";
13
+ import N from "@mui/icons-material/Inventory2Rounded";
14
+ import P from "@mui/icons-material/VaccinesRounded";
15
+ import F from "@mui/icons-material/SearchRounded";
16
+ //#region src/views/ProductsPage.jsx
17
+ function I() {
18
+ let I = M(), L = e().has("update.manage.productManagement.product"), [R, z] = u([]), [B, V] = u(!1), [H, U] = u(""), [W, G] = u(""), [K, q] = u(!1);
19
+ async function J() {
20
+ V(!0), U("");
21
+ try {
22
+ z(await n(void 0, !0));
23
+ } catch (e) {
24
+ let t = e?.response?.data?.message ?? e?.message ?? "Failed to load products";
25
+ U(t), A({
26
+ message: t,
27
+ type: "error"
28
+ });
29
+ } finally {
30
+ V(!1);
31
+ }
32
+ }
33
+ t("view.manage.productManagement.product", () => {
34
+ J();
35
+ }, []);
36
+ let Y = R.filter((e) => {
37
+ if (!K && e.isActive === !1) return !1;
38
+ if (!W.trim()) return !0;
39
+ let t = W.toLowerCase();
40
+ return e.name?.toLowerCase().includes(t) || e.categoryName?.toLowerCase().includes(t) || e.brandName?.toLowerCase().includes(t) || e.vaccineConfig?.vaccineCode?.toLowerCase().includes(t);
41
+ });
42
+ function X() {
43
+ I(`/ui/medplat/manage/pm/products/${p()}`);
44
+ }
45
+ return /* @__PURE__ */ f(h, { children: [/* @__PURE__ */ d(a, {}), /* @__PURE__ */ d(r, {
46
+ perm: "view.manage.productManagement.product",
47
+ children: /* @__PURE__ */ f(i, {
48
+ title: "Products",
49
+ subtitle: "Each product is tracked separately. Vaccines need additional setup for vial size and dosing.",
50
+ chips: ["Catalog"],
51
+ actions: /* @__PURE__ */ f(x, {
52
+ direction: "row",
53
+ spacing: 1,
54
+ children: [/* @__PURE__ */ d(g, {
55
+ variant: "outlined",
56
+ startIcon: /* @__PURE__ */ d(k, {}),
57
+ onClick: () => void J(),
58
+ children: "Refresh"
59
+ }), L ? /* @__PURE__ */ d(g, {
60
+ variant: "contained",
61
+ startIcon: /* @__PURE__ */ d(O, {}),
62
+ onClick: X,
63
+ children: "New Product"
64
+ }) : null]
65
+ }),
66
+ children: [H ? /* @__PURE__ */ d(m, {
67
+ severity: "error",
68
+ sx: { mb: 2 },
69
+ children: H
70
+ }) : null, /* @__PURE__ */ d(o, {
71
+ eyebrow: "Catalog",
72
+ title: "Products",
73
+ subtitle: B ? "Loading…" : `${Y.length} of ${R.length} ${R.length === 1 ? "product" : "products"}${W.trim() ? ` matching "${W.trim()}"` : ""}`,
74
+ actions: /* @__PURE__ */ d(v, {
75
+ control: /* @__PURE__ */ d(j, {
76
+ checked: K,
77
+ onChange: (e) => q(e.target.checked)
78
+ }),
79
+ label: /* @__PURE__ */ d(D, {
80
+ variant: "body2",
81
+ sx: { fontWeight: 600 },
82
+ children: "Show inactive"
83
+ }),
84
+ labelPlacement: "start",
85
+ sx: {
86
+ mr: .5,
87
+ ml: 0
88
+ }
89
+ }),
90
+ toolbar: /* @__PURE__ */ d(E, {
91
+ size: "small",
92
+ fullWidth: !0,
93
+ placeholder: "Search by name, category, brand, or vaccine code",
94
+ value: W,
95
+ onChange: (e) => G(e.target.value),
96
+ InputProps: { startAdornment: /* @__PURE__ */ d(y, {
97
+ position: "start",
98
+ children: /* @__PURE__ */ d(F, {
99
+ fontSize: "small",
100
+ color: "action"
101
+ })
102
+ }) },
103
+ sx: { maxWidth: 540 }
104
+ }),
105
+ children: /* @__PURE__ */ f(s, { children: [/* @__PURE__ */ d(w, { children: /* @__PURE__ */ f(T, { children: [
106
+ /* @__PURE__ */ d(C, { children: "Product" }),
107
+ /* @__PURE__ */ d(C, { children: "Category" }),
108
+ /* @__PURE__ */ d(C, { children: "Brand" }),
109
+ /* @__PURE__ */ d(C, { children: "Unit" }),
110
+ /* @__PURE__ */ d(C, { children: "Expiry" }),
111
+ /* @__PURE__ */ d(C, { children: "Vaccine config" }),
112
+ /* @__PURE__ */ d(C, { children: "Status" }),
113
+ /* @__PURE__ */ d(C, {
114
+ align: "right",
115
+ children: "Action"
116
+ })
117
+ ] }) }), /* @__PURE__ */ d(S, { children: B ? Array.from({ length: 5 }).map((e, t) => /* @__PURE__ */ d(T, { children: Array.from({ length: 8 }).map((e, t) => /* @__PURE__ */ d(C, { children: /* @__PURE__ */ d(b, { height: 22 }) }, t)) }, t)) : Y.length === 0 ? /* @__PURE__ */ d(c, {
118
+ colSpan: 8,
119
+ icon: N,
120
+ title: R.length === 0 ? "No products yet" : "No matches",
121
+ hint: R.length === 0 ? "Create categories and brands first, then add products." : "Try a different search term.",
122
+ action: R.length === 0 && L ? /* @__PURE__ */ d(g, {
123
+ variant: "contained",
124
+ size: "small",
125
+ startIcon: /* @__PURE__ */ d(O, {}),
126
+ onClick: X,
127
+ children: "Create first product"
128
+ }) : null
129
+ }) : Y.map((e) => /* @__PURE__ */ f(T, {
130
+ hover: !0,
131
+ children: [
132
+ /* @__PURE__ */ d(C, {
133
+ sx: { maxWidth: 240 },
134
+ children: /* @__PURE__ */ d(D, {
135
+ variant: "body2",
136
+ sx: {
137
+ fontWeight: 700,
138
+ wordBreak: "break-word"
139
+ },
140
+ children: e.name
141
+ })
142
+ }),
143
+ /* @__PURE__ */ d(C, {
144
+ sx: { maxWidth: 160 },
145
+ children: /* @__PURE__ */ d(D, {
146
+ variant: "caption",
147
+ color: "text.secondary",
148
+ sx: { wordBreak: "break-word" },
149
+ children: e.categoryName
150
+ })
151
+ }),
152
+ /* @__PURE__ */ d(C, {
153
+ sx: { maxWidth: 160 },
154
+ children: /* @__PURE__ */ d(D, {
155
+ variant: "caption",
156
+ color: "text.secondary",
157
+ sx: { wordBreak: "break-word" },
158
+ children: e.brandName
159
+ })
160
+ }),
161
+ /* @__PURE__ */ d(C, { children: /* @__PURE__ */ d(_, {
162
+ size: "small",
163
+ label: l(e.unit, e.unitOther),
164
+ variant: "outlined",
165
+ sx: {
166
+ fontWeight: 700,
167
+ borderRadius: 1.5,
168
+ height: 22
169
+ }
170
+ }) }),
171
+ /* @__PURE__ */ d(C, { children: e.hasExpiry ? /* @__PURE__ */ d(_, {
172
+ size: "small",
173
+ label: "Yes",
174
+ color: "warning",
175
+ variant: "outlined",
176
+ sx: {
177
+ fontWeight: 700,
178
+ borderRadius: 1.5,
179
+ height: 22
180
+ }
181
+ }) : /* @__PURE__ */ d(_, {
182
+ size: "small",
183
+ label: "No",
184
+ variant: "outlined",
185
+ sx: {
186
+ fontWeight: 700,
187
+ borderRadius: 1.5,
188
+ height: 22
189
+ }
190
+ }) }),
191
+ /* @__PURE__ */ d(C, { children: e.vaccineConfig ? /* @__PURE__ */ f(x, {
192
+ direction: "row",
193
+ alignItems: "center",
194
+ spacing: 1,
195
+ children: [/* @__PURE__ */ d(P, { sx: {
196
+ fontSize: 18,
197
+ color: "info.main"
198
+ } }), /* @__PURE__ */ f(h, { children: [/* @__PURE__ */ d(D, {
199
+ variant: "caption",
200
+ sx: {
201
+ fontFamily: "monospace",
202
+ fontWeight: 800,
203
+ display: "block",
204
+ lineHeight: 1.2
205
+ },
206
+ children: e.vaccineConfig.vaccineCode
207
+ }), /* @__PURE__ */ f(D, {
208
+ variant: "caption",
209
+ color: "text.secondary",
210
+ sx: { fontVariantNumeric: "tabular-nums" },
211
+ children: [
212
+ e.vaccineConfig.vialVolumeMl,
213
+ "ml · ",
214
+ e.vaccineConfig.adultDoseMl ?? "—",
215
+ "/",
216
+ e.vaccineConfig.childDoseMl ?? "—",
217
+ " ml"
218
+ ]
219
+ })] })]
220
+ }) : /* @__PURE__ */ d(D, {
221
+ variant: "caption",
222
+ color: "text.disabled",
223
+ children: "—"
224
+ }) }),
225
+ /* @__PURE__ */ d(C, { children: /* @__PURE__ */ d(_, {
226
+ size: "small",
227
+ label: e.isActive ? "Active" : "Inactive",
228
+ color: e.isActive ? "success" : "default",
229
+ variant: e.isActive ? "filled" : "outlined",
230
+ sx: {
231
+ fontWeight: 700,
232
+ borderRadius: 1.5,
233
+ height: 22
234
+ }
235
+ }) }),
236
+ /* @__PURE__ */ d(C, {
237
+ align: "right",
238
+ children: L ? /* @__PURE__ */ d(g, {
239
+ size: "small",
240
+ variant: "outlined",
241
+ onClick: () => I(`/ui/medplat/manage/pm/products/${e.id}`),
242
+ children: "Edit"
243
+ }) : null
244
+ })
245
+ ]
246
+ }, e.id)) })] })
247
+ })]
248
+ })
249
+ })] });
250
+ }
251
+ //#endregion
252
+ export { I as default };
253
+
254
+ //# sourceMappingURL=ProductsPage-C7_r9cKn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductsPage-C7_r9cKn.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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"}