feat(analysis): add 12-month summary row with total variation and accumulated inflation
This commit is contained in:
@@ -54,6 +54,43 @@ function formatPrice(value: number): string {
|
|||||||
return `$${priceFormatter.format(value)}`;
|
return `$${priceFormatter.format(value)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TotalVariation({
|
||||||
|
variation,
|
||||||
|
inflation,
|
||||||
|
}: {
|
||||||
|
variation: number | null;
|
||||||
|
inflation: number | null;
|
||||||
|
}) {
|
||||||
|
if (variation === null) {
|
||||||
|
return (
|
||||||
|
<span className="text-xs text-muted-foreground inline-flex items-center gap-1">
|
||||||
|
--
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`text-xs font-medium inline-flex items-center gap-1 tabular-nums ${
|
||||||
|
inflation !== null
|
||||||
|
? variation > inflation
|
||||||
|
? "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400 px-1.5 py-0.5 rounded"
|
||||||
|
: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400 px-1.5 py-0.5 rounded"
|
||||||
|
: variation >= 0
|
||||||
|
? "text-emerald-500"
|
||||||
|
: "text-red-500"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{variation >= 0 ? (
|
||||||
|
<ArrowUp className="size-3" />
|
||||||
|
) : (
|
||||||
|
<ArrowDown className="size-3" />
|
||||||
|
)}
|
||||||
|
{formatPct(variation)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function VariationIndicator({
|
function VariationIndicator({
|
||||||
variation,
|
variation,
|
||||||
inflation,
|
inflation,
|
||||||
@@ -205,6 +242,34 @@ export function MonthlyVariationTable() {
|
|||||||
.reverse();
|
.reverse();
|
||||||
}, [monthlyData, inflationData]);
|
}, [monthlyData, inflationData]);
|
||||||
|
|
||||||
|
const summary = useMemo(() => {
|
||||||
|
if (rows.length < 2) return null;
|
||||||
|
|
||||||
|
const oldest = rows[rows.length - 1];
|
||||||
|
const newest = rows[0];
|
||||||
|
|
||||||
|
const totalVariation = (
|
||||||
|
first: number | null,
|
||||||
|
last: number | null,
|
||||||
|
): number | null => {
|
||||||
|
if (first === null || last === null || first === 0) return null;
|
||||||
|
return ((last - first) / first) * 100;
|
||||||
|
};
|
||||||
|
|
||||||
|
const accumulatedInflation = rows.reduce<number | null>((acc, row) => {
|
||||||
|
if (row.inflation === null) return acc;
|
||||||
|
return (acc ?? 0) + row.inflation;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
return {
|
||||||
|
BELO: totalVariation(oldest.BELO.current, newest.BELO.current),
|
||||||
|
BLUE: totalVariation(oldest.BLUE.current, newest.BLUE.current),
|
||||||
|
BNA: totalVariation(oldest.BNA.current, newest.BNA.current),
|
||||||
|
inflation: accumulatedInflation,
|
||||||
|
period: `${oldest.label} – ${newest.label}`,
|
||||||
|
};
|
||||||
|
}, [rows]);
|
||||||
|
|
||||||
const isLoading = monthlyLoading || inflationLoading;
|
const isLoading = monthlyLoading || inflationLoading;
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
@@ -279,6 +344,48 @@ export function MonthlyVariationTable() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{summary && (
|
||||||
|
<div className="rounded-lg border bg-muted/50 p-3 text-sm">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<span className="font-semibold">Total 12 meses</span>
|
||||||
|
{summary.inflation !== null ? (
|
||||||
|
<span className="text-xs text-muted-foreground tabular-nums inline-flex items-center gap-1">
|
||||||
|
<TrendingUp className="size-3" />
|
||||||
|
{formatPct(summary.inflation)} inflación
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Sin inflación
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-3 gap-2">
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] text-muted-foreground mb-0.5">BELO</p>
|
||||||
|
<TotalVariation
|
||||||
|
variation={summary.BELO}
|
||||||
|
inflation={summary.inflation}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] text-muted-foreground mb-0.5">Blue</p>
|
||||||
|
<TotalVariation
|
||||||
|
variation={summary.BLUE}
|
||||||
|
inflation={summary.inflation}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-[10px] text-muted-foreground mb-0.5">
|
||||||
|
Oficial
|
||||||
|
</p>
|
||||||
|
<TotalVariation
|
||||||
|
variation={summary.BNA}
|
||||||
|
inflation={summary.inflation}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden overflow-x-auto rounded-lg border sm:block">
|
<div className="hidden overflow-x-auto rounded-lg border sm:block">
|
||||||
@@ -348,6 +455,38 @@ export function MonthlyVariationTable() {
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
{summary && (
|
||||||
|
<tr className="border-t-2 bg-muted/50 font-medium">
|
||||||
|
<td className="px-3 py-2.5 text-xs">Total 12 meses</td>
|
||||||
|
<td className="px-3 py-2.5 text-right">
|
||||||
|
<TotalVariation
|
||||||
|
variation={summary.BELO}
|
||||||
|
inflation={summary.inflation}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2.5 text-right">
|
||||||
|
<TotalVariation
|
||||||
|
variation={summary.BLUE}
|
||||||
|
inflation={summary.inflation}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2.5 text-right">
|
||||||
|
<TotalVariation
|
||||||
|
variation={summary.BNA}
|
||||||
|
inflation={summary.inflation}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="px-3 py-2.5 text-right">
|
||||||
|
{summary.inflation !== null ? (
|
||||||
|
<span className="text-xs font-medium tabular-nums">
|
||||||
|
{formatPct(summary.inflation)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs text-muted-foreground">--</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user