import React, { useState, useMemo } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Progress } from "@/components/ui/progress"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Award, Search, Plus, AlertTriangle, CheckCircle2, Clock, XCircle, Download, Send, Eye, Edit2, ShieldCheck, FileText, Sparkles, Calendar, User, Building2, ChevronRight, Filter, Bell, TrendingUp } from "lucide-react"; import { format, differenceInDays, parseISO } from "date-fns"; import { useToast } from "@/components/ui/use-toast"; import { motion, AnimatePresence } from "framer-motion"; const REQUIRED_CERTIFICATIONS = ["Background Check", "RBS", "Food Handler"]; const CERT_CONFIG = { "Background Check": { color: "from-purple-500 to-purple-600", bgColor: "bg-purple-50", textColor: "text-purple-700", borderColor: "border-purple-200", icon: ShieldCheck, description: "Criminal background verification" }, "RBS": { color: "from-blue-500 to-blue-600", bgColor: "bg-blue-50", textColor: "text-blue-700", borderColor: "border-blue-200", icon: Award, description: "Responsible Beverage Server" }, "Food Handler": { color: "from-emerald-500 to-emerald-600", bgColor: "bg-emerald-50", textColor: "text-emerald-700", borderColor: "border-emerald-200", icon: FileText, description: "Food safety certification" }, }; export default function Certification() { const { toast } = useToast(); const queryClient = useQueryClient(); const [searchTerm, setSearchTerm] = useState(""); const [activeTab, setActiveTab] = useState("all"); const [certTypeFilter, setCertTypeFilter] = useState("all"); const [showAddModal, setShowAddModal] = useState(false); const [editingCert, setEditingCert] = useState(null); const [showReportModal, setShowReportModal] = useState(false); const [selectedEmployee, setSelectedEmployee] = useState(null); const { data: user } = useQuery({ queryKey: ['current-user-cert'], queryFn: () => base44.auth.me(), }); const { data: certifications = [] } = useQuery({ queryKey: ['certifications'], queryFn: () => base44.entities.Certification.list(), initialData: [], }); const { data: staff = [] } = useQuery({ queryKey: ['staff-for-cert'], queryFn: () => base44.entities.Staff.list(), initialData: [], }); const userRole = user?.user_role || user?.role || "admin"; const isVendor = userRole === "vendor"; const isProcurement = userRole === "procurement"; const calculateStatus = (expiryDate) => { if (!expiryDate) return "pending"; const days = differenceInDays(parseISO(expiryDate), new Date()); if (days < 0) return "expired"; if (days <= 30) return "expiring_soon"; return "current"; }; const processedCerts = useMemo(() => { return certifications.map(cert => ({ ...cert, days_until_expiry: cert.expiry_date ? differenceInDays(parseISO(cert.expiry_date), new Date()) : null, status: calculateStatus(cert.expiry_date), })); }, [certifications]); const employeeCertMap = useMemo(() => { const map = {}; staff.forEach(s => { map[s.id] = { employee_id: s.id, employee_name: s.employee_name, vendor_id: s.vendor_id, vendor_name: s.vendor_name, position: s.position, certifications: { "Background Check": null, "RBS": null, "Food Handler": null }, allCurrent: false, hasExpired: false, hasExpiringSoon: false, missingCount: 3, canWork: false, complianceScore: 0, }; }); processedCerts.forEach(cert => { const key = cert.employee_id; if (!map[key]) { map[key] = { employee_id: cert.employee_id, employee_name: cert.employee_name, vendor_id: cert.vendor_id, vendor_name: cert.vendor_name, position: "", certifications: { "Background Check": null, "RBS": null, "Food Handler": null }, allCurrent: false, hasExpired: false, hasExpiringSoon: false, missingCount: 3, canWork: false, complianceScore: 0, }; } if (REQUIRED_CERTIFICATIONS.includes(cert.certification_type)) { map[key].certifications[cert.certification_type] = cert; } }); Object.values(map).forEach(emp => { const certs = Object.values(emp.certifications); const validCerts = certs.filter(c => c && c.status === "current"); const expiredCerts = certs.filter(c => c && c.status === "expired"); const expiringSoonCerts = certs.filter(c => c && c.status === "expiring_soon"); const missingCerts = certs.filter(c => !c); emp.allCurrent = validCerts.length === 3; emp.hasExpired = expiredCerts.length > 0; emp.hasExpiringSoon = expiringSoonCerts.length > 0; emp.missingCount = missingCerts.length; emp.canWork = validCerts.length === 3 || (validCerts.length + expiringSoonCerts.length === 3); emp.complianceScore = Math.round(((validCerts.length + expiringSoonCerts.length * 0.5) / 3) * 100); }); return map; }, [processedCerts, staff]); const employeeList = Object.values(employeeCertMap); const filteredEmployees = useMemo(() => { let filtered = employeeList; if (isVendor && user?.vendor_id) { filtered = filtered.filter(e => e.vendor_id === user.vendor_id); } if (searchTerm) { filtered = filtered.filter(e => e.employee_name?.toLowerCase().includes(searchTerm.toLowerCase()) ); } if (activeTab === "compliant") filtered = filtered.filter(e => e.allCurrent); else if (activeTab === "expiring") filtered = filtered.filter(e => e.hasExpiringSoon); else if (activeTab === "expired") filtered = filtered.filter(e => e.hasExpired); else if (activeTab === "incomplete") filtered = filtered.filter(e => e.missingCount > 0); if (certTypeFilter !== "all") { filtered = filtered.filter(e => { const cert = e.certifications[certTypeFilter]; return cert !== null; }); } return filtered; }, [employeeList, searchTerm, activeTab, certTypeFilter, isVendor, user]); const stats = useMemo(() => { const total = employeeList.length; const compliant = employeeList.filter(e => e.allCurrent).length; const expiring = employeeList.filter(e => e.hasExpiringSoon).length; const expired = employeeList.filter(e => e.hasExpired).length; const incomplete = employeeList.filter(e => e.missingCount > 0).length; const avgCompliance = total > 0 ? Math.round(employeeList.reduce((sum, e) => sum + e.complianceScore, 0) / total) : 0; return { total, compliant, expiring, expired, incomplete, avgCompliance }; }, [employeeList]); const saveCertMutation = useMutation({ mutationFn: async (data) => { if (data.id) return base44.entities.Certification.update(data.id, data); return base44.entities.Certification.create(data); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['certifications'] }); setShowAddModal(false); setEditingCert(null); toast({ title: "✅ Certification saved" }); }, }); const sendExpiryAlert = async (cert) => { try { await base44.integrations.Core.SendEmail({ to: user?.email || "admin@company.com", subject: `⚠️ Certification Expiring: ${cert.employee_name} - ${cert.certification_type}`, body: `
Employee: ${cert.employee_name}
Certification: ${cert.certification_type}
Expiry Date: ${format(parseISO(cert.expiry_date), 'MMM d, yyyy')}
Days Until Expiry: ${cert.days_until_expiry} days
` }); toast({ title: "✅ Alert sent" }); } catch (error) { toast({ title: "Failed to send alert", variant: "destructive" }); } }; const sendComplianceReport = async (clientEmail) => { const compliantEmployees = employeeList.filter(e => e.allCurrent); try { await base44.integrations.Core.SendEmail({ to: clientEmail, subject: "Staff Compliance Report", body: `Generated: ${format(new Date(), 'MMM d, yyyy')}
Total Staff: ${stats.total}
Fully Compliant: ${stats.compliant}
Average Compliance: ${stats.avgCompliance}%
Track & manage workforce compliance
{stats.total}
{stats.compliant}
{stats.expiring}
{stats.expired}
{stats.avgCompliance}%
Try adjusting your search or filters
{emp.position || "Staff Member"}
{type}
{cert ? ({cert.status === "expired" ? "Expired" : `Expires: ${format(parseISO(cert.expiry_date), 'MMM d, yyyy')}`} {cert.days_until_expiry !== null && cert.days_until_expiry >= 0 && ` (${cert.days_until_expiry}d)`}
) : (Not uploaded
)}{stats.total}
{stats.compliant}
{stats.expiring}
{stats.expired}