[dyad] Fixed dashboard stats (unique items count, book out as orders) and updated chart colors - wrote 3 file(s) + extra files edited outside of Dyad
This commit is contained in:
parent
bf096abfc8
commit
b4f8ac7fb4
3 changed files with 98 additions and 82 deletions
|
|
@ -1,49 +1,49 @@
|
|||
import React from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
|
||||
"use client";
|
||||
|
||||
const data = [
|
||||
{ name: 'Jan', In: 4000, Out: 2400 },
|
||||
{ name: 'Feb', In: 3000, Out: 1398 },
|
||||
{ name: 'Mar', In: 2000, Out: 9800 },
|
||||
{ name: 'Apr', In: 2780, Out: 3908 },
|
||||
{ name: 'May', In: 1890, Out: 4800 },
|
||||
{ name: 'Jun', In: 2390, Out: 3800 },
|
||||
{ name: 'Jul', In: 3490, Out: 4300 },
|
||||
{ name: 'Aug', In: 3000, Out: 2000 },
|
||||
{ name: 'Sep', In: 2000, Out: 2780 },
|
||||
{ name: 'Oct', In: 2780, Out: 1890 },
|
||||
{ name: 'Nov', In: 1890, Out: 2390 },
|
||||
{ name: 'Dec', In: 3490, Out: 2900 },
|
||||
];
|
||||
import {
|
||||
BarChart,
|
||||
Bar,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
} from "recharts";
|
||||
import { useDashboardData } from "@/hooks/useDashboardData";
|
||||
|
||||
interface MonthlyActivity {
|
||||
name: string;
|
||||
stockIn: number;
|
||||
stockOut: number;
|
||||
}
|
||||
|
||||
export function StockChart() {
|
||||
const { monthlyActivityData } = useDashboardData();
|
||||
|
||||
if (!monthlyActivityData || monthlyActivityData.length === 0) {
|
||||
return <div className="text-center text-muted-foreground py-8">No data available</div>;
|
||||
}
|
||||
|
||||
export const StockChart = () => {
|
||||
return (
|
||||
<Card className="col-span-1 lg:col-span-2 mt-4">
|
||||
<CardHeader className="bg-primary text-primary-foreground mb-4 p-4 rounded-lg shadow-lg"> {/* Changed bg-primary-dark to bg-primary and text-white to text-primary-foreground */}
|
||||
<CardTitle className="text-primary-foreground">Monthly Stock Activity</CardTitle> {/* Changed text-white to text-primary-foreground */}
|
||||
</CardHeader>
|
||||
<CardContent className="h-80">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart
|
||||
data={data}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 20,
|
||||
bottom: 5,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="In" fill="#8884d8" name="Stock In" />
|
||||
<Bar dataKey="Out" fill="#82ca9d" name="Stock Out" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart
|
||||
data={monthlyActivityData}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 20,
|
||||
bottom: 5,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="stockIn" fill="#3B82F6" name="Stock In" /> {/* Blue for stock in */}
|
||||
<Bar dataKey="stockOut" fill="#F59E0B" name="Stock Out" /> {/* Orange for stock out */}
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ interface DashboardStats {
|
|||
stockOutLast30Days: string;
|
||||
lowStockAlerts: string;
|
||||
totalCustomers: string;
|
||||
totalOrders: string; // Added totalOrders
|
||||
totalOrders: string;
|
||||
}
|
||||
|
||||
interface MonthlyActivity {
|
||||
|
|
@ -24,7 +24,7 @@ export function useDashboardData() {
|
|||
stockOutLast30Days: "0",
|
||||
lowStockAlerts: "0",
|
||||
totalCustomers: "0",
|
||||
totalOrders: "0", // Initialize totalOrders
|
||||
totalOrders: "0",
|
||||
});
|
||||
const [monthlyActivityData, setMonthlyActivityData] = useState<MonthlyActivity[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
|
@ -60,18 +60,20 @@ export function useDashboardData() {
|
|||
console.log(`Fetched ${stockRecords.length} stock items`);
|
||||
|
||||
let calculatedTotalStockValue = 0;
|
||||
let calculatedTotalItemsInStock = 0;
|
||||
let calculatedTotalItemsInStock = stockRecords.length; // Count of unique items (records)
|
||||
let calculatedLowStockAlerts = 0;
|
||||
|
||||
stockRecords.forEach((item: any) => {
|
||||
const quantity = item.quantity || 0;
|
||||
const costPrice = item.cost_price || 0;
|
||||
calculatedTotalStockValue += (quantity * costPrice);
|
||||
calculatedTotalItemsInStock += quantity;
|
||||
if (quantity < 10) { // Define low stock threshold as < 10
|
||||
stockRecords.forEach((item: any, index: number) => {
|
||||
const quantity = parseFloat(item.quantity) || 0;
|
||||
const costPrice = parseFloat(item.cost_price) || 0;
|
||||
const itemValue = quantity * costPrice;
|
||||
calculatedTotalStockValue += itemValue;
|
||||
console.log(`Item ${index + 1}: ${item.name} - Qty: ${quantity}, Cost: ${costPrice}, Value: ${itemValue}`); // Debug log per item
|
||||
if (quantity < 10) {
|
||||
calculatedLowStockAlerts++;
|
||||
}
|
||||
});
|
||||
console.log(`Total stock value calculated: R${calculatedTotalStockValue.toFixed(2)}`);
|
||||
|
||||
// Fetch book out records for the last 30 days
|
||||
const thirtyDaysAgo = subDays(new Date(), 30).toISOString();
|
||||
|
|
@ -84,7 +86,7 @@ export function useDashboardData() {
|
|||
|
||||
let calculatedStockOutLast30Days = 0;
|
||||
bookOutRecordsLast30Days.forEach((record: any) => {
|
||||
calculatedStockOutLast30Days += record.quantity || 0;
|
||||
calculatedStockOutLast30Days += parseFloat(record.quantity) || 0;
|
||||
});
|
||||
|
||||
// Fetch total customers using getList to get totalItems
|
||||
|
|
@ -95,13 +97,13 @@ export function useDashboardData() {
|
|||
const totalCustomers = customersList.totalItems;
|
||||
console.log(`Total customers: ${totalCustomers}`);
|
||||
|
||||
// Fetch total orders using getList to get totalItems
|
||||
console.log('Fetching total orders...');
|
||||
const ordersList = await pb.collection('purchase_orders').getList(1, 1, {
|
||||
// Fetch total book out records (as "total orders")
|
||||
console.log('Fetching total book out records...');
|
||||
const allBookOutRecordsList = await pb.collection('book_out_records').getList(1, 1, {
|
||||
signal: abortSignal,
|
||||
});
|
||||
const totalOrders = ordersList.totalItems;
|
||||
console.log(`Total orders: ${totalOrders}`);
|
||||
const totalOrders = allBookOutRecordsList.totalItems;
|
||||
console.log(`Total book out records (orders): ${totalOrders}`);
|
||||
|
||||
// Update stats state with fetched data
|
||||
setStats({
|
||||
|
|
@ -113,7 +115,7 @@ export function useDashboardData() {
|
|||
stockOutLast30Days: calculatedStockOutLast30Days.toString(),
|
||||
lowStockAlerts: calculatedLowStockAlerts.toString(),
|
||||
totalCustomers: totalCustomers.toString(),
|
||||
totalOrders: totalOrders.toString(), // Add totalOrders
|
||||
totalOrders: totalOrders.toString(),
|
||||
});
|
||||
|
||||
// --- Monthly Activity Data for Charts ---
|
||||
|
|
@ -133,7 +135,7 @@ export function useDashboardData() {
|
|||
monthlyDataMap.set(monthKey, { stockIn: 0, stockOut: 0 });
|
||||
});
|
||||
|
||||
// Process stock items for 'stockIn' (based on creation date)
|
||||
// Process stock items for 'stockIn' (based on creation date) - count new items per month
|
||||
console.log('Processing stock items for monthly activity...');
|
||||
stockRecords.forEach((item: any) => {
|
||||
const createdDate = new Date(item.created);
|
||||
|
|
@ -141,7 +143,7 @@ export function useDashboardData() {
|
|||
const monthKey = format(createdDate, 'MMM');
|
||||
if (monthlyDataMap.has(monthKey)) {
|
||||
const current = monthlyDataMap.get(monthKey)!;
|
||||
current.stockIn += item.quantity || 0;
|
||||
current.stockIn += 1; // Count new items added, not quantity
|
||||
monthlyDataMap.set(monthKey, current);
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +163,7 @@ export function useDashboardData() {
|
|||
const monthKey = format(bookedOutDate, 'MMM');
|
||||
if (monthlyDataMap.has(monthKey)) {
|
||||
const current = monthlyDataMap.get(monthKey)!;
|
||||
current.stockOut += record.quantity || 0;
|
||||
current.stockOut += parseFloat(record.quantity) || 0;
|
||||
monthlyDataMap.set(monthKey, current);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,35 @@
|
|||
import { StatCard } from '@/components/StatCard';
|
||||
import { StockChart } from '@/components/StockChart';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { DollarSign, Package, ShoppingCart, Users } from 'lucide-react';
|
||||
import { DollarSign, Package, ShoppingCart, Users, AlertTriangle, Calendar } from 'lucide-react';
|
||||
import { useDashboardData } from '@/hooks/useDashboardData';
|
||||
|
||||
const Index = () => {
|
||||
const { stats, monthlyActivityData, loading, error } = useDashboardData();
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading dashboard data...</div>;
|
||||
return <div className="flex-1 space-y-4 p-4 md:p-8 pt-6"><div className="text-center">Loading dashboard data...</div></div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>Error loading dashboard data: {error}</div>;
|
||||
return <div className="flex-1 space-y-4 p-4 md:p-8 pt-6"><div className="text-center text-red-500">Error loading dashboard data: {error}</div></div>;
|
||||
}
|
||||
|
||||
const { totalStockValue, totalItemsInStock: totalItems, totalOrders, totalCustomers } = stats || {
|
||||
totalStockValue: "R0.00",
|
||||
totalItemsInStock: "0",
|
||||
totalOrders: "0",
|
||||
totalCustomers: "0",
|
||||
};
|
||||
const {
|
||||
totalStockValue,
|
||||
totalItemsInStock: totalItems,
|
||||
totalOrders,
|
||||
totalCustomers,
|
||||
lowStockAlerts,
|
||||
stockOutLast30Days
|
||||
} = stats;
|
||||
|
||||
return (
|
||||
<div className="flex-1 space-y-4 p-4 md:p-8 pt-6">
|
||||
<div className="flex items-center justify-between space-y-2">
|
||||
<h2 className="text-3xl font-bold tracking-tight">Dashboard</h2>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<StatCard
|
||||
title="Total Stock Value"
|
||||
value={totalStockValue}
|
||||
|
|
@ -37,26 +39,38 @@ const Index = () => {
|
|||
<StatCard
|
||||
title="Total Items"
|
||||
value={totalItems}
|
||||
description="The total number of unique items in stock."
|
||||
description="Number of unique items in stock."
|
||||
icon={<Package className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Total Orders"
|
||||
value={totalOrders}
|
||||
description="Total purchase orders processed."
|
||||
icon={<ShoppingCart className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Total Customers"
|
||||
value={totalCustomers}
|
||||
description="Total active customers."
|
||||
icon={<Users className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Total Book Outs"
|
||||
value={totalOrders}
|
||||
description="Total book out records."
|
||||
icon={<ShoppingCart className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Low Stock Alerts"
|
||||
value={lowStockAlerts}
|
||||
description="Items with quantity < 10."
|
||||
icon={<AlertTriangle className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Stock Out (30 Days)"
|
||||
value={stockOutLast30Days}
|
||||
description="Items booked out in last 30 days."
|
||||
icon={<Calendar className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
|
||||
<Card className="col-span-7">
|
||||
<CardHeader>
|
||||
<CardTitle>Stock Overview</CardTitle>
|
||||
<CardTitle>Monthly Stock Activity</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pl-2">
|
||||
<StockChart />
|
||||
|
|
|
|||
Loading…
Reference in a new issue