import React, { useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Download, Users, TrendingUp, Clock, AlertTriangle, Award } from "lucide-react"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from "recharts"; import { Badge } from "@/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { useToast } from "@/components/ui/use-toast"; import ReportInsightsBanner from "./ReportInsightsBanner"; export default function StaffPerformanceReport({ staff, events, userRole = 'admin' }) { const { toast } = useToast(); // Calculate staff metrics const staffMetrics = staff.map(s => { const assignments = events.filter(e => e.assigned_staff?.some(as => as.staff_id === s.id) ); const completedShifts = assignments.filter(e => e.status === 'Completed').length; const totalShifts = s.total_shifts || assignments.length || 1; const fillRate = totalShifts > 0 ? ((completedShifts / totalShifts) * 100).toFixed(1) : 0; const reliability = s.reliability_score || s.shift_coverage_percentage || 85; return { id: s.id, name: s.employee_name, position: s.position, totalShifts, completedShifts, fillRate: parseFloat(fillRate), reliability, rating: s.rating || 4.2, cancellations: s.cancellation_count || 0, noShows: s.no_show_count || 0, }; }).sort((a, b) => b.reliability - a.reliability); // Top performers const topPerformers = staffMetrics.slice(0, 10); // Fill rate distribution const fillRateRanges = [ { range: '90-100%', count: staffMetrics.filter(s => s.fillRate >= 90).length }, { range: '80-89%', count: staffMetrics.filter(s => s.fillRate >= 80 && s.fillRate < 90).length }, { range: '70-79%', count: staffMetrics.filter(s => s.fillRate >= 70 && s.fillRate < 80).length }, { range: '60-69%', count: staffMetrics.filter(s => s.fillRate >= 60 && s.fillRate < 70).length }, { range: '<60%', count: staffMetrics.filter(s => s.fillRate < 60).length }, ]; const avgReliability = staffMetrics.reduce((sum, s) => sum + s.reliability, 0) / staffMetrics.length || 0; const avgFillRate = staffMetrics.reduce((sum, s) => sum + s.fillRate, 0) / staffMetrics.length || 0; const totalCancellations = staffMetrics.reduce((sum, s) => sum + s.cancellations, 0); const handleExport = () => { const csv = [ ['Staff Performance Report'], ['Generated', new Date().toISOString()], [''], ['Summary'], ['Average Reliability', `${avgReliability.toFixed(1)}%`], ['Average Fill Rate', `${avgFillRate.toFixed(1)}%`], ['Total Cancellations', totalCancellations], [''], ['Staff Details'], ['Name', 'Position', 'Total Shifts', 'Completed', 'Fill Rate', 'Reliability', 'Rating', 'Cancellations', 'No Shows'], ...staffMetrics.map(s => [ s.name, s.position, s.totalShifts, s.completedShifts, `${s.fillRate}%`, `${s.reliability}%`, s.rating, s.cancellations, s.noShows, ]), ].map(row => row.join(',')).join('\n'); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `staff-performance-${new Date().toISOString().split('T')[0]}.csv`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast({ title: "✅ Report Exported", description: "Performance report downloaded as CSV" }); }; // At-risk workers const atRiskWorkers = staffMetrics.filter(s => s.reliability < 70 || s.noShows > 2); return (
Reliability, fill rates & actionable insights
Avg Reliability
{avgReliability.toFixed(1)}%
= 85 ? 'text-green-600' : 'text-amber-600'}`}> {avgReliability >= 85 ? '✓ Excellent' : avgReliability >= 70 ? '⚠ Needs attention' : '✗ Critical'}
Avg Fill Rate
{avgFillRate.toFixed(1)}%
{avgFillRate >= 90 ? 'Above target' : `${(90 - avgFillRate).toFixed(1)}% below target`}
Cancellations
{totalCancellations}
~${(totalCancellations * 150).toLocaleString()} impact
Quick Decision
{atRiskWorkers.length > 0 ? `Review ${atRiskWorkers.length} at-risk workers before next scheduling cycle` : avgFillRate < 90 ? 'Expand worker pool by 15% to improve fill rate' : 'Performance healthy — consider bonuses for top 10%' }