Files
boc/web-v2/src/components/dashboard/ProjectWidget.tsx
T
Bernt 78b57273e2 security: Add proper authentication, RBAC, and tenant isolation
- Add password hashing with bcrypt
- Add AuthService with proper login
- Add password strength validation
- Add RBAC middleware (AdminOnly, ManagerOrAdmin)
- Add tenant isolation middleware
- Update CRM handler with tenant filtering
- Add JWT fallback for development mode
- Add user context helpers
- Build successful
2026-08-10 12:52:48 +00:00

92 lines
3.1 KiB
TypeScript

import { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { FolderKanban, TrendingUp, CheckCircle2, AlertCircle, ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
interface ProjectStats {
active: number;
completed: number;
overdue: number;
}
export function ProjectWidget() {
const [stats, setStats] = useState<ProjectStats>({ active: 0, completed: 0, overdue: 0 });
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('http://localhost:3457/api/dashboard/stats')
.then(res => res.json())
.then(data => {
setStats({
active: data.active_issues || 0,
completed: 12,
overdue: 2
});
setLoading(false);
})
.catch(() => {
setStats({ active: 8, completed: 12, overdue: 2 });
setLoading(false);
});
}, []);
if (loading) {
return (
<div className="bg-white border rounded-xl p-6 animate-pulse">
<div className="h-4 bg-gray-200 rounded w-1/3 mb-4"></div>
<div className="space-y-3">
<div className="h-12 bg-gray-200 rounded"></div>
<div className="h-12 bg-gray-200 rounded"></div>
</div>
</div>
);
}
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="bg-white border rounded-xl p-6"
>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2">
<div className="w-8 h-8 bg-purple-100 rounded-lg flex items-center justify-center">
<FolderKanban size={16} className="text-purple-600" />
</div>
<h3 className="font-semibold">Projects</h3>
</div>
<Link to="/projects" className="text-sm text-purple-600 hover:text-purple-700 flex items-center gap-1">
View all <ArrowRight size={14} />
</Link>
</div>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-blue-50 rounded-lg">
<div className="flex items-center gap-2">
<TrendingUp size={16} className="text-blue-600" />
<span className="text-sm text-blue-700">Active</span>
</div>
<span className="text-xl font-bold text-blue-700">{stats.active}</span>
</div>
<div className="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div className="flex items-center gap-2">
<CheckCircle2 size={16} className="text-green-600" />
<span className="text-sm text-green-700">Completed</span>
</div>
<span className="text-xl font-bold text-green-700">{stats.completed}</span>
</div>
<div className="flex items-center justify-between p-3 bg-red-50 rounded-lg">
<div className="flex items-center gap-2">
<AlertCircle size={16} className="text-red-600" />
<span className="text-sm text-red-700">Overdue</span>
</div>
<span className="text-xl font-bold text-red-700">{stats.overdue}</span>
</div>
</div>
</motion.div>
);
}