feat: complete v0.4.0 implementation - Reports, Charts, Comparison, Dark Mode
Backend (@backend-dev): - ReportService with PDF/CSV generation (reportlab, pandas) - Report API endpoints (POST, GET, DELETE, download with rate limiting) - Professional PDF templates with branding and tables - Storage management with auto-cleanup Frontend (@frontend-dev): - Recharts integration: CostBreakdown, TimeSeries, ComparisonBar - Scenario comparison: multi-select, compare page with side-by-side layout - Reports UI: generation form, list with status badges, download - Dark/Light mode: ThemeProvider, toggle, CSS variables - Responsive design for all components QA (@qa-engineer): - E2E testing setup with Playwright - 100 test cases across 7 spec files - Visual regression baselines - CI/CD workflow configuration - ES modules fixes Documentation: - Add todo.md with testing checklist and future roadmap - Update kickoff prompt for v0.4.0 27 tasks completed, 100% v0.4.0 delivery Closes: v0.4.0 milestone
This commit is contained in:
@@ -38,6 +38,28 @@ interface ChartDataPoint {
|
||||
color: string;
|
||||
}
|
||||
|
||||
// Tooltip component defined outside main component
|
||||
interface BarTooltipProps {
|
||||
active?: boolean;
|
||||
payload?: Array<{ payload: ChartDataPoint }>;
|
||||
formatter?: (value: number) => string;
|
||||
}
|
||||
|
||||
function BarTooltip({ active, payload, formatter }: BarTooltipProps) {
|
||||
if (active && payload && payload.length && formatter) {
|
||||
const item = payload[0].payload;
|
||||
return (
|
||||
<div className="rounded-lg border bg-popover p-3 shadow-md">
|
||||
<p className="font-medium text-popover-foreground">{item.name}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{formatter(item.value)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ComparisonBarChart({
|
||||
scenarios,
|
||||
metricKey,
|
||||
@@ -58,24 +80,6 @@ export function ComparisonBarChart({
|
||||
const minValue = Math.min(...values);
|
||||
const maxValue = Math.max(...values);
|
||||
|
||||
const CustomTooltip = ({ active, payload }: {
|
||||
active?: boolean;
|
||||
payload?: Array<{ name: string; value: number; payload: ChartDataPoint }>;
|
||||
}) => {
|
||||
if (active && payload && payload.length) {
|
||||
const item = payload[0].payload;
|
||||
return (
|
||||
<div className="rounded-lg border bg-popover p-3 shadow-md">
|
||||
<p className="font-medium text-popover-foreground">{item.name}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{formatter(item.value)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const getBarColor = (value: number) => {
|
||||
// For cost metrics, lower is better (green), higher is worse (red)
|
||||
// For other metrics, higher is better
|
||||
@@ -129,7 +133,7 @@ export function ComparisonBarChart({
|
||||
axisLine={false}
|
||||
interval={0}
|
||||
/>
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Tooltip content={<BarTooltip formatter={formatter} />} />
|
||||
<Bar
|
||||
dataKey="value"
|
||||
radius={[0, 4, 4, 0]}
|
||||
|
||||
Reference in New Issue
Block a user