[dyad] Updated StatCard header colors - wrote 5 file(s)
This commit is contained in:
parent
eb4b0949d3
commit
ed4e066273
4 changed files with 169 additions and 76 deletions
79
src/components/RecentActivity.tsx
Normal file
79
src/components/RecentActivity.tsx
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import React from 'react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
|
||||
interface ActivityItem {
|
||||
id: string;
|
||||
type: 'order' | 'stock-in' | 'stock-out';
|
||||
description: string;
|
||||
timestamp: string;
|
||||
user?: {
|
||||
name: string;
|
||||
avatarUrl?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const mockActivity: ActivityItem[] = [
|
||||
{
|
||||
id: '1',
|
||||
type: 'order',
|
||||
description: 'New order #1001 placed by John Doe.',
|
||||
timestamp: '2 hours ago',
|
||||
user: { name: 'John Doe', avatarUrl: 'https://api.dicebear.com/7.x/initials/svg?seed=JD' },
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'stock-in',
|
||||
description: '50 units of "Wireless Mouse" added to stock.',
|
||||
timestamp: '5 hours ago',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: 'stock-out',
|
||||
description: '10 units of "Mechanical Keyboard" booked out for delivery.',
|
||||
timestamp: '1 day ago',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
type: 'order',
|
||||
description: 'Order #1000 updated by Jane Smith.',
|
||||
timestamp: '2 days ago',
|
||||
user: { name: 'Jane Smith', avatarUrl: 'https://api.dicebear.com/7.x/initials/svg?seed=JS' },
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
type: 'stock-in',
|
||||
description: '100 units of "USB-C Cable" received from supplier.',
|
||||
timestamp: '3 days ago',
|
||||
},
|
||||
];
|
||||
|
||||
export const RecentActivity = () => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Recent Activity</CardTitle>
|
||||
<CardDescription>Recent actions in your stock management system.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[300px]">
|
||||
<div className="space-y-4">
|
||||
{mockActivity.map((activity) => (
|
||||
<div key={activity.id} className="flex items-center space-x-4">
|
||||
<Avatar>
|
||||
<AvatarImage src={activity.user?.avatarUrl} />
|
||||
<AvatarFallback>{activity.user ? activity.user.name.substring(0, 2) : 'NA'}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 space-y-1">
|
||||
<p className="text-sm font-medium leading-none">{activity.description}</p>
|
||||
<p className="text-sm text-muted-foreground">{activity.timestamp}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,23 +1,24 @@
|
|||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface StatCardProps {
|
||||
interface StatCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
title: string;
|
||||
value: string;
|
||||
description: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function StatCard({ title, value, description, icon }: StatCardProps) {
|
||||
export const StatCard = ({ title, value, description, icon, className, ...props }: StatCardProps) => {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2 bg-primary-dark text-white rounded-t-lg">
|
||||
<CardTitle className="text-sm font-bold text-white">{title}</CardTitle>
|
||||
<Card className={cn('flex flex-col', className)} {...props}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2 bg-primary text-primary-foreground rounded-t-lg"> {/* Changed bg-primary-dark to bg-primary and text-white to text-primary-foreground */}
|
||||
<CardTitle className="text-sm font-bold text-primary-foreground">{title}</CardTitle> {/* Changed text-white to text-primary-foreground */}
|
||||
{icon}
|
||||
</CardHeader>
|
||||
<CardContent className="pt-6">
|
||||
<CardContent className="flex-grow flex flex-col justify-between pt-4">
|
||||
<div className="text-2xl font-bold">{value}</div>
|
||||
<p className="text-xs text-muted-foreground">{description}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
};
|
||||
21
src/lib/api.ts
Normal file
21
src/lib/api.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Define the interface for the dashboard data
|
||||
export interface DashboardData {
|
||||
totalStockValue: number;
|
||||
totalItems: number;
|
||||
totalOrders: number;
|
||||
totalCustomers: number;
|
||||
}
|
||||
|
||||
// Mock API function to fetch dashboard data
|
||||
export const fetchDashboardData = async (): Promise<DashboardData> => {
|
||||
// Simulate a network delay
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
// Return mock data
|
||||
return {
|
||||
totalStockValue: 125000,
|
||||
totalItems: 750,
|
||||
totalOrders: 120,
|
||||
totalCustomers: 85,
|
||||
};
|
||||
};
|
||||
|
|
@ -1,92 +1,84 @@
|
|||
import StatCard from '@/components/StatCard';
|
||||
import { StatCard } from '@/components/StatCard';
|
||||
import { StockChart } from '@/components/StockChart';
|
||||
import StockActivityChart from '@/components/StockActivityChart';
|
||||
import { useDashboardData } from '@/hooks/useDashboardData';
|
||||
import { RecentActivity } from '@/components/RecentActivity';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { DollarSign, Package, TrendingDown, AlertTriangle } from 'lucide-react';
|
||||
import { DollarSign, Package, ShoppingCart, Users } from 'lucide-react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { fetchDashboardData, DashboardData } from '@/lib/api'; // Import DashboardData interface
|
||||
|
||||
export default function Index() {
|
||||
const { stats, loading, error } = useDashboardData();
|
||||
const Index = () => {
|
||||
const { data, isLoading, error } = useQuery<DashboardData>({ // Use DashboardData for typing
|
||||
queryKey: ['dashboardData'],
|
||||
queryFn: fetchDashboardData,
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="p-4 md:p-6 lg:p-8 space-y-8">
|
||||
<h1 className="text-3xl font-bold">Dashboard</h1>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle className="text-sm font-medium">Loading...</CardTitle>
|
||||
<DollarSign className="h-4 w-4 text-orange-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">...</div>
|
||||
<p className="text-xs text-muted-foreground">...</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
<Card className="col-span-1 lg:col-span-2 mt-4">
|
||||
<CardContent className="h-80 flex items-center justify-center">
|
||||
Loading chart data...
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
if (isLoading) {
|
||||
return <div>Loading dashboard data...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="p-4 md:p-6 lg:p-8 space-y-8">
|
||||
<h1 className="text-3xl font-bold">Dashboard</h1>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<Card className="col-span-4">
|
||||
<CardContent className="h-24 flex items-center justify-center text-red-500">
|
||||
Error loading dashboard data: {error}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <div>Error loading dashboard data: {error.message}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 lg:p-8 space-y-8">
|
||||
<h1 className="text-3xl font-bold">Dashboard</h1>
|
||||
// Provide default values if data is undefined (though useQuery's type should handle this better now)
|
||||
const { totalStockValue, totalItems, totalOrders, totalCustomers } = data || {
|
||||
totalStockValue: 0,
|
||||
totalItems: 0,
|
||||
totalOrders: 0,
|
||||
totalCustomers: 0,
|
||||
};
|
||||
|
||||
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">
|
||||
<StatCard
|
||||
title="Total Stock Value"
|
||||
value={stats.totalStockValue}
|
||||
description="Total value of all items currently in stock"
|
||||
icon={<DollarSign className="h-4 w-4 text-orange-500" />}
|
||||
value={`$${totalStockValue.toLocaleString()}`}
|
||||
description="The total value of all items in stock."
|
||||
icon={<DollarSign className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Total Items in Stock"
|
||||
value={stats.totalItemsInStock}
|
||||
description="Overall quantity of all items"
|
||||
icon={<Package className="h-4 w-4 text-orange-500" />}
|
||||
title="Total Items"
|
||||
value={totalItems.toLocaleString()}
|
||||
description="The total number of unique items in stock."
|
||||
icon={<Package className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Stock Out (Last 30 Days)"
|
||||
value={stats.stockOutLast30Days}
|
||||
description="Items booked out in the last month"
|
||||
icon={<TrendingDown className="h-4 w-4 text-orange-500" />}
|
||||
title="Total Orders"
|
||||
value={totalOrders.toLocaleString()}
|
||||
description="Total orders processed this month."
|
||||
icon={<ShoppingCart className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<StatCard
|
||||
title="Low Stock Alerts"
|
||||
value={stats.lowStockAlerts}
|
||||
description="Items below reorder threshold"
|
||||
icon={<AlertTriangle className="h-4 w-4 text-orange-500" />}
|
||||
title="Total Customers"
|
||||
value={totalCustomers.toLocaleString()}
|
||||
description="Total active customers."
|
||||
icon={<Users className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-2">
|
||||
<StockChart />
|
||||
{/* <StockActivityChart /> */}
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
|
||||
<Card className="col-span-4">
|
||||
<CardHeader>
|
||||
<CardTitle>Stock Overview</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="pl-2">
|
||||
<StockChart />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="col-span-3">
|
||||
<CardHeader>
|
||||
<CardTitle>Recent Activity</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<RecentActivity />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Index;
|
||||
Loading…
Reference in a new issue