Files
alrahma_sunday_school_api/src/pages/inventory/InventoryLowStockPage.tsx
T
2026-06-11 11:06:32 -04:00

113 lines
4.4 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { ApiHttpError } from '../../api/http'
import { fetchLowStock } from '../../api/inventory'
import { INVENTORY_BOOK_BASE } from './inventoryPaths'
/**
* Low Stock Items — items where quantity <= reorder_point.
* Backend: GET /api/v1/administrator/inventory/low-stock
*/
export function InventoryLowStockPage() {
const [items, setItems] = useState<Record<string, unknown>[]>([])
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const load = useCallback(() => {
setLoading(true)
fetchLowStock()
.then((res) => {
if (res && typeof res === 'object') {
const o = res as Record<string, unknown>
const arr = Array.isArray(o.items) ? (o.items as Record<string, unknown>[]) : []
setItems(arr)
setCount(Number(o.count ?? arr.length))
}
setError(null)
})
.catch((e: unknown) => {
setError(e instanceof ApiHttpError ? e.message : 'Unable to load low stock items.')
})
.finally(() => setLoading(false))
}, [])
useEffect(() => {
load()
}, [load])
return (
<div className="container-fluid px-0 mt-3">
<div className="d-flex justify-content-between align-items-center mb-3 px-3 flex-wrap gap-2">
<h3 className="mb-0">Low Stock Items ({count})</h3>
<div className="d-flex gap-2">
<Link className="btn btn-outline-info btn-sm" to="/app/administrator/inventory/reorder-suggestions">
Reorder Suggestions
</Link>
<Link className="btn btn-outline-secondary" to={INVENTORY_BOOK_BASE}>
Back to Items
</Link>
</div>
</div>
{error ? <div className="alert alert-danger mx-3">{error}</div> : null}
{loading ? (
<p className="text-muted px-3">Loading</p>
) : items.length === 0 ? (
<div className="alert alert-success mx-3">
<strong>All stocked up!</strong> No items are below their reorder point.
</div>
) : (
<div className="card mx-3">
<div className="card-body table-responsive">
<table className="table table-striped align-middle" id="lowStockTable">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Category</th>
<th className="text-end">Current Qty</th>
<th className="text-end">Reorder Point</th>
<th className="text-end">Reorder Qty</th>
<th className="text-end">Lead Time (Days)</th>
<th>Preferred Supplier</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{items.map((item) => {
const i = item as Record<string, unknown>
const supplier = i.preferred_supplier as Record<string, unknown> | null
return (
<tr key={String(i.id)}>
<td>{String(i.name ?? '')}</td>
<td style={{ textTransform: 'capitalize' }}>{String(i.type ?? '')}</td>
<td>{String((i.category as Record<string, unknown>)?.name ?? i.category ?? '')}</td>
<td className="text-end text-danger fw-semibold">
{Number(i.quantity ?? 0).toLocaleString()}
</td>
<td className="text-end">{Number(i.reorder_point ?? 0).toLocaleString()}</td>
<td className="text-end">{Number(i.reorder_quantity ?? 0).toLocaleString() || '—'}</td>
<td className="text-end">{Number(i.lead_time_days ?? 0) || '—'}</td>
<td>{supplier ? String(supplier.name ?? '') : '—'}</td>
<td>
<Link
className="btn btn-sm btn-warning"
to={`/app/administrator/inventory/items/${String(i.id)}/adjust`}
>
Adjust Stock
</Link>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
)}
</div>
)
}