Sevastopol (Frontend)
Propósito y Alcance
Section titled “Propósito y Alcance”Este documento proporciona una visión general de la aplicación frontend Sevastopol, que sirve como interfaz de usuario para el sistema de contabilidad Nostromo. Sevastopol está construido usando Astro para renderizado del lado del servidor y enrutamiento, con SolidJS para componentes reactivos del lado del cliente. Esta página cubre la arquitectura general, el stack tecnológico y los patrones de alto nivel utilizados en todo el frontend.
Tecnologías Utilizadas
Section titled “Tecnologías Utilizadas”Sevastopol utiliza un stack de desarrollo web moderno optimizado para rendimiento y experiencia del desarrollador:
| Tecnología | Versión | Propósito |
|---|---|---|
| Astro | 5.12.3 | Framework SSR, enrutamiento, shell de página |
| SolidJS | 1.8.5+ | Islands reactivas, lógica de componentes |
| TailwindCSS | 3.x | Estilos utility-first |
| TypeScript | 5.8.3 | Seguridad de tipos |
| Vite | 6.3.5+ | Herramienta de build, servidor dev, proxy API |
Las bibliotecas adicionales incluyen:
jsPDF+jspdf-autotablepara generación de PDFxlsxpara exportación a Excelrut.jspara validación de RUT chileno@playwright/testpara pruebas E2E
🚀 Instalación y Ejecución
Section titled “🚀 Instalación y Ejecución”Para levantar el entorno de desarrollo local de Sevastopol:
Prerrequisitos
Section titled “Prerrequisitos”- Node.js: v18+ (LTS recomendado)
- Gestor de paquetes: npm (incluido con Node) o pnpm
-
Clonar el repositorio:
Terminal window git clone https://github.com/ChrisTkm/Sevastopol.gitcd Sevastopol -
Instalar dependencias:
Terminal window npm install# o si usas pnpmpnpm install -
Iniciar servidor de desarrollo:
Terminal window npm run devEl servidor estará disponible en
http://localhost:4321.
🎨 Principios de Diseño
Section titled “🎨 Principios de Diseño”Sevastopol no es solo una herramienta administrativa; es una experiencia Premium.
Estética y UX
Section titled “Estética y UX”- Glassmorphism: Uso extensivo de transparencias y blurs para dar profundidad.
- Dark Mode First: Diseñado nativamente para interfaces oscuras, reduciendo fatiga visual.
- Micro-interacciones: Feedback visual inmediato en hovers, clicks y transiciones.
- Tipografía: Uso de familias tipográficas modernas (Inter/Roboto) para máxima legibilidad.
Reglas Críticas
Section titled “Reglas Críticas”- Dynamic Design: La interfaz debe sentirse “viva”. Evitar componentes estáticos aburridos.
- Visual Excellence: No aceptar diseños “MVP” o básicos. Cada pantalla debe tener un acabado profesional.
- Responsividad: Fluidez total entre resoluciones de escritorio y tablet.
Visión General de la Arquitectura
Section titled “Visión General de la Arquitectura”Sevastopol implementa una arquitectura híbrida de SSR + Islands, combinando los beneficios de rendimiento del renderizado del lado del servidor con interactividad selectiva del lado del cliente.
Astro como Fundamento
Section titled “Astro como Fundamento”Astro renderiza el HTML inicial con JavaScript mínimo. La carcasa de la aplicación se renderiza del lado del servidor, proporcionando un First Contentful Paint instantáneo:
Diagrama: Estructura de Archivos y Flujo SSR
Section titled “Diagrama: Estructura de Archivos y Flujo SSR”flowchart LR
%% --- PAGES ---
subgraph P[pages/]
cmd["command.astro<br/>Carcasa principal app"]
idx["index.astro<br/>Formulario de login"]
end
%% --- SRC ---
subgraph SRC["sevastopol/src/"]
vr["view-router.ts Listener<br/>eventos + import dinámico"]
sb["organisms/Sidebar.astro<br/>Menú navegación SSR"]
subgraph ISL["components/islands/"]
subgraph PAY["payroll/"]
pay["PayrollViewIsland.tsx"]
emp["EmployeesViewIsland.tsx"]
con["ContractsViewIsland.tsx"]
end
subgraph ADM["admin/"]
ten["TenantsViewIsland.tsx"]
ses["SessionsViewIsland.tsx"]
end
end
end
%% --- OTROS ---
cfg["config/menu.config.ts<br/>menu: MenuItem[]"]
layout["layouts/Layout.astro<br/>HTML base, fuentes, init tema"]
%% --- FLUJOS ---
cmd -- inicializa --> vr
cmd -- renderiza --> sb
vr -- "import() bajo demanda" --> pay
vr -- "import() bajo demanda" --> emp
vr -- "import() bajo demanda" --> con
vr -- "import() bajo demanda" --> ten
vr -- "import() bajo demanda" --> ses
sb -- lee --> cfg
La página command.astro sirve como la carcasa de la aplicación, conteniendo:
- Componente
<Sidebar />(SSR, sin hidratación) - Contenedor
<div id="command-view">(objetivo para renderizado de islas) - Etiqueta
<script type="module">importandoview-router.ts
Arquitectura de Islas
Section titled “Arquitectura de Islas”Las islas SolidJS se cargan de forma diferida y se hidratan selectivamente—solo los componentes interactivos se convierten en JavaScript vivo, mientras que el resto permanece como HTML estático. Esto reduce drásticamente el tamaño del bundle inicial.
Diagrama: Arquitectura de Islas - Mapeo de Entidades de Código
flowchart LR
%% =========================
%% Browser (SSR + navegación)
%% =========================
subgraph B["Browser · Carga inicial"]
cmd["command.astro (SSR)<br/>~15KB HTML"]
sidebar["Sidebar.astro (estático)<br/>sin hidratación JS"]
mount["div#command-view<br/>contenedor vacío"]
click_evt["click data-view<br/>emite evento sidebar:navigate"]
cmd --> sidebar
cmd --> mount
sidebar --> click_evt
end
%% =========================
%% Router (import dinámico + render)
%% =========================
subgraph VR["view-router.ts"]
listener["window.addEventListener<br/>evento sidebar:navigate"]
unmount_fn["unmount()<br/>dispone isla anterior"]
registry["views: Record<br/>57 entradas"]
importPay["await import<br/>./payroll/PayrollViewIsland.tsx"]
importEmp["await import<br/>./payroll/EmployeesViewIsland.tsx"]
importCon["await import<br/>./payroll/ContractsViewIsland.tsx"]
importTen["await import<br/>./admin/TenantsViewIsland.tsx"]
render_fn["render()<br/>desde solid-js/web"]
end
click_evt --> listener --> unmount_fn --> registry
registry --> importPay --> render_fn
registry --> importEmp --> render_fn
registry --> importCon --> render_fn
registry --> importTen --> render_fn
render_fn -- inyecta en --> mount
%% =========================
%% Chunks (code-split)
%% =========================
subgraph CH["Island Chunks · Code-split"]
pay["PayrollViewIsland.tsx<br/>~65KB chunk"]
emp["EmployeesViewIsland.tsx<br/>~45KB chunk"]
con["ContractsViewIsland.tsx<br/>~40KB chunk"]
ten["TenantsViewIsland.tsx<br/>~35KB chunk"]
end
importPay --> pay
importEmp --> emp
importCon --> con
importTen --> ten
%% =========================
%% Backend
%% =========================
subgraph BE["Orchestrator Backend"]
api["/api/* endpoints<br/>localhost:8000"]
end
pay -->|"authenticatedFetch /api/remuneraciones/payroll"| api
emp -->|"authenticatedFetch /api/employees"| api
ten -->|"authenticatedFetch /api/admin/tenants"| api
con -->|"authenticatedFetch /api/payroll/contracts"| api
Patrón clave: Usuario hace clic en barra lateral → evento sidebar:navigate → router importa isla → render() monta en #command-view → isla obtiene datos de Orchestrator.
Organización de Componentes
Section titled “Organización de Componentes”Los componentes siguen la metodología de Diseño Atómico:
Directorycomponents/
Directoryatoms/
- Bloques de construcción básicos
- Button.tsx
- Input.tsx
- Select.tsx
- Modal.tsx
- SearchBox.tsx
- Pagination.tsx
- SidebarButton.astro
Directorymolecules/
- Combinaciones simples
- SidebarGroup.astro
- SocialLinks.astro
- FilterBar.tsx
Directoryorganisms/
- Secciones UI complejas
- Sidebar.astro
- DataTable.tsx
- IslandBase.tsx
Directoryislands/
- Características interactivas
- view-router.ts
Directoryadmin/
- TenantsViewIsland.tsx
- SessionsViewIsland.tsx
- PlanContableViewIsland.tsx
Directorypayroll/
- PayrollViewIsland.tsx
- EmployeesViewIsland.tsx
- ContractsViewIsland.tsx
Patrón de Estructura de Isla
Section titled “Patrón de Estructura de Isla”Todas las islas siguen una estructura consistente:
export default function EmployeesViewIsland() { // 1. Signals de estado const [data, setData] = createSignal<Employee[]>([]); const [loading, setLoading] = createSignal(true);
// 2. Estado derivado (memos) const filtered = createMemo(() => /* ... */);
// 3. Effects para carga de datos createEffect(() => { /* fetch data */ });
// 4. Manejadores de eventos const handleCreate = async () => { /* ... */ };
// 5. Renderizar usando plantilla IslandBase return ( <IslandBase mode="standard" title="Empleados" stats={[/* ... */]} > <DataTable headers={headers} data={filtered()} /> </IslandBase> );}Gestión de Estado con SolidJS
Section titled “Gestión de Estado con SolidJS”Sevastopol usa el sistema de reactividad de grano fino de SolidJS para gestión de estado. A diferencia de React, SolidJS no usa un DOM virtual ni re-renderiza componentes; en cambio, las actualizaciones son quirúrgicas.
Primitivas Principales
Section titled “Primitivas Principales”| Primitiva | Propósito | Ejemplo |
|---|---|---|
createSignal<T>() | Estado mutable | const [count, setCount] = createSignal(0) |
createMemo<T>() | Estado derivado (cacheado) | const doubled = createMemo(() => count() * 2) |
createEffect() | Efectos secundarios | createEffect(() => console.log(count())) |
Patrón Típico de Estado de Isla
Section titled “Patrón Típico de Estado de Isla”function MyIsland() { // Datos crudos desde API const [items, setItems] = createSignal<Item[]>([]);
// Estado UI const [searchTerm, setSearchTerm] = createSignal(""); const [loading, setLoading] = createSignal(true);
// Datos derivados (se actualiza automáticamente cuando cambian dependencias) const filtered = createMemo(() => { const term = searchTerm().toLowerCase(); return items().filter(item => item.name.toLowerCase().includes(term) ); });
// Effect de carga de datos createEffect(async () => { setLoading(true); const data = await fetchItems(); setItems(data); setLoading(false); });
return <DataTable data={filtered()} />;}Los signals de SolidJS son funciones: llámalas para leer count(), y pasa un valor para escribir setCount(5).
Fuentes: Inferido de patrones SolidJS, no se proporcionó archivo específico
Integración API
Section titled “Integración API”Las islas se comunican con Orchestrator vía authenticatedFetch() de @/lib/api. Esta utilidad adjunta la cookie de sesión sid y maneja errores 401/403.
Diagrama: Flujo de Petición API - Frontend a Backend
flowchart LR
%% ================
%% Componente Island
%% ================
subgraph ISL["Componente Island"]
ce["createEffect()<br/>Carga de datos"]
fetch["authenticatedFetch()<br/>@/lib/api"]
set["setData(employees)"]
ce --> fetch
fetch --> set
end
%% ==========
%% Request URL
%% ==========
req["GET /api/employees?<br/>tenant_id=6000431"]
fetch --> req
%% =============
%% Vite Dev Proxy
%% =============
subgraph VITE["Vite Dev Proxy"]
proxy["Proxy /api/* → localhost:8000"]
end
req --> proxy
%% ===================
%% Orchestrator (app.ts)
%% ===================
subgraph ORCH["Orchestrator · app.ts"]
mw_auth["authenticatedToken<br/>middleware"]
mw_authz["authorizeRoute<br/>middleware"]
route["Manejador ruta Express<br/>/api/employees"]
end
proxy --> mw_auth --> mw_authz --> route
%% =========
%% Database
%% =========
subgraph DB["Database"]
pool["getTenantPool()<br/>Pool específico tenant"]
pg["PostgreSQL<br/>nostromo_6000431"]
end
route --> pool --> pg
pg -->|"200 OK + data<br/>JSON snake_case"| route
route -->|"200 OK + data"| proxy -->|"200 OK + data"| fetch
Patrón típico de carga de datos en isla:
createEffect(async () => { const tenantId = selectedTenant(); if (!tenantId) return;
setLoading(true); const res = await authenticatedFetch(`/api/employees?tenant_id=${tenantId}`); if (res.ok) { const data = await res.json(); setEmployees(data); } setLoading(false);});El servidor dev de Vite hace proxy de /api/* a localhost:8000 (Orchestrator). En producción, ambos servicios corren detrás del mismo dominio, eliminando problemas de CORS.
Módulos de Negocio
Section titled “Módulos de Negocio”Gestiona la configuración global y multi-tenant. - TenantManagement: Creación y edición de tenants. - UserRoles: Asignación de permisos y usuarios.
Contiene la lógica compleja de remuneraciones. - PayrollCalculator: Interfaz para ejecutar cálculos de sueldos. - LiquidacionesPreview: Visualización en tiempo real de liquidaciones. - HistoricoRemuneraciones: Tablas de navegación de datos históricos.
Visualización y gestión de documentos tributarios. - OperationsViewIsland: Tabla unificada de Ventas, Compras y Boletas. - OperationsService: Generación de asientos contables.