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 4b2bb6998a
commit 97a3c84625
163 changed files with 6008 additions and 6310 deletions
+119 -58
View File
@@ -1,73 +1,134 @@
import * as React from "react"
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
import { cn } from "../core/cn"
import { XIcon } from "lucide-react"
import * as React from 'react'
import { Modal, Box, Text, Group } from '@mantine/core'
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />
interface DialogProps {
open?: boolean
onOpenChange?: (open: boolean) => void
children: React.ReactNode
}
function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
}
const DialogContext = React.createContext<{
open: boolean
setOpen: (open: boolean) => void
}>({ open: false, setOpen: () => {} })
function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
}
function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
}
function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {
return (
<DialogPrimitive.Backdrop
data-slot="dialog-overlay"
className={cn("fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className)}
{...props}
/>
function Dialog({ open: controlledOpen, onOpenChange, children }: DialogProps) {
const [internalOpen, setInternalOpen] = React.useState(false)
const open = controlledOpen ?? internalOpen
const setOpen = React.useCallback(
(v: boolean) => {
onOpenChange?.(v)
if (controlledOpen === undefined) setInternalOpen(v)
},
[controlledOpen, onOpenChange],
)
}
function DialogContent({ className, children, showCloseButton = true, ...props }: DialogPrimitive.Popup.Props & { showCloseButton?: boolean }) {
return (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Popup
data-slot="dialog-content"
className={cn("fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-background p-4 text-sm ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className)}
{...props}
>
{children}
{showCloseButton && (
<DialogPrimitive.Close data-slot="dialog-close" className="absolute top-2 right-2 inline-flex size-7 items-center justify-center rounded-md hover:bg-muted">
<XIcon className="size-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Popup>
</DialogPortal>
)
}
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return <div data-slot="dialog-header" className={cn("flex flex-col gap-2", className)} {...props} />
}
function DialogFooter({ className, children, ...props }: React.ComponentProps<"div">) {
return (
<div data-slot="dialog-footer" className={cn("-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end", className)} {...props}>
<DialogContext.Provider value={{ open, setOpen }}>
{children}
</div>
</DialogContext.Provider>
)
}
function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
return <DialogPrimitive.Title data-slot="dialog-title" className={cn("text-base leading-none font-medium", className)} {...props} />
function DialogTrigger({ children, ...props }: React.ComponentProps<'button'>) {
const { setOpen } = React.useContext(DialogContext)
return (
<button type="button" data-slot="dialog-trigger" onClick={() => setOpen(true)} {...props}>
{children}
</button>
)
}
function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) {
return <DialogPrimitive.Description data-slot="dialog-description" className={cn("text-sm text-muted-foreground", className)} {...props} />
function DialogPortal({ children }: { children: React.ReactNode }) {
return <>{children}</>
}
function DialogClose({ children, ...props }: React.ComponentProps<'button'>) {
const { setOpen } = React.useContext(DialogContext)
return (
<button type="button" data-slot="dialog-close" onClick={() => setOpen(false)} {...props}>
{children}
</button>
)
}
function DialogOverlay() {
return null
}
function DialogContent({
children,
showCloseButton = true,
className,
...props
}: React.ComponentProps<'div'> & { showCloseButton?: boolean }) {
const { open, setOpen } = React.useContext(DialogContext)
return (
<Modal
opened={open}
onClose={() => setOpen(false)}
withCloseButton={showCloseButton}
radius="md"
padding="md"
size="sm"
centered
data-slot="dialog-content"
className={className}
{...props}
>
{children}
</Modal>
)
}
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
return <Box data-slot="dialog-header" mb="xs" className={className} {...props} />
}
function DialogFooter({ className, children, ...props }: React.ComponentProps<'div'>) {
return (
<Group
data-slot="dialog-footer"
justify="flex-end"
gap="sm"
mt="md"
pt="sm"
style={{ borderTop: '1px solid var(--mantine-color-default-border)' }}
className={className}
{...props}
>
{children}
</Group>
)
}
function DialogTitle({ className, children, ...props }: React.ComponentProps<'div'>) {
return (
<Text
component="div"
data-slot="dialog-title"
fw={500}
size="md"
className={className}
{...props}
>
{children}
</Text>
)
}
function DialogDescription({ className, children, ...props }: React.ComponentProps<'div'>) {
return (
<Text
component="div"
data-slot="dialog-description"
size="sm"
c="dimmed"
className={className}
{...props}
>
{children}
</Text>
)
}
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger }