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:
@@ -33,6 +33,48 @@ interface TimeSeriesChartProps {
|
||||
chartType?: 'line' | 'area';
|
||||
}
|
||||
|
||||
// Format timestamp for display
|
||||
function formatXAxisLabel(timestamp: string): string {
|
||||
try {
|
||||
const date = new Date(timestamp);
|
||||
return format(date, 'MMM dd HH:mm');
|
||||
} catch {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip component defined outside main component
|
||||
interface TimeTooltipProps {
|
||||
active?: boolean;
|
||||
payload?: Array<{ name: string; value: number; color: string }>;
|
||||
label?: string;
|
||||
yAxisFormatter?: (value: number) => string;
|
||||
}
|
||||
|
||||
function TimeTooltip({ active, payload, label, yAxisFormatter }: TimeTooltipProps) {
|
||||
if (active && payload && payload.length && yAxisFormatter) {
|
||||
return (
|
||||
<div className="rounded-lg border bg-popover p-3 shadow-md">
|
||||
<p className="font-medium text-popover-foreground mb-2">
|
||||
{label ? formatXAxisLabel(label) : ''}
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{payload.map((entry: { name: string; value: number; color: string }) => (
|
||||
<p key={entry.name} className="text-sm text-muted-foreground flex items-center gap-2">
|
||||
<span
|
||||
className="h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: entry.color }}
|
||||
/>
|
||||
{entry.name}: {yAxisFormatter(entry.value)}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function TimeSeriesChart({
|
||||
data,
|
||||
series,
|
||||
@@ -41,42 +83,7 @@ export function TimeSeriesChart({
|
||||
yAxisFormatter = formatNumber,
|
||||
chartType = 'area',
|
||||
}: TimeSeriesChartProps) {
|
||||
const formatXAxis = (timestamp: string) => {
|
||||
try {
|
||||
const date = new Date(timestamp);
|
||||
return format(date, 'MMM dd HH:mm');
|
||||
} catch {
|
||||
return timestamp;
|
||||
}
|
||||
};
|
||||
|
||||
const CustomTooltip = ({ active, payload, label }: {
|
||||
active?: boolean;
|
||||
payload?: Array<{ name: string; value: number; color: string }>;
|
||||
label?: string;
|
||||
}) => {
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div className="rounded-lg border bg-popover p-3 shadow-md">
|
||||
<p className="font-medium text-popover-foreground mb-2">
|
||||
{label ? formatXAxis(label) : ''}
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{payload.map((entry) => (
|
||||
<p key={entry.name} className="text-sm text-muted-foreground flex items-center gap-2">
|
||||
<span
|
||||
className="h-2 w-2 rounded-full"
|
||||
style={{ backgroundColor: entry.color }}
|
||||
/>
|
||||
{entry.name}: {yAxisFormatter(entry.value)}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const formatXAxis = (timestamp: string) => formatXAxisLabel(timestamp);
|
||||
|
||||
const ChartComponent = chartType === 'area' ? AreaChart : LineChart;
|
||||
|
||||
@@ -132,7 +139,7 @@ export function TimeSeriesChart({
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Tooltip content={<TimeTooltip yAxisFormatter={yAxisFormatter} />} />
|
||||
<Legend
|
||||
wrapperStyle={{ paddingTop: '20px' }}
|
||||
iconType="circle"
|
||||
|
||||
Reference in New Issue
Block a user