Files
fn_registry/frontend/functions/ui/settings_page.tsx
T
egutierrez 2d108c295a 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>
2026-04-06 23:46:44 +02:00

124 lines
4.2 KiB
TypeScript

import * as React from 'react'
import { Stack, Group, Title, Text, Paper, TextInput, Textarea, Switch, NativeSelect, Button, Box, Anchor } from '@mantine/core'
interface SettingField {
key: string
label: string
description?: string
type: 'text' | 'number' | 'toggle' | 'select' | 'textarea'
value?: unknown
options?: Array<{ label: string; value: string }>
placeholder?: string
}
interface SettingSection {
id: string
title: string
description?: string
fields: SettingField[]
}
interface SettingsPageProps {
title?: string
subtitle?: string
sections: SettingSection[]
onSave?: (values: Record<string, unknown>) => void
className?: string
}
export function settingsPage({
title = 'Settings',
subtitle,
sections,
onSave,
}: SettingsPageProps): React.ReactElement {
return (
<Stack gap="lg">
{/* Header */}
<Box pb="md" style={{ borderBottom: '1px solid var(--mantine-color-default-border)' }}>
<Title order={2}>{title}</Title>
{subtitle && <Text size="sm" c="dimmed">{subtitle}</Text>}
</Box>
{/* Content with sidebar nav */}
<Group align="flex-start" gap="xl">
<Box w={192} visibleFrom="md" style={{ flexShrink: 0 }}>
<Stack gap={4}>
{sections.map((section) => (
<Anchor
key={section.id}
href={`#${section.id}`}
underline="never"
size="sm"
fw={500}
c="dimmed"
py={6}
px="sm"
style={{ display: 'block', borderRadius: 'var(--mantine-radius-sm)' }}
>
{section.title}
</Anchor>
))}
</Stack>
</Box>
{/* Sections */}
<Stack gap="xl" style={{ flex: 1 }}>
{sections.map((section) => (
<Stack key={section.id} id={section.id} gap="md">
<Box>
<Text size="lg" fw={500}>{section.title}</Text>
{section.description && <Text size="sm" c="dimmed">{section.description}</Text>}
</Box>
<Paper withBorder p="md" radius="md">
<Stack gap="md">
{section.fields.map((field) => (
<Group key={field.key} justify="space-between" align="center" wrap="wrap" gap="md">
<Stack gap={2} style={{ flex: '1 1 auto' }}>
<Text size="sm" fw={500}>{field.label}</Text>
{field.description && <Text size="xs" c="dimmed">{field.description}</Text>}
</Stack>
<Box w={{ base: '100%', sm: 256 }}>
{field.type === 'toggle' ? (
<Switch defaultChecked={!!field.value} />
) : field.type === 'select' ? (
<NativeSelect
data={field.options?.map(o => ({ label: o.label, value: o.value })) ?? []}
size="xs"
/>
) : field.type === 'textarea' ? (
<Textarea
rows={3}
placeholder={field.placeholder}
defaultValue={String(field.value ?? '')}
size="xs"
/>
) : (
<TextInput
type={field.type}
placeholder={field.placeholder}
defaultValue={String(field.value ?? '')}
size="xs"
/>
)}
</Box>
</Group>
))}
</Stack>
</Paper>
</Stack>
))}
{onSave && (
<Group justify="flex-end" pt="md" style={{ borderTop: '1px solid var(--mantine-color-default-border)' }}>
<Button size="xs">Save changes</Button>
</Group>
)}
</Stack>
</Group>
</Stack>
)
}
export type { SettingsPageProps, SettingSection, SettingField }