feat(reports): add low stock report component and integration

Add new LowStockReport component with data fetching and table display functionality. Includes threshold filtering and refresh capability. Update Reports page to include the new low stock section and modify the description text.
This commit is contained in:
davidels1 2026-01-14 09:04:38 +02:00
parent 315a9c7759
commit 05c8981513
3 changed files with 111 additions and 2 deletions

View file

@ -0,0 +1,34 @@
"use client"
import { ColumnDef } from "@tanstack/react-table"
export type LowStockRecord = {
id: string
itemName: string
sku: string
quantity: number
threshold: number
}
export const lowStockColumns: ColumnDef<LowStockRecord>[] = [
{
accessorKey: "itemName",
header: "Item Name",
},
{
accessorKey: "sku",
header: "SKU",
},
{
accessorKey: "quantity",
header: "Qty",
cell: ({ row }) => {
const val = row.getValue("quantity") as number
return <div className="font-medium">{val}</div>
},
},
{
accessorKey: "threshold",
header: "Threshold",
},
]

View file

@ -0,0 +1,68 @@
"use client"
import * as React from "react"
import pb from "@/lib/pocketbase"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { lowStockColumns, LowStockRecord } from "./LowStockColumns"
import { ReportDataTable } from "./ReportDataTable"
import { showError } from "@/utils/toast"
import { AlertTriangle } from "lucide-react"
export function LowStockReport() {
const [threshold, setThreshold] = React.useState<number>(3)
const [data, setData] = React.useState<LowStockRecord[]>([])
const [loading, setLoading] = React.useState(false)
const fetchLowStock = React.useCallback(async () => {
setLoading(true)
try {
const records = await pb.collection("stock_items").getFullList({
filter: `quantity < ${threshold}`,
sort: "quantity",
})
const formatted: LowStockRecord[] = records.map((item: any) => ({
id: item.id,
itemName: item.name,
sku: item.sku,
quantity: Number(item.quantity ?? 0),
threshold,
}))
setData(formatted)
} catch (e) {
console.error("Low stock fetch error:", e)
showError("Failed to load low stock report.")
setData([])
} finally {
setLoading(false)
}
}, [threshold])
React.useEffect(() => {
fetchLowStock()
}, [fetchLowStock])
return (
<div className="space-y-4">
<div className="flex flex-col sm:flex-row sm:items-end gap-2">
<div className="flex-1">
<label className="block text-sm font-medium text-gray-700 mb-1">Threshold</label>
<Input
type="number"
min={0}
step={1}
value={threshold}
onChange={(e) => setThreshold(Number(e.target.value || 0))}
className="max-w-xs"
/>
</div>
<Button onClick={fetchLowStock} disabled={loading}>
<AlertTriangle className="mr-2 h-4 w-4" />
{loading ? "Loading..." : "Refresh"}
</Button>
</div>
<ReportDataTable columns={lowStockColumns} data={data} loading={loading} />
</div>
)
}

View file

@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
import { FileText } from "lucide-react";
import pb from "@/lib/pocketbase";
import { showError } from "@/utils/toast";
import { LowStockReport } from "@/components/reports/LowStockReport";
const Reports = () => {
const [billingCompanies, setBillingCompanies] = useState<{ value: string; label: string }[]>([]);
@ -102,7 +103,7 @@ const Reports = () => {
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4 mb-6">
<div>
<h1 className="text-3xl font-bold">Reports</h1>
<p className="text-gray-500">Generate book-out reports by billing company.</p>
<p className="text-gray-500">Generate book-out reports by billing company and view low stock.</p>
</div>
</div>
@ -125,8 +126,14 @@ const Reports = () => {
</div>
<ReportDataTable columns={columns} data={reportData} loading={loading} />
<div className="mt-8">
<h2 className="text-2xl font-semibold mb-2">Low Stock Report</h2>
<p className="text-gray-500 mb-4">Items below the selected threshold.</p>
<LowStockReport />
</div>
</div>
);
};
export default Reports;
export default Reports;