'use client'; import { InventoryResult } from '@/app/api/inventory'; import { fetchInventoryQtyLots, InventoryQtyLotRow } from '@/app/api/inventory/actions'; import { Box, Button, CircularProgress, Dialog, DialogContent, DialogTitle, Table, TableBody, TableCell, TableHead, TableRow, Typography, } from '@mui/material'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; const formatQty = (value: number) => { if (!Number.isFinite(value)) return '0'; return Number.isInteger(value) ? value.toLocaleString() : value.toLocaleString(undefined, { maximumFractionDigits: 2 }); }; type Bucket = 'jo' | 'do' | 'other' | 'unavailable'; const reasonLabel = (t: (key: string) => string, row: InventoryQtyLotRow) => { const label = row.reason === 'expired' ? t('Expired lot') : row.reason === 'joHold' ? t('Jo hold qty') : row.reason === 'doHold' ? t('Do hold qty') : row.reason === 'otherHold' || row.reason === 'onHold' ? t('Other hold qty') : t('Unavailable lot'); return row.detail ? `${label}:${row.detail}` : label; }; const sumSource = (rows: InventoryQtyLotRow[], source: string) => rows.reduce((sum, row) => (row.bucket === 'onHold' && row.source === source ? sum + Number(row.qty || 0) : sum), 0); const InventoryQtyStory: React.FC<{ inventory: InventoryResult }> = ({ inventory }) => { const { t } = useTranslation('inventory'); const onHand = Number(inventory.onHandQty ?? 0); const onHold = Number(inventory.onHoldQty ?? 0); const unavailable = Number(inventory.unavailableQty ?? 0); const available = Number(inventory.availableQty ?? onHand - onHold - unavailable); const shortUom = inventory.uomShortDesc || ''; const [open, setOpen] = useState(false); const [bucket, setBucket] = useState('unavailable'); const [loading, setLoading] = useState(false); const [rows, setRows] = useState([]); const [splitReady, setSplitReady] = useState(false); const requestRef = useRef(0); useEffect(() => { setOpen(false); setRows([]); setSplitReady(onHold === 0); if (onHold === 0 || inventory.itemId == null) return; const requestId = ++requestRef.current; fetchInventoryQtyLots(inventory.itemId) .then((res) => { if (requestId !== requestRef.current) return; setRows(res ?? []); setSplitReady(true); }) .catch(() => { if (requestId !== requestRef.current) return; setRows([]); setSplitReady(false); }); }, [inventory.itemId, onHold]); const joHold = splitReady ? sumSource(rows, 'jo') : 0; const doHold = splitReady ? sumSource(rows, 'do') : 0; const otherHold = splitReady ? sumSource(rows, 'other') : 0; const openDetail = (next: Bucket) => { const qty = next === 'jo' ? joHold : next === 'do' ? doHold : next === 'other' ? otherHold : unavailable; if (qty === 0) return; setBucket(next); setOpen(true); if (rows.length > 0 || next !== 'unavailable') { setLoading(false); return; } const requestId = ++requestRef.current; setLoading(true); fetchInventoryQtyLots(inventory.itemId) .then((res) => { if (requestId !== requestRef.current) return; setRows(res ?? []); }) .catch(() => { if (requestId !== requestRef.current) return; setRows([]); }) .finally(() => { if (requestId === requestRef.current) setLoading(false); }); }; const visibleRows = rows.filter((row) => bucket === 'unavailable' ? row.bucket === 'unavailable' : row.bucket === 'onHold' && row.source === bucket, ); const title = bucket === 'jo' ? t('Jo hold qty') : bucket === 'do' ? t('Do hold qty') : bucket === 'other' ? t('Other hold qty') : t('Unavailable Qty'); return ( {`${t('On Hand Qty')} ${formatQty(onHand)} −`} {splitReady ? ( <> openDetail('jo')} /> − openDetail('do')} /> {otherHold !== 0 && ( <> − openDetail('other')} /> )} ) : ( undefined} /> )} − openDetail('unavailable')} /> {'= '} {`${t('Available Qty')} ${formatQty(available)}${shortUom}`} setOpen(false)} maxWidth="md" fullWidth> {`${title} · ${t('Qty lot detail')}`} {loading ? ( ) : visibleRows.length === 0 ? ( {t('No qty lots')} ) : ( {t('Lot No')} {t('Expiry Date')} {t('Qty')} {t('Warehouse')} {t('Why')} {visibleRows.map((row) => ( {row.lotNo} {row.expiryDate} {formatQty(row.qty)} {row.warehouseCode} {reasonLabel(t, row)} ))}
)}
); }; const QtyLink = ({ label, enabled, onClick, }: { label: string; enabled: boolean; onClick: () => void; }) => { if (!enabled) { return ( {label} ); } return ( ); }; export default InventoryQtyStory;