Files
fn_registry/frontend/functions/ui/auth_form.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

182 lines
4.9 KiB
TypeScript

import * as React from 'react'
import {
Anchor,
Button,
Checkbox,
Divider,
Group,
Paper,
PasswordInput,
Stack,
Text,
TextInput,
Title,
Container,
type PaperProps,
} from '@mantine/core'
import { useToggle, upperFirst } from '@mantine/hooks'
interface SocialButtonConfig {
label: string
icon?: React.ReactNode
onClick?: () => void
}
interface ExtraFieldConfig {
name: string
label: string
placeholder?: string
required?: boolean
}
interface AuthFormSubmitValues {
type: 'login' | 'register'
email: string
password: string
[key: string]: unknown
}
interface AuthFormConfig {
/** Título principal de la página */
title?: string
/** Botones de autenticación social opcionales */
socialButtons?: SocialButtonConfig[]
/** Campos adicionales que se muestran solo en el modo registro */
extraFields?: ExtraFieldConfig[]
/** Callback invocado al enviar el formulario */
onSubmit?: (values: AuthFormSubmitValues) => void
/** Modo inicial: 'login' (default) o 'register' */
defaultType?: 'login' | 'register'
/** Props adicionales para el Paper contenedor */
paperProps?: PaperProps
}
function AuthForm({
title = 'Welcome',
socialButtons = [],
extraFields = [],
onSubmit,
defaultType = 'login',
paperProps,
}: AuthFormConfig): React.ReactElement {
const [type, toggle] = useToggle<'login' | 'register'>([
defaultType,
defaultType === 'login' ? 'register' : 'login',
])
const [email, setEmail] = React.useState('')
const [password, setPassword] = React.useState('')
const [terms, setTerms] = React.useState(true)
const [extraValues, setExtraValues] = React.useState<Record<string, string>>({})
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onSubmit?.({ type, email, password, ...extraValues })
}
const handleExtraChange = (name: string, value: string) => {
setExtraValues((prev) => ({ ...prev, [name]: value }))
}
return (
<Container size={420} py={40}>
<Paper radius="md" p="lg" withBorder {...paperProps}>
<Title order={2} ta="center" mb="md">
{title}
</Title>
{socialButtons.length > 0 && (
<>
<Group grow mb="md" gap="xs">
{socialButtons.map((btn) => (
<Button
key={btn.label}
variant="default"
radius="xl"
leftSection={btn.icon}
onClick={btn.onClick}
>
{btn.label}
</Button>
))}
</Group>
<Divider label="O continúa con email" labelPosition="center" my="lg" />
</>
)}
<form onSubmit={handleSubmit}>
<Stack gap="sm">
{type === 'register' &&
extraFields.map((field) => (
<TextInput
key={field.name}
label={field.label}
placeholder={field.placeholder}
required={field.required}
value={extraValues[field.name] ?? ''}
onChange={(e) => handleExtraChange(field.name, e.currentTarget.value)}
radius="md"
/>
))}
<TextInput
required
label="Email"
placeholder="tu@email.com"
value={email}
onChange={(e) => setEmail(e.currentTarget.value)}
radius="md"
/>
<PasswordInput
required
label="Contraseña"
placeholder="Tu contraseña"
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
radius="md"
/>
{type === 'register' && (
<Checkbox
label="Acepto los términos y condiciones"
checked={terms}
onChange={(e) => setTerms(e.currentTarget.checked)}
/>
)}
</Stack>
<Group justify="space-between" mt="xl">
<Anchor
component="button"
type="button"
c="dimmed"
size="xs"
onClick={() => toggle()}
>
{type === 'register'
? '¿Ya tienes cuenta? Inicia sesión'
: '¿No tienes cuenta? Regístrate'}
</Anchor>
<Button type="submit" radius="xl">
{upperFirst(type)}
</Button>
</Group>
</form>
{type === 'register' && (
<Text c="dimmed" size="xs" ta="center" mt="md">
Al registrarte aceptas nuestra{' '}
<Anchor size="xs" href="#">
política de privacidad
</Anchor>
.
</Text>
)}
</Paper>
</Container>
)
}
export { AuthForm }
export type { AuthFormConfig, AuthFormSubmitValues, SocialButtonConfig, ExtraFieldConfig }