Plataforma técnica · Sevastopol
Biblioteca de Componentes UI
Sevastopol UI Components Atomic Design
Propósito y Alcance
Section titled “Propósito y Alcance”La Biblioteca de Componentes UI provee un conjunto de componentes SolidJS reutilizables organizados según los principios de Atomic Design. Es la base sobre la que se construyen todas las islands del frontend Sevastopol, asegurando consistencia visual, mantenibilidad y type-safety.
La jerarquía está dividida en cuatro capas:
- Átomos: primitivas de formulario, acción, búsqueda y navegación (
Input,Button,SearchBox, etc.). - Moléculas: composiciones cortas con propósito específico (
PageHeader,StatCard,Breadcrumb). - Organismos: componentes interactivos complejos (
Modal,DataTable). - Plantillas: layouts a nivel de página (
IslandBase).
Todos los componentes soportan modo oscuro vía CSS custom properties y consumen utilidades de estilo centralizadas en sevastopol/src/lib/ui.ts.
Arquitectura
Section titled “Arquitectura”Jerarquía de Atomic Design
Section titled “Jerarquía de Atomic Design”flowchart LR
subgraph TPL["Plantillas"]
ISB["IslandBase.tsx<br/>Layout estándar"]
end
subgraph ORG["Organismos"]
DT["DataTable.tsx<br/>items · headers · sumKeys"]
MD["Modal.tsx<br/>open · onClose · children"]
end
subgraph MOL["Moléculas"]
PH["PageHeader.tsx<br/>title · description · actions"]
SG["StatsGrid.tsx<br/>children (StatCards)"]
SC["StatCard.tsx<br/>label · value · sub"]
FB["FilterBar.tsx<br/>children (filtros)"]
BC["Breadcrumb.tsx<br/>text"]
end
subgraph ATM["Átomos"]
FI["Fields.tsx<br/>Input · Select · Button · Checkbox · TextArea"]
SB["SearchBox.tsx<br/>value · onChange · delay"]
PG["Pagination.tsx<br/>total · limit · offset"]
TP["TenantPicker.tsx<br/>tenants · value · onChange"]
TA["TableAction.tsx<br/>variant · icon · onClick"]
end
ISB --> BC
ISB --> PH
ISB --> SG
ISB --> FB
SG -->|slot| SC
SG -->|slot| TP
FB -->|slot| SB
FB -->|slot| FI
PH -->|slot| FI
DT --> SB
DT --> TA
DT --> PG
MD -->|slot| FI Esta arquitectura habilita máxima reutilización. DataTable es consumido por PayrollViewIsland, EmployeesViewIsland, ContractsViewIsland, TenantsViewIsland y PlanContableViewIsland, cada uno configurado con definiciones de columnas distintas desde sevastopol/src/config/headers.ts.
Átomos
Section titled “Átomos”Los átomos son los bloques de construcción fundamentales. Son primitivas que no pueden descomponerse más. Todos soportan modo oscuro y accesibilidad de teclado.
Por claridad de uso, los agrupamos en tres categorías funcionales: campos de formulario, acciones y búsqueda/navegación.
Form Fields
Section titled “Form Fields”Todos viven en sevastopol/src/components/atoms/Fields.tsx y se exportan desde @/components/atoms.
Envuelve un <input> HTML nativo con estilos estandarizados y gestión de etiqueta.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
label | string | Sí | Texto de etiqueta sobre el input |
name | string | Sí | Nombre del campo de formulario |
...rest | JSX.InputHTMLAttributes | No | Atributos nativos (type, placeholder, value, etc.) |
<Input label="RUT" name="rut" required placeholder="12345678-9" value={editing()?.rut ?? ""}/>Select
Section titled “Select”Menú desplegable con normalización de tipos. Convierte explícitamente tanto el value actual como los IDs de cada opción a string, previniendo desajustes value={0} vs value="0".
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
label | string | No | Texto de etiqueta sobre el select |
name | string | Sí | Nombre del campo |
options | Option[] | Sí | Array de objetos {id, nombre} |
value | string | number | null | No | Valor seleccionado (normalizado a string) |
placeholder | string | No | Placeholder (default: "—") |
density | "normal" | "compact" | No | Variante de densidad visual |
type Option = { id: string; nombre: string; [key: string]: any;};Checkbox
Section titled “Checkbox”Checkbox estilizado con etiqueta opcional y mensaje personalizado.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
label | string | No | Texto de etiqueta sobre el checkbox |
name | string | Sí | Nombre del campo |
message | string | No | Texto junto al checkbox (default: "Activar") |
...rest | JSX.InputHTMLAttributes | No | Atributos nativos |
TextArea
Section titled “TextArea”Entrada multi-línea con estilos consistentes.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
label | string | Sí | Texto de etiqueta sobre el textarea |
name | string | Sí | Nombre del campo |
...rest | JSX.TextareaHTMLAttributes | No | Atributos nativos |
Acciones
Section titled “Acciones”Componentes para disparar acciones del usuario.
Button
Section titled “Button”Botón con tres variantes visuales y soporte opcional para icono. Vive en sevastopol/src/components/atoms/Fields.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
label | string | Sí | Texto del botón |
icon | string | JSX.Element | No | Emoji o componente SVG |
variant | "primary" | "secondary" | "danger" | No | Estilo visual (default: "primary") |
...rest | JSX.ButtonHTMLAttributes | No | Atributos nativos |
| Variante | Estilo |
|---|---|
primary | Fondo negro (oscuro: blanco), alto contraste |
secondary | Fondo blanco con borde, sutil |
danger | Fondo rose-600, para acciones destructivas |
TableAction
Section titled “TableAction”Botón basado en icono optimizado para filas de DataTable. Estados hover sutiles con coloración por variante. Vive en sevastopol/src/components/atoms/TableAction.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
onClick | () => void | Sí | Manejador de click |
icon | any | No | Componente de icono o emoji |
label | string | No | Texto de tooltip/aria-label |
variant | string | No | Variante de estilo visual |
class | string | No | Clases CSS adicionales |
disabled | boolean | No | Estado deshabilitado |
Exporta también un objeto Icons con SVG pre-construidos:
Icons.Edit— lápizIcons.Delete— papeleraIcons.Eye— vista/previewIcons.DatabaseIcons.Download
<TableAction label="Editar" variant="edit" icon={ActionIcons.Edit} onClick={() => startEdit(row)}/>Búsqueda y Navegación
Section titled “Búsqueda y Navegación”SearchBox
Section titled “SearchBox”Input de búsqueda con debounce (createEffect + setTimeout) para reducir peticiones API durante la escritura. Vive en sevastopol/src/components/atoms/SearchBox.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
placeholder | string | No | Texto placeholder |
value | string | Sí | Valor actual |
delay | number | No | Debounce en ms (default: 300) |
onChange | (v: string) => void | Sí | Handler de cambio (llamado después del debounce) |
class | string | No | Clases CSS adicionales |
<SearchBox placeholder="Buscar por nombre, rut o email..." value={q()} onChange={setQ}/>Pagination
Section titled “Pagination”Controles simples para navegar datos paginados. Calcula automáticamente los estados deshabilitados según offset y total. Vive en sevastopol/src/components/atoms/Pagination.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
total | number | Sí | Número total de registros |
limit | number | Sí | Registros por página |
offset | number | Sí | Offset actual |
onPrev | () => void | Sí | Handler de página anterior |
onNext | () => void | Sí | Handler de página siguiente |
TenantPicker
Section titled “TenantPicker”Select para elegir organización tenant, típicamente embebido dentro del StatsGrid de IslandBase. Vive en sevastopol/src/components/atoms/TenantPicker.tsx. Usa la utilidad selectCls de lib/ui.ts para estilos consistentes.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
tenants | Tenant[] | Sí | Tenants disponibles |
value | string | Sí | ID del tenant seleccionado |
onChange | (id: string) => void | Sí | Handler de selección |
type Tenant = { id: string; rut: string; business_name: string; // ... campos adicionales};Moléculas
Section titled “Moléculas”Composiciones cortas construidas a partir de átomos. Viven en sevastopol/src/components/molecules/ y se importan desde @/components/molecules. Se usan mayoritariamente dentro de IslandBase.
PageHeader
Section titled “PageHeader”Encabezado de página: título, descripción opcional y slot de botones de acción. Archivo: molecules/PageHeader.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
title | string | Sí | Título de la página |
description | string | No | Texto descriptivo bajo el título |
actions | JSX.Element | No | Slot para botones (típicamente <Button>) |
StatsGrid
Section titled “StatsGrid”Contenedor responsive para mostrar tarjetas de métricas en cuadrícula. Es un wrapper puro de slot. Archivo: molecules/StatsGrid.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
children | JSX.Element | Sí | Componentes StatCard y opcionalmente TenantPicker embebido |
StatCard
Section titled “StatCard”Tarjeta de visualización de métrica única. Archivo: molecules/StatCard.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
label | string | Sí | Etiqueta (texto pequeño superior) |
value | string | number | Sí | Valor central destacado |
sub | string | Sí | Subtítulo (texto pequeño inferior) |
highlight | boolean | No | Aplica ring rose para destacar |
color | string | No | Clase de color custom para label y valor |
FilterBar
Section titled “FilterBar”Contenedor para SearchBox + controles de filtro personalizados. Wrapper puro de slot. Archivo: molecules/FilterBar.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
children | JSX.Element | Sí | Típicamente SearchBox + uno o más Select de filtro |
Breadcrumb
Section titled “Breadcrumb”Ruta de navegación jerárquica. Muestra MÓDULO · TÍTULO en mayúsculas. Archivo: molecules/Breadcrumb.tsx.
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
text | string | Sí | Texto formateado, ej: "REM · LIQUIDACIONES" |
Organismos
Section titled “Organismos”Componentes complejos que combinan átomos y moléculas, con estado propio.
Overlay de diálogo para formularios y confirmaciones. Backdrop con click-para-cerrar. Archivo: sevastopol/src/components/atoms/Modal.tsx (vive bajo atoms/ en código por convención del proyecto, pero conceptualmente es organismo).
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
open | boolean | Sí | Controla visibilidad |
title | string | No | Título del modal |
onClose | () => void | Sí | Handler de cierre (también disparado por backdrop click) |
children | JSX.Element | Sí | Contenido |
widthClass | string | No | Ancho custom (default: "w-[min(100vw-2rem,1400px)]") |
titleClass | string | No | Estilos custom para el título |
<div class="absolute inset-0 bg-black/60 dark:bg-black/80 backdrop-blur-sm" onClick={props.onClose}/>DataTable
Section titled “DataTable”El componente más complejo y ampliamente usado. Tabla con filtrado, ordenamiento, paginación, formato automático y renderizado personalizable. Archivo: sevastopol/src/components/atoms/DataTable.tsx.
Arquitectura interna
Section titled “Arquitectura interna”flowchart LR
subgraph UTIL["Funciones utilitarias"]
CS["changeSort(key)<br/>alterna sortDesc"]
FN["formatNumber(v)<br/>locale de-DE"]
FD["formatDate(iso)<br/>slice(0,10)"]
INU["isNumeric(v)<br/>type check"]
end
DT["DataTable<br/>(items, headers, sumKeys)"]
subgraph SIG["Señales SolidJS"]
SK["sortKey"]
SD["sortDesc"]
PGN["page"]
FLT["filter"]
end
subgraph DER["Estado derivado<br/>(createMemo)"]
FILT["filteredItems()<br/>filtra por año/mes"]
SORT["sortedItems()<br/>ordena por columna"]
PAG["paginated()<br/>PAGE_SIZE = 10"]
end
TABLE["render con encabezados + filas"]
DT --> SK --> SORT
DT --> SD --> SORT
DT --> PGN --> PAG
DT --> FLT --> FILT
CS --> SK
CS --> SD
FILT --> SORT --> PAG --> TABLE
FN --> TABLE
FD --> TABLE
INU --> TABLE | Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
items | any[] | Sí | Array de objetos a mostrar |
headers | HeaderConfig[] | Sí | Configuración de columnas |
sumKeys | string[] | No | Claves a sumar en fila de pie |
charts | boolean | No | Habilitar visualización de gráficos (default: true) |
variant | "default" | "island" | No | Variante visual |
manualPagination | boolean | No | Deshabilitar paginación interna |
actions | { onEdit?, onDelete? } | No | Handlers de acción de fila |
Tipo HeaderConfig
Section titled “Tipo HeaderConfig”type HeaderConfig = { key: string; // clave de propiedad en items label: string; // texto de encabezado sortable?: boolean; // default true align?: "left" | "center" | "right"; render?: (value: any, row: any) => any; // renderizador custom};Características
Section titled “Características”-
Filtrado automático: auto-detecta columnas
año,mes,year,monthy genera dropdowns de filtro.const filterFields = headers.filter((h) =>["año", "mes", "year", "month"].includes(h.key)); -
Ordenamiento multi-columna: click en encabezado alterna dirección. Estado preservado vía señales.
const [sortKey, setSortKey] = createSignal(headers[0]?.key ?? "");const [sortDesc, setSortDesc] = createSignal(true); -
Formato de números: locale alemán (
de-DE) para separadores de miles.function formatNumber(value: any): string {const num = typeof value === "string" ? parseFloat(value) : Number(value);if (isNaN(num)) return "0";return num.toLocaleString("de-DE", {minimumFractionDigits: 0,maximumFractionDigits: 0,});} -
Formato de fechas: detecta columnas
fechay aplicaYYYY-MM-DD. -
Pie de resumen: cuando se provee
sumKeys, renderiza una<tfoot>con totales/promedios.{sumKeys.length > 0 && (<tfoot class="sticky bottom-0 bg-emerald-50 dark:bg-emerald-900/20"><tr class="font-bold">{/* sumas/promedios para claves especificadas */}</tr></tfoot>)} -
Acciones de fila: columna dedicada con editar/eliminar opcional.
-
Paginación integrada: tamaño configurable (default: 10).
Variantes
Section titled “Variantes”| Variante | Descripción | Caso de uso |
|---|---|---|
default | Tabla estándar con contenedor externo | Tablas independientes |
island | flex-1 con min-height para layouts de página completa | Tablas embebidas en IslandBase |
Plantillas
Section titled “Plantillas”IslandBase
Section titled “IslandBase”Plantilla estandarizada que provee la estructura de layout consistente para todas las vistas de negocio. Archivo: sevastopol/src/components/templates/IslandBase.tsx.
Opera en dos modos: Simple (contenedor básico) y Estándar (layout completo con tenant, stats y filtros). El cambio de modo se activa automáticamente al recibir la prop module.
Arquitectura
Section titled “Arquitectura”flowchart TB
ISB["IslandBase"]
subgraph TOP["Barra superior"]
CLOSE["Botón cerrar<br/>(handleClose)"]
TITLE["Título simple<br/>(opcional)"]
end
subgraph LAY["Elementos de layout<br/>(activos con prop module)"]
BC["Breadcrumb<br/>(MÓDULO · TÍTULO)"]
PH["PageHeader<br/>(title · description · actions)"]
SG["StatsGrid<br/>(métricas + TenantPicker)"]
FB["FilterBar<br/>(SearchBox + filtros)"]
end
CONTENT["Área de contenido<br/>children (vista específica)"]
ISB --> TOP
ISB --> LAY
ISB --> CONTENT
BC --> PH --> SG --> FB --> CONTENT
ISB -.->|"sin prop module"| CONTENT Las props se organizan por funcionalidad.
Contenedor básico:
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
title | string | No | Título de página (usado en ambos modos) |
subtitle | string | No | Subtítulo (sólo modo simple) |
children | JSX.Element | Sí | Contenido específico de la vista |
onClose | () => void | No | Handler custom (default: dispatch sidebar:navigate) |
isLoading | boolean | No | Activa overlay de carga |
hideCloseButton | boolean | No | Oculta el botón de cerrar |
Layout estándar (activado por module):
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
module | string | No | Nombre de módulo (ej: "REMUNERACIONES") — activa modo estándar |
description | string | No | Texto descriptivo bajo el título |
onNew | () => void | No | Handler para botón “Nuevo” |
newLabel | string | No | Etiqueta del botón (default: "+ Nuevo") |
actions | JSX.Element | No | Slot de botones de acción custom |
Gestión de tenant:
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
tenants | Tenant[] | No | Lista de tenants disponibles |
selectedTenantId | string | No | ID del tenant seleccionado |
onTenantChange | (id: string) => void | No | Handler de selección |
Stats y filtros:
| Prop | Tipo | Requerido | Descripción |
|---|---|---|---|
stats | JSX.Element | No | Slot de tarjetas de estadísticas |
searchPlaceholder | string | No | Placeholder del SearchBox |
searchValue | string | No | Valor de búsqueda actual |
onSearchChange | (val: string) => void | No | Handler de cambio de búsqueda |
filters | JSX.Element | No | Slot de filtros custom |
Estructura del Modo Estándar
Section titled “Estructura del Modo Estándar”<Show when={props.module} fallback={props.children}> {/* renderizado del layout estándar */}</Show>En orden vertical:
- Breadcrumb —
MÓDULO · TÍTULOen mayúsculas. - PageHeader — título, descripción y botones de acción.
- StatsGrid — métricas +
TenantPickerintegrado. - FilterBar —
SearchBox+ filtros custom. - Área de contenido —
childrencon manejo de scroll.
TenantPicker integrado
Section titled “TenantPicker integrado”Cuando se pasan tenants y onTenantChange, el componente renderiza el picker dentro del StatsGrid:
<Show when={props.tenants && props.onTenantChange}> <div class="bg-white dark:bg-zinc-800 rounded-xl shadow p-4"> <div class="text-xs font-medium text-zinc-500 dark:text-zinc-400 mb-1"> Organización </div> <TenantPicker tenants={props.tenants!} value={props.selectedTenantId || ""} onChange={props.onTenantChange!} /> </div></Show>Overlay de carga
Section titled “Overlay de carga”<Show when={props.isLoading}> <div class="absolute inset-0 z-50 flex items-center justify-center bg-white/50 dark:bg-zinc-900/50 backdrop-blur-sm"> <div class="w-8 h-8 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin" /> </div></Show>Sistema de configuración de encabezados
Section titled “Sistema de configuración de encabezados”Las definiciones de columnas para DataTable viven centralizadas en sevastopol/src/config/headers.ts y se reutilizan a través de las islands. Este patrón asegura consistencia y habilita lógica de renderizado compleja sin repetirla en cada vista.
flowchart LR
CFG["headers.ts"]
subgraph FOOT["Footer sum keys"]
FSP["FOOTER_SUM_KEYS_PROVEEDORES"]
FSC["FOOTER_SUM_KEYS_CLIENTES"]
end
subgraph HEAD["Definiciones"]
HPROV["HEADERS_PROVEEDORES"]
HCLI["HEADERS_CLIENTES"]
HPAY["HEADERS_PAYROLL"]
HEMP["HEADERS_EMPLEADOS"]
HCON["HEADERS_CONTRATOS"]
HVAC["HEADERS_VACATIONS"]
end
subgraph FMT["Utils de formato"]
CLP["fmtCLP()"]
DATE["fmtDate()"]
INT["fmtInt()"]
end
CFG --> FSP
CFG --> FSC
CFG --> HPROV
CFG --> HCLI
CFG --> HPAY
CFG --> HEMP
CFG --> HCON
CFG --> HVAC
HPAY --> CLP
HVAC --> DATE
HPAY --> INT Encabezados simples
Section titled “Encabezados simples”export const HEADERS_PROVEEDORES = [ { key: "rut_proveedor", label: "RUT" }, { key: "razon_social", label: "Razón Social" }, { key: "año", label: "Año" }, { key: "mes", label: "Mes" }, { key: "total_documentos", label: "# Docs" }, // ... más columnas];Encabezados con renderizadores
Section titled “Encabezados con renderizadores”Los renderizadores habilitan formato complejo y JSX inline:
export const HEADERS_PAYROLL = [ { key: "periodo", label: "Periodo" }, { key: "empleado_nombre", label: "Empleado" }, { key: "total_haberes", label: "Haberes", render: (v) => fmtCLP(num(v)) }, { key: "total_descuentos", label: "Descuentos", render: (v) => fmtCLP(num(v)) }, // ...];Renderizadores conscientes de fila
Section titled “Renderizadores conscientes de fila”Pueden acceder al valor de celda y al objeto de fila completo:
export const HEADERS_CONTRATOS = [ { key: "id", label: "Trabajador" }, { key: "numero_contrato", label: "N°", render: (v) => v ?? "—" }, { key: "tipo_contrato", label: "Tipo" }, { key: "fecha_inicio", label: "Inicio", render: (v) => fmtDate(v) }, { key: "semaforo", label: "Estado", render: (v, row: Contrato) => v ?? row.estado ?? "—", },];Footer sum keys
Section titled “Footer sum keys”Para cada conjunto de encabezados, las claves de suma definen qué columnas agregar en el pie:
export const FOOTER_SUM_KEYS_PROVEEDORES = [ "total_documentos", "total_neto", "total_iva_recuperable", "total_iva_no_recuperable", "total_compras", "total_exento", "total_activo_fijo", "total_iva_activo_fijo", "total_facturas", "total_notas_credito", "total_notas_debito", "promedio_monto_documentos",];Uso en islands
Section titled “Uso en islands”import { HEADERS_PAYROLL } from "@/config/headers";
<DataTable items={filteredPayrolls()} headers={HEADERS_PAYROLL} sumKeys={["total_haberes", "total_descuentos", "total_liquido"]} actions={{ onEdit: (row) => handleEdit(row), onDelete: (row) => handleDelete(row), }}/>Sistema de tipos
Section titled “Sistema de tipos”Los tipos UI centrales viven en sevastopol/src/types/ui.ts y aseguran type-safety a través de todos los componentes.
export type Option = { id: string; nombre: string; [key: string]: any;};
export type HeaderConfig = { key: string; label: string; sortable?: boolean; align?: "left" | "center" | "right"; render?: (value: any, row: any) => any;};
export type StatusType = | "ACTIVO" | "INACTIVO" | "PENDIENTE" | "APROBADO" | "RECHAZADO";
export type Semaforo = "VENCIDO" | "POR_VENCER" | "VIGENTE" | null;
export type ToastKind = "ok" | "error" | "info" | "warning";
export type PaginationMeta = { page: number; pageSize: number; totalPages: number; totalRecords: number;};
export type StatCardProps = { label: string; value: string | number; sub: string; highlight?: boolean; color?: string;};Patrones de composición
Section titled “Patrones de composición”Patrón 1: Modal de formulario con campos
Section titled “Patrón 1: Modal de formulario con campos”Modal de crear/editar usado a través de la aplicación:
<Modal open={showFormModal()} onClose={() => setShowFormModal(false)} title={editing() ? "Editar" : "Nuevo"}> <form onSubmit={handleSave}> <div class="grid grid-cols-2 gap-x-4 gap-y-3"> <Input label="RUT" name="rut" required value={editing()?.rut ?? ""} /> <Input label="Nombre" name="nombre" required value={editing()?.nombre ?? ""} /> <Select label="Tipo" name="tipo" options={tipoOptions} value={editing()?.tipo ?? ""} /> <Checkbox label="Activo" name="activo" checked={editing()?.activo ?? true} /> </div> <div class="flex gap-3 mt-6 justify-end"> <Button label="Cancelar" variant="secondary" onClick={handleCancel} /> <Button label="Guardar" variant="primary" type="submit" /> </div> </form></Modal>Patrón 2: IslandBase + DataTable
Section titled “Patrón 2: IslandBase + DataTable”Vista de lista con filtrado, stats y acciones:
<IslandBase module="REMUNERACIONES" title="Liquidaciones" description="Gestión de liquidaciones de sueldo" onNew={handleNew} searchValue={q()} onSearchChange={setQ} stats={<><StatCard label="Total" value={total} sub="Liquidaciones" /></>} tenants={tenants()} selectedTenantId={selectedTenantId()} onTenantChange={handleTenantChange}> <DataTable items={filteredItems()} headers={HEADERS_PAYROLL} sumKeys={["total_haberes", "total_descuentos", "total_liquido"]} actions={{ onEdit: handleEdit, onDelete: handleDelete, }} /></IslandBase>Patrón 3: Renderizadores custom con TableAction
Section titled “Patrón 3: Renderizadores custom con TableAction”const tableHeaders: HeaderConfig[] = [ { key: "rut", label: "RUT" }, { key: "nombre", label: "Nombre" }, { key: "actions", label: "", render: (_, row) => ( <div class="flex items-center justify-end gap-1"> <TableAction label="Editar" variant="edit" icon={ActionIcons.Edit} onClick={() => handleEdit(row)} /> <TableAction label="Borrar" variant="delete" icon={ActionIcons.Delete} onClick={() => handleDelete(row)} /> </div> ), },];Sistema de exportación
Section titled “Sistema de exportación”Todos los átomos se exportan centralmente desde sevastopol/src/components/atoms/index.ts:
export { TenantPicker, type Tenant } from "./TenantPicker";export { ToastProvider, useToast } from "./ToastProvider";export { Input, Select, TextArea, Checkbox, Button } from "./Fields";export { Modal } from "./Modal";export { SearchBox } from "./SearchBox";export { Pagination } from "./Pagination";export { TableAction, Icons as ActionIcons } from "./TableAction";export { DataTable } from "./DataTable";Esto habilita importaciones limpias en las islands:
import { Input, Select, Button, Modal, DataTable, TableAction, ActionIcons,} from "@/components/atoms";Utilidades de estilos
Section titled “Utilidades de estilos”Constantes centralizadas en sevastopol/src/lib/ui.ts:
export const inputCls = "w-full bg-white dark:bg-zinc-800 text-black dark:text-white ...";export const selectCls = "w-full bg-white dark:bg-zinc-800 ... bg-[url('data:image/svg+xml;...')]";export const chipCls = "px-2 py-0.5 rounded text-[11px]";Aseguran estilos consistentes en todos los componentes de formulario con soporte integrado de modo oscuro. selectCls incluye una flecha dropdown SVG embebida vía data URL.
Configuración Tailwind: el modo oscuro se activa por estrategia de clase:
darkMode: ["class", '[data-theme="dark"]'];Inventario completo
Section titled “Inventario completo”Lista canónica de todos los componentes presentes en sevastopol/src/components/. Los marcados como pendiente aún no tienen sección dedicada en esta página.
Átomos (sevastopol/src/components/atoms/)
Section titled “Átomos (sevastopol/src/components/atoms/)”| Componente | Archivo | Props clave | Documentado |
|---|---|---|---|
Input | Fields.tsx | label, name, ...rest | ✓ |
Select | Fields.tsx | options, value, onChange | ✓ |
Button | Fields.tsx | label, variant, icon | ✓ |
Checkbox | Fields.tsx | name, checked, message | ✓ |
TextArea | Fields.tsx | label, name | ✓ |
SearchBox | SearchBox.tsx | value, onChange, delay | ✓ |
Pagination | Pagination.tsx | total, limit, offset | ✓ |
TenantPicker | TenantPicker.tsx | tenants, value, onChange | ✓ |
TableAction | TableAction.tsx | variant, icon, onClick | ✓ |
ToastProvider | ToastProvider.tsx | (provider + useToast hook) | pendiente |
ChartContainer | ChartContainer.tsx | (wrapper para charts) | pendiente |
ExportButtons | ExportButtons.tsx | (exportación CSV/Excel/PDF) | pendiente |
LoadingSpinner | LoadingSpinner.tsx | (spinner standalone) | pendiente |
MarkdownEditor | MarkdownEditor.tsx | (editor markdown) | pendiente |
MarkdownView | MarkdownView.tsx | (visor markdown) | pendiente |
PlotlyChart | PlotlyChart.tsx | (gráfico Plotly) | pendiente |
Moléculas (sevastopol/src/components/molecules/)
Section titled “Moléculas (sevastopol/src/components/molecules/)”| Componente | Archivo | Props clave | Documentado |
|---|---|---|---|
PageHeader | PageHeader.tsx | title, description, actions | ✓ |
StatsGrid | StatsGrid.tsx | children | ✓ |
StatCard | StatCard.tsx | label, value, sub, highlight | ✓ |
FilterBar | FilterBar.tsx | children | ✓ |
Breadcrumb | Breadcrumb.tsx | text | ✓ |
BoolBadge | BoolBadge.tsx | (badge booleano) | pendiente |
StatusBadge | StatusBadge.tsx | (badge de StatusType) | pendiente |
TypeBadge | TypeBadge.tsx | (badge por tipo) | pendiente |
RegimeBadge | RegimeBadge.tsx | (badge de régimen tributario) | pendiente |
PeriodicidadBadge | PeriodicidadBadge.tsx | (badge de periodicidad) | pendiente |
ParameterFormModal | ParameterFormModal.tsx | (modal de formulario parametrizado) | pendiente |
Organismos
Section titled “Organismos”| Componente | Archivo | Props clave | Documentado |
|---|---|---|---|
Modal | atoms/Modal.tsx | open, onClose, children | ✓ |
DataTable | atoms/DataTable.tsx | items, headers, sumKeys, actions | ✓ |
Plantillas (sevastopol/src/components/templates/)
Section titled “Plantillas (sevastopol/src/components/templates/)”| Componente | Archivo | Props clave | Documentado |
|---|---|---|---|
IslandBase | IslandBase.tsx | module, title, stats, children | ✓ |
WorkspaceTheme | WorkspaceTheme.tsx | (wrapper de tema para workspaces) | pendiente |