@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
@@ -1 +1 @@
1
- {"version":3,"file":"FacilityPicker-Coi8I3yE.js","names":[],"sources":["../src/components/FacilityPicker.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Autocomplete, CircularProgress, TextField } from '@mui/material';\nimport { getFacilitiesByIds, listFacilities } from '@pm/services/pmApi';\n\n// Synchronous facility picker. The backend now scopes /pm/facilities to the\n// current operator's assigned set (joined off user_health_infrastructure),\n// which is small — typically a handful of facilities — so we just fetch the\n// whole list once and let MUI's default client-side filter handle search.\n//\n// `value` is the facility id (string). Editing a saved row whose facility is\n// no longer in the assigned set (e.g. assignment was revoked) is supported\n// via /facilities/by-ids hydration so the picker still shows a name.\nexport default function FacilityPicker({\n value,\n onChange,\n label = 'Facility',\n required = false,\n disabled = false,\n error = false,\n helperText = '',\n onBlur,\n size = 'small',\n fullWidth = true,\n}) {\n const [open, setOpen] = useState(false);\n const [facilities, setFacilities] = useState([]);\n const [loading, setLoading] = useState(false);\n const [loaded, setLoaded] = useState(false);\n const [selectedFacility, setSelectedFacility] = useState(null);\n\n // Hydrate the selected facility's name when `value` changes. Tries the\n // already-loaded assigned list first, then /by-ids as a fallback so a\n // facility that's no longer assigned still resolves to a name on edit.\n useEffect(() => {\n if (!value) {\n setSelectedFacility(null);\n return undefined;\n }\n if (selectedFacility?.id === value) return undefined;\n const fromAssigned = facilities.find((f) => f.id === value);\n if (fromAssigned) {\n setSelectedFacility(fromAssigned);\n return undefined;\n }\n let cancelled = false;\n (async () => {\n try {\n const found = await getFacilitiesByIds([value]);\n if (!cancelled && found[0]) setSelectedFacility(found[0]);\n } catch {\n // Leave the picker showing a raw id rather than crashing — operator\n // can still re-pick.\n }\n })();\n return () => { cancelled = true; };\n // facilities is intentionally excluded from the dep array so the picker\n // doesn't re-hydrate every time the list loads; the in-list lookup runs\n // synchronously on each render anyway.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n // Load the assigned-facility list once on first open. The list isn't huge\n // and the backend scopes it; no point in pre-fetching at mount.\n useEffect(() => {\n if (!open || loaded) return undefined;\n let cancelled = false;\n setLoading(true);\n (async () => {\n try {\n const rows = await listFacilities();\n if (!cancelled) setFacilities(rows);\n } catch {\n if (!cancelled) setFacilities([]);\n } finally {\n if (!cancelled) {\n setLoading(false);\n setLoaded(true);\n }\n }\n })();\n return () => { cancelled = true; };\n }, [open, loaded]);\n\n // Make sure the currently-selected facility is in the option set so the\n // Autocomplete can render its label without an out-of-range warning.\n const displayedOptions = useMemo(() => {\n if (!selectedFacility) return facilities;\n if (facilities.some((o) => o.id === selectedFacility.id)) return facilities;\n return [selectedFacility, ...facilities];\n }, [facilities, selectedFacility]);\n\n const noOptionsText = loading\n ? 'Loading…'\n : loaded && facilities.length === 0\n ? 'No facilities assigned to your account'\n : 'No matches';\n\n const defaultHelper = 'Only facilities assigned to your account are shown. Type to filter.';\n\n return (\n <Autocomplete\n size={size}\n fullWidth={fullWidth}\n disabled={disabled}\n open={open}\n onOpen={() => setOpen(true)}\n onClose={() => setOpen(false)}\n value={selectedFacility}\n isOptionEqualToValue={(a, b) => a?.id === b?.id}\n getOptionLabel={(o) => o?.name ?? ''}\n options={displayedOptions}\n loading={loading}\n onChange={(_, next) => {\n setSelectedFacility(next ?? null);\n onChange?.(next?.id ?? '');\n }}\n noOptionsText={noOptionsText}\n renderInput={(params) => (\n <TextField\n {...params}\n required={required}\n label={label}\n error={error}\n helperText={helperText || defaultHelper}\n onBlur={onBlur}\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <>\n {loading ? <CircularProgress color=\"inherit\" size={16} /> : null}\n {params.InputProps.endAdornment}\n </>\n ),\n }}\n />\n )}\n />\n );\n}\n"],"mappings":";;;;;AAYA,SAAwB,EAAe,EACrC,UACA,aACA,WAAQ,YACR,cAAW,IACX,cAAW,IACX,WAAQ,IACR,gBAAa,IACb,WACA,UAAO,SACP,eAAY,MACX;CACD,IAAM,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAY,KAAiB,EAAS,CAAC,CAAC,GACzC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,CAAC,GAAkB,KAAuB,EAAS,IAAI;CAuE7D,OAlEA,QAAgB;EACd,IAAI,CAAC,GAAO;GACV,EAAoB,IAAI;GACxB;EACF;EACA,IAAI,GAAkB,OAAO,GAAO;EACpC,IAAM,IAAe,EAAW,MAAM,MAAM,EAAE,OAAO,CAAK;EAC1D,IAAI,GAAc;GAChB,EAAoB,CAAY;GAChC;EACF;EACA,IAAI,IAAY;EAUhB,QATC,YAAY;GACX,IAAI;IACF,IAAM,IAAQ,MAAM,EAAmB,CAAC,CAAK,CAAC;IAC9C,AAAI,CAAC,KAAa,EAAM,MAAI,EAAoB,EAAM,EAAE;GAC1D,QAAQ,CAGR;EACF,GAAG,SACU;GAAE,IAAY;EAAM;CAKnC,GAAG,CAAC,CAAK,CAAC,GAIV,QAAgB;EACd,IAAI,CAAC,KAAQ,GAAQ;EACrB,IAAI,IAAY;EAehB,OAdA,EAAW,EAAI,IACd,YAAY;GACX,IAAI;IACF,IAAM,IAAO,MAAM,EAAe;IAClC,AAAK,KAAW,EAAc,CAAI;GACpC,QAAQ;IACN,AAAK,KAAW,EAAc,CAAC,CAAC;GAClC,UAAU;IACR,AAAK,MACH,EAAW,EAAK,GAChB,EAAU,EAAI;GAElB;EACF,GAAG,SACU;GAAE,IAAY;EAAM;CACnC,GAAG,CAAC,GAAM,CAAM,CAAC,GAmBf,kBAAC,GAAD;EACQ;EACK;EACD;EACJ;EACN,cAAc,EAAQ,EAAI;EAC1B,eAAe,EAAQ,EAAK;EAC5B,OAAO;EACP,uBAAuB,GAAG,MAAM,GAAG,OAAO,GAAG;EAC7C,iBAAiB,MAAM,GAAG,QAAQ;EAClC,SAzBqB,QACnB,CAAC,KACD,EAAW,MAAM,MAAM,EAAE,OAAO,EAAiB,EAAE,IAAU,IAC1D,CAAC,GAAkB,GAAG,CAAU,GACtC,CAAC,GAAY,CAAgB,CAqBnB;EACA;EACT,WAAW,GAAG,MAAS;GAErB,AADA,EAAoB,KAAQ,IAAI,GAChC,IAAW,GAAM,MAAM,EAAE;EAC3B;EACe,eAzBG,IAClB,aACA,KAAU,EAAW,WAAW,IAC9B,2CACA;EAsBF,cAAc,MACZ,kBAAC,GAAD;GACE,GAAI;GACM;GACH;GACA;GACP,YAAY,KAAc;GAClB;GACR,YAAY;IACV,GAAG,EAAO;IACV,cACE,kBAAA,GAAA,EAAA,UAAA,CACG,IAAU,kBAAC,GAAD;KAAkB,OAAM;KAAU,MAAM;IAAK,CAAA,IAAI,MAC3D,EAAO,WAAW,YACnB,EAAA,CAAA;GAEN;EACD,CAAA;CAEJ,CAAA;AAEL"}
1
+ {"version":3,"file":"FacilityPicker-Dv_H95WW.js","names":[],"sources":["../src/components/FacilityPicker.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Autocomplete, CircularProgress, TextField } from '@mui/material';\nimport { getFacilitiesByIds, listFacilities } from '@pm/services/pmApi';\n\n// Synchronous facility picker. The backend now scopes /pm/facilities to the\n// current operator's assigned set (joined off user_health_infrastructure),\n// which is small — typically a handful of facilities — so we just fetch the\n// whole list once and let MUI's default client-side filter handle search.\n//\n// `value` is the facility id (string). Editing a saved row whose facility is\n// no longer in the assigned set (e.g. assignment was revoked) is supported\n// via /facilities/by-ids hydration so the picker still shows a name.\nexport default function FacilityPicker({\n value,\n onChange,\n label = 'Facility',\n required = false,\n disabled = false,\n error = false,\n helperText = '',\n onBlur,\n size = 'small',\n fullWidth = true,\n}) {\n const [open, setOpen] = useState(false);\n const [facilities, setFacilities] = useState([]);\n const [loading, setLoading] = useState(false);\n const [loaded, setLoaded] = useState(false);\n const [selectedFacility, setSelectedFacility] = useState(null);\n\n // Hydrate the selected facility's name when `value` changes. Tries the\n // already-loaded assigned list first, then /by-ids as a fallback so a\n // facility that's no longer assigned still resolves to a name on edit.\n useEffect(() => {\n if (!value) {\n setSelectedFacility(null);\n return undefined;\n }\n if (selectedFacility?.id === value) return undefined;\n const fromAssigned = facilities.find((f) => f.id === value);\n if (fromAssigned) {\n setSelectedFacility(fromAssigned);\n return undefined;\n }\n let cancelled = false;\n (async () => {\n try {\n const found = await getFacilitiesByIds([value]);\n if (!cancelled && found[0]) setSelectedFacility(found[0]);\n } catch {\n // Leave the picker showing a raw id rather than crashing — operator\n // can still re-pick.\n }\n })();\n return () => { cancelled = true; };\n // facilities is intentionally excluded from the dep array so the picker\n // doesn't re-hydrate every time the list loads; the in-list lookup runs\n // synchronously on each render anyway.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value]);\n\n // Load the assigned-facility list once on first open. The list isn't huge\n // and the backend scopes it; no point in pre-fetching at mount.\n useEffect(() => {\n if (!open || loaded) return undefined;\n let cancelled = false;\n setLoading(true);\n (async () => {\n try {\n const rows = await listFacilities();\n if (!cancelled) setFacilities(rows);\n } catch {\n if (!cancelled) setFacilities([]);\n } finally {\n if (!cancelled) {\n setLoading(false);\n setLoaded(true);\n }\n }\n })();\n return () => { cancelled = true; };\n }, [open, loaded]);\n\n // Make sure the currently-selected facility is in the option set so the\n // Autocomplete can render its label without an out-of-range warning.\n const displayedOptions = useMemo(() => {\n if (!selectedFacility) return facilities;\n if (facilities.some((o) => o.id === selectedFacility.id)) return facilities;\n return [selectedFacility, ...facilities];\n }, [facilities, selectedFacility]);\n\n const noOptionsText = loading\n ? 'Loading…'\n : loaded && facilities.length === 0\n ? 'No facilities assigned to your account'\n : 'No matches';\n\n const defaultHelper = 'Only facilities assigned to your account are shown. Type to filter.';\n\n return (\n <Autocomplete\n size={size}\n fullWidth={fullWidth}\n disabled={disabled}\n open={open}\n onOpen={() => setOpen(true)}\n onClose={() => setOpen(false)}\n value={selectedFacility}\n isOptionEqualToValue={(a, b) => a?.id === b?.id}\n getOptionLabel={(o) => o?.name ?? ''}\n options={displayedOptions}\n loading={loading}\n onChange={(_, next) => {\n setSelectedFacility(next ?? null);\n onChange?.(next?.id ?? '');\n }}\n noOptionsText={noOptionsText}\n renderInput={(params) => (\n <TextField\n {...params}\n required={required}\n label={label}\n error={error}\n helperText={helperText || defaultHelper}\n onBlur={onBlur}\n InputProps={{\n ...params.InputProps,\n endAdornment: (\n <>\n {loading ? <CircularProgress color=\"inherit\" size={16} /> : null}\n {params.InputProps.endAdornment}\n </>\n ),\n }}\n />\n )}\n />\n );\n}\n"],"mappings":";;;;;AAYA,SAAwB,EAAe,EACrC,UACA,aACA,WAAQ,YACR,cAAW,IACX,cAAW,IACX,WAAQ,IACR,gBAAa,IACb,WACA,UAAO,SACP,eAAY,MACX;CACD,IAAM,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAY,KAAiB,EAAS,CAAC,CAAC,GACzC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,CAAC,GAAkB,KAAuB,EAAS,IAAI;CAuE7D,OAlEA,QAAgB;EACd,IAAI,CAAC,GAAO;GACV,EAAoB,IAAI;GACxB;EACF;EACA,IAAI,GAAkB,OAAO,GAAO;EACpC,IAAM,IAAe,EAAW,MAAM,MAAM,EAAE,OAAO,CAAK;EAC1D,IAAI,GAAc;GAChB,EAAoB,CAAY;GAChC;EACF;EACA,IAAI,IAAY;EAUhB,QATC,YAAY;GACX,IAAI;IACF,IAAM,IAAQ,MAAM,EAAmB,CAAC,CAAK,CAAC;IAC9C,AAAI,CAAC,KAAa,EAAM,MAAI,EAAoB,EAAM,EAAE;GAC1D,QAAQ,CAGR;EACF,GAAG,SACU;GAAE,IAAY;EAAM;CAKnC,GAAG,CAAC,CAAK,CAAC,GAIV,QAAgB;EACd,IAAI,CAAC,KAAQ,GAAQ;EACrB,IAAI,IAAY;EAehB,OAdA,EAAW,EAAI,IACd,YAAY;GACX,IAAI;IACF,IAAM,IAAO,MAAM,EAAe;IAClC,AAAK,KAAW,EAAc,CAAI;GACpC,QAAQ;IACN,AAAK,KAAW,EAAc,CAAC,CAAC;GAClC,UAAU;IACR,AAAK,MACH,EAAW,EAAK,GAChB,EAAU,EAAI;GAElB;EACF,GAAG,SACU;GAAE,IAAY;EAAM;CACnC,GAAG,CAAC,GAAM,CAAM,CAAC,GAmBf,kBAAC,GAAD;EACQ;EACK;EACD;EACJ;EACN,cAAc,EAAQ,EAAI;EAC1B,eAAe,EAAQ,EAAK;EAC5B,OAAO;EACP,uBAAuB,GAAG,MAAM,GAAG,OAAO,GAAG;EAC7C,iBAAiB,MAAM,GAAG,QAAQ;EAClC,SAzBqB,QACnB,CAAC,KACD,EAAW,MAAM,MAAM,EAAE,OAAO,EAAiB,EAAE,IAAU,IAC1D,CAAC,GAAkB,GAAG,CAAU,GACtC,CAAC,GAAY,CAAgB,CAqBnB;EACA;EACT,WAAW,GAAG,MAAS;GAErB,AADA,EAAoB,KAAQ,IAAI,GAChC,IAAW,GAAM,MAAM,EAAE;EAC3B;EACe,eAzBG,IAClB,aACA,KAAU,EAAW,WAAW,IAC9B,2CACA;EAsBF,cAAc,MACZ,kBAAC,GAAD;GACE,GAAI;GACM;GACH;GACA;GACP,YAAY,KAAc;GAClB;GACR,YAAY;IACV,GAAG,EAAO;IACV,cACE,kBAAA,GAAA,EAAA,UAAA,CACG,IAAU,kBAAC,GAAD;KAAkB,OAAM;KAAU,MAAM;IAAK,CAAA,IAAI,MAC3D,EAAO,WAAW,YACnB,EAAA,CAAA;GAEN;EACD,CAAA;CAEJ,CAAA;AAEL"}
@@ -1,8 +1,8 @@
1
- import { P as e, Z as t, a as n, i as ee, it as te, n as ne, r as re, t as ie } from "./PmGuard-CjJBisaN.js";
2
- import { a as ae, i as r, o as i, t as oe } from "./PmTable-BuXZQZCU.js";
1
+ import { F as e, Q as t, a as n, at as ee, i as te, n as ne, o as re, r as ie } from "./currency-ClRLIEg2.js";
2
+ import { a as ae, c as r, s as i, t as oe } from "./PmTable-B9970kPY.js";
3
3
  import { m as a } from "./validation-Ba7puFIO.js";
4
4
  import { a as se, i as o, t as ce } from "./format-UOyOu75i.js";
5
- import { a as s, p as c, t as l } from "./constants-BrA_hfAI.js";
5
+ import { a as s, p as c, t as l } from "./constants-5ZCZo-Zm.js";
6
6
  import { useMemo as u, useState as d } from "react";
7
7
  import { jsx as f, jsxs as p } from "react/jsx-runtime";
8
8
  import { v4 as m } from "uuid";
@@ -19,7 +19,7 @@ function P(e) {
19
19
  return e ? e.status === "EXPIRED" ? !0 : o(e.expiryDate) : !1;
20
20
  }
21
21
  function F() {
22
- let o = ee().has("update.manage.productManagement.inventory"), [F, Se] = d([]), [I, L] = d(!1), [R, z] = d(""), [B, V] = d({
22
+ let o = n().has("update.manage.productManagement.inventory"), [F, Se] = d([]), [I, L] = d(!1), [R, z] = d(""), [B, V] = d({
23
23
  open: !1,
24
24
  batch: null,
25
25
  reason: "OTHER",
@@ -39,7 +39,7 @@ function F() {
39
39
  L(!1);
40
40
  }
41
41
  }
42
- re("view.manage.productManagement.inventory", () => {
42
+ te("view.manage.productManagement.inventory", () => {
43
43
  X();
44
44
  }, []);
45
45
  let Z = u(() => {
@@ -80,7 +80,7 @@ function F() {
80
80
  }
81
81
  async function Te(e) {
82
82
  try {
83
- await te({
83
+ await ee({
84
84
  id: m(),
85
85
  batchId: e.id
86
86
  }), O({
@@ -94,9 +94,9 @@ function F() {
94
94
  });
95
95
  }
96
96
  }
97
- return /* @__PURE__ */ p(h, { children: [/* @__PURE__ */ f(ne, {}), /* @__PURE__ */ p(ie, {
97
+ return /* @__PURE__ */ p(h, { children: [/* @__PURE__ */ f(ie, {}), /* @__PURE__ */ p(ne, {
98
98
  perm: "view.manage.productManagement.inventory",
99
- children: [/* @__PURE__ */ p(n, {
99
+ children: [/* @__PURE__ */ p(re, {
100
100
  title: "Inventory",
101
101
  subtitle: "Your stock batches. Click to see history or quarantine items if there's a problem.",
102
102
  chips: ["Stock"],
@@ -114,7 +114,7 @@ function F() {
114
114
  severity: "error",
115
115
  sx: { mb: 2 },
116
116
  children: R
117
- }) : null, /* @__PURE__ */ p(i, {
117
+ }) : null, /* @__PURE__ */ p(r, {
118
118
  eyebrow: "Stock on hand",
119
119
  title: "Inventory batches",
120
120
  subtitle: I ? "Loading…" : Q ? `${Z.length} of ${F.length} ${F.length === 1 ? "batch" : "batches"} match` : `${F.length} ${F.length === 1 ? "batch" : "batches"} visible`,
@@ -215,7 +215,7 @@ function F() {
215
215
  children: "Clear"
216
216
  })
217
217
  ]
218
- }), /* @__PURE__ */ p(ae, {
218
+ }), /* @__PURE__ */ p(i, {
219
219
  dense: !0,
220
220
  children: [/* @__PURE__ */ f(ye, { children: /* @__PURE__ */ p(w, { children: [
221
221
  /* @__PURE__ */ f(C, { children: "Product" }),
@@ -260,7 +260,7 @@ function F() {
260
260
  lineHeight: 1.2
261
261
  },
262
262
  children: e.productName
263
- }), /* @__PURE__ */ f(r, { id: e.serialId })]
263
+ }), /* @__PURE__ */ f(ae, { id: e.serialId })]
264
264
  }) }),
265
265
  /* @__PURE__ */ f(C, { children: e.lotNumber ? /* @__PURE__ */ f(h, {
266
266
  component: "span",
@@ -514,4 +514,4 @@ function F() {
514
514
  //#endregion
515
515
  export { F as default };
516
516
 
517
- //# sourceMappingURL=InventoryPage-BR6jOzg2.js.map
517
+ //# sourceMappingURL=InventoryPage-CPbzfDMR.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InventoryPage-BR6jOzg2.js","names":[],"sources":["../src/views/InventoryPage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControl,\n FormHelperText,\n InputAdornment,\n InputLabel,\n LinearProgress,\n MenuItem,\n Select,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Tooltip,\n Typography,\n} from \"@mui/material\";\nimport { alpha, useTheme } from \"@mui/material/styles\";\nimport WidgetsRoundedIcon from \"@mui/icons-material/WidgetsRounded\";\nimport { PmEmptyRow, PmRef, PmTable, PmTableCard } from \"@pm/components/PmTable\";\nimport RefreshRoundedIcon from \"@mui/icons-material/RefreshRounded\";\nimport LockRoundedIcon from \"@mui/icons-material/LockRounded\";\nimport LockOpenRoundedIcon from \"@mui/icons-material/LockOpenRounded\";\nimport SearchRoundedIcon from \"@mui/icons-material/SearchRounded\";\nimport { showToast } from \"@/common/toaster/toaster\";\nimport PmPageShell from \"@pm/components/PmPageShell\";\nimport PmNav from \"@pm/components/PmNav\";\nimport PmGuard from \"@pm/components/PmGuard\";\nimport { usePermissionedEffect, usePmPermissions } from \"@pm/utils/permissions\";\nimport {\n listBatches,\n quarantineBatch,\n unquarantineBatch,\n} from \"@pm/services/pmApi\";\nimport { BATCH_STATUSES, QUARANTINE_REASONS, labelOf } from \"@pm/utils/constants\";\nimport { formatDate, isPastExpiry, statusColor } from \"@pm/utils/format\";\nimport { validateRequired } from \"@pm/utils/validation\";\n\n// Shared \"is this batch past its printed expiry?\" check. Used for both the\n// per-row pill and the \"Not yet expired\" filter so they can never disagree.\n// A batch whose expiryDate equals today is still valid — only the day AFTER\n// the printed expiry counts as expired (matches the unit packaging rule).\nfunction isBatchExpired(b) {\n if (!b) return false;\n if (b.status === 'EXPIRED') return true;\n return isPastExpiry(b.expiryDate);\n}\n\nexport default function InventoryPage() {\n const perms = usePmPermissions();\n const canWrite = perms.has('update.manage.productManagement.inventory');\n const [items, setItems] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [quarantineDialog, setQuarantineDialog] = useState({\n open: false,\n batch: null,\n reason: \"OTHER\",\n notes: \"\",\n });\n const theme = useTheme();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState('ALL');\n // Filter chips. notYetExpired excludes anything past its printed expiry\n // (regardless of status). inStock keeps rows with qtyRemaining > 0.\n const [notYetExpired, setNotYetExpired] = useState(false);\n const [inStock, setInStock] = useState(false);\n\n async function load() {\n setLoading(true);\n setError(\"\");\n try {\n setItems(await listBatches());\n } catch (err) {\n const message =\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to load inventory\";\n setError(message);\n showToast({ message, type: \"error\" });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect('view.manage.productManagement.inventory', () => {\n void load();\n }, []);\n\n // Filters apply in-memory over whatever the list endpoint returned. Search\n // matches product name + lot (case-insensitive); chips + status narrow the\n // remaining set with AND semantics.\n const filteredItems = useMemo(() => {\n const q = search.trim().toLowerCase();\n return items.filter((b) => {\n if (q) {\n const hay = `${b.productName ?? ''} ${b.lotNumber ?? ''}`.toLowerCase();\n if (!hay.includes(q)) return false;\n }\n if (statusFilter !== 'ALL' && b.status !== statusFilter) return false;\n if (notYetExpired && isBatchExpired(b)) return false;\n if (inStock && !(Number(b.qtyRemaining ?? 0) > 0)) return false;\n return true;\n });\n }, [items, search, statusFilter, notYetExpired, inStock]);\n\n const hasFilter = search.trim() !== '' || statusFilter !== 'ALL' || notYetExpired || inStock;\n\n function clearFilters() {\n setSearch('');\n setStatusFilter('ALL');\n setNotYetExpired(false);\n setInStock(false);\n }\n\n async function doQuarantine() {\n try {\n await quarantineBatch({\n id: uuidv4(),\n batchId: quarantineDialog.batch.id,\n reason: quarantineDialog.reason,\n notes: quarantineDialog.notes || null,\n });\n showToast({ message: \"Batch quarantined.\", type: \"success\" });\n setQuarantineDialog({\n open: false,\n batch: null,\n reason: \"OTHER\",\n notes: \"\",\n });\n await load();\n } catch (err) {\n const message =\n err?.response?.data?.message ?? err?.message ?? \"Failed to quarantine\";\n showToast({ message, type: \"error\" });\n }\n }\n\n async function doUnquarantine(batch) {\n try {\n await unquarantineBatch({ id: uuidv4(), batchId: batch.id });\n showToast({ message: \"Quarantine lifted.\", type: \"success\" });\n await load();\n } catch (err) {\n const message =\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to unquarantine\";\n showToast({ message, type: \"error\" });\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'view.manage.productManagement.inventory'}>\n <PmPageShell\n title=\"Inventory\"\n subtitle=\"Your stock batches. Click to see history or quarantine items if there's a problem.\"\n chips={[\"Stock\"]}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n startIcon={<RefreshRoundedIcon />}\n onClick={() => void load()}\n >\n Refresh\n </Button>\n </Stack>\n }\n >\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n\n <PmTableCard\n eyebrow=\"Stock on hand\"\n title=\"Inventory batches\"\n subtitle={\n loading\n ? \"Loading…\"\n : hasFilter\n ? `${filteredItems.length} of ${items.length} ${items.length === 1 ? 'batch' : 'batches'} match`\n : `${items.length} ${items.length === 1 ? 'batch' : 'batches'} visible`\n }\n >\n <Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5} sx={{ mb: 2 }} alignItems={{ md: 'center' }}>\n <TextField\n size=\"small\"\n fullWidth\n placeholder=\"Search product or lot…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchRoundedIcon fontSize=\"small\" />\n </InputAdornment>\n ),\n }}\n sx={{ maxWidth: { md: 360 } }}\n />\n <FormControl size=\"small\" sx={{ minWidth: 160 }}>\n <InputLabel>Status</InputLabel>\n <Select\n label=\"Status\"\n value={statusFilter}\n onChange={(e) => {\n const next = e.target.value;\n setStatusFilter(next);\n // Picking a terminal status from the dropdown auto-clears the\n // opposing chip, so the filter pair can never resolve to an\n // empty set by construction.\n if (next === 'EXPIRED') setNotYetExpired(false);\n if (next === 'EXHAUSTED') setInStock(false);\n }}\n >\n <MenuItem value=\"ALL\">All statuses</MenuItem>\n {BATCH_STATUSES.map((s) => {\n const blocked = (s.value === 'EXPIRED' && notYetExpired) || (s.value === 'EXHAUSTED' && inStock);\n return (\n <MenuItem key={s.value} value={s.value} disabled={blocked}>\n {s.label}\n {blocked ? (\n <Typography component=\"span\" variant=\"caption\" color=\"text.disabled\" sx={{ ml: 1 }}>\n (clear chip first)\n </Typography>\n ) : null}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n <Stack direction=\"row\" spacing={1} useFlexGap flexWrap=\"wrap\">\n <Chip\n label=\"Not yet expired\"\n size=\"small\"\n disabled={statusFilter === 'EXPIRED'}\n onClick={() => {\n // Auto-reset the conflicting dropdown value so the two\n // controls never produce an impossible combination.\n setNotYetExpired((prev) => {\n const next = !prev;\n if (next && statusFilter === 'EXPIRED') setStatusFilter('ALL');\n return next;\n });\n }}\n color={notYetExpired ? 'primary' : 'default'}\n variant={notYetExpired ? 'filled' : 'outlined'}\n sx={{ fontWeight: 700, borderRadius: 1.5 }}\n />\n <Chip\n label=\"Stock still left\"\n size=\"small\"\n disabled={statusFilter === 'EXHAUSTED'}\n onClick={() => {\n setInStock((prev) => {\n const next = !prev;\n if (next && statusFilter === 'EXHAUSTED') setStatusFilter('ALL');\n return next;\n });\n }}\n color={inStock ? 'primary' : 'default'}\n variant={inStock ? 'filled' : 'outlined'}\n sx={{ fontWeight: 700, borderRadius: 1.5 }}\n />\n </Stack>\n <Box sx={{ flexGrow: 1 }} />\n <Button variant=\"text\" onClick={clearFilters} disabled={!hasFilter}>Clear</Button>\n </Stack>\n\n <PmTable dense>\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell>Owner</TableCell>\n <TableCell>Expiry</TableCell>\n <TableCell align=\"right\">Stock</TableCell>\n <TableCell>Storage</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}>\n <Skeleton height={22} />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : filteredItems.length === 0 ? (\n <PmEmptyRow\n colSpan={8}\n icon={WidgetsRoundedIcon}\n title={hasFilter ? 'No batches match your filters' : 'No batches in stock'}\n hint={hasFilter\n ? 'Adjust your search or filters.'\n : 'Record a purchase to add stock.'}\n action={hasFilter ? (\n <Button variant=\"text\" size=\"small\" onClick={clearFilters}>Clear filters</Button>\n ) : null}\n />\n ) : (\n filteredItems.map((b) => {\n const isExpired = isBatchExpired(b);\n const remainingRatio =\n b.qtyPurchased > 0 ? b.qtyRemaining / b.qtyPurchased : 0;\n return (\n <TableRow key={b.id} hover>\n <TableCell>\n <Stack\n direction=\"row\"\n alignItems=\"baseline\"\n spacing={1}\n sx={{ minWidth: 0 }}\n >\n <Typography\n variant=\"body2\"\n sx={{ fontWeight: 700, lineHeight: 1.2 }}\n >\n {b.productName}\n </Typography>\n <PmRef id={b.serialId} />\n </Stack>\n </TableCell>\n <TableCell>\n {b.lotNumber ? (\n <Box\n component=\"span\"\n sx={{\n display: \"inline-flex\",\n px: 0.7,\n py: 0.1,\n borderRadius: 1,\n bgcolor: alpha(theme.palette.primary.main, 0.06),\n color: \"primary.dark\",\n fontFamily: \"monospace\",\n fontSize: \"0.72rem\",\n fontWeight: 700,\n }}\n >\n {b.lotNumber}\n </Box>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {b.ownerName || (b.ownerType === 'USER' ? 'Personal' : 'Facility')}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {b.ownerType}\n </Typography>\n </TableCell>\n <TableCell>\n {b.expiryDate ? (\n <Tooltip\n title={isExpired ? \"Past printed expiry\" : \"\"}\n >\n <Typography\n variant=\"body2\"\n sx={{\n fontWeight: 700,\n fontVariantNumeric: \"tabular-nums\",\n color: isExpired\n ? \"error.dark\"\n : \"text.primary\",\n }}\n >\n {formatDate(b.expiryDate)}\n </Typography>\n </Tooltip>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell align=\"right\" sx={{ minWidth: 150 }}>\n <Stack alignItems=\"flex-end\" spacing={0.5}>\n <Typography\n variant=\"body2\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n {b.qtyRemaining}\n <Typography\n component=\"span\"\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{\n ml: 0.5,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n / {b.qtyPurchased}\n </Typography>\n </Typography>\n <Box sx={{ width: 80 }}>\n <LinearProgress\n variant=\"determinate\"\n value={Math.min(\n 100,\n Math.max(0, remainingRatio * 100),\n )}\n sx={{ height: 4, borderRadius: 2 }}\n color={\n remainingRatio < 0.25 ? \"warning\" : \"primary\"\n }\n />\n </Box>\n </Stack>\n </TableCell>\n <TableCell>\n {b.storageUnitName ? (\n <Typography\n variant=\"caption\"\n sx={{ fontWeight: 700 }}\n >\n {b.storageUnitName}\n </Typography>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell>\n <Stack\n direction=\"row\"\n spacing={0.5}\n flexWrap=\"wrap\"\n useFlexGap\n >\n <Chip\n size=\"small\"\n color={statusColor(b.status)}\n label={labelOf(BATCH_STATUSES, b.status)}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n {b.isQuarantined ? (\n <Chip\n size=\"small\"\n color=\"error\"\n label={labelOf(QUARANTINE_REASONS, b.quarantineReason) || \"Quarantined\"}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : null}\n {isExpired && b.status !== \"EXPIRED\" ? (\n <Chip\n size=\"small\"\n color=\"warning\"\n label=\"Expired\"\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : null}\n {b.sourceType ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={\n b.sourceType === 'PICKUP' ? 'info'\n : b.sourceType === 'TRANSFER_IN' ? 'primary'\n : 'default'\n }\n label={b.sourceType === 'TRANSFER_IN' ? 'Transfer' : b.sourceType === 'PICKUP' ? 'Pickup' : b.sourceType === 'PURCHASE' ? 'Purchase' : b.sourceType}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : null}\n </Stack>\n </TableCell>\n <TableCell align=\"right\">\n {!canWrite ? null : b.isQuarantined ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"success\"\n startIcon={<LockOpenRoundedIcon />}\n onClick={() => void doUnquarantine(b)}\n >\n Unquarantine\n </Button>\n ) : isExpired ||\n b.status === \"EXHAUSTED\" ||\n b.status === \"IN_USE\" ? null : (\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"error\"\n startIcon={<LockRoundedIcon />}\n onClick={() =>\n setQuarantineDialog({\n open: true,\n batch: b,\n reason: \"OTHER\",\n notes: \"\",\n })\n }\n >\n Quarantine\n </Button>\n )}\n </TableCell>\n </TableRow>\n );\n })\n )}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n\n <Dialog\n open={quarantineDialog.open}\n onClose={() =>\n setQuarantineDialog({ ...quarantineDialog, open: false })\n }\n fullWidth\n maxWidth=\"sm\"\n >\n <DialogTitle>Quarantine Batch</DialogTitle>\n <DialogContent dividers>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\">\n Product: <strong>{quarantineDialog.batch?.productName}</strong>\n <br />\n Lot: <strong>{quarantineDialog.batch?.lotNumber || \"—\"}</strong>\n <br />\n Remaining: <strong>{quarantineDialog.batch?.qtyRemaining}</strong>\n </Typography>\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={!!validateRequired(quarantineDialog.reason, \"Reason\")}\n >\n <InputLabel>Reason</InputLabel>\n <Select\n label=\"Reason\"\n value={quarantineDialog.reason}\n onChange={(e) =>\n setQuarantineDialog({\n ...quarantineDialog,\n reason: e.target.value,\n })\n }\n >\n {QUARANTINE_REASONS.map((r) => (\n <MenuItem key={r.value} value={r.value}>\n {r.label}\n </MenuItem>\n ))}\n </Select>\n {validateRequired(quarantineDialog.reason, \"Reason\") ? (\n <FormHelperText>\n {validateRequired(quarantineDialog.reason, \"Reason\")}\n </FormHelperText>\n ) : null}\n </FormControl>\n <TextField\n label=\"Notes\"\n size=\"small\"\n multiline\n minRows={2}\n fullWidth\n value={quarantineDialog.notes}\n onChange={(e) =>\n setQuarantineDialog({\n ...quarantineDialog,\n notes: e.target.value,\n })\n }\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() =>\n setQuarantineDialog({ ...quarantineDialog, open: false })\n }\n >\n Cancel\n </Button>\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={!!validateRequired(quarantineDialog.reason, \"Reason\")}\n onClick={() => void doQuarantine()}\n >\n Quarantine\n </Button>\n </DialogActions>\n </Dialog>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InventoryPage-CPbzfDMR.js","names":[],"sources":["../src/views/InventoryPage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControl,\n FormHelperText,\n InputAdornment,\n InputLabel,\n LinearProgress,\n MenuItem,\n Select,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Tooltip,\n Typography,\n} from \"@mui/material\";\nimport { alpha, useTheme } from \"@mui/material/styles\";\nimport WidgetsRoundedIcon from \"@mui/icons-material/WidgetsRounded\";\nimport { PmEmptyRow, PmRef, PmTable, PmTableCard } from \"@pm/components/PmTable\";\nimport RefreshRoundedIcon from \"@mui/icons-material/RefreshRounded\";\nimport LockRoundedIcon from \"@mui/icons-material/LockRounded\";\nimport LockOpenRoundedIcon from \"@mui/icons-material/LockOpenRounded\";\nimport SearchRoundedIcon from \"@mui/icons-material/SearchRounded\";\nimport { showToast } from \"@/common/toaster/toaster\";\nimport PmPageShell from \"@pm/components/PmPageShell\";\nimport PmNav from \"@pm/components/PmNav\";\nimport PmGuard from \"@pm/components/PmGuard\";\nimport { usePermissionedEffect, usePmPermissions } from \"@pm/utils/permissions\";\nimport {\n listBatches,\n quarantineBatch,\n unquarantineBatch,\n} from \"@pm/services/pmApi\";\nimport { BATCH_STATUSES, QUARANTINE_REASONS, labelOf } from \"@pm/utils/constants\";\nimport { formatDate, isPastExpiry, statusColor } from \"@pm/utils/format\";\nimport { validateRequired } from \"@pm/utils/validation\";\n\n// Shared \"is this batch past its printed expiry?\" check. Used for both the\n// per-row pill and the \"Not yet expired\" filter so they can never disagree.\n// A batch whose expiryDate equals today is still valid — only the day AFTER\n// the printed expiry counts as expired (matches the unit packaging rule).\nfunction isBatchExpired(b) {\n if (!b) return false;\n if (b.status === 'EXPIRED') return true;\n return isPastExpiry(b.expiryDate);\n}\n\nexport default function InventoryPage() {\n const perms = usePmPermissions();\n const canWrite = perms.has('update.manage.productManagement.inventory');\n const [items, setItems] = useState([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState(\"\");\n const [quarantineDialog, setQuarantineDialog] = useState({\n open: false,\n batch: null,\n reason: \"OTHER\",\n notes: \"\",\n });\n const theme = useTheme();\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState('ALL');\n // Filter chips. notYetExpired excludes anything past its printed expiry\n // (regardless of status). inStock keeps rows with qtyRemaining > 0.\n const [notYetExpired, setNotYetExpired] = useState(false);\n const [inStock, setInStock] = useState(false);\n\n async function load() {\n setLoading(true);\n setError(\"\");\n try {\n setItems(await listBatches());\n } catch (err) {\n const message =\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to load inventory\";\n setError(message);\n showToast({ message, type: \"error\" });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect('view.manage.productManagement.inventory', () => {\n void load();\n }, []);\n\n // Filters apply in-memory over whatever the list endpoint returned. Search\n // matches product name + lot (case-insensitive); chips + status narrow the\n // remaining set with AND semantics.\n const filteredItems = useMemo(() => {\n const q = search.trim().toLowerCase();\n return items.filter((b) => {\n if (q) {\n const hay = `${b.productName ?? ''} ${b.lotNumber ?? ''}`.toLowerCase();\n if (!hay.includes(q)) return false;\n }\n if (statusFilter !== 'ALL' && b.status !== statusFilter) return false;\n if (notYetExpired && isBatchExpired(b)) return false;\n if (inStock && !(Number(b.qtyRemaining ?? 0) > 0)) return false;\n return true;\n });\n }, [items, search, statusFilter, notYetExpired, inStock]);\n\n const hasFilter = search.trim() !== '' || statusFilter !== 'ALL' || notYetExpired || inStock;\n\n function clearFilters() {\n setSearch('');\n setStatusFilter('ALL');\n setNotYetExpired(false);\n setInStock(false);\n }\n\n async function doQuarantine() {\n try {\n await quarantineBatch({\n id: uuidv4(),\n batchId: quarantineDialog.batch.id,\n reason: quarantineDialog.reason,\n notes: quarantineDialog.notes || null,\n });\n showToast({ message: \"Batch quarantined.\", type: \"success\" });\n setQuarantineDialog({\n open: false,\n batch: null,\n reason: \"OTHER\",\n notes: \"\",\n });\n await load();\n } catch (err) {\n const message =\n err?.response?.data?.message ?? err?.message ?? \"Failed to quarantine\";\n showToast({ message, type: \"error\" });\n }\n }\n\n async function doUnquarantine(batch) {\n try {\n await unquarantineBatch({ id: uuidv4(), batchId: batch.id });\n showToast({ message: \"Quarantine lifted.\", type: \"success\" });\n await load();\n } catch (err) {\n const message =\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to unquarantine\";\n showToast({ message, type: \"error\" });\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={'view.manage.productManagement.inventory'}>\n <PmPageShell\n title=\"Inventory\"\n subtitle=\"Your stock batches. Click to see history or quarantine items if there's a problem.\"\n chips={[\"Stock\"]}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n startIcon={<RefreshRoundedIcon />}\n onClick={() => void load()}\n >\n Refresh\n </Button>\n </Stack>\n }\n >\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n\n <PmTableCard\n eyebrow=\"Stock on hand\"\n title=\"Inventory batches\"\n subtitle={\n loading\n ? \"Loading…\"\n : hasFilter\n ? `${filteredItems.length} of ${items.length} ${items.length === 1 ? 'batch' : 'batches'} match`\n : `${items.length} ${items.length === 1 ? 'batch' : 'batches'} visible`\n }\n >\n <Stack direction={{ xs: 'column', md: 'row' }} spacing={1.5} sx={{ mb: 2 }} alignItems={{ md: 'center' }}>\n <TextField\n size=\"small\"\n fullWidth\n placeholder=\"Search product or lot…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchRoundedIcon fontSize=\"small\" />\n </InputAdornment>\n ),\n }}\n sx={{ maxWidth: { md: 360 } }}\n />\n <FormControl size=\"small\" sx={{ minWidth: 160 }}>\n <InputLabel>Status</InputLabel>\n <Select\n label=\"Status\"\n value={statusFilter}\n onChange={(e) => {\n const next = e.target.value;\n setStatusFilter(next);\n // Picking a terminal status from the dropdown auto-clears the\n // opposing chip, so the filter pair can never resolve to an\n // empty set by construction.\n if (next === 'EXPIRED') setNotYetExpired(false);\n if (next === 'EXHAUSTED') setInStock(false);\n }}\n >\n <MenuItem value=\"ALL\">All statuses</MenuItem>\n {BATCH_STATUSES.map((s) => {\n const blocked = (s.value === 'EXPIRED' && notYetExpired) || (s.value === 'EXHAUSTED' && inStock);\n return (\n <MenuItem key={s.value} value={s.value} disabled={blocked}>\n {s.label}\n {blocked ? (\n <Typography component=\"span\" variant=\"caption\" color=\"text.disabled\" sx={{ ml: 1 }}>\n (clear chip first)\n </Typography>\n ) : null}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n <Stack direction=\"row\" spacing={1} useFlexGap flexWrap=\"wrap\">\n <Chip\n label=\"Not yet expired\"\n size=\"small\"\n disabled={statusFilter === 'EXPIRED'}\n onClick={() => {\n // Auto-reset the conflicting dropdown value so the two\n // controls never produce an impossible combination.\n setNotYetExpired((prev) => {\n const next = !prev;\n if (next && statusFilter === 'EXPIRED') setStatusFilter('ALL');\n return next;\n });\n }}\n color={notYetExpired ? 'primary' : 'default'}\n variant={notYetExpired ? 'filled' : 'outlined'}\n sx={{ fontWeight: 700, borderRadius: 1.5 }}\n />\n <Chip\n label=\"Stock still left\"\n size=\"small\"\n disabled={statusFilter === 'EXHAUSTED'}\n onClick={() => {\n setInStock((prev) => {\n const next = !prev;\n if (next && statusFilter === 'EXHAUSTED') setStatusFilter('ALL');\n return next;\n });\n }}\n color={inStock ? 'primary' : 'default'}\n variant={inStock ? 'filled' : 'outlined'}\n sx={{ fontWeight: 700, borderRadius: 1.5 }}\n />\n </Stack>\n <Box sx={{ flexGrow: 1 }} />\n <Button variant=\"text\" onClick={clearFilters} disabled={!hasFilter}>Clear</Button>\n </Stack>\n\n <PmTable dense>\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell>Owner</TableCell>\n <TableCell>Expiry</TableCell>\n <TableCell align=\"right\">Stock</TableCell>\n <TableCell>Storage</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}>\n <Skeleton height={22} />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : filteredItems.length === 0 ? (\n <PmEmptyRow\n colSpan={8}\n icon={WidgetsRoundedIcon}\n title={hasFilter ? 'No batches match your filters' : 'No batches in stock'}\n hint={hasFilter\n ? 'Adjust your search or filters.'\n : 'Record a purchase to add stock.'}\n action={hasFilter ? (\n <Button variant=\"text\" size=\"small\" onClick={clearFilters}>Clear filters</Button>\n ) : null}\n />\n ) : (\n filteredItems.map((b) => {\n const isExpired = isBatchExpired(b);\n const remainingRatio =\n b.qtyPurchased > 0 ? b.qtyRemaining / b.qtyPurchased : 0;\n return (\n <TableRow key={b.id} hover>\n <TableCell>\n <Stack\n direction=\"row\"\n alignItems=\"baseline\"\n spacing={1}\n sx={{ minWidth: 0 }}\n >\n <Typography\n variant=\"body2\"\n sx={{ fontWeight: 700, lineHeight: 1.2 }}\n >\n {b.productName}\n </Typography>\n <PmRef id={b.serialId} />\n </Stack>\n </TableCell>\n <TableCell>\n {b.lotNumber ? (\n <Box\n component=\"span\"\n sx={{\n display: \"inline-flex\",\n px: 0.7,\n py: 0.1,\n borderRadius: 1,\n bgcolor: alpha(theme.palette.primary.main, 0.06),\n color: \"primary.dark\",\n fontFamily: \"monospace\",\n fontSize: \"0.72rem\",\n fontWeight: 700,\n }}\n >\n {b.lotNumber}\n </Box>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {b.ownerName || (b.ownerType === 'USER' ? 'Personal' : 'Facility')}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {b.ownerType}\n </Typography>\n </TableCell>\n <TableCell>\n {b.expiryDate ? (\n <Tooltip\n title={isExpired ? \"Past printed expiry\" : \"\"}\n >\n <Typography\n variant=\"body2\"\n sx={{\n fontWeight: 700,\n fontVariantNumeric: \"tabular-nums\",\n color: isExpired\n ? \"error.dark\"\n : \"text.primary\",\n }}\n >\n {formatDate(b.expiryDate)}\n </Typography>\n </Tooltip>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell align=\"right\" sx={{ minWidth: 150 }}>\n <Stack alignItems=\"flex-end\" spacing={0.5}>\n <Typography\n variant=\"body2\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n {b.qtyRemaining}\n <Typography\n component=\"span\"\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{\n ml: 0.5,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n / {b.qtyPurchased}\n </Typography>\n </Typography>\n <Box sx={{ width: 80 }}>\n <LinearProgress\n variant=\"determinate\"\n value={Math.min(\n 100,\n Math.max(0, remainingRatio * 100),\n )}\n sx={{ height: 4, borderRadius: 2 }}\n color={\n remainingRatio < 0.25 ? \"warning\" : \"primary\"\n }\n />\n </Box>\n </Stack>\n </TableCell>\n <TableCell>\n {b.storageUnitName ? (\n <Typography\n variant=\"caption\"\n sx={{ fontWeight: 700 }}\n >\n {b.storageUnitName}\n </Typography>\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell>\n <Stack\n direction=\"row\"\n spacing={0.5}\n flexWrap=\"wrap\"\n useFlexGap\n >\n <Chip\n size=\"small\"\n color={statusColor(b.status)}\n label={labelOf(BATCH_STATUSES, b.status)}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n {b.isQuarantined ? (\n <Chip\n size=\"small\"\n color=\"error\"\n label={labelOf(QUARANTINE_REASONS, b.quarantineReason) || \"Quarantined\"}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : null}\n {isExpired && b.status !== \"EXPIRED\" ? (\n <Chip\n size=\"small\"\n color=\"warning\"\n label=\"Expired\"\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : null}\n {b.sourceType ? (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={\n b.sourceType === 'PICKUP' ? 'info'\n : b.sourceType === 'TRANSFER_IN' ? 'primary'\n : 'default'\n }\n label={b.sourceType === 'TRANSFER_IN' ? 'Transfer' : b.sourceType === 'PICKUP' ? 'Pickup' : b.sourceType === 'PURCHASE' ? 'Purchase' : b.sourceType}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : null}\n </Stack>\n </TableCell>\n <TableCell align=\"right\">\n {!canWrite ? null : b.isQuarantined ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"success\"\n startIcon={<LockOpenRoundedIcon />}\n onClick={() => void doUnquarantine(b)}\n >\n Unquarantine\n </Button>\n ) : isExpired ||\n b.status === \"EXHAUSTED\" ||\n b.status === \"IN_USE\" ? null : (\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"error\"\n startIcon={<LockRoundedIcon />}\n onClick={() =>\n setQuarantineDialog({\n open: true,\n batch: b,\n reason: \"OTHER\",\n notes: \"\",\n })\n }\n >\n Quarantine\n </Button>\n )}\n </TableCell>\n </TableRow>\n );\n })\n )}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n\n <Dialog\n open={quarantineDialog.open}\n onClose={() =>\n setQuarantineDialog({ ...quarantineDialog, open: false })\n }\n fullWidth\n maxWidth=\"sm\"\n >\n <DialogTitle>Quarantine Batch</DialogTitle>\n <DialogContent dividers>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <Typography variant=\"body2\">\n Product: <strong>{quarantineDialog.batch?.productName}</strong>\n <br />\n Lot: <strong>{quarantineDialog.batch?.lotNumber || \"—\"}</strong>\n <br />\n Remaining: <strong>{quarantineDialog.batch?.qtyRemaining}</strong>\n </Typography>\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={!!validateRequired(quarantineDialog.reason, \"Reason\")}\n >\n <InputLabel>Reason</InputLabel>\n <Select\n label=\"Reason\"\n value={quarantineDialog.reason}\n onChange={(e) =>\n setQuarantineDialog({\n ...quarantineDialog,\n reason: e.target.value,\n })\n }\n >\n {QUARANTINE_REASONS.map((r) => (\n <MenuItem key={r.value} value={r.value}>\n {r.label}\n </MenuItem>\n ))}\n </Select>\n {validateRequired(quarantineDialog.reason, \"Reason\") ? (\n <FormHelperText>\n {validateRequired(quarantineDialog.reason, \"Reason\")}\n </FormHelperText>\n ) : null}\n </FormControl>\n <TextField\n label=\"Notes\"\n size=\"small\"\n multiline\n minRows={2}\n fullWidth\n value={quarantineDialog.notes}\n onChange={(e) =>\n setQuarantineDialog({\n ...quarantineDialog,\n notes: e.target.value,\n })\n }\n />\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button\n onClick={() =>\n setQuarantineDialog({ ...quarantineDialog, open: false })\n }\n >\n Cancel\n </Button>\n <Button\n variant=\"contained\"\n color=\"error\"\n disabled={!!validateRequired(quarantineDialog.reason, \"Reason\")}\n onClick={() => void doQuarantine()}\n >\n Quarantine\n </Button>\n </DialogActions>\n </Dialog>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { D as e, i as t } from "./PmGuard-CjJBisaN.js";
1
+ import { O as e, a as t } from "./currency-ClRLIEg2.js";
2
2
  import { r as n } from "./format-UOyOu75i.js";
3
3
  import { useEffect as r, useState as i } from "react";
4
4
  import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
@@ -269,4 +269,4 @@ function C({ icon: e, label: t, value: n }) {
269
269
  //#endregion
270
270
  export { S as t };
271
271
 
272
- //# sourceMappingURL=MemberProfileCard-DbHZCS5n.js.map
272
+ //# sourceMappingURL=MemberProfileCard-0vjO3Gzd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MemberProfileCard-DbHZCS5n.js","names":[],"sources":["../src/components/MemberProfileCard.jsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Avatar, Box, Chip, LinearProgress, Stack, Typography } from '@mui/material';\nimport { alpha } from '@mui/material/styles';\nimport BadgeRoundedIcon from '@mui/icons-material/BadgeRounded';\nimport GroupsRoundedIcon from '@mui/icons-material/GroupsRounded';\nimport CakeRoundedIcon from '@mui/icons-material/CakeRounded';\nimport PaymentsRoundedIcon from '@mui/icons-material/PaymentsRounded';\nimport LockOutlinedIcon from '@mui/icons-material/LockOutlined';\nimport { getMemberProfile } from '@pm/services/pmApi';\nimport { formatNumber } from '@pm/utils/format';\nimport { usePmPermissions } from '@pm/utils/permissions';\n\nfunction initials(name) {\n if (!name) return '?';\n return name.split(/\\s+/).filter(Boolean).slice(0, 2).map((s) => s[0]).join('').toUpperCase();\n}\n\n// Whole-year age from DOB (string or Date). Returns null when DOB is unset.\nfunction computeAge(dob) {\n if (!dob) return null;\n const birth = dob instanceof Date ? dob : new Date(dob);\n if (Number.isNaN(birth.getTime())) return null;\n const today = new Date();\n let age = today.getFullYear() - birth.getFullYear();\n const m = today.getMonth() - birth.getMonth();\n if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age -= 1;\n return age >= 0 ? age : null;\n}\n\n// Profile card for Sales / Payments. Receives the memberId and fetches the\n// profile + computes age inline. Outstanding dues are passed in by the\n// caller because the dues query is separate and the caller often has it\n// loaded already.\nexport default function MemberProfileCard({\n memberId,\n previousDue = 0,\n currencySymbol = '',\n onLoad,\n}) {\n const perms = usePmPermissions();\n const canSeeMember = perms.has('view.manage.productManagement.member');\n const [profile, setProfile] = useState(null);\n const [loading, setLoading] = useState(false);\n const [missing, setMissing] = useState(false);\n\n useEffect(() => {\n if (!memberId) {\n setProfile(null);\n setMissing(false);\n setLoading(false);\n return undefined;\n }\n if (!canSeeMember) {\n // No access — don't fire the /members/{id} call, the filter would 403.\n // The card renders a friendly \"no access\" panel below.\n setProfile(null);\n setMissing(false);\n setLoading(false);\n return undefined;\n }\n let cancelled = false;\n setLoading(true);\n setMissing(false);\n (async () => {\n try {\n const data = await getMemberProfile(memberId);\n if (cancelled) return;\n if (!data) {\n setProfile(null);\n setMissing(true);\n } else {\n setProfile(data);\n setMissing(false);\n onLoad?.(data);\n }\n } catch {\n if (!cancelled) {\n setProfile(null);\n setMissing(true);\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n return () => { cancelled = true; };\n // onLoad is intentionally excluded; passing a fresh function each render\n // shouldn't re-fire the fetch. The caller should memoize if needed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [memberId, canSeeMember]);\n\n if (!canSeeMember && memberId) {\n return (\n <Box sx={(theme) => ({\n p: 2,\n borderRadius: 3,\n border: `1px solid ${alpha(theme.palette.warning.main, 0.4)}`,\n bgcolor: alpha(theme.palette.warning.main, 0.05),\n })}>\n <Stack direction=\"row\" alignItems=\"center\" spacing={1.25}>\n <LockOutlinedIcon fontSize=\"small\" sx={{ color: 'warning.dark' }} />\n <Box>\n <Typography variant=\"body2\" sx={{ fontWeight: 700, color: 'warning.dark' }}>\n Member details hidden\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n You don’t have permission to view member profiles. Ask an admin\n to grant access if you need to see this customer’s details.\n </Typography>\n </Box>\n </Stack>\n </Box>\n );\n }\n\n if (loading) {\n return (\n <Box sx={{ p: 2, borderRadius: 3, border: '1px solid', borderColor: 'divider' }}>\n <LinearProgress />\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ mt: 1, display: 'block' }}>\n Loading member…\n </Typography>\n </Box>\n );\n }\n\n if (missing) {\n return (\n <Box sx={(theme) => ({\n p: 2,\n borderRadius: 3,\n border: `1px solid ${alpha(theme.palette.error.main, 0.4)}`,\n bgcolor: alpha(theme.palette.error.main, 0.05),\n })}>\n <Typography variant=\"body2\" sx={{ fontWeight: 700, color: 'error.dark' }}>\n Member not found\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n The id passed via the URL didn’t resolve in the configured member table, or the column mapping in Settings is incomplete.\n </Typography>\n </Box>\n );\n }\n\n if (!profile) return null;\n\n const age = computeAge(profile.dob);\n\n return (\n <Box\n sx={(theme) => ({\n p: 2.5,\n borderRadius: 3,\n border: '1px solid',\n borderColor: 'divider',\n background: `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.04)} 0%, ${theme.palette.background.paper} 100%)`,\n })}\n >\n <Stack direction={{ xs: 'column', md: 'row' }} alignItems={{ md: 'center' }} spacing={2}>\n <Avatar sx={{ width: 56, height: 56, bgcolor: 'primary.main', fontWeight: 800, fontSize: 18 }}>\n {initials(profile.name)}\n </Avatar>\n <Box sx={{ flexGrow: 1, minWidth: 0 }}>\n <Typography variant=\"h6\" sx={{ fontWeight: 800, lineHeight: 1.2 }}>\n {profile.name || <Typography component=\"span\" variant=\"caption\" color=\"text.disabled\">No name configured</Typography>}\n </Typography>\n <Stack direction=\"row\" flexWrap=\"wrap\" useFlexGap spacing={1.25} sx={{ mt: 1 }}>\n {profile.identifier ? (\n <DetailChip icon={<BadgeRoundedIcon fontSize=\"small\" />} label=\"Identifier\" value={profile.identifier} />\n ) : null}\n {profile.familyId ? (\n <DetailChip icon={<GroupsRoundedIcon fontSize=\"small\" />} label=\"Family ID\" value={profile.familyId} />\n ) : null}\n {age != null ? (\n <DetailChip icon={<CakeRoundedIcon fontSize=\"small\" />} label=\"Age\" value={`${age} ${age === 1 ? 'year' : 'years'}`} />\n ) : null}\n </Stack>\n </Box>\n <Box sx={{ minWidth: 160, textAlign: { xs: 'left', md: 'right' } }}>\n {previousDue > 0 ? (\n <>\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.75} justifyContent={{ xs: 'flex-start', md: 'flex-end' }} sx={{ color: 'text.secondary' }}>\n <PaymentsRoundedIcon fontSize=\"small\" />\n <Typography variant=\"caption\" sx={{ fontWeight: 700, letterSpacing: 0.6, textTransform: 'uppercase' }}>\n Outstanding\n </Typography>\n </Stack>\n <Typography variant=\"h5\" sx={{ fontWeight: 800, color: 'warning.dark', fontVariantNumeric: 'tabular-nums' }}>\n {currencySymbol}{formatNumber(previousDue)}\n </Typography>\n </>\n ) : (\n <Chip size=\"small\" color=\"success\" label=\"No outstanding dues\" sx={{ fontWeight: 700 }} />\n )}\n </Box>\n </Stack>\n </Box>\n );\n}\n\nfunction DetailChip({ icon, label, value }) {\n return (\n <Box\n sx={(theme) => ({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 0.75,\n px: 1,\n py: 0.4,\n borderRadius: 1.5,\n bgcolor: alpha(theme.palette.primary.main, 0.06),\n border: `1px solid ${alpha(theme.palette.primary.main, 0.16)}`,\n color: 'text.primary',\n })}\n >\n <Box sx={{ display: 'flex', color: 'text.secondary' }}>{icon}</Box>\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\" sx={{ fontWeight: 700, letterSpacing: 0.4, textTransform: 'uppercase' }}>\n {label}\n </Typography>\n <Typography component=\"span\" variant=\"body2\" sx={{ fontWeight: 700 }}>\n {value}\n </Typography>\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAYA,SAAS,EAAS,GAAM;CAEtB,OADK,IACE,EAAK,MAAM,KAAK,EAAE,OAAO,OAAO,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,YAAY,IADzE;AAEpB;AAGA,SAAS,EAAW,GAAK;CACvB,IAAI,CAAC,GAAK,OAAO;CACjB,IAAM,IAAQ,aAAe,OAAO,IAAM,IAAI,KAAK,CAAG;CACtD,IAAI,OAAO,MAAM,EAAM,QAAQ,CAAC,GAAG,OAAO;CAC1C,IAAM,oBAAQ,IAAI,KAAK,GACnB,IAAM,EAAM,YAAY,IAAI,EAAM,YAAY,GAC5C,IAAI,EAAM,SAAS,IAAI,EAAM,SAAS;CAE5C,QADI,IAAI,KAAM,MAAM,KAAK,EAAM,QAAQ,IAAI,EAAM,QAAQ,MAAI,KACtD,KAAO,IAAI,IAAM;AAC1B;AAMA,SAAwB,EAAkB,EACxC,aACA,iBAAc,GACd,oBAAiB,IACjB,aACC;CAED,IAAM,IADQ,EACO,EAAM,IAAI,sCAAsC,GAC/D,CAAC,GAAS,KAAc,EAAS,IAAI,GACrC,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAS,KAAc,EAAS,EAAK;CA+C5C,IA7CA,QAAgB;EACd,IAAI,CAAC,GAAU;GAGb,AAFA,EAAW,IAAI,GACf,EAAW,EAAK,GAChB,EAAW,EAAK;GAChB;EACF;EACA,IAAI,CAAC,GAAc;GAKjB,AAFA,EAAW,IAAI,GACf,EAAW,EAAK,GAChB,EAAW,EAAK;GAChB;EACF;EACA,IAAI,IAAY;EAwBhB,OAvBA,EAAW,EAAI,GACf,EAAW,EAAK,IACf,YAAY;GACX,IAAI;IACF,IAAM,IAAO,MAAM,EAAiB,CAAQ;IAC5C,IAAI,GAAW;IACf,AAAK,KAIH,EAAW,CAAI,GACf,EAAW,EAAK,GAChB,IAAS,CAAI,MALb,EAAW,IAAI,GACf,EAAW,EAAI;GAMnB,QAAQ;IACN,AAAK,MACH,EAAW,IAAI,GACf,EAAW,EAAI;GAEnB,UAAU;IACR,AAAK,KAAW,EAAW,EAAK;GAClC;EACF,GAAG,SACU;GAAE,IAAY;EAAM;CAInC,GAAG,CAAC,GAAU,CAAY,CAAC,GAEvB,CAAC,KAAgB,GACnB,OACE,kBAAC,GAAD;EAAK,KAAK,OAAW;GACnB,GAAG;GACH,cAAc;GACd,QAAQ,aAAa,EAAM,EAAM,QAAQ,QAAQ,MAAM,EAAG;GAC1D,SAAS,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;EACjD;YACE,kBAAC,GAAD;GAAO,WAAU;GAAM,YAAW;GAAS,SAAS;aAApD,CACE,kBAAC,GAAD;IAAkB,UAAS;IAAQ,IAAI,EAAE,OAAO,eAAe;GAAI,CAAA,GACnE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAY,SAAQ;IAAQ,IAAI;KAAE,YAAY;KAAK,OAAO;IAAe;cAAG;GAEhE,CAAA,GACZ,kBAAC,GAAD;IAAY,SAAQ;IAAU,OAAM;cAAiB;GAGzC,CAAA,CACT,EAAA,CAAA,CACA;;CACJ,CAAA;CAIT,IAAI,GACF,OACE,kBAAC,GAAD;EAAK,IAAI;GAAE,GAAG;GAAG,cAAc;GAAG,QAAQ;GAAa,aAAa;EAAU;YAA9E,CACE,kBAAC,GAAD,CAAiB,CAAA,GACjB,kBAAC,GAAD;GAAY,SAAQ;GAAU,OAAM;GAAiB,IAAI;IAAE,IAAI;IAAG,SAAS;GAAQ;aAAG;EAE1E,CAAA,CACT;;CAIT,IAAI,GACF,OACE,kBAAC,GAAD;EAAK,KAAK,OAAW;GACnB,GAAG;GACH,cAAc;GACd,QAAQ,aAAa,EAAM,EAAM,QAAQ,MAAM,MAAM,EAAG;GACxD,SAAS,EAAM,EAAM,QAAQ,MAAM,MAAM,GAAI;EAC/C;YALA,CAME,kBAAC,GAAD;GAAY,SAAQ;GAAQ,IAAI;IAAE,YAAY;IAAK,OAAO;GAAa;aAAG;EAE9D,CAAA,GACZ,kBAAC,GAAD;GAAY,SAAQ;GAAU,OAAM;aAAiB;EAEzC,CAAA,CACT;;CAIT,IAAI,CAAC,GAAS,OAAO;CAErB,IAAM,IAAM,EAAW,EAAQ,GAAG;CAElC,OACE,kBAAC,GAAD;EACE,KAAK,OAAW;GACd,GAAG;GACH,cAAc;GACd,QAAQ;GACR,aAAa;GACb,YAAY,2BAA2B,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI,EAAE,OAAO,EAAM,QAAQ,WAAW,MAAM;EACvH;YAEA,kBAAC,GAAD;GAAO,WAAW;IAAE,IAAI;IAAU,IAAI;GAAM;GAAG,YAAY,EAAE,IAAI,SAAS;GAAG,SAAS;aAAtF;IACE,kBAAC,GAAD;KAAQ,IAAI;MAAE,OAAO;MAAI,QAAQ;MAAI,SAAS;MAAgB,YAAY;MAAK,UAAU;KAAG;eACzF,EAAS,EAAQ,IAAI;IAChB,CAAA;IACR,kBAAC,GAAD;KAAK,IAAI;MAAE,UAAU;MAAG,UAAU;KAAE;eAApC,CACE,kBAAC,GAAD;MAAY,SAAQ;MAAK,IAAI;OAAE,YAAY;OAAK,YAAY;MAAI;gBAC7D,EAAQ,QAAQ,kBAAC,GAAD;OAAY,WAAU;OAAO,SAAQ;OAAU,OAAM;iBAAgB;MAA8B,CAAA;KAC1G,CAAA,GACZ,kBAAC,GAAD;MAAO,WAAU;MAAM,UAAS;MAAO,YAAA;MAAW,SAAS;MAAM,IAAI,EAAE,IAAI,EAAE;gBAA7E;OACG,EAAQ,aACP,kBAAC,GAAD;QAAY,MAAM,kBAAC,GAAD,EAAkB,UAAS,QAAS,CAAA;QAAG,OAAM;QAAa,OAAO,EAAQ;OAAa,CAAA,IACtG;OACH,EAAQ,WACP,kBAAC,GAAD;QAAY,MAAM,kBAAC,GAAD,EAAmB,UAAS,QAAS,CAAA;QAAG,OAAM;QAAY,OAAO,EAAQ;OAAW,CAAA,IACpG;OACH,KAAO,OAEJ,OADF,kBAAC,GAAD;QAAY,MAAM,kBAAC,GAAD,EAAiB,UAAS,QAAS,CAAA;QAAG,OAAM;QAAM,OAAO,GAAG,EAAI,GAAG,MAAQ,IAAI,SAAS;OAAY,CAAA;MAEnH;OACJ;;IACL,kBAAC,GAAD;KAAK,IAAI;MAAE,UAAU;MAAK,WAAW;OAAE,IAAI;OAAQ,IAAI;MAAQ;KAAE;eAC9D,IAAc,IACb,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;MAAO,WAAU;MAAM,YAAW;MAAS,SAAS;MAAM,gBAAgB;OAAE,IAAI;OAAc,IAAI;MAAW;MAAG,IAAI,EAAE,OAAO,iBAAiB;gBAA9I,CACE,kBAAC,GAAD,EAAqB,UAAS,QAAS,CAAA,GACvC,kBAAC,GAAD;OAAY,SAAQ;OAAU,IAAI;QAAE,YAAY;QAAK,eAAe;QAAK,eAAe;OAAY;iBAAG;MAE3F,CAAA,CACP;SACP,kBAAC,GAAD;MAAY,SAAQ;MAAK,IAAI;OAAE,YAAY;OAAK,OAAO;OAAgB,oBAAoB;MAAe;gBAA1G,CACG,GAAgB,EAAa,CAAW,CAC/B;OACZ,EAAA,CAAA,IAEF,kBAAC,GAAD;MAAM,MAAK;MAAQ,OAAM;MAAU,OAAM;MAAsB,IAAI,EAAE,YAAY,IAAI;KAAI,CAAA;IAExF,CAAA;GACA;;CACJ,CAAA;AAET;AAEA,SAAS,EAAW,EAAE,SAAM,UAAO,YAAS;CAC1C,OACE,kBAAC,GAAD;EACE,KAAK,OAAW;GACd,SAAS;GACT,YAAY;GACZ,KAAK;GACL,IAAI;GACJ,IAAI;GACJ,cAAc;GACd,SAAS,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;GAC/C,QAAQ,aAAa,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;GAC3D,OAAO;EACT;YAXF;GAaE,kBAAC,GAAD;IAAK,IAAI;KAAE,SAAS;KAAQ,OAAO;IAAiB;cAAI;GAAU,CAAA;GAClE,kBAAC,GAAD;IAAY,WAAU;IAAO,SAAQ;IAAU,OAAM;IAAiB,IAAI;KAAE,YAAY;KAAK,eAAe;KAAK,eAAe;IAAY;cACzI;GACS,CAAA;GACZ,kBAAC,GAAD;IAAY,WAAU;IAAO,SAAQ;IAAQ,IAAI,EAAE,YAAY,IAAI;cAChE;GACS,CAAA;EACT;;AAET"}
1
+ {"version":3,"file":"MemberProfileCard-0vjO3Gzd.js","names":[],"sources":["../src/components/MemberProfileCard.jsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Avatar, Box, Chip, LinearProgress, Stack, Typography } from '@mui/material';\nimport { alpha } from '@mui/material/styles';\nimport BadgeRoundedIcon from '@mui/icons-material/BadgeRounded';\nimport GroupsRoundedIcon from '@mui/icons-material/GroupsRounded';\nimport CakeRoundedIcon from '@mui/icons-material/CakeRounded';\nimport PaymentsRoundedIcon from '@mui/icons-material/PaymentsRounded';\nimport LockOutlinedIcon from '@mui/icons-material/LockOutlined';\nimport { getMemberProfile } from '@pm/services/pmApi';\nimport { formatNumber } from '@pm/utils/format';\nimport { usePmPermissions } from '@pm/utils/permissions';\n\nfunction initials(name) {\n if (!name) return '?';\n return name.split(/\\s+/).filter(Boolean).slice(0, 2).map((s) => s[0]).join('').toUpperCase();\n}\n\n// Whole-year age from DOB (string or Date). Returns null when DOB is unset.\nfunction computeAge(dob) {\n if (!dob) return null;\n const birth = dob instanceof Date ? dob : new Date(dob);\n if (Number.isNaN(birth.getTime())) return null;\n const today = new Date();\n let age = today.getFullYear() - birth.getFullYear();\n const m = today.getMonth() - birth.getMonth();\n if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age -= 1;\n return age >= 0 ? age : null;\n}\n\n// Profile card for Sales / Payments. Receives the memberId and fetches the\n// profile + computes age inline. Outstanding dues are passed in by the\n// caller because the dues query is separate and the caller often has it\n// loaded already.\nexport default function MemberProfileCard({\n memberId,\n previousDue = 0,\n currencySymbol = '',\n onLoad,\n}) {\n const perms = usePmPermissions();\n const canSeeMember = perms.has('view.manage.productManagement.member');\n const [profile, setProfile] = useState(null);\n const [loading, setLoading] = useState(false);\n const [missing, setMissing] = useState(false);\n\n useEffect(() => {\n if (!memberId) {\n setProfile(null);\n setMissing(false);\n setLoading(false);\n return undefined;\n }\n if (!canSeeMember) {\n // No access — don't fire the /members/{id} call, the filter would 403.\n // The card renders a friendly \"no access\" panel below.\n setProfile(null);\n setMissing(false);\n setLoading(false);\n return undefined;\n }\n let cancelled = false;\n setLoading(true);\n setMissing(false);\n (async () => {\n try {\n const data = await getMemberProfile(memberId);\n if (cancelled) return;\n if (!data) {\n setProfile(null);\n setMissing(true);\n } else {\n setProfile(data);\n setMissing(false);\n onLoad?.(data);\n }\n } catch {\n if (!cancelled) {\n setProfile(null);\n setMissing(true);\n }\n } finally {\n if (!cancelled) setLoading(false);\n }\n })();\n return () => { cancelled = true; };\n // onLoad is intentionally excluded; passing a fresh function each render\n // shouldn't re-fire the fetch. The caller should memoize if needed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [memberId, canSeeMember]);\n\n if (!canSeeMember && memberId) {\n return (\n <Box sx={(theme) => ({\n p: 2,\n borderRadius: 3,\n border: `1px solid ${alpha(theme.palette.warning.main, 0.4)}`,\n bgcolor: alpha(theme.palette.warning.main, 0.05),\n })}>\n <Stack direction=\"row\" alignItems=\"center\" spacing={1.25}>\n <LockOutlinedIcon fontSize=\"small\" sx={{ color: 'warning.dark' }} />\n <Box>\n <Typography variant=\"body2\" sx={{ fontWeight: 700, color: 'warning.dark' }}>\n Member details hidden\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n You don’t have permission to view member profiles. Ask an admin\n to grant access if you need to see this customer’s details.\n </Typography>\n </Box>\n </Stack>\n </Box>\n );\n }\n\n if (loading) {\n return (\n <Box sx={{ p: 2, borderRadius: 3, border: '1px solid', borderColor: 'divider' }}>\n <LinearProgress />\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ mt: 1, display: 'block' }}>\n Loading member…\n </Typography>\n </Box>\n );\n }\n\n if (missing) {\n return (\n <Box sx={(theme) => ({\n p: 2,\n borderRadius: 3,\n border: `1px solid ${alpha(theme.palette.error.main, 0.4)}`,\n bgcolor: alpha(theme.palette.error.main, 0.05),\n })}>\n <Typography variant=\"body2\" sx={{ fontWeight: 700, color: 'error.dark' }}>\n Member not found\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n The id passed via the URL didn’t resolve in the configured member table, or the column mapping in Settings is incomplete.\n </Typography>\n </Box>\n );\n }\n\n if (!profile) return null;\n\n const age = computeAge(profile.dob);\n\n return (\n <Box\n sx={(theme) => ({\n p: 2.5,\n borderRadius: 3,\n border: '1px solid',\n borderColor: 'divider',\n background: `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.04)} 0%, ${theme.palette.background.paper} 100%)`,\n })}\n >\n <Stack direction={{ xs: 'column', md: 'row' }} alignItems={{ md: 'center' }} spacing={2}>\n <Avatar sx={{ width: 56, height: 56, bgcolor: 'primary.main', fontWeight: 800, fontSize: 18 }}>\n {initials(profile.name)}\n </Avatar>\n <Box sx={{ flexGrow: 1, minWidth: 0 }}>\n <Typography variant=\"h6\" sx={{ fontWeight: 800, lineHeight: 1.2 }}>\n {profile.name || <Typography component=\"span\" variant=\"caption\" color=\"text.disabled\">No name configured</Typography>}\n </Typography>\n <Stack direction=\"row\" flexWrap=\"wrap\" useFlexGap spacing={1.25} sx={{ mt: 1 }}>\n {profile.identifier ? (\n <DetailChip icon={<BadgeRoundedIcon fontSize=\"small\" />} label=\"Identifier\" value={profile.identifier} />\n ) : null}\n {profile.familyId ? (\n <DetailChip icon={<GroupsRoundedIcon fontSize=\"small\" />} label=\"Family ID\" value={profile.familyId} />\n ) : null}\n {age != null ? (\n <DetailChip icon={<CakeRoundedIcon fontSize=\"small\" />} label=\"Age\" value={`${age} ${age === 1 ? 'year' : 'years'}`} />\n ) : null}\n </Stack>\n </Box>\n <Box sx={{ minWidth: 160, textAlign: { xs: 'left', md: 'right' } }}>\n {previousDue > 0 ? (\n <>\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.75} justifyContent={{ xs: 'flex-start', md: 'flex-end' }} sx={{ color: 'text.secondary' }}>\n <PaymentsRoundedIcon fontSize=\"small\" />\n <Typography variant=\"caption\" sx={{ fontWeight: 700, letterSpacing: 0.6, textTransform: 'uppercase' }}>\n Outstanding\n </Typography>\n </Stack>\n <Typography variant=\"h5\" sx={{ fontWeight: 800, color: 'warning.dark', fontVariantNumeric: 'tabular-nums' }}>\n {currencySymbol}{formatNumber(previousDue)}\n </Typography>\n </>\n ) : (\n <Chip size=\"small\" color=\"success\" label=\"No outstanding dues\" sx={{ fontWeight: 700 }} />\n )}\n </Box>\n </Stack>\n </Box>\n );\n}\n\nfunction DetailChip({ icon, label, value }) {\n return (\n <Box\n sx={(theme) => ({\n display: 'inline-flex',\n alignItems: 'center',\n gap: 0.75,\n px: 1,\n py: 0.4,\n borderRadius: 1.5,\n bgcolor: alpha(theme.palette.primary.main, 0.06),\n border: `1px solid ${alpha(theme.palette.primary.main, 0.16)}`,\n color: 'text.primary',\n })}\n >\n <Box sx={{ display: 'flex', color: 'text.secondary' }}>{icon}</Box>\n <Typography component=\"span\" variant=\"caption\" color=\"text.secondary\" sx={{ fontWeight: 700, letterSpacing: 0.4, textTransform: 'uppercase' }}>\n {label}\n </Typography>\n <Typography component=\"span\" variant=\"body2\" sx={{ fontWeight: 700 }}>\n {value}\n </Typography>\n </Box>\n );\n}\n"],"mappings":"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"}
@@ -1,10 +1,9 @@
1
- import { E as e, T as t, _ as n, a as r, c as i, i as a, n as o, r as ee, t as te } from "./PmGuard-CjJBisaN.js";
2
- import { t as ne } from "./currency-BDDCDgeE.js";
1
+ import { D as e, E as t, a as n, i as r, l as i, n as a, o, r as ee, t as te, v as ne } from "./currency-ClRLIEg2.js";
3
2
  import { l as re, t as s, u as ie } from "./validation-Ba7puFIO.js";
4
3
  import { t as ae } from "./useFieldErrors-hDG8ox_x.js";
5
4
  import { r as c, t as l } from "./format-UOyOu75i.js";
6
- import { n as oe } from "./constants-BrA_hfAI.js";
7
- import { t as u } from "./MemberProfileCard-DbHZCS5n.js";
5
+ import { n as oe } from "./constants-5ZCZo-Zm.js";
6
+ import { t as u } from "./MemberProfileCard-0vjO3Gzd.js";
8
7
  import { useMemo as d, useState as f } from "react";
9
8
  import { Fragment as se, jsx as p, jsxs as m } from "react/jsx-runtime";
10
9
  import { v4 as ce } from "uuid";
@@ -21,7 +20,7 @@ function M(e) {
21
20
  return Math.max(0, Math.floor(n / (24 * 3600 * 1e3)));
22
21
  }
23
22
  function N() {
24
- let { memberId: N } = A(), F = k(), I = ne(i()?.currencyCode), L = a().has("view.manage.productManagement.dues"), [R, z] = f(""), [B, ve] = f(""), [ye, V] = f(0), [H, U] = f(null), [be, W] = f(!1), G = ae(), xe = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10), [K, q] = f({
23
+ let { memberId: N } = A(), F = k(), I = te(i()?.currencyCode), L = n().has("view.manage.productManagement.dues"), [R, z] = f(""), [B, ve] = f(""), [ye, V] = f(0), [H, U] = f(null), [be, W] = f(!1), G = ae(), xe = (/* @__PURE__ */ new Date()).toISOString().substring(0, 10), [K, q] = f({
25
24
  id: ce(),
26
25
  paymentDate: xe,
27
26
  paymentMode: "CASH",
@@ -52,7 +51,7 @@ function N() {
52
51
  W(!1);
53
52
  }
54
53
  }
55
- ee("update.manage.productManagement.payment", () => {
54
+ r("update.manage.productManagement.payment", () => {
56
55
  Se(), q((e) => ({
57
56
  ...e,
58
57
  collectingNow: ""
@@ -80,7 +79,7 @@ function N() {
80
79
  return;
81
80
  }
82
81
  try {
83
- await n({
82
+ await ne({
84
83
  id: K.id,
85
84
  memberId: N,
86
85
  amount: Number(Y.toFixed(2)),
@@ -100,10 +99,10 @@ function N() {
100
99
  });
101
100
  }
102
101
  }
103
- return /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(o, {}), /* @__PURE__ */ p(te, {
102
+ return /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(ee, {}), /* @__PURE__ */ p(a, {
104
103
  perm: "update.manage.productManagement.payment",
105
104
  title: "You don’t have permission to collect payments",
106
- children: /* @__PURE__ */ m(r, {
105
+ children: /* @__PURE__ */ m(o, {
107
106
  title: "Collect Payment",
108
107
  subtitle: "Collect payment for what a customer owes. Payments are matched to sales in order received.",
109
108
  actions: /* @__PURE__ */ m(S, {
@@ -484,4 +483,4 @@ function P({ total: e, oldestDueDays: t, oldestDueDate: n, lastSaleDays: r, last
484
483
  //#endregion
485
484
  export { N as default };
486
485
 
487
- //# sourceMappingURL=PaymentCreatePage-Ci3ERnby.js.map
486
+ //# sourceMappingURL=PaymentCreatePage-BYmDZAIB.js.map