Add color-coded backgrounds to DecisionCard
All checks were successful
Deploy / deploy (push) Successful in 2m26s

Per dashboard.md spec requirements:
- RED background and text for REST decisions
- YELLOW background and text for GENTLE/LIGHT/REDUCED decisions
- GREEN background and text for TRAIN decisions

Added 8 new tests for color-coded backgrounds (19 total).
Updated IMPLEMENTATION_PLAN.md to mark spec gap as complete.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-11 23:11:10 +00:00
parent 31932a88bf
commit 5cac8f3267
3 changed files with 148 additions and 16 deletions

View File

@@ -122,6 +122,112 @@ describe("DecisionCard", () => {
});
});
describe("color-coded backgrounds", () => {
it("renders REST with red background", () => {
const decision: Decision = {
status: "REST",
reason: "Recovery day",
icon: "🛌",
};
const { container } = render(<DecisionCard decision={decision} />);
const card = container.firstChild as HTMLElement;
expect(card).toHaveClass("bg-red-100", "border-red-300");
});
it("renders GENTLE with yellow background", () => {
const decision: Decision = {
status: "GENTLE",
reason: "Light movement",
icon: "🧘",
};
const { container } = render(<DecisionCard decision={decision} />);
const card = container.firstChild as HTMLElement;
expect(card).toHaveClass("bg-yellow-100", "border-yellow-300");
});
it("renders LIGHT with yellow background", () => {
const decision: Decision = {
status: "LIGHT",
reason: "Moderate intensity",
icon: "🚶",
};
const { container } = render(<DecisionCard decision={decision} />);
const card = container.firstChild as HTMLElement;
expect(card).toHaveClass("bg-yellow-100", "border-yellow-300");
});
it("renders REDUCED with yellow background", () => {
const decision: Decision = {
status: "REDUCED",
reason: "Lower intensity",
icon: "⬇️",
};
const { container } = render(<DecisionCard decision={decision} />);
const card = container.firstChild as HTMLElement;
expect(card).toHaveClass("bg-yellow-100", "border-yellow-300");
});
it("renders TRAIN with green background", () => {
const decision: Decision = {
status: "TRAIN",
reason: "Full intensity approved",
icon: "🏃",
};
const { container } = render(<DecisionCard decision={decision} />);
const card = container.firstChild as HTMLElement;
expect(card).toHaveClass("bg-green-100", "border-green-300");
});
it("renders reason with appropriate text color for REST", () => {
const decision: Decision = {
status: "REST",
reason: "Rest message",
icon: "🛌",
};
render(<DecisionCard decision={decision} />);
const reason = screen.getByText("Rest message");
expect(reason).toHaveClass("text-red-700");
});
it("renders reason with appropriate text color for GENTLE", () => {
const decision: Decision = {
status: "GENTLE",
reason: "Gentle message",
icon: "🧘",
};
render(<DecisionCard decision={decision} />);
const reason = screen.getByText("Gentle message");
expect(reason).toHaveClass("text-yellow-700");
});
it("renders reason with appropriate text color for TRAIN", () => {
const decision: Decision = {
status: "TRAIN",
reason: "Train message",
icon: "🏃",
};
render(<DecisionCard decision={decision} />);
const reason = screen.getByText("Train message");
expect(reason).toHaveClass("text-green-700");
});
});
describe("styling", () => {
it("renders within a bordered container", () => {
const decision: Decision = {
@@ -133,7 +239,7 @@ describe("DecisionCard", () => {
const { container } = render(<DecisionCard decision={decision} />);
const card = container.firstChild as HTMLElement;
expect(card).toHaveClass("rounded-lg", "border", "p-6");
expect(card).toHaveClass("rounded-lg", "p-6");
});
it("renders status as bold heading", () => {
@@ -150,17 +256,17 @@ describe("DecisionCard", () => {
expect(heading).toHaveClass("font-bold");
});
it("renders reason with muted color", () => {
it("renders reason with status-specific color", () => {
const decision: Decision = {
status: "REST",
reason: "Muted reason text",
reason: "Reason with status color",
icon: "🛌",
};
render(<DecisionCard decision={decision} />);
const reason = screen.getByText("Muted reason text");
expect(reason).toHaveClass("text-gray-600");
const reason = screen.getByText("Reason with status color");
expect(reason).toHaveClass("text-red-700");
});
});
});

View File

@@ -1,17 +1,43 @@
// ABOUTME: Dashboard card displaying today's training decision.
// ABOUTME: Shows decision status, icon, and reason prominently.
// ABOUTME: Shows decision status, icon, and reason with color-coded backgrounds.
import type { Decision } from "@/types";
interface DecisionCardProps {
decision: Decision;
}
function getStatusColors(status: Decision["status"]): {
background: string;
text: string;
} {
switch (status) {
case "REST":
return { background: "bg-red-100 border-red-300", text: "text-red-700" };
case "GENTLE":
case "LIGHT":
case "REDUCED":
return {
background: "bg-yellow-100 border-yellow-300",
text: "text-yellow-700",
};
case "TRAIN":
return {
background: "bg-green-100 border-green-300",
text: "text-green-700",
};
default:
return { background: "border", text: "text-gray-600" };
}
}
export function DecisionCard({ decision }: DecisionCardProps) {
const colors = getStatusColors(decision.status);
return (
<div className="rounded-lg border p-6">
<div className={`rounded-lg p-6 ${colors.background}`}>
<div className="text-4xl mb-2">{decision.icon}</div>
<h2 className="text-2xl font-bold">{decision.status}</h2>
<p className="text-gray-600">{decision.reason}</p>
<p className={colors.text}>{decision.reason}</p>
</div>
);
}