Re_Figma_Code/components/Dashboard.tsx
2025-10-22 10:27:06 +05:30

371 lines
15 KiB
TypeScript

import React, { useMemo } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
import { Button } from './ui/button';
import { Progress } from './ui/progress';
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
import {
FileText,
Clock,
AlertTriangle,
TrendingUp,
CheckCircle,
Users,
Zap,
Shield,
ArrowRight,
Bell,
Star,
Activity,
Calendar,
Target,
Flame,
Settings
} from 'lucide-react';
interface DashboardProps {
onNavigate?: (page: string) => void;
onNewRequest?: () => void;
}
// Static data to prevent re-creation on each render
const STATS_DATA = [
{
title: 'Open Requests',
value: '24',
description: '+3 from last week',
icon: FileText,
trend: 'up',
color: 'text-emerald-600',
bgColor: 'bg-emerald-50',
change: '+12.5%'
},
{
title: 'Avg. SLA Compliance',
value: '87%',
description: '+5% from last month',
icon: Target,
trend: 'up',
color: 'text-blue-600',
bgColor: 'bg-blue-50',
change: '+5.8%'
},
{
title: 'High Priority Alerts',
value: '5',
description: 'Requires immediate attention',
icon: Flame,
trend: 'neutral',
color: 'text-red-600',
bgColor: 'bg-red-50',
change: '-2'
},
{
title: 'Approved This Month',
value: '142',
description: '+12% from last month',
icon: CheckCircle,
trend: 'up',
color: 'text-green-600',
bgColor: 'bg-green-50',
change: '+18.2%'
}
];
const RECENT_ACTIVITY = [
{
id: 'RE-REQ-024',
title: 'Marketing Campaign Approval',
user: 'Sarah Chen',
action: 'approved',
time: '2 minutes ago',
avatar: 'SC',
priority: 'high'
},
{
id: 'RE-REQ-023',
title: 'Budget Allocation Request',
user: 'Mike Johnson',
action: 'commented',
time: '15 minutes ago',
avatar: 'MJ',
priority: 'medium'
},
{
id: 'RE-REQ-022',
title: 'Vendor Contract Review',
user: 'David Kumar',
action: 'submitted',
time: '1 hour ago',
avatar: 'DK',
priority: 'low'
},
{
id: 'RE-REQ-021',
title: 'IT Equipment Purchase',
user: 'Lisa Wong',
action: 'escalated',
time: '2 hours ago',
avatar: 'LW',
priority: 'high'
},
{
id: 'RE-REQ-020',
title: 'Office Space Lease',
user: 'John Doe',
action: 'rejected',
time: '3 hours ago',
avatar: 'JD',
priority: 'medium'
}
];
const HIGH_PRIORITY_REQUESTS = [
{ id: 'RE-REQ-001', title: 'Emergency Equipment Purchase', sla: '2 hours left', progress: 85 },
{ id: 'RE-REQ-005', title: 'Critical Vendor Agreement', sla: '4 hours left', progress: 60 },
{ id: 'RE-REQ-012', title: 'Urgent Marketing Approval', sla: '6 hours left', progress: 40 }
];
// Utility functions outside component
const getActionColor = (action: string) => {
switch (action) {
case 'approved': return 'text-emerald-700 bg-emerald-100 border-emerald-200';
case 'rejected': return 'text-red-700 bg-red-100 border-red-200';
case 'commented': return 'text-blue-700 bg-blue-100 border-blue-200';
case 'escalated': return 'text-orange-700 bg-orange-100 border-orange-200';
case 'submitted': return 'text-purple-700 bg-purple-100 border-purple-200';
default: return 'text-gray-700 bg-gray-100 border-gray-200';
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'high': return 'bg-red-100 text-red-800 border-red-200';
case 'medium': return 'bg-orange-100 text-orange-800 border-orange-200';
case 'low': return 'bg-green-100 text-green-800 border-green-200';
default: return 'bg-gray-100 text-gray-800 border-gray-200';
}
};
export function Dashboard({ onNavigate, onNewRequest }: DashboardProps) {
// Memoize quick actions to prevent recreation on each render
const quickActions = useMemo(() => [
{ label: 'New Request', icon: FileText, action: () => onNewRequest?.(), color: 'bg-emerald-600 hover:bg-emerald-700' },
{ label: 'View Pending', icon: Clock, action: () => onNavigate?.('open-requests'), color: 'bg-blue-600 hover:bg-blue-700' },
{ label: 'Reports', icon: Activity, action: () => {}, color: 'bg-purple-600 hover:bg-purple-700' },
{ label: 'Settings', icon: Settings, action: () => {}, color: 'bg-slate-600 hover:bg-slate-700' }
], [onNavigate, onNewRequest]);
return (
<div className="space-y-6 max-w-7xl mx-auto p-4">
{/* Hero Section with Clear Background */}
<Card className="relative overflow-hidden shadow-xl border-0">
<div className="absolute inset-0 bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900"></div>
<CardContent className="relative z-10 p-8 lg:p-12">
<div className="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-6">
<div className="text-white">
<div className="flex items-center gap-4 mb-6">
<div className="w-16 h-16 bg-yellow-400 rounded-xl flex items-center justify-center shadow-lg">
<Shield className="w-8 h-8 text-slate-900" />
</div>
<div>
<h1 className="text-4xl font-bold mb-2 text-white">Welcome to Royal Enfield Portal</h1>
<p className="text-xl text-gray-200">Rev up your workflow with streamlined approvals</p>
</div>
</div>
<div className="flex flex-wrap gap-4 mt-8">
{quickActions.map((action, index) => (
<Button
key={index}
onClick={action.action}
className={`${action.color} text-white border-0 shadow-lg hover:shadow-xl transition-all duration-200`}
size="lg"
>
<action.icon className="w-5 h-5 mr-2" />
{action.label}
</Button>
))}
</div>
</div>
{/* Decorative Elements */}
<div className="hidden lg:flex items-center gap-4">
<div className="w-24 h-24 bg-yellow-400/20 rounded-full flex items-center justify-center">
<div className="w-16 h-16 bg-yellow-400/30 rounded-full flex items-center justify-center">
<Zap className="w-8 h-8 text-yellow-400" />
</div>
</div>
<div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center">
<Activity className="w-6 h-6 text-white/80" />
</div>
</div>
</div>
</CardContent>
</Card>
{/* Stats Cards with Better Contrast */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{STATS_DATA.map((stat, index) => (
<Card key={index} className="hover:shadow-xl transition-all duration-300 hover:scale-105 cursor-pointer shadow-lg">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
{stat.title}
</CardTitle>
<div className={`p-3 rounded-lg ${stat.bgColor} shadow-sm`}>
<stat.icon className={`h-5 w-5 ${stat.color}`} />
</div>
</CardHeader>
<CardContent>
<div className="flex items-end justify-between">
<div>
<div className="text-3xl font-bold text-gray-900 mb-1">{stat.value}</div>
<div className="flex items-center gap-2">
{stat.trend === 'up' && (
<div className="flex items-center gap-1 text-emerald-600">
<TrendingUp className="h-3 w-3" />
<span className="text-xs font-medium">{stat.change}</span>
</div>
)}
</div>
</div>
<ArrowRight className="h-4 w-4 text-muted-foreground hover:text-emerald-600 transition-colors" />
</div>
<p className="text-xs text-muted-foreground mt-2">{stat.description}</p>
</CardContent>
</Card>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* High Priority Alerts */}
<Card className="lg:col-span-1 shadow-lg hover:shadow-xl transition-shadow">
<CardHeader className="pb-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-3 bg-red-100 rounded-lg">
<Flame className="h-5 w-5 text-red-600" />
</div>
<div>
<CardTitle className="text-lg text-gray-900">Critical Alerts</CardTitle>
<CardDescription className="text-gray-600">Urgent attention required</CardDescription>
</div>
</div>
<Badge variant="destructive" className="animate-pulse font-semibold">
{HIGH_PRIORITY_REQUESTS.length}
</Badge>
</div>
</CardHeader>
<CardContent className="space-y-4">
{HIGH_PRIORITY_REQUESTS.map((request) => (
<div key={request.id} className="p-4 bg-red-50 rounded-xl border border-red-100 hover:shadow-md transition-all duration-200">
<div className="flex items-start justify-between gap-2 mb-3">
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<p className="font-semibold text-sm text-gray-900">{request.id}</p>
<Star className="h-3 w-3 text-red-500" />
</div>
<p className="text-sm text-gray-700">{request.title}</p>
</div>
<Badge variant="outline" className="text-xs bg-white border-red-200 text-red-700 font-medium">
{request.sla}
</Badge>
</div>
<div className="space-y-2">
<div className="flex justify-between text-xs text-gray-600">
<span>Progress</span>
<span className="font-medium">{request.progress}%</span>
</div>
<Progress
value={request.progress}
className="h-2"
/>
</div>
</div>
))}
<Button
variant="outline"
className="w-full mt-4 border-red-200 text-red-700 hover:bg-red-50 hover:border-red-300 font-medium"
onClick={() => onNavigate?.('open-requests')}
>
<AlertTriangle className="w-4 h-4 mr-2" />
View All Critical Items
</Button>
</CardContent>
</Card>
{/* Recent Activity */}
<Card className="lg:col-span-2 shadow-lg hover:shadow-xl transition-shadow">
<CardHeader className="pb-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-3 bg-blue-100 rounded-lg">
<Activity className="h-5 w-5 text-blue-600" />
</div>
<div>
<CardTitle className="text-lg text-gray-900">Recent Activity</CardTitle>
<CardDescription className="text-gray-600">Latest updates across all requests</CardDescription>
</div>
</div>
<Button variant="ghost" size="sm" className="text-blue-600 hover:bg-blue-50 font-medium">
<Calendar className="w-4 h-4 mr-2" />
View All
</Button>
</div>
</CardHeader>
<CardContent>
<div className="space-y-3">
{RECENT_ACTIVITY.map((activity) => (
<div
key={activity.id}
className="flex items-center gap-4 p-4 rounded-xl hover:bg-gray-50 transition-all duration-200 cursor-pointer border border-transparent hover:border-gray-200"
>
<div className="relative">
<Avatar className="h-10 w-10 ring-2 ring-white shadow-md">
<AvatarImage src="" />
<AvatarFallback className="bg-slate-700 text-white font-semibold">
{activity.avatar}
</AvatarFallback>
</Avatar>
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-white rounded-full flex items-center justify-center shadow-sm">
{activity.action === 'approved' && <CheckCircle className="w-3 h-3 text-green-600" />}
{activity.action === 'rejected' && <AlertTriangle className="w-3 h-3 text-red-600" />}
{activity.action === 'commented' && <Bell className="w-3 h-3 text-blue-600" />}
{activity.action === 'escalated' && <Flame className="w-3 h-3 text-orange-600" />}
{activity.action === 'submitted' && <FileText className="w-3 h-3 text-purple-600" />}
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1 flex-wrap">
<span className="font-semibold text-sm text-gray-900">{activity.id}</span>
<Badge
variant="outline"
className={`text-xs border ${getActionColor(activity.action)} font-medium`}
>
{activity.action}
</Badge>
<Badge
variant="outline"
className={`text-xs ${getPriorityColor(activity.priority)} font-medium`}
>
{activity.priority}
</Badge>
</div>
<p className="text-sm text-gray-700 line-clamp-1 mb-1">{activity.title}</p>
<p className="text-xs text-muted-foreground">
by <span className="font-medium text-gray-900">{activity.user}</span> {activity.time}
</p>
</div>
<ArrowRight className="h-4 w-4 text-muted-foreground hover:text-blue-600 transition-colors" />
</div>
))}
</div>
</CardContent>
</Card>
</div>
</div>
);
}