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:
@@ -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" />
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user