GastosApp es una aplicación de escritorio construida sobre Electron 42, React 19 y Vite 8. El proceso principal de Electron controla la ventana nativa y expone una API segura al renderer a través deDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Davidmallega/Gastos-App/llms.txt
Use this file to discover all available pages before exploring further.
contextBridge; el renderer ejecuta la aplicación React, que gestiona su estado globalmente mediante un store singleton sin librerías externas. Todo el almacenamiento ocurre en localStorage del proceso Electron, sin ningún servidor ni conexión a internet.
Procesos Electron
Electron opera con dos procesos separados que se comunican mediante IPC:- Proceso principal (
electron/main.js, ESM): crea y controla laBrowserWindow, registra los handlers IPC para los controles de ventana y carga la aplicación React (Vite dev server en desarrollo,dist/index.htmlen producción). - Proceso renderer (React app): ejecuta la UI completa dentro del contexto Chromium de Electron. Se comunica con el proceso principal exclusivamente a través de
window.electronAPI, el objeto expuesto porcontextBridge.
Proceso principal (main.js)
El archivoelectron/main.js está escrito en ESM (usa import/export nativos de Node.js). Al arrancar crea la ventana principal con la siguiente configuración:
- Dimensiones: 1280 × 820 px por defecto, con un mínimo de 900 px de ancho para garantizar que la Sidebar y el contenido principal convivan correctamente.
titleBarStyle: 'hidden'(o'hiddenInset'en macOS): oculta la barra de título nativa del sistema operativo. GastosApp implementa su propia barra de 36 px con los botones de minimizar, maximizar y cerrar usando la regiónWebkitAppRegion: 'drag'.contextIsolation: true+nodeIntegration: false: configuración de seguridad recomendada por Electron. El renderer no tiene acceso directo a las APIs de Node.js; todo pasa porcontextBridge.show: false: la ventana se muestra recién después del eventoready-to-show, evitando el parpadeo blanco inicial.
createWindow) para evitar registros duplicados si la ventana se vuelve a crear:
setWindowOpenHandler permite que las ventanas about:blank se abran (necesario para la impresión PDF desde el renderer) y redirige cualquier otra URL al navegador del sistema:
Preload y contextBridge
El archivoelectron/preload.cjs está en formato CommonJS (requerido por Electron para los scripts de preload) y utiliza contextBridge para exponer de forma segura un subconjunto de la API IPC al renderer:
contextBridge.exposeInMainWorld inyecta el objeto electronAPI en window del renderer de forma aislada, respetando el límite de contexto que impone contextIsolation: true. Desde cualquier componente React se puede llamar, por ejemplo, window.electronAPI?.minimize(). El operador opcional ?. es intencional: en modo web (browser, sin Electron) window.electronAPI no existe y la llamada simplemente no ocurre.
window.electronAPI expone exactamente tres métodos:
| Método | Canal IPC | Acción |
|---|---|---|
minimize() | window-minimize | Minimiza la ventana |
maximize() | window-maximize | Maximiza o restaura la ventana |
close() | window-close | Cierra la ventana |
Renderer (React)
El punto de entrada del renderer essrc/main.jsx, que monta la aplicación en el nodo #root:
src/App.jsx es el componente raíz. Gestiona:
- El estado de navegación (
currentPage), inicializado en'dashboard'. - El tema claro/oscuro, persistido en
localStoragebajo la clave'theme'. - La barra de título personalizada con los botones de control de ventana.
- La composición
<Sidebar> + <CurrentPage>.
<Sidebar> contiene los 12 ítems de navegación definidos en navItems:
| ID | Etiqueta |
|---|---|
dashboard | Dashboard |
gastos-caja | Gastos por Caja |
facturas | Facturas SII |
pendientes | Pagos Pendientes |
pagos-realizados | Pagos Realizados |
compromisos | Compromisos |
informes | Informes |
categorias | Categorías |
proveedores | Proveedores |
importar | Importar CSV SII |
backup | Respaldo |
papelera | Papelera |
<CurrentPage> es simplemente const Page = pages[currentPage] || Dashboard, donde pages es un objeto que mapea cada ID a su componente de página correspondiente.
Navegación sin React Router
GastosApp implementa navegación mediante un Custom Event del DOM en lugar de React Router. Cualquier componente, sin importar su profundidad en el árbol, puede navegar a otra página con una sola línea:App.jsx escucha este evento en un useEffect y actualiza el estado currentPage:
- Sin prop drilling: un botón dentro de un modal anidado en tres niveles puede navegar sin que ninguno de sus componentes padre reciba ni propague un callback
onNavigate. - Sin dependencias extra: no se instala
react-router-domni ningún otro paquete de routing. - Consistencia con el modelo desktop: la app tiene un único nivel de “rutas” (12 páginas planas), por lo que las capacidades avanzadas de React Router (rutas anidadas, parámetros de URL, history API) no aportan valor en este contexto.
La app usa un custom event para navegación, no React Router. Esto permite que componentes anidados naveguen sin prop drilling.
Modal con createPortal
Los componentesModal y Dialog de src/components/ui/index.jsx renderizan su contenido directamente en document.body usando createPortal:
overflow: hidden — como el <aside> del Sidebar — cualquier descendiente con position: fixed queda recortado dentro de ese contexto de apilamiento. Al teleportar el modal a document.body con createPortal, el overlay y el panel flotante escapan completamente del árbol del Sidebar y se renderizan sobre la totalidad de la ventana, con el z-index correcto.
Fechas como string YYYY-MM-DD
Todos los campos de fecha de los modelos de datos (fecha, fechaPago) se almacenan como strings en formato 'YYYY-MM-DD', no como objetos Date ni timestamps Unix.
Esta decisión evita un problema real en Electron: al hacer new Date('2025-06-15').toISOString() en una zona horaria UTC-4 o UTC-5, el resultado es '2025-06-14T...' — la fecha se desfasa un día al cruzar la medianoche UTC. Almacenando la fecha directamente como string, la comparación y visualización son siempre exactas:
createdAt, deletedAt, ultimoRegistro, creadoEn) son marcas de tiempo internas que nunca se muestran como fechas de calendario al usuario.