Files
Krow-workspace/frontend-web/src/components/savings/SavingsOverviewCards.jsx
2025-12-26 15:14:51 -05:00

359 lines
14 KiB
JavaScript

import React from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import {
DollarSign, TrendingUp, Target, Users, Zap,
ArrowUpRight, CheckCircle, AlertTriangle, Shield,
Clock, Award, Calendar, Package, Star, BarChart3
} from "lucide-react";
export default function SavingsOverviewCards({ metrics, projections, timeRange, userRole }) {
const getProjectedSavings = () => {
switch (timeRange) {
case "7days": return projections.sevenDays;
case "30days": return projections.thirtyDays;
case "quarter": return projections.quarter;
case "year": return projections.year;
default: return projections.thirtyDays;
}
};
const getTimeLabel = () => {
switch (timeRange) {
case "7days": return "7-Day";
case "30days": return "30-Day";
case "quarter": return "Quarterly";
case "year": return "Annual";
default: return "30-Day";
}
};
// Role-specific card configurations
const getRoleCards = () => {
switch (userRole) {
case "procurement":
return [
{
title: "Vendor Performance Score",
value: `${(metrics.avgReliability + 5).toFixed(0)}%`,
change: `Top ${metrics.activeVendors} vendors tracked`,
trend: "up",
icon: Award,
color: "blue",
description: "Network-wide average",
},
{
title: "Contract Compliance",
value: `${metrics.contractedRatio.toFixed(1)}%`,
change: `${(100 - metrics.contractedRatio).toFixed(1)}% non-compliant`,
trend: metrics.contractedRatio > 70 ? "up" : "down",
icon: Shield,
color: metrics.contractedRatio > 70 ? "green" : "red",
description: "Spend under contract",
},
{
title: "Rate Optimization",
value: `$${(metrics.avgNonContractedRate - metrics.avgContractedRate).toFixed(2)}`,
change: "per hour savings potential",
trend: "up",
icon: DollarSign,
color: "emerald",
description: "Contract vs. spot rates",
},
{
title: "SLA Adherence",
value: `${metrics.fillRate.toFixed(1)}%`,
change: `${metrics.noShowRate.toFixed(1)}% no-show rate`,
trend: metrics.fillRate > 90 ? "up" : "down",
icon: CheckCircle,
color: metrics.fillRate > 90 ? "green" : "amber",
description: "Vendor delivery rate",
},
{
title: "Network Savings",
value: `$${getProjectedSavings().toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
change: `${getTimeLabel()} projection`,
trend: "up",
icon: TrendingUp,
color: "purple",
description: "From vendor optimization",
},
];
case "operator":
return [
{
title: "Enterprise Fill Rate",
value: `${metrics.fillRate.toFixed(1)}%`,
change: `${metrics.completedOrders} orders fulfilled`,
trend: metrics.fillRate > 90 ? "up" : "down",
icon: Target,
color: metrics.fillRate > 90 ? "green" : "amber",
description: "Cross-sector average",
},
{
title: "Labor Efficiency",
value: `${metrics.avgReliability.toFixed(0)}%`,
change: `${metrics.noShowRate.toFixed(1)}% absence rate`,
trend: metrics.avgReliability > 85 ? "up" : "down",
icon: Users,
color: "blue",
description: "Workforce productivity",
},
{
title: "Cost per Order",
value: `$${(metrics.totalSpend / Math.max(metrics.completedOrders, 1)).toFixed(0)}`,
change: "average fulfillment cost",
trend: "up",
icon: DollarSign,
color: "purple",
description: "Operational efficiency",
},
{
title: "Sector Coverage",
value: `${metrics.activeVendors}`,
change: "active vendor partners",
trend: "up",
icon: Package,
color: "indigo",
description: "Available resources",
},
{
title: "Operational Savings",
value: `$${getProjectedSavings().toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
change: `${getTimeLabel()} potential`,
trend: "up",
icon: TrendingUp,
color: "emerald",
description: "From efficiency gains",
},
];
case "sector":
return [
{
title: "Location Fill Rate",
value: `${metrics.fillRate.toFixed(1)}%`,
change: `${100 - metrics.fillRate > 0 ? (100 - metrics.fillRate).toFixed(1) + '% gaps' : 'No gaps'}`,
trend: metrics.fillRate > 90 ? "up" : "down",
icon: Target,
color: metrics.fillRate > 90 ? "green" : "red",
description: "Position coverage",
},
{
title: "Staff Reliability",
value: `${metrics.avgReliability.toFixed(0)}%`,
change: `${metrics.noShowRate.toFixed(1)}% no-shows`,
trend: metrics.avgReliability > 85 ? "up" : "down",
icon: Users,
color: metrics.avgReliability > 85 ? "blue" : "amber",
description: "At your location",
},
{
title: "Weekly Hours",
value: `${Math.floor(metrics.totalWorkforce * 32)}`,
change: "scheduled this period",
trend: "up",
icon: Clock,
color: "purple",
description: "Labor hours planned",
},
{
title: "Local Spend",
value: `$${metrics.totalSpend.toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
change: "labor investment",
trend: "up",
icon: DollarSign,
color: "slate",
description: "Your location budget",
},
];
case "client":
return [
{
title: "Event Coverage",
value: `${metrics.fillRate.toFixed(1)}%`,
change: `${metrics.completedOrders} events staffed`,
trend: metrics.fillRate > 90 ? "up" : "down",
icon: Calendar,
color: metrics.fillRate > 90 ? "green" : "red",
description: "Position fill rate",
},
{
title: "Staff Quality",
value: `${metrics.avgReliability.toFixed(0)}%`,
change: "reliability score",
trend: metrics.avgReliability > 85 ? "up" : "down",
icon: Star,
color: metrics.avgReliability > 85 ? "amber" : "orange",
description: "Assigned workforce",
},
{
title: "Cost Savings",
value: `$${getProjectedSavings().toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
change: `${metrics.potentialSavingsPercent.toFixed(0)}% vs. spot rates`,
trend: "up",
icon: DollarSign,
color: "emerald",
description: `${getTimeLabel()} savings`,
},
{
title: "On-Time Rate",
value: `${(100 - metrics.noShowRate).toFixed(1)}%`,
change: "staff attendance",
trend: metrics.noShowRate < 5 ? "up" : "down",
icon: CheckCircle,
color: metrics.noShowRate < 5 ? "green" : "amber",
description: "Punctuality score",
},
];
case "vendor":
return [
{
title: "Your Fill Rate",
value: `${metrics.fillRate.toFixed(1)}%`,
change: `${metrics.completedOrders} orders completed`,
trend: metrics.fillRate > 90 ? "up" : "down",
icon: Target,
color: metrics.fillRate > 90 ? "green" : "amber",
description: "Order fulfillment",
},
{
title: "Workforce Reliability",
value: `${metrics.avgReliability.toFixed(0)}%`,
change: `${metrics.noShowRate.toFixed(1)}% no-show rate`,
trend: metrics.avgReliability > 85 ? "up" : "down",
icon: Users,
color: metrics.avgReliability > 85 ? "blue" : "orange",
description: "Your team score",
},
{
title: "Competitive Edge",
value: `$${(metrics.avgNonContractedRate - metrics.avgContractedRate).toFixed(2)}/hr`,
change: "savings vs. gig rates",
trend: "up",
icon: Zap,
color: "amber",
description: "Your value proposition",
},
{
title: "Revenue Potential",
value: `$${(metrics.totalSpend * 1.2).toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
change: "if 100% fill rate",
trend: "up",
icon: TrendingUp,
color: "purple",
description: "Growth opportunity",
},
{
title: "Active Workforce",
value: metrics.totalWorkforce.toString(),
change: "ready to deploy",
trend: "up",
icon: Shield,
color: "indigo",
description: "Available staff",
},
];
default: // admin
return [
{
title: `${getTimeLabel()} Potential Savings`,
value: `$${getProjectedSavings().toLocaleString(undefined, { maximumFractionDigits: 0 })}`,
change: `${metrics.potentialSavingsPercent.toFixed(1)}% opportunity`,
trend: "up",
icon: DollarSign,
color: "emerald",
description: "From contract conversion",
},
{
title: "Contract Coverage",
value: `${metrics.contractedRatio.toFixed(1)}%`,
change: `${(100 - metrics.contractedRatio).toFixed(1)}% non-contracted`,
trend: metrics.contractedRatio > 70 ? "up" : "down",
icon: Target,
color: metrics.contractedRatio > 70 ? "blue" : "amber",
description: "Labor under contract",
},
{
title: "Platform Reliability",
value: `${metrics.avgReliability.toFixed(0)}%`,
change: `${metrics.noShowRate.toFixed(1)}% no-show rate`,
trend: metrics.avgReliability > 85 ? "up" : "down",
icon: Users,
color: metrics.avgReliability > 85 ? "purple" : "orange",
description: "Workforce average",
},
{
title: "Fill Rate",
value: `${metrics.fillRate.toFixed(1)}%`,
change: `${metrics.completedOrders} orders completed`,
trend: metrics.fillRate > 90 ? "up" : "down",
icon: CheckCircle,
color: metrics.fillRate > 90 ? "green" : "red",
description: "Order fulfillment",
},
{
title: "Network Size",
value: `${metrics.activeVendors} / ${metrics.totalWorkforce}`,
change: "vendors / workforce",
trend: "up",
icon: BarChart3,
color: "indigo",
description: "Platform capacity",
},
];
}
};
const cards = getRoleCards();
const colorClasses = {
emerald: { bg: "bg-emerald-50", icon: "bg-emerald-500", text: "text-emerald-700", badge: "bg-emerald-100 text-emerald-700" },
blue: { bg: "bg-blue-50", icon: "bg-blue-500", text: "text-blue-700", badge: "bg-blue-100 text-blue-700" },
purple: { bg: "bg-purple-50", icon: "bg-purple-500", text: "text-purple-700", badge: "bg-purple-100 text-purple-700" },
green: { bg: "bg-green-50", icon: "bg-green-500", text: "text-green-700", badge: "bg-green-100 text-green-700" },
amber: { bg: "bg-amber-50", icon: "bg-amber-500", text: "text-amber-700", badge: "bg-amber-100 text-amber-700" },
orange: { bg: "bg-orange-50", icon: "bg-orange-500", text: "text-orange-700", badge: "bg-orange-100 text-orange-700" },
red: { bg: "bg-red-50", icon: "bg-red-500", text: "text-red-700", badge: "bg-red-100 text-red-700" },
indigo: { bg: "bg-indigo-50", icon: "bg-indigo-500", text: "text-indigo-700", badge: "bg-indigo-100 text-indigo-700" },
slate: { bg: "bg-slate-50", icon: "bg-slate-500", text: "text-slate-700", badge: "bg-slate-100 text-slate-700" },
};
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{cards.map((card, index) => {
const colors = colorClasses[card.color];
const Icon = card.icon;
return (
<Card key={index} className={`${colors.bg} border-0 shadow-sm hover:shadow-md transition-all`}>
<CardContent className="p-5">
<div className="flex items-start justify-between mb-3">
<div className={`w-12 h-12 ${colors.icon} rounded-xl flex items-center justify-center`}>
<Icon className="w-6 h-6 text-white" />
</div>
<Badge className={`${colors.badge} border-0 text-xs font-medium`}>
{card.trend === "up" ? (
<ArrowUpRight className="w-3 h-3 mr-1" />
) : (
<AlertTriangle className="w-3 h-3 mr-1" />
)}
{card.change}
</Badge>
</div>
<p className={`text-xs ${colors.text} uppercase tracking-wider font-semibold mb-1`}>
{card.title}
</p>
<p className={`text-3xl font-bold ${colors.text}`}>{card.value}</p>
<p className="text-xs text-slate-500 mt-1">{card.description}</p>
</CardContent>
</Card>
);
})}
</div>
);
}