refactor: migrate frontend from shadcn/Tailwind to Mantine v9

Reescribe todos los componentes UI para usar Mantine v9 en lugar de shadcn/Tailwind.
Elimina cn(), CVA, components.json, theme_provider custom y globals.css con Tailwind.
Añade 25+ componentes nuevos (AppShell, AuthForm, DatePickerInput, Dropzone, etc.)
y MantineProvider como wrapper estándar del sistema de temas.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-06 23:46:44 +02:00
parent 73a4c3a148
commit 2d108c295a
163 changed files with 6008 additions and 6310 deletions
+40 -44
View File
@@ -1,5 +1,5 @@
import * as React from 'react'
import { cn } from '../core/cn'
import { Stack, Group, Title, Text, Paper, SimpleGrid } from '@mantine/core'
interface MetricConfig {
label: string
@@ -34,67 +34,63 @@ export function analyticsPage({
metrics,
charts,
actions,
className,
}: AnalyticsPageProps): React.ReactElement {
const metricCols = metrics.length <= 2 ? { base: 1, md: 2 } : metrics.length <= 3 ? { base: 1, md: 3 } : { base: 1, md: 2, lg: 4 }
return (
<div className={cn('space-y-6', className)}>
<Stack gap="lg">
{/* Header */}
<div className="flex items-center justify-between border-b pb-4">
<div className="space-y-1">
<h1 className="text-2xl font-semibold tracking-tight">{title}</h1>
{subtitle && <p className="text-sm text-muted-foreground">{subtitle}</p>}
</div>
<div className="flex items-center gap-2">
<Group justify="space-between" pb="md" style={{ borderBottom: '1px solid var(--mantine-color-default-border)' }}>
<Stack gap={4}>
<Title order={2}>{title}</Title>
{subtitle && <Text size="sm" c="dimmed">{subtitle}</Text>}
</Stack>
<Group gap="xs">
{dateRange}
{actions}
</div>
</div>
</Group>
</Group>
{/* KPI Row */}
<div className={cn(
'grid gap-4',
metrics.length <= 2 ? 'grid-cols-1 md:grid-cols-2' :
metrics.length <= 3 ? 'grid-cols-1 md:grid-cols-3' :
'grid-cols-1 md:grid-cols-2 lg:grid-cols-4'
)}>
<SimpleGrid cols={metricCols} spacing="md">
{metrics.map((metric, i) => (
<div key={i} className="rounded-lg border bg-card p-4 text-card-foreground shadow-sm">
<p className="text-sm text-muted-foreground">{metric.label}</p>
<div className="mt-2 flex items-end justify-between gap-4">
<div className="space-y-1">
<p className="text-3xl font-bold tracking-tight">{metric.value}</p>
<Paper key={i} p="md" withBorder shadow="xs">
<Text size="sm" c="dimmed">{metric.label}</Text>
<Group mt="xs" justify="space-between" align="flex-end" gap="md">
<Stack gap={4}>
<Text fz={30} fw={700} lh={1}>{metric.value}</Text>
{metric.delta && (
<div className={cn(
'flex items-center gap-1 text-sm font-medium',
metric.delta.value === 0 ? 'text-muted-foreground' :
metric.delta.isPositive ? 'text-green-600 dark:text-green-500' :
'text-red-600 dark:text-red-500'
)}>
<span>{metric.delta.value > 0 ? '+' : ''}{metric.delta.value}%</span>
</div>
<Text
size="sm"
fw={500}
c={metric.delta.value === 0 ? 'dimmed' : metric.delta.isPositive ? 'green' : 'red'}
>
{metric.delta.value > 0 ? '+' : ''}{metric.delta.value}%
</Text>
)}
</div>
</div>
</div>
</Stack>
</Group>
</Paper>
))}
</div>
</SimpleGrid>
{/* Charts Grid */}
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<SimpleGrid cols={{ base: 1, lg: 2 }} spacing="md">
{charts.map((chart) => (
<div
<Paper
key={chart.id}
className={cn(
'rounded-xl border bg-card p-4 text-card-foreground shadow-sm',
chart.span === 2 && 'lg:col-span-2'
)}
p="md"
withBorder
shadow="xs"
radius="md"
style={chart.span === 2 ? { gridColumn: 'span 2' } : undefined}
>
<h3 className="mb-3 text-sm font-medium text-muted-foreground">{chart.title}</h3>
<Text size="sm" fw={500} c="dimmed" mb="sm">{chart.title}</Text>
{chart.content}
</div>
</Paper>
))}
</div>
</div>
</SimpleGrid>
</Stack>
)
}