fix(ui): add dark mode variants to dashboard, layout, notification and chargeability components

Add missing dark: class variants for backgrounds, borders, and text across
dashboard widgets, AppShell sidebar, notification cards, and the chargeability
report table. Replace hardcoded slate/gray hex values with CSS variable
references. Fix chargeability hover tint and remove ineffective sticky thead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 09:20:50 +02:00
parent 2a91257e69
commit 9ba49c9ab8
9 changed files with 46 additions and 42 deletions
@@ -16,14 +16,14 @@ export function AddWidgetModal({ onAdd, onClose }: AddWidgetModalProps) {
if (e.target === e.currentTarget) onClose(); if (e.target === e.currentTarget) onClose();
}} }}
> >
<div className="bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col"> <div className="bg-white dark:bg-gray-900 rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col border border-transparent dark:border-gray-700/60">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200"> <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<h2 className="text-lg font-semibold text-gray-900">Add Widget</h2> <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Add Widget</h2>
<button <button
type="button" type="button"
onClick={onClose} onClick={onClose}
className="text-gray-400 hover:text-gray-600 text-2xl leading-none" className="text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 text-2xl leading-none"
> >
× ×
</button> </button>
@@ -40,13 +40,13 @@ export function AddWidgetModal({ onAdd, onClose }: AddWidgetModalProps) {
onAdd(def.type); onAdd(def.type);
onClose(); onClose();
}} }}
className="flex items-start gap-4 p-4 border border-gray-200 rounded-xl hover:border-brand-400 hover:bg-brand-50 transition-colors text-left" className="flex items-start gap-4 p-4 border border-gray-200 dark:border-gray-700 rounded-xl hover:border-brand-400 hover:bg-brand-50 dark:hover:border-[rgb(var(--accent-500))] dark:hover:bg-[rgb(var(--accent-500)/0.08)] transition-colors text-left"
> >
<span className="text-3xl shrink-0">{def.icon}</span> <span className="text-3xl shrink-0">{def.icon}</span>
<div> <div>
<div className="font-semibold text-gray-900 text-sm">{def.label}</div> <div className="font-semibold text-gray-900 dark:text-gray-100 text-sm">{def.label}</div>
<div className="text-xs text-gray-500 mt-1">{def.description}</div> <div className="text-xs text-gray-500 dark:text-gray-400 mt-1">{def.description}</div>
<div className="text-xs text-gray-400 mt-1"> <div className="text-xs text-gray-400 dark:text-gray-500 mt-1">
Default: {def.defaultSize.w}×{def.defaultSize.h} grid units Default: {def.defaultSize.w}×{def.defaultSize.h} grid units
</div> </div>
</div> </div>
@@ -74,7 +74,7 @@ export function WidgetContainer({
className={`rounded-xl border px-3 py-1.5 text-[11px] font-semibold transition ${ className={`rounded-xl border px-3 py-1.5 text-[11px] font-semibold transition ${
showDetails showDetails
? "border-brand-200 bg-brand-50 text-brand-700 hover:bg-brand-100 dark:border-brand-500/30 dark:bg-brand-500/10 dark:text-brand-300" ? "border-brand-200 bg-brand-50 text-brand-700 hover:bg-brand-100 dark:border-brand-500/30 dark:bg-brand-500/10 dark:text-brand-300"
: "border-gray-200 bg-white/80 text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:border-gray-700 dark:bg-gray-900/70 dark:text-gray-400 dark:hover:text-gray-200" : "border-gray-200 bg-white/80 text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:border-gray-600/60 dark:bg-[rgb(var(--surface-elevated))] dark:text-gray-400 dark:hover:text-gray-200"
}`} }`}
title={showDetails ? "Hide details" : "Show details"} title={showDetails ? "Hide details" : "Show details"}
> >
@@ -97,7 +97,7 @@ export function WidgetContainer({
</div> </div>
</div> </div>
<div className="mx-4 border-t border-gray-200/80 dark:border-gray-800" /> <div className="mx-4 border-t border-gray-200/80 dark:border-gray-700/40" />
<div className="flex-1 overflow-auto p-4 pt-3">{children}</div> <div className="flex-1 overflow-auto p-4 pt-3">{children}</div>
</motion.div> </motion.div>
@@ -97,8 +97,8 @@ export default function PeakTimesChart({
} }
return ( return (
<div className="flex h-full min-h-[15rem] flex-col rounded-[22px] border border-slate-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-slate-700/70 dark:bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(15,23,42,0.98))]"> <div className="flex h-full min-h-[15rem] flex-col rounded-[22px] border border-gray-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-gray-700/60 dark:bg-[linear-gradient(180deg,rgb(var(--surface-card)/0.97),rgb(var(--surface-card)/0.99))]">
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200/70 pb-2 dark:border-slate-700/60"> <div className="flex flex-wrap items-center justify-between gap-2 border-b border-gray-200/70 pb-2 dark:border-gray-700/60">
<div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400"> <div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400">
Overall Utilization Overall Utilization
</div> </div>
@@ -173,15 +173,15 @@ export default function PeakTimesChart({
onClick={() => onSelectedPeriodChange?.(row.period)} onClick={() => onSelectedPeriodChange?.(row.period)}
style={{ style={{
backgroundColor: isPinned backgroundColor: isPinned
? "rgba(14, 165, 233, 0.08)" ? "rgb(var(--accent-400) / 0.12)"
: isActive : isActive
? "rgba(148, 163, 184, 0.08)" ? "rgb(var(--accent-400) / 0.05)"
: "transparent", : "transparent",
}} }}
> >
<div className="relative flex min-h-0 flex-1 w-full items-end justify-center px-0.5"> <div className="relative flex min-h-0 flex-1 w-full items-end justify-center px-0.5">
<div className="relative h-full w-full max-w-[34px] sm:max-w-[42px]"> <div className="relative h-full w-full max-w-[34px] sm:max-w-[42px]">
<div className="absolute inset-x-0 bottom-0 h-full rounded-t-xl bg-slate-100 dark:bg-slate-800/80" /> <div className="absolute inset-x-0 bottom-0 h-full rounded-t-xl bg-gray-100 dark:bg-gray-700/60" />
<div <div
className={`absolute inset-x-0 bottom-0 rounded-t-xl transition-all duration-150 ${utilizationBarTone(row.utilizationPct)} ${ className={`absolute inset-x-0 bottom-0 rounded-t-xl transition-all duration-150 ${utilizationBarTone(row.utilizationPct)} ${
isActive ? "opacity-100" : "opacity-80 group-hover:opacity-100" isActive ? "opacity-100" : "opacity-80 group-hover:opacity-100"
@@ -286,7 +286,7 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
].map((metric) => ( ].map((metric) => (
<div <div
key={metric.label} key={metric.label}
className="min-w-0 flex-1 rounded-full border border-slate-200/80 bg-white/85 px-3 py-1.5 shadow-sm dark:border-slate-700/70 dark:bg-slate-900/60" className="min-w-0 flex-1 rounded-full border border-gray-200/80 bg-white/85 px-3 py-1.5 shadow-sm dark:border-gray-700/60 dark:bg-[rgb(var(--surface-elevated)/0.85)]"
> >
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<span className="text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-400"> <span className="text-[10px] font-semibold uppercase tracking-[0.14em] text-slate-400">
@@ -329,8 +329,8 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
</div> </div>
<div className="mt-2 min-h-0 lg:mt-0"> <div className="mt-2 min-h-0 lg:mt-0">
<div className="flex h-full flex-col rounded-[22px] border border-slate-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-slate-700/70 dark:bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(15,23,42,0.98))]"> <div className="flex h-full flex-col rounded-[22px] border border-gray-200/80 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(248,250,252,0.96))] p-3 shadow-sm dark:border-gray-700/60 dark:bg-[linear-gradient(180deg,rgb(var(--surface-card)/0.97),rgb(var(--surface-card)/0.99))]">
<div className="flex flex-wrap items-start justify-between gap-2 border-b border-slate-200/70 pb-2 dark:border-slate-700/60"> <div className="flex flex-wrap items-start justify-between gap-2 border-b border-gray-200/70 pb-2 dark:border-gray-700/60">
<div> <div>
<div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400"> <div className="text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400">
Department Utilization Department Utilization
@@ -373,7 +373,7 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
</div> </div>
</div> </div>
<div <div
className="relative h-2.5 overflow-visible rounded-full bg-slate-100 dark:bg-slate-800/80" className="relative h-2.5 overflow-visible rounded-full bg-gray-100 dark:bg-gray-700/60"
title={`${group.name}: ${group.utilizationPct}% utilization, ${formatHours(group.hours)}h booked, ${formatHours(group.capacityHours)}h capacity, ${formatHours(group.remainingHours)}h free, ${formatHours(group.overbookedHours)}h overbooked`} title={`${group.name}: ${group.utilizationPct}% utilization, ${formatHours(group.hours)}h booked, ${formatHours(group.capacityHours)}h capacity, ${formatHours(group.remainingHours)}h free, ${formatHours(group.overbookedHours)}h overbooked`}
> >
<div <div
@@ -391,7 +391,7 @@ export function PeakTimesWidget({ config, onConfigChange }: WidgetProps) {
); );
}) })
) : ( ) : (
<div className="rounded-2xl border border-dashed border-slate-200 bg-slate-50/80 px-3 py-4 text-sm text-slate-400 dark:border-slate-700 dark:bg-slate-900/40"> <div className="rounded-2xl border border-dashed border-gray-200 bg-gray-50/80 px-3 py-4 text-sm text-gray-400 dark:border-gray-700 dark:bg-gray-800/40">
No department data in the selected month. No department data in the selected month.
</div> </div>
)} )}
@@ -48,7 +48,7 @@ function StatCard({
return ( return (
<FadeIn delay={delay} direction="up"> <FadeIn delay={delay} direction="up">
<div <div
className={`rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm dark:border-gray-700 dark:bg-gray-900/70 hover-lift cursor-default ${ className={`rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm dark:border-gray-700/60 dark:bg-[rgb(var(--surface-elevated))] hover-lift cursor-default ${
accentColor ? `border-l-[3px] ${accentBorder}` : "" accentColor ? `border-l-[3px] ${accentBorder}` : ""
}`} }`}
> >
@@ -112,7 +112,7 @@ export function StatCardsWidget(props: Partial<WidgetProps> = {}) {
{[...Array(4)].map((_, i) => ( {[...Array(4)].map((_, i) => (
<div <div
key={i} key={i}
className="rounded-2xl border border-gray-200 bg-gray-100 p-4 dark:border-gray-700 dark:bg-gray-800" className="rounded-2xl border border-gray-200 bg-gray-100 p-4 dark:border-gray-700/60 dark:bg-[rgb(var(--surface-elevated))]"
> >
<div className="h-3 w-20 shimmer-skeleton rounded" /> <div className="h-3 w-20 shimmer-skeleton rounded" />
<div className="h-7 w-16 bg-gray-300 dark:bg-gray-600 rounded" /> <div className="h-7 w-16 bg-gray-300 dark:bg-gray-600 rounded" />
+7 -3
View File
@@ -17,9 +17,13 @@ import { NavProgressBar } from "~/components/ui/NavProgressBar.js";
const SIDEBAR_COLLAPSED_KEY = "capakraken_sidebar_collapsed"; const SIDEBAR_COLLAPSED_KEY = "capakraken_sidebar_collapsed";
function IconFrame({ children }: { children: ReactNode }) { function IconFrame({ children, isActive }: { children: ReactNode; isActive?: boolean }) {
return ( return (
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl border border-white/60 bg-white/80 text-slate-600 shadow-sm transition-shadow duration-200 hover:shadow-[0_0_12px_rgba(var(--accent-500),0.15)] dark:border-slate-800 dark:bg-slate-900/70 dark:text-slate-300"> <span className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-xl border shadow-sm transition-all duration-200 ${
isActive
? "border-[rgb(var(--accent-300))] bg-[rgb(var(--accent-50))] text-[rgb(var(--accent-600))] shadow-[0_0_10px_rgba(var(--accent-500),0.2)] dark:border-[rgb(var(--accent-700))] dark:bg-[rgb(var(--accent-500)/0.15)] dark:text-[rgb(var(--accent-300))]"
: "border-white/60 bg-white/80 text-slate-600 hover:shadow-[0_0_12px_rgba(var(--accent-500),0.15)] dark:border-gray-700/80 dark:bg-gray-800/70 dark:text-gray-400"
}`}>
{children} {children}
</span> </span>
); );
@@ -348,7 +352,7 @@ const NavItemLink = memo(function NavItemLink({
transition={{ type: "spring", stiffness: 350, damping: 30 }} transition={{ type: "spring", stiffness: 350, damping: 30 }}
/> />
)} )}
<IconFrame>{icon}</IconFrame> <IconFrame isActive={isActive}>{icon}</IconFrame>
{!collapsed && <span className="relative flex-1">{label}</span>} {!collapsed && <span className="relative flex-1">{label}</span>}
</Link> </Link>
</NavTooltip> </NavTooltip>
@@ -245,7 +245,7 @@ export function NotificationCenterClient() {
if (isUnread) markRead.mutate({ id: n.id }); if (isUnread) markRead.mutate({ id: n.id });
}} }}
className={`w-full text-left rounded-xl border border-gray-200 dark:border-gray-700 px-4 py-3 transition-colors hover:bg-gray-50 dark:hover:bg-gray-800 ${ className={`w-full text-left rounded-xl border border-gray-200 dark:border-gray-700 px-4 py-3 transition-colors hover:bg-gray-50 dark:hover:bg-gray-800 ${
isUnread ? "bg-blue-50/60 dark:bg-blue-900/10" : "bg-white dark:bg-gray-900/70" isUnread ? "bg-blue-50/60 dark:bg-blue-900/10" : "bg-white dark:bg-[rgb(var(--surface-elevated))]"
}`} }`}
> >
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
@@ -320,7 +320,7 @@ export function NotificationCenterClient() {
reminders.map((r) => ( reminders.map((r) => (
<div <div
key={r.id} key={r.id}
className="rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900/70 px-4 py-3" className="rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-[rgb(var(--surface-elevated))] px-4 py-3"
> >
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
@@ -64,7 +64,7 @@ export function TaskCard({ task, onStatusChange, compact }: TaskCardProps) {
); );
return ( return (
<div className={`rounded-xl border border-gray-200 dark:border-gray-700 ${isDone ? "bg-gray-50 dark:bg-gray-900/40" : "bg-white dark:bg-gray-900/70"} ${compact ? "p-3" : "p-4"} transition-colors`}> <div className={`rounded-xl border border-gray-200 dark:border-gray-700 ${isDone ? "bg-gray-50 dark:bg-[rgb(var(--surface-card)/0.5)]" : "bg-white dark:bg-[rgb(var(--surface-elevated))]"} ${compact ? "p-3" : "p-4"} transition-colors`}>
{/* Header row */} {/* Header row */}
<div className="flex items-start gap-2"> <div className="flex items-start gap-2">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
@@ -539,10 +539,10 @@ export function ChargeabilityReportClient() {
return ( return (
<tr <tr
key={r.id} key={r.id}
className="cursor-pointer transition-colors hover:bg-gray-50/90 dark:hover:bg-gray-800/50" className="cursor-pointer transition-colors hover:bg-gray-50/90 dark:hover:bg-[rgb(var(--surface-elevated)/0.5)]"
onClick={() => setExpandedResource(expandedResource === r.id ? null : r.id)} onClick={() => setExpandedResource(expandedResource === r.id ? null : r.id)}
> >
<td className="sticky left-0 z-10 bg-white/95 px-4 py-3 backdrop-blur dark:bg-slate-950/95"> <td className="sticky left-0 z-10 bg-white/95 px-4 py-3 backdrop-blur dark:bg-[rgb(var(--surface-card)/0.97)]">
<div className="font-semibold text-gray-900 dark:text-gray-100">{r.displayName}</div> <div className="font-semibold text-gray-900 dark:text-gray-100">{r.displayName}</div>
<div className="mt-1 text-xs text-gray-500 dark:text-gray-400"> <div className="mt-1 text-xs text-gray-500 dark:text-gray-400">
{[r.eid, formatLocation(r), r.orgUnit].filter(Boolean).join(" · ")} {[r.eid, formatLocation(r), r.orgUnit].filter(Boolean).join(" · ")}
@@ -574,8 +574,8 @@ export function ChargeabilityReportClient() {
function renderExpandedRow(r: ResourceRow) { function renderExpandedRow(r: ResourceRow) {
if (expandedResource !== r.id) return null; if (expandedResource !== r.id) return null;
return ( return (
<tr key={`${r.id}-detail`} className="bg-gray-50/80 dark:bg-slate-900/70"> <tr key={`${r.id}-detail`} className="bg-gray-50/80 dark:bg-[rgb(var(--surface-elevated)/0.7)]">
<td className="sticky left-0 z-10 bg-gray-50/95 px-4 py-3 backdrop-blur dark:bg-slate-900/95" colSpan={3}> <td className="sticky left-0 z-10 bg-gray-50/95 px-4 py-3 backdrop-blur dark:bg-[rgb(var(--surface-elevated)/0.97)]" colSpan={3}>
<div className="space-y-1 text-xs text-gray-500 dark:text-gray-400"> <div className="space-y-1 text-xs text-gray-500 dark:text-gray-400">
<div>Mgmt: {r.mgmtGroup ?? "—"} / {r.mgmtLevel ?? "—"}</div> <div>Mgmt: {r.mgmtGroup ?? "—"} / {r.mgmtLevel ?? "—"}</div>
<div>Calendar basis: {formatLocation(r)}</div> <div>Calendar basis: {formatLocation(r)}</div>
@@ -592,7 +592,7 @@ export function ChargeabilityReportClient() {
</td> </td>
{r.months.map((m) => ( {r.months.map((m) => (
<td key={m.monthKey} className="px-3 py-3 text-center"> <td key={m.monthKey} className="px-3 py-3 text-center">
<div className="grid grid-cols-1 gap-1 rounded-xl bg-white/70 px-2 py-2 text-[10px] text-gray-500 shadow-sm dark:bg-slate-950/40 dark:text-gray-400"> <div className="grid grid-cols-1 gap-1 rounded-xl bg-white/70 px-2 py-2 text-[10px] text-gray-500 shadow-sm dark:bg-[rgb(var(--surface-elevated)/0.6)] dark:text-gray-400">
<span className="font-semibold text-green-600 dark:text-green-400">{pct(monthChargeabilityRatio(m))}</span> <span className="font-semibold text-green-600 dark:text-green-400">{pct(monthChargeabilityRatio(m))}</span>
<span>{pct(monthBusinessDevelopmentRatio(m))}</span> <span>{pct(monthBusinessDevelopmentRatio(m))}</span>
<span>{pct(monthMarketDevelopmentInnovationRatio(m))}</span> <span>{pct(monthMarketDevelopmentInnovationRatio(m))}</span>
@@ -629,7 +629,7 @@ export function ChargeabilityReportClient() {
) { ) {
const bg = isOverall const bg = isOverall
? "bg-brand-50/90 dark:bg-brand-900/25" ? "bg-brand-50/90 dark:bg-brand-900/25"
: "bg-slate-100/90 dark:bg-slate-800/70"; : "bg-slate-100/90 dark:bg-[rgb(var(--surface-elevated)/0.7)]";
return ( return (
<tr <tr
className={`${bg} font-semibold ${onClick ? "cursor-pointer" : ""}`} className={`${bg} font-semibold ${onClick ? "cursor-pointer" : ""}`}
@@ -849,29 +849,29 @@ export function ChargeabilityReportClient() {
</div> </div>
<div className="mt-4 grid gap-4 lg:grid-cols-3"> <div className="mt-4 grid gap-4 lg:grid-cols-3">
<div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-800 dark:bg-slate-900/60"> <div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-700/60 dark:bg-gray-800/50">
<div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Location basis</div> <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Location basis</div>
<div className="mt-2 flex flex-wrap gap-2"> <div className="mt-2 flex flex-wrap gap-2">
{data.explainability.locationFields.map((field) => ( {data.explainability.locationFields.map((field) => (
<span key={field} className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:bg-slate-950 dark:text-gray-300"> <span key={field} className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300">
{toTitleLabel(field)} {toTitleLabel(field)}
</span> </span>
))} ))}
</div> </div>
</div> </div>
<div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-800 dark:bg-slate-900/60"> <div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-700/60 dark:bg-gray-800/50">
<div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Month derivation</div> <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Month derivation</div>
<div className="mt-2 flex flex-wrap gap-2"> <div className="mt-2 flex flex-wrap gap-2">
{data.explainability.monthDerivationFields.map((field) => ( {data.explainability.monthDerivationFields.map((field) => (
<span key={field} className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:bg-slate-950 dark:text-gray-300"> <span key={field} className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300">
{toTitleLabel(field)} {toTitleLabel(field)}
</span> </span>
))} ))}
</div> </div>
</div> </div>
<div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-800 dark:bg-slate-900/60"> <div className="rounded-2xl border border-gray-200/80 bg-gray-50/70 p-3 dark:border-gray-700/60 dark:bg-gray-800/50">
<div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Active filters</div> <div className="text-[11px] font-semibold uppercase tracking-[0.16em] text-gray-500">Active filters</div>
<div className="mt-2 flex flex-wrap gap-2"> <div className="mt-2 flex flex-wrap gap-2">
{data.explainability.activeFilters.length > 0 ? data.explainability.activeFilters.map((field) => ( {data.explainability.activeFilters.length > 0 ? data.explainability.activeFilters.map((field) => (
@@ -879,7 +879,7 @@ export function ChargeabilityReportClient() {
{toTitleLabel(field)} {toTitleLabel(field)}
</span> </span>
)) : ( )) : (
<span className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-500 dark:border-gray-700 dark:bg-slate-950 dark:text-gray-400"> <span className="rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400">
none none
</span> </span>
)} )}
@@ -913,8 +913,8 @@ export function ChargeabilityReportClient() {
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="min-w-full text-sm"> <table className="min-w-full text-sm">
<thead> <thead>
<tr> <tr className="bg-gray-50 dark:bg-[rgb(var(--surface-elevated))]">
<th className="sticky left-0 z-10 min-w-[240px] bg-gray-50/95 px-4 py-3 text-left backdrop-blur dark:bg-gray-800/95"> <th className="sticky left-0 z-10 min-w-[240px] bg-gray-50 px-4 py-3 text-left dark:bg-[rgb(var(--surface-elevated))]">
Resource Resource
</th> </th>
<th className="w-20 px-3 py-3 text-center"><span className="inline-flex items-center justify-center gap-0.5">FTE<InfoTooltip content="Full-Time Equivalent. 1.0 = full-time, 0.5 = half-time. Used to weight chargeability averages." /></span></th> <th className="w-20 px-3 py-3 text-center"><span className="inline-flex items-center justify-center gap-0.5">FTE<InfoTooltip content="Full-Time Equivalent. 1.0 = full-time, 0.5 = half-time. Used to weight chargeability averages." /></span></th>