@argusoft/medplat-product-management 1.0.2 → 1.0.4
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.
- package/dist/{BrandsPage-CSA3meke.js → BrandsPage-BE3kn-8b.js} +77 -75
- package/dist/BrandsPage-BE3kn-8b.js.map +1 -0
- package/dist/{CategoriesPage-BrspzkJL.js → CategoriesPage-COqi3pMi.js} +71 -70
- package/dist/{CategoriesPage-BrspzkJL.js.map → CategoriesPage-COqi3pMi.js.map} +1 -1
- package/dist/{DashboardPage-CqaquOs2.js → DashboardPage-C_H1Jrey.js} +90 -89
- package/dist/DashboardPage-C_H1Jrey.js.map +1 -0
- package/dist/{FacilityPicker-Dv_H95WW.js → FacilityPicker-Diq2hnYg.js} +2 -2
- package/dist/{FacilityPicker-Dv_H95WW.js.map → FacilityPicker-Diq2hnYg.js.map} +1 -1
- package/dist/{InventoryPage-CPbzfDMR.js → InventoryPage-Bjpw6vTz.js} +106 -115
- package/dist/InventoryPage-Bjpw6vTz.js.map +1 -0
- package/dist/{MemberProfileCard-0vjO3Gzd.js → MemberProfileCard-BLUVRILO.js} +10 -9
- package/dist/MemberProfileCard-BLUVRILO.js.map +1 -0
- package/dist/{PaymentCreatePage-BYmDZAIB.js → PaymentCreatePage-B5VOgTx9.js} +173 -177
- package/dist/PaymentCreatePage-B5VOgTx9.js.map +1 -0
- package/dist/{PaymentsPage-BXPvKon-.js → PaymentsPage-DY6BclkN.js} +24 -23
- package/dist/{PaymentsPage-BXPvKon-.js.map → PaymentsPage-DY6BclkN.js.map} +1 -1
- package/dist/PickupCreatePage-CCSiyFQF.js +706 -0
- package/dist/PickupCreatePage-CCSiyFQF.js.map +1 -0
- package/dist/{PickupsPage-D66_Wta4.js → PickupsPage-DGi6T5Of.js} +83 -90
- package/dist/PickupsPage-DGi6T5Of.js.map +1 -0
- package/dist/PmGuard-C90yhpsN.js +592 -0
- package/dist/PmGuard-C90yhpsN.js.map +1 -0
- package/dist/{PmTable-B9970kPY.js → PmTable-BkK65bJQ.js} +98 -98
- package/dist/PmTable-BkK65bJQ.js.map +1 -0
- package/dist/{ProductEditPage-B8qFTCW9.js → ProductEditPage-CQj9egzW.js} +138 -137
- package/dist/ProductEditPage-CQj9egzW.js.map +1 -0
- package/dist/{ProductsPage-C7_r9cKn.js → ProductsPage-i8QVw_Jm.js} +65 -64
- package/dist/{ProductsPage-C7_r9cKn.js.map → ProductsPage-i8QVw_Jm.js.map} +1 -1
- package/dist/PurchaseCreatePage-Bp6bl9n8.js +591 -0
- package/dist/PurchaseCreatePage-Bp6bl9n8.js.map +1 -0
- package/dist/{PurchaseRequestDetailPage-C7aAwEqw.js → PurchaseRequestDetailPage-CNAqIFef.js} +216 -215
- package/dist/PurchaseRequestDetailPage-CNAqIFef.js.map +1 -0
- package/dist/{PurchaseRequestsPage-BwCkbpaX.js → PurchaseRequestsPage-BJqDlNrc.js} +57 -56
- package/dist/{PurchaseRequestsPage-BwCkbpaX.js.map → PurchaseRequestsPage-BJqDlNrc.js.map} +1 -1
- package/dist/{PurchasesPage-CvaZouVy.js → PurchasesPage-BQEToymQ.js} +76 -75
- package/dist/{PurchasesPage-CvaZouVy.js.map → PurchasesPage-BQEToymQ.js.map} +1 -1
- package/dist/{SaleCreatePage-DP1Qmltu.js → SaleCreatePage-DbfBK4PH.js} +149 -173
- package/dist/SaleCreatePage-DbfBK4PH.js.map +1 -0
- package/dist/{SalesPage-CO3cdYX5.js → SalesPage-CTBJMxZe.js} +50 -49
- package/dist/SalesPage-CTBJMxZe.js.map +1 -0
- package/dist/SettingsPage-CiwVmzM3.js +663 -0
- package/dist/SettingsPage-CiwVmzM3.js.map +1 -0
- package/dist/{StorageUnitsPage-B9Hr_RkW.js → StorageUnitsPage-BV7nK_iE.js} +57 -56
- package/dist/{StorageUnitsPage-B9Hr_RkW.js.map → StorageUnitsPage-BV7nK_iE.js.map} +1 -1
- package/dist/{TransactionsPage-HKIGwEZN.js → TransactionsPage-Cocn4901.js} +55 -54
- package/dist/TransactionsPage-Cocn4901.js.map +1 -0
- package/dist/TransferCreatePage-DbjcjuOg.js +316 -0
- package/dist/TransferCreatePage-DbjcjuOg.js.map +1 -0
- package/dist/TransfersPage-BFVF7Xvc.js +471 -0
- package/dist/TransfersPage-BFVF7Xvc.js.map +1 -0
- package/dist/VialsPage-BNzYTLVd.js +321 -0
- package/dist/VialsPage-BNzYTLVd.js.map +1 -0
- package/dist/index.js +652 -34
- package/dist/index.js.map +1 -1
- package/dist/settingsStore-KW217unV.js +417 -0
- package/dist/settingsStore-KW217unV.js.map +1 -0
- package/package.json +1 -1
- package/dist/BrandsPage-CSA3meke.js.map +0 -1
- package/dist/DashboardPage-CqaquOs2.js.map +0 -1
- package/dist/InventoryPage-CPbzfDMR.js.map +0 -1
- package/dist/MemberProfileCard-0vjO3Gzd.js.map +0 -1
- package/dist/PaymentCreatePage-BYmDZAIB.js.map +0 -1
- package/dist/PickupCreatePage-CMGwoAXj.js +0 -426
- package/dist/PickupCreatePage-CMGwoAXj.js.map +0 -1
- package/dist/PickupsPage-D66_Wta4.js.map +0 -1
- package/dist/PmTable-B9970kPY.js.map +0 -1
- package/dist/ProductEditPage-B8qFTCW9.js.map +0 -1
- package/dist/PurchaseCreatePage-CDfNDcKG.js +0 -627
- package/dist/PurchaseCreatePage-CDfNDcKG.js.map +0 -1
- package/dist/PurchaseRequestDetailPage-C7aAwEqw.js.map +0 -1
- package/dist/SaleCreatePage-DP1Qmltu.js.map +0 -1
- package/dist/SalesPage-CO3cdYX5.js.map +0 -1
- package/dist/SettingsPage-DvVRwSFw.js +0 -711
- package/dist/SettingsPage-DvVRwSFw.js.map +0 -1
- package/dist/TransactionsPage-HKIGwEZN.js.map +0 -1
- package/dist/TransferCreatePage-DMuZqQ3c.js +0 -363
- package/dist/TransferCreatePage-DMuZqQ3c.js.map +0 -1
- package/dist/TransfersPage-COIyouL4.js +0 -480
- package/dist/TransfersPage-COIyouL4.js.map +0 -1
- package/dist/VialsPage-BNPUwGCU.js +0 -922
- package/dist/VialsPage-BNPUwGCU.js.map +0 -1
- package/dist/currency-ClRLIEg2.js +0 -1372
- package/dist/currency-ClRLIEg2.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PurchaseCreatePage-CDfNDcKG.js","names":[],"sources":["../src/views/PurchaseCreatePage.jsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Chip,\n Divider,\n FormControl,\n FormHelperText,\n Grid,\n IconButton,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport DeleteOutlineRoundedIcon from '@mui/icons-material/DeleteOutlineRounded';\nimport AddRoundedIcon from '@mui/icons-material/AddRounded';\nimport SaveRoundedIcon from '@mui/icons-material/SaveRounded';\nimport Inventory2RoundedIcon from '@mui/icons-material/Inventory2Rounded';\nimport { showToast } from '@/common/toaster/toaster';\nimport PmPageShell from '@pm/components/PmPageShell';\nimport PmNav from '@pm/components/PmNav';\nimport PmGuard from '@pm/components/PmGuard';\nimport {\n createPurchase,\n getProducts,\n getStorageUnits,\n listBatches,\n submitPurchaseRequest,\n} from '@pm/services/pmApi';\nimport FacilityPicker from '@pm/components/FacilityPicker';\nimport { BATCH_STATUSES, PAYMENT_MODES, labelOf } from '@pm/utils/constants';\nimport {\n isClean,\n validateFutureDate,\n validatePastOrTodayDate,\n validatePrice,\n validateQuantity,\n validateRequired,\n} from '@pm/utils/validation';\nimport { formatDate, formatNumber, statusColor } from '@pm/utils/format';\nimport { PmFormSection } from '@pm/components/PmTable';\nimport { useFieldErrors } from '@pm/utils/useFieldErrors';\nimport { usePermissionedEffect } from '@pm/utils/permissions';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\n// Purchase type → backend owner_type mapping. \"Personal Use\" puts the stock\n// against the acting user; \"Facility Use\" puts it against a facility the user\n// has access to. The Personal option is filtered out when the deployment-level\n// allowPersonalPurchase setting is off (see PURCHASE_TYPES filter below).\nconst ALL_PURCHASE_TYPES = [\n { value: 'USER', label: 'Personal Use' },\n { value: 'FACILITY', label: 'Facility Use' },\n];\n\nfunction emptyLine() {\n return {\n key: uuidv4(),\n batchId: uuidv4(),\n productId: '',\n quantity: 1,\n buyPrice: '',\n expiryDate: '',\n lotNumber: '',\n manufactureDate: '',\n storageUnitId: '',\n };\n}\n\nexport default function PurchaseCreatePage() {\n const navigate = useNavigate();\n const settings = usePmSettings();\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n const allowPersonalPurchase = settings?.allowPersonalPurchase !== false;\n // When confirmation is on, the form submits to the request endpoint and\n // the user lands on the request detail page instead of the purchases list.\n const requireConfirmation = settings?.requirePurchaseConfirmation === true;\n // Personal Use is hidden entirely when the deployment doesn't allow it; the\n // dropdown then has a single option and the form defaults to FACILITY.\n const PURCHASE_TYPES = useMemo(\n () => (allowPersonalPurchase ? ALL_PURCHASE_TYPES : ALL_PURCHASE_TYPES.filter((t) => t.value !== 'USER')),\n [allowPersonalPurchase],\n );\n const [products, setProducts] = useState([]);\n const [storageUnits, setStorageUnits] = useState([]);\n const [batches, setBatches] = useState([]);\n const [error, setError] = useState('');\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n ownerType: allowPersonalPurchase ? 'USER' : 'FACILITY',\n ownerId: '',\n purchaseDate: today,\n paymentMode: 'CASH',\n notes: '',\n items: [emptyLine()],\n });\n\n // When the deployment setting flips from allowing personal purchases to not\n // allowing them (or vice-versa) after the form has rendered, snap the\n // ownerType back to a valid choice so the user can't end up submitting USER\n // against a deployment that doesn't accept it.\n useEffect(() => {\n if (!allowPersonalPurchase && form.ownerType === 'USER') {\n setForm((f) => ({ ...f, ownerType: 'FACILITY', ownerId: '' }));\n }\n }, [allowPersonalPurchase, form.ownerType]);\n\n usePermissionedEffect('update.manage.productManagement.purchase', () => {\n async function load() {\n try {\n const [p, su, b] = await Promise.all([\n getProducts(),\n getStorageUnits({ activeOnly: true }),\n // Pulled here so the existing-inventory panel renders alongside the\n // purchase form — the operator can see what they / the facility\n // already hold before deciding what to buy.\n listBatches().catch(() => []),\n ]);\n setProducts(p);\n setStorageUnits(su);\n setBatches(b);\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to load reference data');\n }\n }\n void load();\n }, []);\n\n // Existing-stock view for the currently-selected owner. Personal purchases\n // show the operator's USER-owned batches; facility purchases show batches\n // owned by the picked facility. Exhausted/expired rows are skipped to keep\n // the panel decision-relevant.\n const existingInventory = useMemo(() => {\n if (form.ownerType === 'USER') {\n return batches.filter((b) => b.ownerType === 'USER' && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n if (form.ownerType === 'FACILITY' && form.ownerId) {\n return batches.filter((b) => b.ownerType === 'FACILITY' && b.ownerId === form.ownerId\n && Number(b.qtyRemaining ?? 0) > 0\n && b.status !== 'EXPIRED' && b.status !== 'EXHAUSTED');\n }\n return [];\n }, [batches, form.ownerType, form.ownerId]);\n\n // Storage units filtered by the selected facility (when applicable).\n // Personal purchases don't reference a storage unit at all.\n const eligibleStorageUnits = useMemo(() => {\n if (form.ownerType !== 'FACILITY' || !form.ownerId) return [];\n return storageUnits.filter((u) => u.ownerId === form.ownerId);\n }, [storageUnits, form.ownerType, form.ownerId]);\n\n function patchLine(idx, k, v) {\n setForm((f) => {\n const items = f.items.slice();\n items[idx] = { ...items[idx], [k]: v };\n return { ...f, items };\n });\n }\n\n function removeLine(idx) {\n setForm((f) => ({ ...f, items: f.items.filter((_, i) => i !== idx) }));\n }\n\n function addLine() {\n setForm((f) => ({ ...f, items: [...f.items, emptyLine()] }));\n }\n\n // Sum of (qty * buy price) for lines that have a price. Used to validate\n // the optional Total Amount header field.\n const computedLineTotal = useMemo(() => {\n let priced = false;\n const sum = form.items.reduce((acc, line) => {\n if (line.buyPrice === '' || line.quantity === '' || line.quantity == null) return acc;\n const qty = Number(line.quantity);\n const price = Number(line.buyPrice);\n if (Number.isNaN(qty) || Number.isNaN(price)) return acc;\n priced = true;\n return acc + qty * price;\n }, 0);\n return { sum, priced };\n }, [form.items]);\n\n // Header + per-line errors. The total is auto-derived from the line items so it\n // never needs its own validation message.\n const headerErrors = {\n ownerType: validateRequired(form.ownerType, 'Purchase type'),\n ownerId: form.ownerType === 'FACILITY' ? validateRequired(form.ownerId, 'Facility') : '',\n purchaseDate: validateRequired(form.purchaseDate, 'Purchase date'),\n };\n const lineErrors = form.items.map((line) => {\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n // Storage units only apply to facility-owned stock — personal purchases keep\n // the field disabled, so the requirement is only enforced for facility flows.\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n let storageErr = storageRequired ? validateRequired(line.storageUnitId, 'Storage unit') : '';\n // Cold-chain check: if the product has a vaccine config with a required\n // storage range, the selected storage unit's operating range must fit\n // inside it. Mirror the backend STORAGE_UNIT_TEMP_MISMATCH rule.\n if (!storageErr && line.storageUnitId && product?.vaccineConfig) {\n const cfg = product.vaccineConfig;\n const unit = storageUnits.find((u) => u.id === line.storageUnitId);\n if (cfg.storageTempMinCelsius != null && cfg.storageTempMaxCelsius != null && unit) {\n const uMin = unit.operatingTempMinCelsius;\n const uMax = unit.operatingTempMaxCelsius;\n // Unit must be able to reach every temp the vaccine tolerates: its\n // operating range must contain the vaccine's required range.\n if (uMin == null || uMax == null\n || Number(uMin) > Number(cfg.storageTempMinCelsius)\n || Number(uMax) < Number(cfg.storageTempMaxCelsius)) {\n storageErr = `Unit operates ${uMin ?? '?'}°C → ${uMax ?? '?'}°C, which cannot cover this product's required ${cfg.storageTempMinCelsius}°C → ${cfg.storageTempMaxCelsius}°C.`;\n }\n }\n }\n return {\n productId: validateRequired(line.productId, 'Product'),\n quantity: validateQuantity(line.quantity),\n // When purchase amounts are managed, a positive buy price is mandatory;\n // otherwise it stays optional. Mirrors the backend enforcement.\n buyPrice: manageAmounts\n ? (validateRequired(line.buyPrice, 'Buy price')\n || validatePrice(line.buyPrice)\n || (Number(line.buyPrice) > 0 ? '' : 'Buy price must be greater than 0.'))\n : validatePrice(line.buyPrice),\n // In request mode the approver fills expiry at confirmation, so we\n // don't require it from the requester even when the product carries\n // hasExpiry. The approver gets to enter the actual expiry off the\n // packaging on PurchaseRequestDetailPage.\n expiryDate: requireConfirmation\n ? ''\n : expiryRequired\n ? (validateRequired(line.expiryDate, 'Expiry date')\n || validateFutureDate(line.expiryDate, { label: 'Expiry' }))\n : validateFutureDate(line.expiryDate, { label: 'Expiry' }),\n manufactureDate: validatePastOrTodayDate(line.manufactureDate, { label: 'Manufacture date' }),\n storageUnitId: storageErr,\n };\n });\n const formIsValid = isClean(headerErrors) && lineErrors.every(isClean) && form.items.length > 0;\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError('Please fix the highlighted fields and try again.');\n return;\n }\n // Payload shape is the same for direct purchase and request submission —\n // the difference is the endpoint and what we navigate to afterwards.\n const payload = {\n id: form.id,\n ownerType: form.ownerType,\n ownerId: form.ownerType === 'FACILITY' ? form.ownerId : null,\n purchaseDate: form.purchaseDate,\n paymentMode: manageAmounts ? (form.paymentMode || null) : null,\n // Amount tracking is a per-deployment toggle. When it's off, scrub money\n // values to null so the backend's validatePurchaseTotal stays a no-op\n // and inventory rows don't carry stale prices that aren't surfaced anywhere.\n totalAmount: manageAmounts && computedLineTotal.priced ? Number(computedLineTotal.sum.toFixed(2)) : null,\n notes: form.notes || null,\n items: form.items.map((line) => ({\n batchId: line.batchId,\n productId: line.productId,\n quantity: Number(line.quantity),\n buyPrice: manageAmounts && line.buyPrice !== '' ? Number(line.buyPrice) : null,\n expiryDate: line.expiryDate || null,\n lotNumber: line.lotNumber || null,\n manufactureDate: line.manufactureDate || null,\n storageUnitId: line.storageUnitId || null,\n })),\n };\n try {\n if (requireConfirmation) {\n const submitted = await submitPurchaseRequest(payload);\n showToast({ message: 'Purchase request submitted for approval.', type: 'success' });\n navigate(`/ui/medplat/manage/pm/purchase-requests/${submitted?.id ?? form.id}`);\n } else {\n await createPurchase(payload);\n showToast({ message: 'Purchase recorded.', type: 'success' });\n navigate('/ui/medplat/manage/pm/purchases');\n }\n } catch (err) {\n setError(err?.response?.data?.message\n ?? err?.message\n ?? (requireConfirmation ? 'Failed to submit purchase request' : 'Failed to record purchase'));\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={'update.manage.productManagement.purchase'}\n title={requireConfirmation\n ? \"You don’t have permission to submit purchase requests\"\n : \"You don’t have permission to record purchases\"}>\n <PmPageShell\n title={requireConfirmation ? 'Submit Purchase Request' : 'Record Purchase'}\n subtitle={requireConfirmation\n ? 'Purchases need approval before stock is added. Submit the request and wait for a manager to confirm.'\n : 'Each item creates a batch that you can track and a record in your ledger.'}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\"\n onClick={() => navigate(requireConfirmation\n ? '/ui/medplat/manage/pm/purchase-requests'\n : '/ui/medplat/manage/pm/purchases')}>Cancel</Button>\n <Button variant=\"contained\" startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}>{requireConfirmation ? 'Submit Request' : 'Save'}</Button>\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n {/* ---------- Ownership ---------- */}\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Purchase for\"\n hint=\"Choose who will own this stock: yourself or a facility.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <FormControl required fullWidth size=\"small\"\n error={fieldErrors.show('ownerType') && !!headerErrors.ownerType}>\n <InputLabel>Purchase Type</InputLabel>\n <Select label=\"Purchase Type\" value={form.ownerType}\n onChange={(e) => setForm({ ...form, ownerType: e.target.value, ownerId: '' })}\n onBlur={fieldErrors.onBlur('ownerType')}>\n {PURCHASE_TYPES.map((t) => (\n <MenuItem key={t.value} value={t.value}>{t.label}</MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {fieldErrors.show('ownerType') && headerErrors.ownerType\n ? headerErrors.ownerType\n : 'Personal stock vs. facility stock'}\n </FormHelperText>\n </FormControl>\n </Grid>\n {form.ownerType === 'FACILITY' ? (\n <Grid item xs={12} md={4}>\n <FacilityPicker\n required\n value={form.ownerId}\n onChange={(id) => setForm({ ...form, ownerId: id })}\n onBlur={fieldErrors.onBlur('ownerId')}\n error={fieldErrors.show('ownerId') && !!headerErrors.ownerId}\n helperText={\n fieldErrors.show('ownerId') && headerErrors.ownerId\n ? headerErrors.ownerId\n : 'Search for the facility.'\n }\n />\n </Grid>\n ) : null}\n </Grid>\n </CardContent>\n </Card>\n\n {/* ---------- Existing inventory for the picked owner ---------- */}\n {(form.ownerType === 'USER' || (form.ownerType === 'FACILITY' && form.ownerId)) ? (\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Context\"\n title=\"Existing inventory\"\n hint={form.ownerType === 'USER'\n ? 'Your current stock.'\n : 'What this facility already has, including where it is stored.'}\n sx={{ mb: 1.5 }}\n />\n {existingInventory.length === 0 ? (\n <Stack direction=\"row\" spacing={1.25} alignItems=\"center\" sx={{ py: 1.5, color: 'text.secondary' }}>\n <Inventory2RoundedIcon fontSize=\"small\" />\n <Typography variant=\"body2\">No active stock yet for this owner.</Typography>\n </Stack>\n ) : (\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell align=\"right\">Remaining</TableCell>\n <TableCell>Expiry</TableCell>\n {form.ownerType === 'FACILITY' ? <TableCell>Storage</TableCell> : null}\n <TableCell>Status</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {existingInventory.map((b) => (\n <TableRow key={b.id}>\n <TableCell sx={{ fontWeight: 700 }}>{b.productName}</TableCell>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: '0.78rem' }}>{b.lotNumber || '—'}</TableCell>\n <TableCell align=\"right\" sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 700 }}>\n {b.qtyRemaining}\n </TableCell>\n <TableCell>{b.expiryDate ? formatDate(b.expiryDate) : '—'}</TableCell>\n {form.ownerType === 'FACILITY' ? (\n <TableCell>{b.storageUnitName || '—'}</TableCell>\n ) : null}\n <TableCell>\n <Chip size=\"small\" label={labelOf(BATCH_STATUSES, b.status)} color={statusColor(b.status)} />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n )}\n </CardContent>\n </Card>\n ) : null}\n\n {/* ---------- Lines ---------- */}\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <Stack direction=\"row\" justifyContent=\"space-between\" alignItems=\"flex-start\" sx={{ mb: 2 }}>\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item creates one batch in your inventory and a receipt record.\"\n />\n <Button variant=\"outlined\" startIcon={<AddRoundedIcon />} onClick={addLine}>Add Line</Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const lerr = lineErrors[idx] || {};\n const product = products.find((p) => p.id === line.productId);\n const expiryRequired = product?.hasExpiry === true;\n const storageRequired = product?.needsStorageUnit === true && form.ownerType === 'FACILITY';\n return (\n <Card key={line.key} variant=\"outlined\" sx={{ p: 2, borderRadius: 3 }}>\n <Grid container spacing={2}>\n <Grid item xs={12} md={3}>\n <FormControl required fullWidth size=\"small\"\n error={fieldErrors.show(`items.${idx}.productId`) && !!lerr.productId}>\n <InputLabel>Product</InputLabel>\n <Select label=\"Product\" value={line.productId}\n onChange={(e) => patchLine(idx, 'productId', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.productId`)}>\n {products.map((p) => <MenuItem key={p.id} value={p.id}>{p.name}</MenuItem>)}\n </Select>\n {fieldErrors.show(`items.${idx}.productId`) && lerr.productId\n ? <FormHelperText>{lerr.productId}</FormHelperText> : null}\n </FormControl>\n </Grid>\n <Grid item xs={6} md={1}>\n <TextField required fullWidth size=\"small\" type=\"number\" label=\"Qty\"\n inputProps={{ min: 1 }}\n value={line.quantity}\n onChange={(e) => patchLine(idx, 'quantity', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={fieldErrors.show(`items.${idx}.quantity`) && !!lerr.quantity}\n helperText={fieldErrors.show(`items.${idx}.quantity`) ? lerr.quantity : ''} />\n </Grid>\n {manageAmounts ? (\n <Grid item xs={6} md={2}>\n <TextField required fullWidth size=\"small\" type=\"number\" inputProps={{ step: '0.01', min: 0 }}\n label=\"Buy Price\"\n value={line.buyPrice}\n onChange={(e) => patchLine(idx, 'buyPrice', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.buyPrice`)}\n error={fieldErrors.show(`items.${idx}.buyPrice`) && !!lerr.buyPrice}\n helperText={fieldErrors.show(`items.${idx}.buyPrice`) ? lerr.buyPrice : ''} />\n </Grid>\n ) : null}\n {/* Expiry is captured at confirmation time alongside Lot —\n the requester is asking for stock they don't yet have,\n so they can't read the expiry off the packaging. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField required={expiryRequired} fullWidth size=\"small\" type=\"date\" label=\"Expiry\"\n InputLabelProps={{ shrink: true }}\n inputProps={{ min: today }}\n value={line.expiryDate}\n onChange={(e) => patchLine(idx, 'expiryDate', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.expiryDate`)}\n error={fieldErrors.show(`items.${idx}.expiryDate`) && !!lerr.expiryDate}\n helperText={\n fieldErrors.show(`items.${idx}.expiryDate`) && lerr.expiryDate\n ? lerr.expiryDate\n : (expiryRequired ? 'Required for this product' : '')\n } />\n </Grid>\n )}\n {/* Lot # is captured at confirmation time by the approver,\n who physically receives the goods. Hidden when this\n form is in request-mode so the requester doesn't waste\n time guessing a lot they don't have yet. */}\n {requireConfirmation ? null : (\n <Grid item xs={6} md={2}>\n <TextField fullWidth size=\"small\" label=\"Lot #\"\n value={line.lotNumber}\n onChange={(e) => patchLine(idx, 'lotNumber', e.target.value)} />\n </Grid>\n )}\n <Grid item xs={10} md={1}>\n <FormControl fullWidth size=\"small\"\n required={storageRequired}\n disabled={form.ownerType !== 'FACILITY' || !form.ownerId}\n error={fieldErrors.show(`items.${idx}.storageUnitId`) && !!lerr.storageUnitId}>\n <InputLabel>Storage</InputLabel>\n <Select label=\"Storage\" value={line.storageUnitId || ''}\n onChange={(e) => patchLine(idx, 'storageUnitId', e.target.value)}\n onBlur={fieldErrors.onBlur(`items.${idx}.storageUnitId`)}>\n <MenuItem value=\"\"><em>—</em></MenuItem>\n {eligibleStorageUnits.map((u) => <MenuItem key={u.id} value={u.id}>{u.unitName}</MenuItem>)}\n </Select>\n {fieldErrors.show(`items.${idx}.storageUnitId`) && lerr.storageUnitId ? (\n <FormHelperText>{lerr.storageUnitId}</FormHelperText>\n ) : storageRequired ? (\n <FormHelperText>Required for this product</FormHelperText>\n ) : null}\n </FormControl>\n </Grid>\n <Grid item xs={2} md={1} sx={{ textAlign: 'right' }}>\n <IconButton color=\"error\" onClick={() => removeLine(idx)} disabled={form.items.length === 1}>\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Summary footer (like a bill) ---------- */}\n <Card sx={{\n borderRadius: 4,\n bgcolor: 'grey.50',\n border: '1px solid',\n borderColor: 'divider',\n boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)',\n }}>\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title={manageAmounts ? 'Bill summary' : 'Receipt details'}\n hint={manageAmounts\n ? 'The total is calculated from items. Add payment mode and notes. The date is set automatically.'\n : 'Add notes about this receipt. The date is set automatically.'}\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n {manageAmounts ? (\n <Grid item xs={12} md={6}>\n <FormControl fullWidth size=\"small\">\n <InputLabel>Payment Mode</InputLabel>\n <Select label=\"Payment Mode\" value={form.paymentMode || ''}\n onChange={(e) => setForm({ ...form, paymentMode: e.target.value })}>\n <MenuItem value=\"\"><em>—</em></MenuItem>\n {PAYMENT_MODES.map((m) => <MenuItem key={m.value} value={m.value}>{m.label}</MenuItem>)}\n </Select>\n </FormControl>\n </Grid>\n ) : null}\n <Grid item xs={12} md={manageAmounts ? 6 : 12}>\n <TextField fullWidth size=\"small\" label=\"Notes\" value={form.notes}\n onChange={(e) => setForm({ ...form, notes: e.target.value })} />\n </Grid>\n </Grid>\n\n {manageAmounts ? (\n <>\n <Divider sx={{ my: 2 }} />\n\n <Stack direction=\"row\" justifyContent=\"flex-end\" alignItems=\"baseline\" spacing={2}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Total ({form.items.length} {form.items.length === 1 ? 'line' : 'lines'})\n </Typography>\n <Typography variant=\"h4\" sx={{ fontWeight: 800, fontVariantNumeric: 'tabular-nums' }}>\n {computedLineTotal.priced ? formatNumber(computedLineTotal.sum) : '—'}\n </Typography>\n </Stack>\n {!computedLineTotal.priced ? (\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', textAlign: 'right' }}>\n Add a Buy Price on at least one line to compute the total.\n </Typography>\n ) : null}\n </>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PurchaseRequestDetailPage-C7aAwEqw.js","names":[],"sources":["../src/views/PurchaseRequestDetailPage.jsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Chip,\n Divider,\n Stack,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TextField,\n Typography,\n} from '@mui/material';\nimport { alpha } from '@mui/material/styles';\nimport RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';\nimport ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';\nimport CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';\nimport CancelRoundedIcon from '@mui/icons-material/CancelRounded';\nimport DoNotDisturbRoundedIcon from '@mui/icons-material/DoNotDisturbRounded';\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 { PmFormSection, PmMoney, PmRef } from '@pm/components/PmTable';\nimport { usePermissionedEffect, usePmPermissions } from '@pm/utils/permissions';\nimport {\n cancelPurchaseRequest,\n decidePurchaseRequest,\n getPurchaseRequest,\n} from '@pm/services/pmApi';\nimport { formatDate, formatDateTime } from '@pm/utils/format';\nimport { usePmSettings } from '@pm/utils/settingsStore';\n\nfunction statusChipColor(status) {\n switch (status) {\n case 'PENDING': return 'info';\n case 'APPROVED': return 'success';\n case 'PARTIALLY_APPROVED': return 'warning';\n case 'REJECTED': return 'error';\n case 'CANCELLED': return 'default';\n default: return 'default';\n }\n}\n\nfunction prettyStatus(status) {\n if (!status) return '';\n return status\n .toLowerCase()\n .split('_')\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join(' ');\n}\n\nexport default function PurchaseRequestDetailPage() {\n const navigate = useNavigate();\n const { requestId } = useParams();\n const perms = usePmPermissions();\n const canDecide = perms.has('approve.manage.productManagement.purchase');\n const settings = usePmSettings();\n const manageAmounts = settings?.managePurchaseAmounts !== false;\n\n const [request, setRequest] = useState(null);\n const [loading, setLoading] = useState(true);\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState('');\n // Per-line approved quantities the approver is currently editing. Keyed by\n // item.id so the controls stay decoupled from order in the source list.\n const [approvedDraft, setApprovedDraft] = useState({});\n // Per-line lot numbers the approver fills at decision time — the requester\n // doesn't have the goods yet, so they don't know what lot will be shipped.\n // The approver enters this when confirming receipt.\n const [lotDraft, setLotDraft] = useState({});\n // Same logic for expiry — the requester can't read the expiry off a vial\n // they don't have. Captured by the approver.\n const [expiryDraft, setExpiryDraft] = useState({});\n const [decisionNotes, setDecisionNotes] = useState('');\n\n async function load() {\n setLoading(true);\n setError('');\n try {\n const data = await getPurchaseRequest(requestId);\n setRequest(data);\n // Seed the draft with full approval — the typical case is \"approve everything\".\n const draft = {};\n const lots = {};\n const expiries = {};\n (data?.items ?? []).forEach((it) => {\n draft[it.id] = String(it.approvedQuantity != null ? it.approvedQuantity : it.requestedQuantity);\n lots[it.id] = it.lotNumber ?? '';\n // Expiry from the existing item, normalized to yyyy-MM-dd for the date input.\n expiries[it.id] = it.expiryDate ? new Date(it.expiryDate).toISOString().substring(0, 10) : '';\n });\n setApprovedDraft(draft);\n setLotDraft(lots);\n setExpiryDraft(expiries);\n setDecisionNotes(data?.decisionNotes ?? '');\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to load purchase request');\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect(\n ['view.manage.productManagement.purchase', 'approve.manage.productManagement.purchase'],\n () => { void load(); },\n [requestId],\n );\n\n const isPending = request?.status === 'PENDING';\n\n // Sum the *approved* dollars for the in-flight decision so the approver\n // can see the cost of what they're about to confirm.\n const approvedTotal = useMemo(() => {\n if (!request || !manageAmounts) return null;\n let total = 0;\n let priced = false;\n (request.items ?? []).forEach((it) => {\n const qty = Number(approvedDraft[it.id] ?? 0);\n if (!Number.isFinite(qty) || qty <= 0) return;\n if (it.buyPrice == null) return;\n total += Number(it.buyPrice) * qty;\n priced = true;\n });\n return priced ? total : null;\n }, [request, approvedDraft, manageAmounts]);\n\n function patchDraft(itemId, value) {\n setApprovedDraft((d) => ({ ...d, [itemId]: value }));\n }\n\n function patchLot(itemId, value) {\n setLotDraft((d) => ({ ...d, [itemId]: value }));\n }\n\n function patchExpiry(itemId, value) {\n setExpiryDraft((d) => ({ ...d, [itemId]: value }));\n }\n\n function setAllToRequested() {\n if (!request) return;\n const draft = {};\n request.items.forEach((it) => { draft[it.id] = String(it.requestedQuantity); });\n setApprovedDraft(draft);\n }\n\n function setAllToZero() {\n if (!request) return;\n const draft = {};\n request.items.forEach((it) => { draft[it.id] = '0'; });\n setApprovedDraft(draft);\n }\n\n // Validates each line's approved qty is an integer in [0, requestedQty].\n // Returns a per-id error map; empty means \"ready to submit\".\n const draftErrors = useMemo(() => {\n if (!request) return {};\n const errs = {};\n (request.items ?? []).forEach((it) => {\n const raw = approvedDraft[it.id];\n if (raw == null || raw === '') {\n errs[it.id] = 'Required';\n return;\n }\n const n = Number(raw);\n if (!Number.isInteger(n) || n < 0) errs[it.id] = 'Must be 0 or a positive whole number';\n else if (n > it.requestedQuantity) errs[it.id] = `At most ${it.requestedQuantity}`;\n });\n return errs;\n }, [request, approvedDraft]);\n // Lot is required on every line the approver is approving (>0). Rejected\n // lines don't need a lot since no stock lands.\n const lotErrors = useMemo(() => {\n if (!request) return {};\n const errs = {};\n (request.items ?? []).forEach((it) => {\n const qty = Number(approvedDraft[it.id] ?? 0);\n if (!Number.isFinite(qty) || qty <= 0) return;\n const lot = (lotDraft[it.id] ?? '').trim();\n if (!lot) errs[it.id] = 'Lot is required when approving this line';\n });\n return errs;\n }, [request, approvedDraft, lotDraft]);\n // Expiry mirrors lot — required for any approved line, must be in the future.\n const expiryErrors = useMemo(() => {\n if (!request) return {};\n const errs = {};\n const todayStr = new Date().toISOString().substring(0, 10);\n (request.items ?? []).forEach((it) => {\n const qty = Number(approvedDraft[it.id] ?? 0);\n if (!Number.isFinite(qty) || qty <= 0) return;\n const exp = (expiryDraft[it.id] ?? '').trim();\n if (!exp) {\n errs[it.id] = 'Expiry is required when approving this line';\n } else if (exp < todayStr) {\n errs[it.id] = 'Expiry must be today or later';\n }\n });\n return errs;\n }, [request, approvedDraft, expiryDraft]);\n const draftIsClean = Object.keys(draftErrors).length === 0\n && Object.keys(lotErrors).length === 0\n && Object.keys(expiryErrors).length === 0;\n\n async function submit() {\n if (!draftIsClean || !request) return;\n setSubmitting(true);\n setError('');\n try {\n const updated = await decidePurchaseRequest(request.id, {\n decisionNotes: decisionNotes || null,\n items: request.items.map((it) => {\n const lot = (lotDraft[it.id] ?? '').trim();\n const exp = (expiryDraft[it.id] ?? '').trim();\n return {\n itemId: it.id,\n approvedQuantity: Number(approvedDraft[it.id]),\n lotNumber: lot ? lot : null,\n expiryDate: exp ? exp : null,\n };\n }),\n });\n setRequest(updated);\n showToast({ message: `Request ${prettyStatus(updated.status).toLowerCase()}.`, type: 'success' });\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to submit decision');\n } finally {\n setSubmitting(false);\n }\n }\n\n async function cancel() {\n if (!request) return;\n setSubmitting(true);\n setError('');\n try {\n const updated = await cancelPurchaseRequest(request.id);\n setRequest(updated);\n showToast({ message: 'Request cancelled.', type: 'success' });\n } catch (err) {\n setError(err?.response?.data?.message ?? err?.message ?? 'Failed to cancel request');\n } finally {\n setSubmitting(false);\n }\n }\n\n // Coarse-grained UI states for the approver: clean PENDING + decide perm →\n // show the editable decision form. Otherwise the same table is shown\n // read-only with the historical approved quantities.\n const showDecisionForm = isPending && canDecide;\n\n return (\n <Box>\n <PmNav />\n <PmGuard anyOf={['view.manage.productManagement.purchase', 'approve.manage.productManagement.purchase']}>\n <PmPageShell\n title={request?.serialId ? `Purchase Request #${request.serialId}` : 'Purchase Request'}\n subtitle=\"Approve or reject each item. Your decision closes the entire request.\"\n chips={request?.status ? [prettyStatus(request.status)] : []}\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button variant=\"outlined\" startIcon={<ArrowBackRoundedIcon />} onClick={() => navigate('/ui/medplat/manage/pm/purchase-requests')}>Back</Button>\n <Button variant=\"outlined\" startIcon={<RefreshRoundedIcon />} onClick={() => void load()}>Refresh</Button>\n </Stack>\n }\n >\n {error ? <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert> : null}\n\n {!loading && request ? (\n <>\n {/* ---------- Summary ---------- */}\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <Stack direction={{ xs: 'column', md: 'row' }} spacing={2} alignItems={{ md: 'flex-start' }}>\n <Box sx={{ flexGrow: 1, minWidth: 0 }}>\n <Stack direction=\"row\" alignItems=\"center\" spacing={1.5} sx={{ mb: 0.5 }}>\n <PmRef id={request.serialId} />\n <Chip size=\"small\" label={prettyStatus(request.status)} color={statusChipColor(request.status)} />\n <Typography variant=\"caption\" color=\"text.secondary\">{request.ownerType}</Typography>\n </Stack>\n <Typography variant=\"h6\" sx={{ fontWeight: 800 }}>\n {request.ownerName || (request.ownerType === 'USER' ? 'Personal' : 'Facility')}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block' }}>\n For purchase date {formatDate(request.purchaseDate)}\n </Typography>\n </Box>\n <Stack spacing={0.25} sx={{ minWidth: 260 }}>\n <Row label=\"Requested by\">{request.requestedByName || `#${request.requestedBy}`}</Row>\n <Row label=\"Requested on\">{formatDateTime(request.requestedOn)}</Row>\n {request.decidedBy ? (\n <>\n <Row label=\"Decided by\">{request.decidedByName || `#${request.decidedBy}`}</Row>\n <Row label=\"Decided on\">{formatDateTime(request.decidedOn)}</Row>\n </>\n ) : null}\n {request.paymentMode ? <Row label=\"Payment\">{request.paymentMode}</Row> : null}\n </Stack>\n </Stack>\n {request.notes ? (\n <>\n <Divider sx={{ my: 1.5 }} />\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', mb: 0.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>\n Requester notes\n </Typography>\n <Typography variant=\"body2\">{request.notes}</Typography>\n </>\n ) : null}\n {request.decisionNotes ? (\n <>\n <Divider sx={{ my: 1.5 }} />\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ display: 'block', mb: 0.5, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.6 }}>\n Decision notes\n </Typography>\n <Typography variant=\"body2\">{request.decisionNotes}</Typography>\n </>\n ) : null}\n </CardContent>\n </Card>\n\n {/* ---------- Line items ---------- */}\n <Card sx={{ borderRadius: 4, mb: 2, border: '1px solid', borderColor: 'divider', boxShadow: '0 12px 32px rgba(15, 23, 42, 0.045)' }}>\n <CardContent sx={{ p: 2.5 }}>\n <Stack direction={{ xs: 'column', md: 'row' }} alignItems={{ md: 'flex-end' }} justifyContent=\"space-between\" sx={{ mb: 1.5 }}>\n <PmFormSection\n eyebrow=\"Lines\"\n title={showDecisionForm ? 'Decide per-line quantities' : 'Requested items'}\n hint={showDecisionForm\n ? 'Enter 0 to reject an item, or less than requested to partially approve.'\n : null}\n />\n {showDecisionForm ? (\n <Stack direction=\"row\" spacing={1}>\n <Button size=\"small\" variant=\"outlined\" onClick={setAllToRequested}>Approve all</Button>\n <Button size=\"small\" variant=\"outlined\" color=\"error\" onClick={setAllToZero}>Reject all</Button>\n </Stack>\n ) : null}\n </Stack>\n\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Product</TableCell>\n <TableCell>Lot</TableCell>\n <TableCell>Expiry</TableCell>\n {manageAmounts ? <TableCell align=\"right\">Buy Price</TableCell> : null}\n <TableCell align=\"right\">Requested</TableCell>\n <TableCell align=\"right\">{showDecisionForm ? 'Approve' : 'Approved'}</TableCell>\n {request.ownerType === 'FACILITY' ? <TableCell>Storage</TableCell> : null}\n </TableRow>\n </TableHead>\n <TableBody>\n {request.items.map((it) => {\n const lineErr = draftErrors[it.id];\n const lotErr = lotErrors[it.id];\n const expiryErr = expiryErrors[it.id];\n const approveQty = Number(approvedDraft[it.id] ?? 0);\n const lotInputActive = showDecisionForm && Number.isFinite(approveQty) && approveQty > 0;\n return (\n <TableRow key={it.id}>\n <TableCell sx={{ fontWeight: 700 }}>{it.productName}</TableCell>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: '0.78rem' }}>\n {showDecisionForm ? (\n <TextField\n size=\"small\"\n placeholder=\"Enter lot\"\n value={lotDraft[it.id] ?? ''}\n onChange={(e) => patchLot(it.id, e.target.value)}\n disabled={!lotInputActive}\n error={!!lotErr}\n helperText={lotErr || (lotInputActive ? '' : 'Approve qty > 0 to enter lot')}\n inputProps={{ style: { fontFamily: 'monospace', fontSize: '0.78rem' } }}\n sx={{ minWidth: 140 }}\n />\n ) : (it.lotNumber || '—')}\n </TableCell>\n <TableCell>\n {showDecisionForm ? (\n <TextField\n size=\"small\"\n type=\"date\"\n InputLabelProps={{ shrink: true }}\n value={expiryDraft[it.id] ?? ''}\n onChange={(e) => patchExpiry(it.id, e.target.value)}\n disabled={!lotInputActive}\n error={!!expiryErr}\n helperText={expiryErr || (lotInputActive ? '' : '')}\n sx={{ minWidth: 150 }}\n />\n ) : (it.expiryDate ? formatDate(it.expiryDate) : '—')}\n </TableCell>\n {manageAmounts ? (\n <TableCell align=\"right\">\n {it.buyPrice != null ? <PmMoney value={it.buyPrice} bold={false} /> : <Typography variant=\"caption\" color=\"text.disabled\">—</Typography>}\n </TableCell>\n ) : null}\n <TableCell align=\"right\" sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 700 }}>\n {it.requestedQuantity}\n </TableCell>\n <TableCell align=\"right\">\n {showDecisionForm ? (\n <TextField\n size=\"small\"\n type=\"number\"\n inputProps={{ min: 0, max: it.requestedQuantity, style: { textAlign: 'right' } }}\n value={approvedDraft[it.id] ?? ''}\n onChange={(e) => patchDraft(it.id, e.target.value)}\n error={!!lineErr}\n helperText={lineErr || ''}\n sx={{ maxWidth: 110 }}\n />\n ) : (\n <Typography sx={{ fontVariantNumeric: 'tabular-nums', fontWeight: 700 }}>\n {it.approvedQuantity == null ? '—' : it.approvedQuantity}\n </Typography>\n )}\n </TableCell>\n {request.ownerType === 'FACILITY' ? (\n <TableCell>{it.storageUnitName || '—'}</TableCell>\n ) : null}\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n\n {showDecisionForm ? (\n <>\n <Divider sx={{ my: 2 }} />\n <Stack direction={{ xs: 'column', md: 'row' }} spacing={2} alignItems={{ md: 'flex-start' }}>\n <TextField\n fullWidth\n size=\"small\"\n label=\"Decision notes (optional)\"\n placeholder=\"Rationale shown to the requester\"\n value={decisionNotes}\n onChange={(e) => setDecisionNotes(e.target.value)}\n multiline\n minRows={2}\n />\n {manageAmounts && approvedTotal != null ? (\n <Box sx={(t) => ({\n minWidth: 220,\n p: 1.5,\n borderRadius: 3,\n border: '1px solid',\n borderColor: 'divider',\n bgcolor: alpha(t.palette.primary.main, 0.04),\n textAlign: 'right',\n })}>\n <Typography variant=\"caption\" color=\"text.secondary\"\n sx={{ display: 'block', fontWeight: 800, textTransform: 'uppercase', letterSpacing: 0.6 }}>\n Total if approved\n </Typography>\n <PmMoney value={Number(approvedTotal.toFixed(2))} size=\"big\" />\n </Box>\n ) : null}\n </Stack>\n <Stack direction=\"row\" spacing={1} justifyContent=\"flex-end\" sx={{ mt: 2 }}>\n <Button\n variant=\"outlined\" color=\"error\" startIcon={<CancelRoundedIcon />}\n disabled={submitting}\n onClick={() => { setAllToZero(); }}\n >\n Set all to 0\n </Button>\n <Button\n variant=\"contained\" startIcon={<CheckCircleRoundedIcon />}\n disabled={submitting || !draftIsClean}\n onClick={() => void submit()}\n >\n {submitting ? 'Submitting…' : 'Submit Decision'}\n </Button>\n </Stack>\n </>\n ) : null}\n\n {/* Requester withdrawal — only on PENDING + only when the\n current user is the original requester. The backend\n enforces this too; the UI just hides the button when it's\n not applicable. */}\n {isPending && !canDecide ? (\n <Stack direction=\"row\" spacing={1} justifyContent=\"flex-end\" sx={{ mt: 2 }}>\n <Button\n variant=\"outlined\" color=\"error\" startIcon={<DoNotDisturbRoundedIcon />}\n disabled={submitting} onClick={() => void cancel()}\n >\n {submitting ? 'Cancelling…' : 'Cancel Request'}\n </Button>\n </Stack>\n ) : null}\n\n {request.approvedPurchaseId ? (\n <Alert severity=\"success\" sx={{ mt: 2 }}>\n Stock was added to inventory under purchase id <strong>{request.approvedPurchaseId}</strong>.\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n </>\n ) : null}\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n\nfunction Row({ label, children }) {\n return (\n <Stack direction=\"row\" spacing={1} sx={{ minWidth: 0 }}>\n <Typography variant=\"caption\" color=\"text.secondary\" sx={{ minWidth: 100, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.5 }}>\n {label}\n </Typography>\n <Typography variant=\"caption\" sx={{ minWidth: 0, fontWeight: 700 }}>\n {children}\n </Typography>\n </Stack>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SaleCreatePage-DP1Qmltu.js","names":[],"sources":["../src/views/SaleCreatePage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { useNavigate, useParams } from \"react-router-dom\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n Alert,\n Box,\n Button,\n Card,\n CardContent,\n Divider,\n FormControl,\n FormHelperText,\n Grid,\n IconButton,\n InputAdornment,\n InputLabel,\n MenuItem,\n Select,\n Stack,\n TextField,\n Typography,\n} from \"@mui/material\";\nimport AddRoundedIcon from \"@mui/icons-material/AddRounded\";\nimport DeleteOutlineRoundedIcon from \"@mui/icons-material/DeleteOutlineRounded\";\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 { usePermissionedEffect, usePmPermissions } from \"@pm/utils/permissions\";\nimport {\n createSale,\n getMemberOutstandingDues,\n getProducts,\n listBatches,\n} from \"@pm/services/pmApi\";\nimport MemberProfileCard from \"@pm/components/MemberProfileCard\";\nimport { PAYMENT_MODES } from \"@pm/utils/constants\";\nimport { formatNumber, isPastExpiry } from \"@pm/utils/format\";\nimport { currencySymbol } from \"@pm/utils/currency\";\nimport { usePmSettings } from \"@pm/utils/settingsStore\";\nimport { PmFormSection } from \"@pm/components/PmTable\";\nimport {\n isClean,\n validateInteger,\n validatePrice,\n validateRequired,\n} from \"@pm/utils/validation\";\nimport { useFieldErrors } from \"@pm/utils/useFieldErrors\";\n\nfunction emptyLine() {\n return { key: uuidv4(), batchId: \"\", quantity: 1, salePrice: \"\" };\n}\n\nexport default function SaleCreatePage() {\n const navigate = useNavigate();\n const { memberId } = useParams();\n const settings = usePmSettings();\n const cur = currencySymbol(settings?.currencyCode);\n const [batches, setBatches] = useState([]);\n const [products, setProducts] = useState([]);\n // Map of memberId -> previousDue. The dues endpoint only returns members\n // with non-zero dues so the map stays small. Looked up by route memberId.\n const [duesByMember, setDuesByMember] = useState({});\n // Cached after MemberProfileCard fetches — used by the warning Alert that\n // references the member by name when remaining dues will be recorded.\n const [memberName, setMemberName] = useState(\"\");\n const [error, setError] = useState(\"\");\n const perms = usePmPermissions();\n // When the operator can't see dues, previousDue gracefully degrades to 0\n // instead of populating duesByMember from a 403'ing call.\n const canSeeDues = perms.has(\"view.manage.productManagement.dues\");\n const fieldErrors = useFieldErrors();\n const today = new Date().toISOString().substring(0, 10);\n const [form, setForm] = useState({\n id: uuidv4(),\n saleDate: today,\n paymentMode: \"CASH\",\n collectingNow: \"\",\n notes: \"\",\n items: [emptyLine()],\n });\n\n usePermissionedEffect(\n \"update.manage.productManagement.sale\",\n () => {\n async function load() {\n try {\n // Members are picked via async search now — no upfront load. Dues\n // come from the (aggregated) backend query and get attached to a\n // picked member by id.\n const [b, p, dues] = await Promise.all([\n listBatches(),\n getProducts().catch(() => []),\n // Silent degrade when the operator lacks the dues permission —\n // previousDue will stay 0 and the sale itself still works.\n canSeeDues\n ? getMemberOutstandingDues().catch(() => [])\n : Promise.resolve([]),\n ]);\n setBatches(b);\n setProducts(p);\n setDuesByMember(\n Object.fromEntries(\n (dues ?? []).map((d) => [d.memberId, Number(d.totalDue)]),\n ),\n );\n } catch (err) {\n setError(\n err?.response?.data?.message ??\n err?.message ??\n \"Failed to load reference data\",\n );\n }\n }\n void load();\n },\n [],\n );\n\n const batchById = (id) => batches.find((b) => b.id === id);\n\n // Batches that can be sold: stock on hand, not quarantined, not past expiry, and\n // the underlying product is not flagged as not-for-sale. Computed once per render.\n const sellableBatches = useMemo(() => {\n const notForSaleProductIds = new Set(\n products.filter((p) => p.notForSale === true).map((p) => p.id),\n );\n return batches.filter((b) => {\n if (b.qtyRemaining <= 0) return false;\n if (b.isQuarantined) return false;\n if (notForSaleProductIds.has(b.productId)) return false;\n // A batch is sellable on the day of its printed expiry; only the\n // following day counts as expired (matches the unit packaging rule).\n if (isPastExpiry(b.expiryDate)) return false;\n return true;\n });\n }, [batches, products]);\n\n function patchLine(idx, k, v) {\n setForm((f) => {\n const items = f.items.slice();\n items[idx] = { ...items[idx], [k]: v };\n return { ...f, items };\n });\n }\n\n // -------- Money math --------\n const saleAmount = useMemo(\n () =>\n form.items.reduce((sum, line) => {\n if (!line.salePrice || !line.quantity) return sum;\n return sum + Number(line.salePrice) * Number(line.quantity);\n }, 0),\n [form.items],\n );\n\n const previousDue = Number(memberId ? (duesByMember[memberId] ?? 0) : 0);\n const collectedRaw = Number(form.collectingNow || 0);\n // Clip the collected amount at the current sale's total — anything more would\n // need to be applied to the member's old dues, which is a separate Payment flow.\n const collected = Math.min(Math.max(collectedRaw, 0), saleAmount);\n const saleDue = Math.max(saleAmount - collected, 0);\n const remainingDue = saleDue + previousDue;\n\n // -------- Validation --------\n const headerErrors = {\n saleDate: validateRequired(form.saleDate, \"Sale date\"),\n member: memberId ? \"\" : \"No member selected\",\n };\n // Collecting Now: optional; if filled, must be a non-negative number ≤ sale amount.\n const collectingErr =\n form.collectingNow === \"\" ? \"\" : validatePrice(form.collectingNow);\n if (\n !collectingErr &&\n form.collectingNow !== \"\" &&\n Number(form.collectingNow) > saleAmount\n ) {\n headerErrors.collectingNow = `Cannot collect more than the sale amount (${formatNumber(saleAmount)}). Settle old dues via Payments.`;\n } else if (collectingErr) {\n headerErrors.collectingNow = collectingErr;\n }\n\n const sellableBatchIds = new Set(sellableBatches.map((b) => b.id));\n const lineErrors = form.items.map((line) => {\n const batch = batchById(line.batchId);\n const qty = validateInteger(line.quantity, \"Quantity\", {\n required: true,\n min: 1,\n });\n let qtyErr = qty;\n if (!qtyErr && batch && Number(line.quantity) > batch.qtyRemaining) {\n qtyErr = `Only ${batch.qtyRemaining} available in selected batch`;\n }\n let batchErr = validateRequired(line.batchId, \"Batch\");\n if (!batchErr && line.batchId && !sellableBatchIds.has(line.batchId)) {\n batchErr =\n \"Batch is not available for sale (expired, quarantined, out of stock, or not-for-sale).\";\n }\n return {\n batchId: batchErr,\n quantity: qtyErr,\n salePrice:\n validatePrice(line.salePrice) ||\n validateRequired(line.salePrice, \"Unit price\"),\n };\n });\n const formIsValid =\n isClean(headerErrors) &&\n lineErrors.every(isClean) &&\n form.items.length > 0 &&\n saleAmount > 0;\n\n async function save() {\n if (!formIsValid) {\n fieldErrors.markAllTouched();\n setError(\"Please fix the highlighted fields and try again.\");\n return;\n }\n try {\n await createSale({\n id: form.id,\n saleDate: form.saleDate,\n memberId,\n paymentMode: form.paymentMode || null,\n totalAmount: Number(saleAmount.toFixed(2)),\n paidAmount: Number(collected.toFixed(2)),\n dueAmount: Number(saleDue.toFixed(2)),\n notes: form.notes || null,\n items: form.items.map((l) => {\n const batch = batchById(l.batchId);\n return {\n batchId: l.batchId,\n productId: batch?.productId,\n quantity: Number(l.quantity),\n salePrice: Number(l.salePrice),\n };\n }),\n });\n showToast({ message: \"Sale recorded.\", type: \"success\" });\n navigate(\"/ui/medplat/manage/pm/sales\");\n } catch (err) {\n setError(\n err?.response?.data?.message ?? err?.message ?? \"Failed to record sale\",\n );\n }\n }\n\n return (\n <Box>\n <PmNav />\n <PmGuard\n perm={\"update.manage.productManagement.sale\"}\n title=\"You don’t have permission to record sales\"\n >\n <PmPageShell\n title=\"Record Sale\"\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n onClick={() => navigate(\"/ui/medplat/manage/pm/sales\")}\n >\n Cancel\n </Button>\n <Button\n variant=\"contained\"\n startIcon={<SaveRoundedIcon />}\n disabled={!formIsValid}\n onClick={() => void save()}\n >\n Save\n </Button>\n </Stack>\n }\n >\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n\n {/* ---------- Member ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n mb: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 1\"\n title=\"Customer\"\n hint=\"The customer is set from the link you opened. Their balance shows below.\"\n sx={{ mb: 2 }}\n />\n {memberId ? (\n <MemberProfileCard\n memberId={memberId}\n previousDue={previousDue}\n currencySymbol={cur}\n onLoad={(p) => setMemberName(p?.name ?? \"\")}\n />\n ) : (\n <Alert severity=\"warning\">No member selected</Alert>\n )}\n </CardContent>\n </Card>\n\n {/* ---------- Line items ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n mb: 2,\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"flex-start\"\n sx={{ mb: 2 }}\n >\n <PmFormSection\n eyebrow=\"Step 2\"\n title=\"Line items\"\n hint=\"Each item comes from a batch. Buy price is shown as a cost reference.\"\n />\n <Button\n variant=\"outlined\"\n startIcon={<AddRoundedIcon />}\n onClick={() =>\n setForm({ ...form, items: [...form.items, emptyLine()] })\n }\n >\n Add Line\n </Button>\n </Stack>\n <Divider sx={{ mb: 2 }} />\n <Stack spacing={2}>\n {form.items.map((line, idx) => {\n const batch = batchById(line.batchId);\n const lerr = lineErrors[idx] || {};\n const subtotal =\n line.salePrice && line.quantity\n ? Number(line.salePrice) * Number(line.quantity)\n : 0;\n return (\n <Card\n key={line.key}\n variant=\"outlined\"\n sx={{ p: 2, borderRadius: 3 }}\n >\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs={12} md={5}>\n <FormControl\n required\n fullWidth\n size=\"small\"\n error={\n fieldErrors.show(`items.${idx}.batchId`) &&\n !!lerr.batchId\n }\n >\n <InputLabel>Batch</InputLabel>\n <Select\n label=\"Batch\"\n value={line.batchId}\n onChange={(e) =>\n patchLine(idx, \"batchId\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(\n `items.${idx}.batchId`,\n )}\n >\n {sellableBatches.map((b) => (\n <MenuItem key={b.id} value={b.id}>\n {b.productName} · lot {b.lotNumber || \"—\"} ·{\" \"}\n {b.qtyRemaining} left\n </MenuItem>\n ))}\n </Select>\n <FormHelperText>\n {fieldErrors.show(`items.${idx}.batchId`) &&\n lerr.batchId\n ? lerr.batchId\n : batch?.buyPrice != null\n ? `Buy price: ${cur}${formatNumber(batch.buyPrice)} / unit`\n : \" \"}\n </FormHelperText>\n </FormControl>\n </Grid>\n <Grid item xs={6} md={2}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"number\"\n label=\"Qty\"\n inputProps={{ min: 1, max: batch?.qtyRemaining }}\n value={line.quantity}\n onChange={(e) =>\n patchLine(idx, \"quantity\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(`items.${idx}.quantity`)}\n error={\n fieldErrors.show(`items.${idx}.quantity`) &&\n !!lerr.quantity\n }\n helperText={\n fieldErrors.show(`items.${idx}.quantity`)\n ? lerr.quantity\n : \"\"\n }\n />\n </Grid>\n <Grid item xs={6} md={2}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"number\"\n inputProps={{ step: \"0.01\", min: 0 }}\n label=\"Unit Price\"\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n {cur}\n </InputAdornment>\n ),\n }}\n value={line.salePrice}\n onChange={(e) =>\n patchLine(idx, \"salePrice\", e.target.value)\n }\n onBlur={fieldErrors.onBlur(\n `items.${idx}.salePrice`,\n )}\n error={\n fieldErrors.show(`items.${idx}.salePrice`) &&\n !!lerr.salePrice\n }\n helperText={\n fieldErrors.show(`items.${idx}.salePrice`)\n ? lerr.salePrice\n : \"\"\n }\n />\n </Grid>\n <Grid item xs={6} md={2} sx={{ textAlign: \"right\" }}>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{ display: \"block\" }}\n >\n Subtotal\n </Typography>\n <Typography\n variant=\"body1\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n }}\n >\n {cur}\n {formatNumber(subtotal)}\n </Typography>\n </Grid>\n <Grid item xs={6} md={1} sx={{ textAlign: \"right\" }}>\n <IconButton\n color=\"error\"\n onClick={() =>\n setForm({\n ...form,\n items: form.items.filter((_, i) => i !== idx),\n })\n }\n disabled={form.items.length === 1}\n >\n <DeleteOutlineRoundedIcon />\n </IconButton>\n </Grid>\n </Grid>\n </Card>\n );\n })}\n </Stack>\n </CardContent>\n </Card>\n\n {/* ---------- Bill Summary footer ---------- */}\n <Card\n sx={{\n borderRadius: 4,\n bgcolor: \"grey.50\",\n border: \"1px solid\",\n borderColor: \"divider\",\n boxShadow: \"0 12px 32px rgba(15, 23, 42, 0.045)\",\n }}\n >\n <CardContent sx={{ p: 2.5 }}>\n <PmFormSection\n eyebrow=\"Step 3\"\n title=\"Bill summary\"\n hint=\"Shows totals, previous balance, and what you're collecting. Any remainder becomes a note of what they owe.\"\n sx={{ mb: 2 }}\n />\n <Grid container spacing={2}>\n <Grid item xs={12} md={4}>\n <TextField\n required\n fullWidth\n size=\"small\"\n type=\"date\"\n label=\"Date\"\n InputLabelProps={{ shrink: true }}\n value={form.saleDate}\n onChange={(e) =>\n setForm({ ...form, saleDate: e.target.value })\n }\n onBlur={fieldErrors.onBlur(\"saleDate\")}\n error={\n fieldErrors.show(\"saleDate\") && !!headerErrors.saleDate\n }\n helperText={\n fieldErrors.show(\"saleDate\") ? headerErrors.saleDate : \"\"\n }\n />\n </Grid>\n <Grid item xs={12} md={4}>\n <FormControl fullWidth size=\"small\">\n <InputLabel>Payment Mode</InputLabel>\n <Select\n label=\"Payment Mode\"\n value={form.paymentMode || \"\"}\n onChange={(e) =>\n setForm({ ...form, paymentMode: e.target.value })\n }\n >\n <MenuItem value=\"\">\n <em>—</em>\n </MenuItem>\n {PAYMENT_MODES.map((m) => (\n <MenuItem key={m.value} value={m.value}>\n {m.label}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12} md={4}>\n <TextField\n fullWidth\n size=\"small\"\n label=\"Notes\"\n value={form.notes}\n onChange={(e) =>\n setForm({ ...form, notes: e.target.value })\n }\n />\n </Grid>\n </Grid>\n\n <Divider sx={{ my: 2 }} />\n\n <Stack spacing={1.25}>\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Sale Amount ({form.items.length}{\" \"}\n {form.items.length === 1 ? \"line\" : \"lines\"})\n </Typography>\n <Typography\n variant=\"h5\"\n sx={{ fontWeight: 800, fontVariantNumeric: \"tabular-nums\" }}\n >\n {cur}\n {formatNumber(saleAmount)}\n </Typography>\n </Stack>\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Previous Due\n </Typography>\n <Typography\n variant=\"body1\"\n sx={{\n fontWeight: 700,\n fontVariantNumeric: \"tabular-nums\",\n color: previousDue > 0 ? \"warning.dark\" : \"text.primary\",\n }}\n >\n {cur}\n {formatNumber(previousDue)}\n </Typography>\n </Stack>\n\n <Divider sx={{ my: 0.5 }} />\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"center\"\n spacing={2}\n >\n <Typography\n variant=\"body2\"\n color=\"text.secondary\"\n sx={{ flexShrink: 0 }}\n >\n Amount Collecting Now\n </Typography>\n <TextField\n size=\"small\"\n type=\"number\"\n inputProps={{\n step: \"0.01\",\n min: 0,\n max: saleAmount,\n style: { textAlign: \"right\", fontWeight: 700 },\n }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">{cur}</InputAdornment>\n ),\n }}\n sx={{ width: 200 }}\n value={form.collectingNow}\n placeholder=\"0.00\"\n onChange={(e) =>\n setForm({ ...form, collectingNow: e.target.value })\n }\n onBlur={fieldErrors.onBlur(\"collectingNow\")}\n error={\n fieldErrors.show(\"collectingNow\") &&\n !!headerErrors.collectingNow\n }\n helperText={\n fieldErrors.show(\"collectingNow\")\n ? headerErrors.collectingNow\n : \"\"\n }\n />\n </Stack>\n\n <Stack\n direction=\"row\"\n justifyContent=\"space-between\"\n alignItems=\"baseline\"\n >\n <Typography variant=\"body2\" color=\"text.secondary\">\n Remaining Due (after this sale)\n </Typography>\n <Typography\n variant=\"h5\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n color: remainingDue > 0 ? \"warning.dark\" : \"success.dark\",\n }}\n >\n {cur}\n {formatNumber(remainingDue)}\n </Typography>\n </Stack>\n </Stack>\n\n {memberId && remainingDue > 0 ? (\n <Alert severity=\"warning\" sx={{ mt: 2 }}>\n {cur}\n {formatNumber(remainingDue)} will be recorded as pending dues\n {memberName ? (\n <>\n {\" \"}\n for <strong>{memberName}</strong>\n </>\n ) : null}\n {saleDue > 0 && previousDue > 0\n ? ` (${cur}${formatNumber(saleDue)} from this sale + ${cur}${formatNumber(previousDue)} previously outstanding).`\n : saleDue > 0\n ? \" from this sale.\"\n : \" from previously outstanding dues.\"}\n </Alert>\n ) : null}\n </CardContent>\n </Card>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SalesPage-CO3cdYX5.js","names":[],"sources":["../src/views/SalesPage.jsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport {\n Alert,\n Avatar,\n Box,\n Button,\n Chip,\n Skeleton,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from \"@mui/material\";\nimport { alpha, useTheme } from \"@mui/material/styles\";\nimport AddRoundedIcon from \"@mui/icons-material/AddRounded\";\nimport RefreshRoundedIcon from \"@mui/icons-material/RefreshRounded\";\nimport PointOfSaleRoundedIcon from \"@mui/icons-material/PointOfSaleRounded\";\nimport ReceiptLongRoundedIcon from \"@mui/icons-material/ReceiptLongRounded\";\nimport PaymentsRoundedIcon from \"@mui/icons-material/PaymentsRounded\";\nimport TrendingUpRoundedIcon from \"@mui/icons-material/TrendingUpRounded\";\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 } from \"@pm/utils/permissions\";\nimport {\n PmEmptyRow,\n PmMoney,\n PmRef,\n PmTable,\n PmTableCard,\n} from \"@pm/components/PmTable\";\nimport { getMembersByIds, listSales } from \"@pm/services/pmApi\";\nimport { formatDate, formatNumber } from \"@pm/utils/format\";\nimport { currencySymbol } from \"@pm/utils/currency\";\nimport { usePmSettings } from \"@pm/utils/settingsStore\";\n\nfunction initials(name) {\n if (!name) return \"?\";\n return name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((s) => s[0])\n .join(\"\")\n .toUpperCase();\n}\n\n// Mini KPI tile shown above the table. Compact, accent-tinted icon, big\n// number — gives the operator a \"what happened lately\" read at a glance.\nfunction SummaryTile({\n icon: Icon,\n label,\n value,\n accent = \"primary\",\n loading,\n}) {\n const theme = useTheme();\n const accentMain = theme.palette[accent]?.main ?? accent;\n const accentDark = theme.palette[accent]?.dark ?? accent;\n return (\n <Box\n sx={{\n flex: 1,\n p: 2,\n borderRadius: 3,\n border: \"1px solid\",\n borderColor: alpha(accentMain, 0.18),\n background: `linear-gradient(135deg, ${alpha(accentMain, 0.05)} 0%, ${alpha(theme.palette.background.paper, 1)} 70%)`,\n position: \"relative\",\n overflow: \"hidden\",\n }}\n >\n <Stack direction=\"row\" alignItems=\"center\" spacing={1.5}>\n <Box\n sx={{\n width: 36,\n height: 36,\n borderRadius: 2,\n display: \"grid\",\n placeItems: \"center\",\n background: `linear-gradient(135deg, ${accentMain} 0%, ${accentDark} 100%)`,\n color: \"#fff\",\n boxShadow: `0 6px 14px ${alpha(accentMain, 0.32)}`,\n }}\n >\n <Icon sx={{ fontSize: 18 }} />\n </Box>\n <Box sx={{ minWidth: 0 }}>\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n sx={{\n fontWeight: 700,\n letterSpacing: 0.6,\n textTransform: \"uppercase\",\n }}\n >\n {label}\n </Typography>\n {loading ? (\n <Skeleton width={80} height={26} />\n ) : (\n <Typography\n variant=\"h6\"\n sx={{\n fontWeight: 800,\n fontVariantNumeric: \"tabular-nums\",\n lineHeight: 1.2,\n }}\n >\n {value}\n </Typography>\n )}\n </Box>\n </Stack>\n </Box>\n );\n}\n\nexport default function SalesPage() {\n const navigate = useNavigate();\n const theme = useTheme();\n const settings = usePmSettings();\n const cur = currencySymbol(settings?.currencyCode);\n const [items, setItems] = useState([]);\n // Map of memberId -> display name, hydrated for memberIds present on the\n // loaded sales. Avoids loading the entire member directory just to render\n // the \"Customer\" column.\n const [memberNames, setMemberNames] = useState({});\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState(\"\");\n\n async function load() {\n setLoading(true);\n setError(\"\");\n try {\n const list = await listSales(0, 50);\n setItems(list);\n const ids = Array.from(\n new Set(list.map((s) => s.memberId).filter(Boolean)),\n );\n if (ids.length === 0) {\n setMemberNames({});\n } else {\n try {\n const refs = await getMembersByIds(ids);\n const map = {};\n refs.forEach((r) => {\n map[r.id] = r.name;\n });\n setMemberNames(map);\n } catch {\n setMemberNames({});\n }\n }\n } catch (err) {\n const message =\n err?.response?.data?.message ?? err?.message ?? \"Failed to load sales\";\n setError(message);\n showToast({ message, type: \"error\" });\n } finally {\n setLoading(false);\n }\n }\n\n usePermissionedEffect(\n \"view.manage.productManagement.sale\",\n () => {\n void load();\n },\n [],\n );\n\n const summary = useMemo(() => {\n const totalRevenue = items.reduce(\n (s, x) => s + Number(x.totalAmount || 0),\n 0,\n );\n const totalDue = items.reduce((s, x) => s + Number(x.dueAmount || 0), 0);\n const openSales = items.filter((x) => Number(x.dueAmount) > 0).length;\n return { totalRevenue, totalDue, openSales, count: items.length };\n }, [items]);\n\n return (\n <Box>\n <PmNav />\n <PmGuard perm={\"view.manage.productManagement.sale\"}>\n <PmPageShell\n title=\"Sales\"\n subtitle=\"Customer sales and payments. Each sale is tracked with the batches used.\"\n actions={\n <Stack direction=\"row\" spacing={1}>\n <Button\n variant=\"outlined\"\n startIcon={<RefreshRoundedIcon />}\n onClick={() => void load()}\n >\n Refresh\n </Button>\n {/* DEMO-ONLY: in production a sale is started by passing the member id\n via the URL (/sales/new/:memberId) from an outside flow. Unhide\n this button to launch the create page directly for demos. */}\n {false && (\n <Button\n variant=\"contained\"\n startIcon={<AddRoundedIcon />}\n onClick={() => navigate(\"/ui/medplat/manage/pm/sales/new\")}\n >\n New Sale\n </Button>\n )}\n </Stack>\n }\n >\n {/* ---------- Summary strip ---------- */}\n <Stack\n direction={{ xs: \"column\", md: \"row\" }}\n spacing={2}\n sx={{ mb: 2 }}\n >\n <SummaryTile\n icon={ReceiptLongRoundedIcon}\n label=\"Sales (last 50)\"\n value={summary.count}\n accent=\"primary\"\n loading={loading}\n />\n <SummaryTile\n icon={TrendingUpRoundedIcon}\n label=\"Total billed\"\n value={`${cur}${formatNumber(summary.totalRevenue)}`}\n accent=\"success\"\n loading={loading}\n />\n <SummaryTile\n icon={PaymentsRoundedIcon}\n label=\"Outstanding dues\"\n value={`${cur}${formatNumber(summary.totalDue)}`}\n accent=\"warning\"\n loading={loading}\n />\n <SummaryTile\n icon={PointOfSaleRoundedIcon}\n label=\"Open sales\"\n value={summary.openSales}\n accent=\"info\"\n loading={loading}\n />\n </Stack>\n\n {error ? (\n <Alert severity=\"error\" sx={{ mb: 2 }}>\n {error}\n </Alert>\n ) : null}\n\n {/* ---------- Table ---------- */}\n <PmTableCard\n eyebrow=\"Recent activity\"\n title=\"Sales transactions\"\n subtitle={\n loading\n ? \"Loading…\"\n : `${summary.count} ${summary.count === 1 ? \"sale\" : \"sales\"} in the last window`\n }\n >\n <PmTable dense>\n <TableHead>\n <TableRow>\n <TableCell>Ref</TableCell>\n <TableCell>Date</TableCell>\n <TableCell>Customer</TableCell>\n <TableCell>Payment</TableCell>\n <TableCell align=\"right\">Total</TableCell>\n <TableCell align=\"right\">Paid</TableCell>\n <TableCell align=\"right\">Due</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n Array.from({ length: 5 }).map((_, i) => (\n <TableRow key={i}>\n {Array.from({ length: 7 }).map((__, j) => (\n <TableCell key={j}>\n <Skeleton height={22} />\n </TableCell>\n ))}\n </TableRow>\n ))\n ) : items.length === 0 ? (\n <PmEmptyRow\n colSpan={7}\n icon={PointOfSaleRoundedIcon}\n title=\"No sales yet\"\n hint=\"Record a sale to see it here with payment details.\"\n action={\n <Button\n variant=\"contained\"\n size=\"small\"\n startIcon={<AddRoundedIcon />}\n onClick={() =>\n navigate(\"/ui/medplat/manage/pm/sales/new\")\n }\n >\n Record first sale\n </Button>\n }\n />\n ) : (\n items.map((s) => {\n const due = Number(s.dueAmount || 0);\n const isPaid = due <= 0;\n const memberName = memberNames[s.memberId];\n return (\n <TableRow key={s.id} hover>\n <TableCell>\n <PmRef id={s.serialId} />\n </TableCell>\n <TableCell>\n <Typography variant=\"body2\" sx={{ fontWeight: 700 }}>\n {formatDate(s.saleDate)}\n </Typography>\n </TableCell>\n <TableCell>\n {memberName ? (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n spacing={1.25}\n >\n <Avatar\n sx={{\n width: 28,\n height: 28,\n fontSize: 11,\n fontWeight: 700,\n bgcolor: alpha(\n theme.palette.primary.main,\n 0.12,\n ),\n color: \"primary.dark\",\n }}\n >\n {initials(memberName)}\n </Avatar>\n <Box sx={{ minWidth: 0 }}>\n <Typography\n variant=\"body2\"\n sx={{ fontWeight: 700, lineHeight: 1.2 }}\n >\n {memberName}\n </Typography>\n </Box>\n </Stack>\n ) : s.memberId ? (\n <Typography\n variant=\"caption\"\n sx={{\n fontFamily: \"monospace\",\n color: \"text.secondary\",\n }}\n >\n {s.memberId.slice(0, 8)}…\n </Typography>\n ) : (\n <Chip\n size=\"small\"\n label=\"Walk-in\"\n variant=\"outlined\"\n sx={{ fontWeight: 700, borderRadius: 1.5 }}\n />\n )}\n </TableCell>\n <TableCell>\n {s.paymentMode ? (\n <Chip\n size=\"small\"\n label={s.paymentMode}\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n bgcolor: alpha(\n theme.palette.primary.main,\n 0.08,\n ),\n color: \"primary.dark\",\n border: `1px solid ${alpha(theme.palette.primary.main, 0.18)}`,\n }}\n />\n ) : (\n <Typography variant=\"caption\" color=\"text.disabled\">\n —\n </Typography>\n )}\n </TableCell>\n <TableCell align=\"right\">\n <PmMoney value={s.totalAmount} />\n </TableCell>\n <TableCell align=\"right\">\n <PmMoney\n value={s.paidAmount}\n tone={isPaid ? \"success\" : \"muted\"}\n />\n </TableCell>\n <TableCell align=\"right\">\n {isPaid ? (\n <Chip\n size=\"small\"\n color=\"success\"\n label=\"Paid\"\n sx={{\n fontWeight: 700,\n borderRadius: 1.5,\n height: 22,\n }}\n />\n ) : (\n <PmMoney value={due} tone=\"warning\" size=\"big\" />\n )}\n </TableCell>\n </TableRow>\n );\n })\n )}\n </TableBody>\n </PmTable>\n </PmTableCard>\n </PmPageShell>\n </PmGuard>\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAwCA,SAAS,EAAS,GAAM;CAEtB,OADK,IACE,EACJ,MAAM,KAAK,EACX,OAAO,OAAO,EACd,MAAM,GAAG,CAAC,EACV,KAAK,MAAM,EAAE,EAAE,EACf,KAAK,EAAE,EACP,YAAY,IAPG;AAQpB;AAIA,SAAS,EAAY,EACnB,MAAM,GACN,UACA,UACA,YAAS,WACT,cACC;CACD,IAAM,IAAQ,EAAS,GACjB,IAAa,EAAM,QAAQ,IAAS,QAAQ,GAC5C,IAAa,EAAM,QAAQ,IAAS,QAAQ;CAClD,OACE,kBAAC,GAAD;EACE,IAAI;GACF,MAAM;GACN,GAAG;GACH,cAAc;GACd,QAAQ;GACR,aAAa,EAAM,GAAY,GAAI;GACnC,YAAY,2BAA2B,EAAM,GAAY,GAAI,EAAE,OAAO,EAAM,EAAM,QAAQ,WAAW,OAAO,CAAC,EAAE;GAC/G,UAAU;GACV,UAAU;EACZ;YAEA,kBAAC,GAAD;GAAO,WAAU;GAAM,YAAW;GAAS,SAAS;aAApD,CACE,kBAAC,GAAD;IACE,IAAI;KACF,OAAO;KACP,QAAQ;KACR,cAAc;KACd,SAAS;KACT,YAAY;KACZ,YAAY,2BAA2B,EAAW,OAAO,EAAW;KACpE,OAAO;KACP,WAAW,cAAc,EAAM,GAAY,GAAI;IACjD;cAEA,kBAAC,GAAD,EAAM,IAAI,EAAE,UAAU,GAAG,EAAI,CAAA;GAC1B,CAAA,GACL,kBAAC,GAAD;IAAK,IAAI,EAAE,UAAU,EAAE;cAAvB,CACE,kBAAC,GAAD;KACE,SAAQ;KACR,OAAM;KACN,IAAI;MACF,YAAY;MACZ,eAAe;MACf,eAAe;KACjB;eAEC;IACS,CAAA,GACX,IACC,kBAAC,GAAD;KAAU,OAAO;KAAI,QAAQ;IAAK,CAAA,IAElC,kBAAC,GAAD;KACE,SAAQ;KACR,IAAI;MACF,YAAY;MACZ,oBAAoB;MACpB,YAAY;KACd;eAEC;IACS,CAAA,CAEX;KACA;;CACJ,CAAA;AAET;AAEA,SAAwB,IAAY;CAClC,IAAM,IAAW,EAAY,GACvB,IAAQ,EAAS,GAEjB,IAAM,EADK,EACU,GAAU,YAAY,GAC3C,CAAC,GAAO,KAAY,EAAS,CAAC,CAAC,GAI/B,CAAC,GAAa,KAAkB,EAAS,CAAC,CAAC,GAC3C,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAO,KAAY,EAAS,EAAE;CAErC,eAAe,IAAO;EAEpB,AADA,EAAW,EAAI,GACf,EAAS,EAAE;EACX,IAAI;GACF,IAAM,IAAO,MAAM,EAAU,GAAG,EAAE;GAClC,EAAS,CAAI;GACb,IAAM,IAAM,MAAM,KAChB,IAAI,IAAI,EAAK,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO,OAAO,CAAC,CACrD;GACA,IAAI,EAAI,WAAW,GACjB,EAAe,CAAC,CAAC;QAEjB,IAAI;IACF,IAAM,IAAO,MAAM,EAAgB,CAAG,GAChC,IAAM,CAAC;IAIb,AAHA,EAAK,SAAS,MAAM;KAClB,EAAI,EAAE,MAAM,EAAE;IAChB,CAAC,GACD,EAAe,CAAG;GACpB,QAAQ;IACN,EAAe,CAAC,CAAC;GACnB;EAEJ,SAAS,GAAK;GACZ,IAAM,IACJ,GAAK,UAAU,MAAM,WAAW,GAAK,WAAW;GAElD,AADA,EAAS,CAAO,GAChB,EAAU;IAAE;IAAS,MAAM;GAAQ,CAAC;EACtC,UAAU;GACR,EAAW,EAAK;EAClB;CACF;CAEA,EACE,4CACM;EACJ,EAAU;CACZ,GACA,CAAC,CACH;CAEA,IAAM,IAAU,SAOP;EAAE,cANY,EAAM,QACxB,GAAG,MAAM,IAAI,OAAO,EAAE,eAAe,CAAC,GACvC,CAIO;EAAc,UAFN,EAAM,QAAQ,GAAG,MAAM,IAAI,OAAO,EAAE,aAAa,CAAC,GAAG,CAE/C;EAAU,WADf,EAAM,QAAQ,MAAM,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE;EACnB,OAAO,EAAM;CAAO,IAC/D,CAAC,CAAK,CAAC;CAEV,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,CAAQ,CAAA,GACR,kBAAC,GAAD;EAAS,MAAM;YACb,kBAAC,GAAD;GACE,OAAM;GACN,UAAS;GACT,SACE,kBAAC,GAAD;IAAO,WAAU;IAAM,SAAS;cAAhC,CACE,kBAAC,GAAD;KACE,SAAQ;KACR,WAAW,kBAAC,GAAD,CAAqB,CAAA;KAChC,eAAe,KAAK,EAAK;eAC1B;IAEO,CAAA,GAIP,EASI;;aAxBX;IA4BE,kBAAC,GAAD;KACE,WAAW;MAAE,IAAI;MAAU,IAAI;KAAM;KACrC,SAAS;KACT,IAAI,EAAE,IAAI,EAAE;eAHd;MAKE,kBAAC,GAAD;OACE,MAAM;OACN,OAAM;OACN,OAAO,EAAQ;OACf,QAAO;OACE;MACV,CAAA;MACD,kBAAC,GAAD;OACE,MAAM;OACN,OAAM;OACN,OAAO,GAAG,IAAM,EAAa,EAAQ,YAAY;OACjD,QAAO;OACE;MACV,CAAA;MACD,kBAAC,GAAD;OACE,MAAM;OACN,OAAM;OACN,OAAO,GAAG,IAAM,EAAa,EAAQ,QAAQ;OAC7C,QAAO;OACE;MACV,CAAA;MACD,kBAAC,GAAD;OACE,MAAM;OACN,OAAM;OACN,OAAO,EAAQ;OACf,QAAO;OACE;MACV,CAAA;KACI;;IAEN,IACC,kBAAC,GAAD;KAAO,UAAS;KAAQ,IAAI,EAAE,IAAI,EAAE;eACjC;IACI,CAAA,IACL;IAGJ,kBAAC,GAAD;KACE,SAAQ;KACR,OAAM;KACN,UACE,IACI,aACA,GAAG,EAAQ,MAAM,GAAG,EAAQ,UAAU,IAAI,SAAS,QAAQ;eAGjE,kBAAC,GAAD;MAAS,OAAA;gBAAT,CACE,kBAAC,IAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;OACE,kBAAC,GAAD,EAAA,UAAW,MAAc,CAAA;OACzB,kBAAC,GAAD,EAAA,UAAW,OAAe,CAAA;OAC1B,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA;OAC9B,kBAAC,GAAD,EAAA,UAAW,UAAkB,CAAA;OAC7B,kBAAC,GAAD;QAAW,OAAM;kBAAQ;OAAgB,CAAA;OACzC,kBAAC,GAAD;QAAW,OAAM;kBAAQ;OAAe,CAAA;OACxC,kBAAC,GAAD;QAAW,OAAM;kBAAQ;OAAc,CAAA;MAC/B,EAAA,CAAA,EACD,CAAA,GACX,kBAAC,GAAD,EAAA,UACG,IACC,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAG,MAChC,kBAAC,GAAD,EAAA,UACG,MAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,GAAI,MAClC,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAU,QAAQ,GAAK,CAAA,EACd,GAFK,CAEL,CACZ,EACO,GANK,CAML,CACX,IACC,EAAM,WAAW,IACnB,kBAAC,GAAD;OACE,SAAS;OACT,MAAM;OACN,OAAM;OACN,MAAK;OACL,QACE,kBAAC,GAAD;QACE,SAAQ;QACR,MAAK;QACL,WAAW,kBAAC,GAAD,CAAiB,CAAA;QAC5B,eACE,EAAS,iCAAiC;kBAE7C;OAEO,CAAA;MAEX,CAAA,IAED,EAAM,KAAK,MAAM;OACf,IAAM,IAAM,OAAO,EAAE,aAAa,CAAC,GAC7B,IAAS,KAAO,GAChB,IAAa,EAAY,EAAE;OACjC,OACE,kBAAC,GAAD;QAAqB,OAAA;kBAArB;SACE,kBAAC,GAAD,EAAA,UACE,kBAAC,IAAD,EAAO,IAAI,EAAE,SAAW,CAAA,EACf,CAAA;SACX,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;UAAY,SAAQ;UAAQ,IAAI,EAAE,YAAY,IAAI;oBAC/C,EAAW,EAAE,QAAQ;SACZ,CAAA,EACH,CAAA;SACX,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,GAAD;UACE,WAAU;UACV,YAAW;UACX,SAAS;oBAHX,CAKE,kBAAC,GAAD;WACE,IAAI;YACF,OAAO;YACP,QAAQ;YACR,UAAU;YACV,YAAY;YACZ,SAAS,EACP,EAAM,QAAQ,QAAQ,MACtB,GACF;YACA,OAAO;WACT;qBAEC,EAAS,CAAU;UACd,CAAA,GACR,kBAAC,GAAD;WAAK,IAAI,EAAE,UAAU,EAAE;qBACrB,kBAAC,GAAD;YACE,SAAQ;YACR,IAAI;aAAE,YAAY;aAAK,YAAY;YAAI;sBAEtC;WACS,CAAA;UACT,CAAA,CACA;cACL,EAAE,WACJ,kBAAC,GAAD;UACE,SAAQ;UACR,IAAI;WACF,YAAY;WACZ,OAAO;UACT;oBALF,CAOG,EAAE,SAAS,MAAM,GAAG,CAAC,GAAE,GACd;cAEZ,kBAAC,GAAD;UACE,MAAK;UACL,OAAM;UACN,SAAQ;UACR,IAAI;WAAE,YAAY;WAAK,cAAc;UAAI;SAC1C,CAAA,EAEM,CAAA;SACX,kBAAC,GAAD,EAAA,UACG,EAAE,cACD,kBAAC,GAAD;UACE,MAAK;UACL,OAAO,EAAE;UACT,IAAI;WACF,YAAY;WACZ,cAAc;WACd,QAAQ;WACR,SAAS,EACP,EAAM,QAAQ,QAAQ,MACtB,GACF;WACA,OAAO;WACP,QAAQ,aAAa,EAAM,EAAM,QAAQ,QAAQ,MAAM,GAAI;UAC7D;SACD,CAAA,IAED,kBAAC,GAAD;UAAY,SAAQ;UAAU,OAAM;oBAAgB;SAExC,CAAA,EAEL,CAAA;SACX,kBAAC,GAAD;UAAW,OAAM;oBACf,kBAAC,GAAD,EAAS,OAAO,EAAE,YAAc,CAAA;SACvB,CAAA;SACX,kBAAC,GAAD;UAAW,OAAM;oBACf,kBAAC,GAAD;WACE,OAAO,EAAE;WACT,MAAM,IAAS,YAAY;UAC5B,CAAA;SACQ,CAAA;SACX,kBAAC,GAAD;UAAW,OAAM;oBACd,IACC,kBAAC,GAAD;WACE,MAAK;WACL,OAAM;WACN,OAAM;WACN,IAAI;YACF,YAAY;YACZ,cAAc;YACd,QAAQ;WACV;UACD,CAAA,IAED,kBAAC,GAAD;WAAS,OAAO;WAAK,MAAK;WAAU,MAAK;UAAO,CAAA;SAEzC,CAAA;QACH;UA3GK,EAAE,EA2GP;MAEd,CAAC,EAEM,CAAA,CACJ;;IACE,CAAA;GACF;;CACN,CAAA,CACN,EAAA,CAAA;AAET"}
|