Files
Nexus/apps/web/src/components/bench/BenchResourceCard.tsx
T
Hartmut 8f7c69056f refactor(web): remove unnecessary "use client" from 6 pure-render components
BenchResourceCard, MobileProjectCard, MobileCapacityCard, DynamicFieldRenderer,
BudgetStatusBar, and TimelineHeader use no hooks, event handlers, or browser APIs —
they can be server components, reducing client bundle size.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 23:36:34 +02:00

100 lines
3.3 KiB
TypeScript

import Link from "next/link";
interface BenchResourceCardProps {
id: string;
name: string;
eid: string;
role: string | null;
chapter: string | null;
availableHours: number;
availableHoursPerDay: number;
workingDays: number;
}
export function BenchResourceCard({
id,
name,
eid,
role,
chapter,
availableHours,
availableHoursPerDay,
}: BenchResourceCardProps) {
const initials = name
.split(" ")
.slice(0, 2)
.map((w) => w[0]?.toUpperCase() ?? "")
.join("");
const availabilityLevel =
availableHoursPerDay >= 6 ? "high" : availableHoursPerDay >= 3 ? "medium" : "low";
const levelClass =
availabilityLevel === "high"
? "border-emerald-300 dark:border-emerald-700 bg-emerald-50 dark:bg-emerald-950/20"
: availabilityLevel === "medium"
? "border-amber-300 dark:border-amber-700 bg-amber-50 dark:bg-amber-950/20"
: "border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900/20";
const barColor =
availabilityLevel === "high"
? "bg-emerald-500"
: availabilityLevel === "medium"
? "bg-amber-500"
: "bg-gray-400";
const barWidth = Math.min(100, Math.round((availableHoursPerDay / 8) * 100));
return (
<div className={`rounded-xl border p-4 space-y-3 ${levelClass}`}>
<div className="flex items-start gap-3">
<div className="h-10 w-10 shrink-0 rounded-full bg-brand-100 dark:bg-brand-900/40 flex items-center justify-center">
<span className="text-sm font-semibold text-brand-700 dark:text-brand-300">
{initials}
</span>
</div>
<div className="min-w-0 flex-1">
<div className="font-medium text-sm text-gray-900 dark:text-gray-100 truncate">
{name}
</div>
<div className="text-xs text-gray-500 dark:text-gray-400">{eid}</div>
</div>
</div>
{(role ?? chapter) && (
<div className="flex flex-wrap gap-1.5">
{role && (
<span className="rounded-full bg-brand-100 dark:bg-brand-900/40 px-2 py-0.5 text-[11px] font-medium text-brand-700 dark:text-brand-300">
{role}
</span>
)}
{chapter && (
<span className="rounded-full bg-gray-100 dark:bg-gray-800 px-2 py-0.5 text-[11px] text-gray-600 dark:text-gray-400">
{chapter}
</span>
)}
</div>
)}
<div>
<div className="flex items-center justify-between text-xs mb-1">
<span className="text-gray-500 dark:text-gray-400">Available capacity</span>
<span className="font-semibold text-gray-800 dark:text-gray-200">
{availableHoursPerDay.toFixed(1)}h/day · {availableHours.toFixed(0)}h total
</span>
</div>
<div className="h-1.5 rounded-full bg-gray-200 dark:bg-gray-700 overflow-hidden">
<div className={`h-full rounded-full ${barColor}`} style={{ width: `${barWidth}%` }} />
</div>
</div>
<Link
href={`/resources/${id}`}
className="block w-full rounded-lg border border-gray-200 dark:border-gray-700 py-1.5 text-center text-xs font-medium text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
View Profile
</Link>
</div>
);
}