feat: Sprint 2 — data storytelling and visual richness
Timeline project color system: - 16-color deterministic palette (same project = same color always) - Resource panel: allocation blocks colored by project instead of uniform green - Project panel: colored left border + dot on project headers - ProjectColorLegend: floating strip showing color-to-project mapping - Utilization intensity tint: subtle background gradient on resource rows Table visual enhancements: - Resources: inline 3px utilization bar below chargeability percentage - Resources: 32px avatar circles with initials + role-derived colors - Projects: animated budget bars, styled resource count badges - Allocations: 3px left border colored by status (green/amber/blue/gray/red) KPI progress rings: - Budget utilization: ProgressRing wrapping AnimatedNumber on dashboard - Chargeability report: ring on average chargeability summary card - Resource detail: rings on chargeability target + actual metrics - Vacation balance: ring showing remaining days with color thresholds - Demand widget: mini rings on FTE fill rate per project - Resource detail: FadeIn on SkillRadarChart Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -5,8 +5,15 @@ import type { WidgetProps } from "~/components/dashboard/widget-registry.js";
|
||||
import { formatMoney } from "~/lib/format.js";
|
||||
import { InfoTooltip } from "~/components/ui/InfoTooltip.js";
|
||||
import { AnimatedNumber } from "~/components/ui/AnimatedNumber.js";
|
||||
import { ProgressRing } from "~/components/ui/ProgressRing.js";
|
||||
import { FadeIn } from "~/components/ui/FadeIn.js";
|
||||
|
||||
const ACCENT_COLORS = {
|
||||
green: "var(--color-green-500, #22c55e)",
|
||||
amber: "var(--color-amber-500, #f59e0b)",
|
||||
red: "var(--color-red-500, #ef4444)",
|
||||
} as const;
|
||||
|
||||
function StatCard({
|
||||
label,
|
||||
value,
|
||||
@@ -15,6 +22,7 @@ function StatCard({
|
||||
info,
|
||||
accentColor,
|
||||
delay = 0,
|
||||
ring,
|
||||
}: {
|
||||
label: string;
|
||||
value: number;
|
||||
@@ -23,6 +31,7 @@ function StatCard({
|
||||
info?: React.ReactNode;
|
||||
accentColor?: "green" | "amber" | "red";
|
||||
delay?: number;
|
||||
ring?: { value: number; color: string };
|
||||
}) {
|
||||
const accentBorder = accentColor === "red"
|
||||
? "border-l-red-500"
|
||||
@@ -43,9 +52,17 @@ function StatCard({
|
||||
{label}
|
||||
{info && <InfoTooltip content={info} />}
|
||||
</span>
|
||||
<span className="mt-2 text-2xl font-semibold text-gray-900 dark:text-gray-50">
|
||||
<AnimatedNumber value={value} suffix={suffix} />
|
||||
</span>
|
||||
{ring ? (
|
||||
<div className="mt-2 flex items-center gap-3">
|
||||
<ProgressRing value={ring.value} size={56} strokeWidth={4} color={ring.color}>
|
||||
<AnimatedNumber value={value} suffix={suffix} className="text-lg font-semibold text-gray-900 dark:text-gray-50" />
|
||||
</ProgressRing>
|
||||
</div>
|
||||
) : (
|
||||
<span className="mt-2 text-2xl font-semibold text-gray-900 dark:text-gray-50">
|
||||
<AnimatedNumber value={value} suffix={suffix} />
|
||||
</span>
|
||||
)}
|
||||
{sub && <span className="mt-1 text-xs text-gray-500 dark:text-gray-400">{sub}</span>}
|
||||
</div>
|
||||
</FadeIn>
|
||||
@@ -111,6 +128,7 @@ export function StatCardsWidget(_props: Partial<WidgetProps> = {}) {
|
||||
info="Sum of costs across non-cancelled allocations divided by total project budgets. Cost = resource LCR × booked hours."
|
||||
accentColor={budgetAccent}
|
||||
delay={0.15}
|
||||
ring={{ value: budgetPct, color: ACCENT_COLORS[budgetAccent] }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user