diff --git a/apps/web/components/charts/bar-chart.tsx b/apps/web/components/charts/bar-chart.tsx new file mode 100644 index 0000000..5316dfc --- /dev/null +++ b/apps/web/components/charts/bar-chart.tsx @@ -0,0 +1,87 @@ +'use client'; + +import { + BarChart as RechartsBarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, + Legend, +} from 'recharts'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; + +interface BarChartProps { + title: string; + data: { mes: string; ingresos: number; egresos: number }[]; +} + +const formatCurrency = (value: number) => { + if (value >= 1000000) { + return `$${(value / 1000000).toFixed(1)}M`; + } + if (value >= 1000) { + return `$${(value / 1000).toFixed(0)}K`; + } + return `$${value}`; +}; + +export function BarChart({ title, data }: BarChartProps) { + return ( + + + {title} + + +
+ + + + + + + new Intl.NumberFormat('es-MX', { + style: 'currency', + currency: 'MXN', + }).format(value) + } + contentStyle={{ + backgroundColor: 'hsl(var(--card))', + border: '1px solid hsl(var(--border))', + borderRadius: '8px', + }} + /> + + + + + +
+
+
+ ); +} diff --git a/apps/web/components/charts/index.ts b/apps/web/components/charts/index.ts new file mode 100644 index 0000000..745e938 --- /dev/null +++ b/apps/web/components/charts/index.ts @@ -0,0 +1,2 @@ +export { KpiCard } from './kpi-card'; +export { BarChart } from './bar-chart'; diff --git a/apps/web/components/charts/kpi-card.tsx b/apps/web/components/charts/kpi-card.tsx new file mode 100644 index 0000000..64b7451 --- /dev/null +++ b/apps/web/components/charts/kpi-card.tsx @@ -0,0 +1,71 @@ +import { Card, CardContent } from '@/components/ui/card'; +import { cn } from '@/lib/utils'; +import { TrendingUp, TrendingDown, Minus } from 'lucide-react'; + +interface KpiCardProps { + title: string; + value: string | number; + subtitle?: string; + trend?: 'up' | 'down' | 'neutral'; + trendValue?: string; + icon?: React.ReactNode; + className?: string; +} + +export function KpiCard({ + title, + value, + subtitle, + trend, + trendValue, + icon, + className, +}: KpiCardProps) { + const formatValue = (val: string | number) => { + if (typeof val === 'number') { + return new Intl.NumberFormat('es-MX', { + style: 'currency', + currency: 'MXN', + minimumFractionDigits: 0, + maximumFractionDigits: 0, + }).format(val); + } + return val; + }; + + return ( + + +
+

{title}

+ {icon &&
{icon}
} +
+
+

{formatValue(value)}

+ {(subtitle || trend) && ( +
+ {trend && ( + + {trend === 'up' && } + {trend === 'down' && } + {trend === 'neutral' && } + {trendValue} + + )} + {subtitle && ( + {subtitle} + )} +
+ )} +
+
+
+ ); +}