[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:
[dyad] 2025-09-23 16:26:56 +02:00
parent bf096abfc8
commit b4f8ac7fb4
3 changed files with 98 additions and 82 deletions

View file

@ -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>
);
};
}

View file

@ -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);
}
}

View file

@ -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 />