Files
rapid_dashboards/frontend/src/components/Section.tsx
T
egutierrez 4ec62f5ed6 refactor(frontend): migracion a Mantine y limpieza de widgets
- Migra el frontend a Mantine v9 siguiendo la regla de theming del registry (@fn_library, sin Tailwind/cn/CVA).
- Reescribe DashboardShell, FilterBar, Section, WidgetRenderer y todos los widgets (Area/Bar/Line/Pie/KPI/Sparkline/Table) con componentes y props de Mantine.
- Ajusta vite.config, main.tsx, App.tsx, app.css y env.d.ts.
- Añade postcss.config.cjs requerido por Mantine.
- Actualiza package.json y pnpm-lock.
- Ajusta config.go, main.go y los ejemplos (fn_registry_apps/overview) para el nuevo esquema de tipos en frontend/src/types.ts.
2026-04-13 23:33:04 +02:00

70 lines
2.6 KiB
TypeScript

import { useState } from 'react'
import { Box, UnstyledButton, Group, Text } from '@mantine/core'
import { IconChevronDown, IconChevronRight } from '@tabler/icons-react'
import { WidgetRenderer } from './WidgetRenderer'
import type { SectionDef, QueryDef } from '../types'
import type { FilterValues } from '../hooks/useFilterState'
interface SectionProps {
section: SectionDef
queries: Record<string, QueryDef>
filters: FilterValues
globalColumns: number
getData: (widgetID: string, filters: FilterValues) => Promise<Record<string, unknown>[]>
fillHeight?: boolean
}
export function Section({ section, queries, filters, globalColumns, getData, fillHeight }: SectionProps) {
const [collapsed, setCollapsed] = useState(false)
const columns = section.columns || globalColumns
return (
<Box style={fillHeight ? { flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', gap: 'var(--mantine-spacing-xs)' } : { display: 'flex', flexDirection: 'column', gap: 'var(--mantine-spacing-xs)' }}>
<UnstyledButton
onClick={() => section.collapsible && setCollapsed(c => !c)}
style={{ flexShrink: 0, cursor: section.collapsible ? 'pointer' : 'default', userSelect: 'none' }}
>
<Group gap="xs">
{section.collapsible && (
collapsed
? <IconChevronRight size={16} style={{ color: 'var(--mantine-color-dimmed)' }} />
: <IconChevronDown size={16} style={{ color: 'var(--mantine-color-dimmed)' }} />
)}
<Text size="xs" fw={500} c="dimmed" tt="uppercase" style={{ letterSpacing: '0.05em' }}>
{section.title}
</Text>
</Group>
</UnstyledButton>
{!collapsed && (
<Box
style={{
display: 'grid',
gap: 'var(--mantine-spacing-xs)',
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
...(fillHeight ? { flex: 1, minHeight: 0 } : {}),
}}
>
{section.widgets.map(widget => (
<Box
key={widget.id}
style={{
gridColumn: `span ${Math.min(widget.span || 1, columns)}`,
gridRow: widget.rowSpan ? `span ${widget.rowSpan}` : undefined,
...(fillHeight ? { minHeight: 0, overflow: 'hidden' } : {}),
}}
>
<WidgetRenderer
widget={widget}
queryDef={queries[widget.query]}
filters={filters}
getData={getData}
/>
</Box>
))}
</Box>
)}
</Box>
)
}