pmtool / src /pages /dashboard.tsx
devarshia5's picture
Upload 487 files
d97b8f9 verified
Raw
History Blame Contribute Delete
21 kB
import React, { useEffect, useState } from "react";
import { Briefcase, Calendar, CheckSquare, DollarSign, Grid3x3, TrendingUp, Users } from "lucide-react";
import { Button } from "@/components/ui/button";
import Layout from "@/components/layout/layout";
import StatsCard from "@/components/dashboard/stats-card";
import ChartCard from "@/components/dashboard/chart-card";
import WorkFocus from "@/components/dashboard/work-focus";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { mockDashboardStats, mockRevenueData, mockSalesData } from "@/lib/data";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { useAuth } from "@/lib/auth-context";
import { useNavigate } from "react-router-dom";
import { leaveApi } from "@/services/leaveApi";
import { employeeApi } from "@/services/employeeApi";
import { Leave } from "@/types";
const Dashboard = () => {
const { userData } = useAuth();
const navigate = useNavigate();
const [leavesToday, setLeavesToday] = useState<Leave[]>([]);
const [employees, setEmployees] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
// Redirect client users to production-bugs page
useEffect(() => {
if (userData?.roleName?.toLowerCase() === "client") {
navigate("/production-bugs", { state: { from: "dashboard" } });
}
}, [userData, navigate]);
// Fetch employees on leave today
useEffect(() => {
const fetchLeavesAndEmployees = async () => {
try {
setLoading(true);
const [leavesData, employeesData] = await Promise.all([
leaveApi.getAllLeavesToday(),
employeeApi.getAllForAdmin()
]);
setLeavesToday(leavesData);
setEmployees(employeesData);
} catch (error) {
console.error('Error fetching leaves or employees:', error);
} finally {
setLoading(false);
}
};
fetchLeavesAndEmployees();
}, []);
// Get employee details by ID
const getEmployeeById = (employeeId: number) => {
return employees.find(emp => emp.id === employeeId);
};
// Get initials from name
const getInitials = (firstName: string, lastName: string) => {
return `${firstName?.charAt(0) || ''}${lastName?.charAt(0) || ''}`.toUpperCase();
};
// Format date
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', { day: 'numeric', month: 'short', year: 'numeric' });
};
// Get badge variant based on status
const getStatusBadgeVariant = (status: string) => {
switch (status) {
case 'Approved':
return 'default';
case 'Pending':
return 'outline';
case 'Rejected':
return 'destructive';
default:
return 'outline';
}
};
// Get badge color classes
const getStatusBadgeClass = (status: string) => {
switch (status) {
case 'Approved':
return 'bg-green-50 text-green-500 border-green-200';
case 'Pending':
return 'bg-yellow-50 text-yellow-600 border-yellow-200';
case 'Rejected':
return 'bg-red-50 text-red-500 border-red-200';
default:
return 'bg-gray-50 text-gray-500 border-gray-200';
}
};
// Format the revenue data for charts
const revenueChartData = [mockRevenueData];
const salesChartData = [
mockSalesData.filter((_, i) => i < 9),
mockSalesData.filter((_, i) => i >= 9),
];
return (
<Layout>
<div className="space-y-6 animate-fadeIn">
<div className="flex flex-col gap-2">
<h1 className="text-3xl font-bold tracking-tight">Dashboard</h1>
<p className="text-muted-foreground">
Welcome back! Here's an overview of your projects and team performance.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<StatsCard
title="Projects"
value={mockDashboardStats.projects}
icon={Briefcase}
iconColor="orange"
/>
<StatsCard
title="Clients"
value={mockDashboardStats.clients}
icon={Users}
iconColor="blue"
/>
<StatsCard
title="Tasks"
value={mockDashboardStats.tasks}
icon={CheckSquare}
iconColor="green"
/>
<StatsCard
title="Employees"
value={mockDashboardStats.employees}
icon={Users}
iconColor="purple"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<ChartCard
title="Total Revenue"
data={revenueChartData}
type="bar"
showGrid
/>
<ChartCard
title="Sales Overview"
data={salesChartData}
type="line"
colors={["#E53E3E", "#F97316"]}
showGrid
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
<Card className="col-span-1 hover-scale">
<CardHeader className="pb-2">
<CardTitle className="text-lg">New Employees</CardTitle>
<CardDescription>
<span className="text-green-500">+20%</span> from last month
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mb-4">10</div>
<div className="w-full bg-secondary rounded-full h-2 mb-1">
<div className="bg-orange-500 h-2 rounded-full" style={{ width: '65%' }}></div>
</div>
<div className="text-xs text-muted-foreground">
Overall Employees: 218
</div>
</CardContent>
</Card>
<Card className="col-span-1 hover-scale">
<CardHeader className="pb-2">
<CardTitle className="text-lg">Earnings</CardTitle>
<CardDescription>
<span className="text-green-500">+35.3%</span> from previous month
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mb-4">$142,500</div>
<div className="w-full bg-secondary rounded-full h-2 mb-1">
<div className="bg-orange-500 h-2 rounded-full" style={{ width: '78%' }}></div>
</div>
<div className="text-xs text-muted-foreground">
Previous Month: $105,400
</div>
</CardContent>
</Card>
<Card className="col-span-1 hover-scale">
<CardHeader className="pb-2">
<CardTitle className="text-lg">Expenses</CardTitle>
<CardDescription>
<span className="text-red-500">-5.6%</span> from previous month
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mb-4">$58,500</div>
<div className="w-full bg-secondary rounded-full h-2 mb-1">
<div className="bg-coral-500 h-2 rounded-full" style={{ width: '42%' }}></div>
</div>
<div className="text-xs text-muted-foreground">
Previous Month: $62,000
</div>
</CardContent>
</Card>
<Card className="col-span-1 hover-scale">
<CardHeader className="pb-2">
<CardTitle className="text-lg">Profit</CardTitle>
<CardDescription>
<span className="text-green-500">+70%</span> from previous month
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-3xl font-bold mb-4">$84,000</div>
<div className="w-full bg-secondary rounded-full h-2 mb-1">
<div className="bg-green-500 h-2 rounded-full" style={{ width: '85%' }}></div>
</div>
<div className="text-xs text-muted-foreground">
Previous Month: $49,400
</div>
</CardContent>
</Card>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="hover-scale">
<CardHeader>
<CardTitle>Task Statistics</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 gap-4 mb-6">
<div className="text-center">
<div className="text-3xl font-bold">365</div>
<div className="text-sm text-muted-foreground">Total Tasks</div>
</div>
<div className="text-center">
<div className="text-3xl font-bold">75</div>
<div className="text-sm text-muted-foreground">Completed Tasks</div>
</div>
</div>
<div className="space-y-5">
<div>
<div className="flex justify-between mb-1">
<span className="text-sm font-medium">Completed Tasks</span>
<span className="text-sm font-medium">156</span>
</div>
<div className="w-full bg-secondary rounded-full h-2">
<div className="bg-orange-500 h-2 rounded-full" style={{ width: '42%' }}></div>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm font-medium">In Progress Tasks</span>
<span className="text-sm font-medium">75</span>
</div>
<div className="w-full bg-secondary rounded-full h-2">
<div className="bg-purple-500 h-2 rounded-full" style={{ width: '21%' }}></div>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm font-medium">On Hold Tasks</span>
<span className="text-sm font-medium">21</span>
</div>
<div className="w-full bg-secondary rounded-full h-2">
<div className="bg-amber-500 h-2 rounded-full" style={{ width: '6%' }}></div>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm font-medium">Pending Tasks</span>
<span className="text-sm font-medium">32</span>
</div>
<div className="w-full bg-secondary rounded-full h-2">
<div className="bg-blue-500 h-2 rounded-full" style={{ width: '9%' }}></div>
</div>
</div>
<div>
<div className="flex justify-between mb-1">
<span className="text-sm font-medium">Review Tasks</span>
<span className="text-sm font-medium">5</span>
</div>
<div className="w-full bg-secondary rounded-full h-2">
<div className="bg-teal-500 h-2 rounded-full" style={{ width: '1%' }}></div>
</div>
</div>
</div>
</CardContent>
</Card>
<Card className="hover-scale">
<CardHeader>
<CardTitle>Today Absent</CardTitle>
{leavesToday.length > 2 && (
<Button variant="ghost" size="sm" className="text-xs">View all</Button>
)}
</CardHeader>
<CardContent>
{loading ? (
<div className="text-center py-8 text-muted-foreground">Loading...</div>
) : leavesToday.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
No employees on leave today
</div>
) : (
<div className="space-y-4 max-h-[400px] overflow-y-auto pr-2">
{leavesToday.map((leave) => {
const employee = getEmployeeById(leave.employeeId);
if (!employee) return null;
return (
<div
key={leave.id}
className="flex items-center justify-between p-2 rounded-lg transition-colors hover:bg-yellow-50 border border-yellow-200 bg-yellow-50/50"
>
<div className="flex items-center gap-3">
<Avatar className="border-2 border-yellow-400">
<AvatarImage src={employee.avatarUrl} />
<AvatarFallback className="bg-yellow-100 text-yellow-700">
{getInitials(employee.firstName, employee.lastName)}
</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium">
{employee.firstName} {employee.lastName}
</p>
<p className="text-xs text-muted-foreground">
{leave.type || 'Leave'} {leave.halfDay ? '(Half Day)' : ''}
</p>
</div>
</div>
<div className="text-right">
<p className="text-sm font-medium">
{formatDate(leave.startDate)}
</p>
<Badge
variant="outline"
className={getStatusBadgeClass(leave.status)}
>
{leave.status}
</Badge>
</div>
</div>
);
})}
</div>
)}
</CardContent>
</Card>
</div>
{/* Daily Work Focus Component */}
<WorkFocus />
<div className="grid grid-cols-1 gap-6">
<Card className="hover-scale">
<CardHeader className="pb-0">
<div className="flex justify-between items-center">
<CardTitle>Recent Projects</CardTitle>
<Button variant="ghost" size="sm">View all projects</Button>
</div>
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="border-b text-left">
<th className="py-3 px-4 text-sm font-medium text-muted-foreground">Project Name</th>
<th className="py-3 px-4 text-sm font-medium text-muted-foreground">Progress</th>
<th className="py-3 px-4 text-sm font-medium text-muted-foreground">Status</th>
<th className="py-3 px-4 text-sm font-medium text-muted-foreground">Action</th>
</tr>
</thead>
<tbody>
<tr className="border-b">
<td className="py-3 px-4">
<div className="flex items-center gap-3">
<Avatar className="h-8 w-8">
<AvatarFallback className="bg-orange-100 text-orange-700">OM</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium">Office Management</p>
<p className="text-xs text-muted-foreground">Client: Delta Networks</p>
</div>
</div>
</td>
<td className="py-3 px-4">
<div className="space-y-1">
<div className="text-xs text-right">65%</div>
<div className="w-32 h-2 bg-secondary rounded-full">
<div className="h-2 bg-orange-500 rounded-full" style={{ width: '65%' }}></div>
</div>
</div>
</td>
<td className="py-3 px-4">
<Badge variant="outline" className="bg-green-50 text-green-600 border-green-100">Active</Badge>
</td>
<td className="py-3 px-4">
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<TrendingUp className="h-4 w-4" />
</Button>
</td>
</tr>
<tr className="border-b">
<td className="py-3 px-4">
<div className="flex items-center gap-3">
<Avatar className="h-8 w-8">
<AvatarFallback className="bg-blue-100 text-blue-700">HA</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium">Hospital Administration</p>
<p className="text-xs text-muted-foreground">Client: City Medical</p>
</div>
</div>
</td>
<td className="py-3 px-4">
<div className="space-y-1">
<div className="text-xs text-right">40%</div>
<div className="w-32 h-2 bg-secondary rounded-full">
<div className="h-2 bg-blue-500 rounded-full" style={{ width: '40%' }}></div>
</div>
</div>
</td>
<td className="py-3 px-4">
<Badge variant="outline" className="bg-green-50 text-green-600 border-green-100">Active</Badge>
</td>
<td className="py-3 px-4">
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<TrendingUp className="h-4 w-4" />
</Button>
</td>
</tr>
<tr className="border-b">
<td className="py-3 px-4">
<div className="flex items-center gap-3">
<Avatar className="h-8 w-8">
<AvatarFallback className="bg-purple-100 text-purple-700">PM</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium">Project Management</p>
<p className="text-xs text-muted-foreground">Client: CreativeTech</p>
</div>
</div>
</td>
<td className="py-3 px-4">
<div className="space-y-1">
<div className="text-xs text-right">25%</div>
<div className="w-32 h-2 bg-secondary rounded-full">
<div className="h-2 bg-purple-500 rounded-full" style={{ width: '25%' }}></div>
</div>
</div>
</td>
<td className="py-3 px-4">
<Badge variant="outline" className="bg-green-50 text-green-600 border-green-100">Active</Badge>
</td>
<td className="py-3 px-4">
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<TrendingUp className="h-4 w-4" />
</Button>
</td>
</tr>
</tbody>
</table>
</div>
</CardContent>
</Card>
</div>
</div>
</Layout>
);
};
export default Dashboard;