[dyad] Updated dashboard to display dynamic data from PocketBase - wrote 2 file(s)
This commit is contained in:
parent
f48d7fdb2f
commit
06cb8c1a6b
2 changed files with 98 additions and 4 deletions
82
src/hooks/useDashboardData.ts
Normal file
82
src/hooks/useDashboardData.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { useState, useEffect, useCallback } from "react";
|
||||
import pb from "@/lib/pocketbase";
|
||||
import { subDays } from "date-fns";
|
||||
|
||||
interface DashboardStats {
|
||||
totalStockValue: string;
|
||||
totalItemsInStock: string;
|
||||
stockOutLast30Days: string;
|
||||
lowStockAlerts: string;
|
||||
}
|
||||
|
||||
export function useDashboardData() {
|
||||
const [stats, setStats] = useState<DashboardStats>({
|
||||
totalStockValue: "R0.00",
|
||||
totalItemsInStock: "0",
|
||||
stockOutLast30Days: "0",
|
||||
lowStockAlerts: "0",
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchDashboardStats = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
// Fetch all stock items
|
||||
const stockRecords = await pb.collection('stock_items').getFullList({
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
let calculatedTotalStockValue = 0;
|
||||
let calculatedTotalItemsInStock = 0;
|
||||
let calculatedLowStockAlerts = 0;
|
||||
|
||||
stockRecords.forEach((item: any) => {
|
||||
calculatedTotalStockValue += (item.quantity * item.cost_price);
|
||||
calculatedTotalItemsInStock += item.quantity;
|
||||
if (item.quantity < 10) { // Define low stock threshold as < 10
|
||||
calculatedLowStockAlerts++;
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch book out records for the last 30 days
|
||||
const thirtyDaysAgo = subDays(new Date(), 30).toISOString();
|
||||
const bookOutRecords = await pb.collection('book_out_records').getFullList({
|
||||
filter: `date_booked_out >= "${thirtyDaysAgo}"`,
|
||||
});
|
||||
|
||||
let calculatedStockOutLast30Days = 0;
|
||||
bookOutRecords.forEach((record: any) => {
|
||||
calculatedStockOutLast30Days += record.quantity;
|
||||
});
|
||||
|
||||
setStats({
|
||||
totalStockValue: new Intl.NumberFormat("en-ZA", {
|
||||
style: "currency",
|
||||
currency: "ZAR",
|
||||
}).format(calculatedTotalStockValue),
|
||||
totalItemsInStock: calculatedTotalItemsInStock.toString(),
|
||||
stockOutLast30Days: calculatedStockOutLast30Days.toString(),
|
||||
lowStockAlerts: calculatedLowStockAlerts.toString(),
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch dashboard data:", err);
|
||||
setError("Failed to load dashboard data.");
|
||||
setStats({
|
||||
totalStockValue: "R0.00",
|
||||
totalItemsInStock: "0",
|
||||
stockOutLast30Days: "0",
|
||||
lowStockAlerts: "0",
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDashboardStats();
|
||||
}, [fetchDashboardStats]);
|
||||
|
||||
return { stats, loading, error, refresh: fetchDashboardStats };
|
||||
}
|
||||
|
|
@ -2,37 +2,49 @@ import StatCard from '@/components/StatCard';
|
|||
import StockChart from '@/components/StockChart';
|
||||
import StockActivityChart from '@/components/StockActivityChart';
|
||||
import { DollarSign, Package, ArrowDown, AlertTriangle } from 'lucide-react';
|
||||
import { useDashboardData } from '@/hooks/useDashboardData'; // Import the new hook
|
||||
|
||||
const Index = () => {
|
||||
const { stats, loading, error } = useDashboardData();
|
||||
|
||||
if (loading) {
|
||||
return <div className="text-center text-gray-500">Loading dashboard data...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className="text-center text-red-500">Error: {error}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<StatCard
|
||||
title="Total Stock Value"
|
||||
value="R2,253,478.35"
|
||||
value={stats.totalStockValue}
|
||||
icon={<DollarSign className="h-6 w-6 text-white" />}
|
||||
iconBgColor="bg-slate-blue"
|
||||
/>
|
||||
<StatCard
|
||||
title="Items in Stock"
|
||||
value="2,350"
|
||||
value={stats.totalItemsInStock}
|
||||
icon={<Package className="h-6 w-6 text-white" />}
|
||||
iconBgColor="bg-teal-accent"
|
||||
/>
|
||||
<StatCard
|
||||
title="Stock Out (30 days)"
|
||||
value="924"
|
||||
value={stats.stockOutLast30Days}
|
||||
icon={<ArrowDown className="h-6 w-6 text-white" />}
|
||||
iconBgColor="bg-slate-blue"
|
||||
/>
|
||||
<StatCard
|
||||
title="Low Stock Alerts"
|
||||
value="12"
|
||||
value={stats.lowStockAlerts}
|
||||
icon={<AlertTriangle className="h-6 w-6 text-white" />}
|
||||
iconBgColor="bg-primary-dark"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-8 md:grid-cols-1 lg:grid-cols-3">
|
||||
{/* These charts still use static data. We can update them in a future request if needed! */}
|
||||
<StockChart />
|
||||
<StockActivityChart />
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue