Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AdriP-maker/JAAR_Antigravity/llms.txt

Use this file to discover all available pages before exploring further.

Overview

All application data lives inside an IndexedDB database named SIMAPDigital, managed by Dexie.js. This local database is the primary source of truth — every read and write goes through Dexie regardless of network status. Supabase (PostgreSQL) is a synchronization target, not the operational database. The schema has grown across six versioned migrations adding a total of 15 tables, each extending the schema as the product expanded from basic cobros to gamification, messaging, and notifications.

Tables at a glance

TableKey / IndexesPurpose
usuarios++id, nombre, sectorCommunity member (vecino) records
pagos++idPago, usuarioId, tipo, mesTarget, fechaAll payment cobros
saldos[usuarioId+mes], estadoPer-user, per-month ledger (composite key)
gastos++id, fechaJunta expense records
jornales++id, miembroId, fechaCommunity work attendance logs
pendingSync++id, type, timestampOffline sync queue
configkeySystem configuration store
registeredUsersuser, rol, estadoApp user accounts and their approval states
foro++id, fecha, autorCommunity bulletin board posts
juntas++id, nombre, ruc, estadoB2B water board registrations
auditoria++id, timestamp, accion, user_id, afectado_idImmutable audit log
archivos++id, pagoId, fileHash, fileNamePayment attachment metadata
puntos_historial++id, usuarioId, fecha, tipo_transaccionGamification points history
notificaciones++id, usuarioId, fecha, leidoIn-app notifications
mensajes++id, conversacionId, de, para, fecha, leidoDirect messages between users

Full Dexie schema definition

The schema is defined in src/services/db.js. Each db.version(n).stores({}) call is additive — later versions extend the schema without replacing earlier tables:
// src/services/db.js
import Dexie from 'dexie';

export const db = new Dexie('SIMAPDigital');

db.version(1).stores({
  usuarios:       '++id, nombre, sector',
  pagos:          '++idPago, usuarioId, tipo, mesTarget, fecha',
  saldos:         '[usuarioId+mes], estado',
  gastos:         '++id, fecha',
  jornales:       '++id, miembroId, fecha',
  pendingSync:    '++id, type, timestamp',
  config:         'key',
  registeredUsers: 'user, rol, estado',
});

db.version(2).stores({
  foro: '++id, fecha, autor',
});

db.version(3).stores({
  juntas:    '++id, nombre, ruc, estado',       // B2B water board registration
  auditoria: '++id, timestamp, accion, user_id, afectado_id', // Audit logs
  archivos:  '++id, pagoId, fileHash, fileName', // Blob storage metadata
});

db.version(4).stores({
  puntos_historial: '++id, usuarioId, fecha, tipo_transaccion',
});

db.version(5).stores({
  notificaciones: '++id, usuarioId, fecha, leido',
});

db.version(6).stores({
  mensajes: '++id, conversacionId, de, para, fecha, leido',
});

Schema versioning history

VersionTables addedWhat it enabled
1usuarios, pagos, saldos, gastos, jornales, pendingSync, config, registeredUsersCore offline cobros, jornal logging, and sync queue
2foroCommunity bulletin board / announcements
3juntas, auditoria, archivosB2B junta onboarding, immutable audit trail, payment file attachments
4puntos_historialGamification — points earned per payment and jornal
5notificacionesPer-user in-app notification inbox
6mensajesDirect messaging between cobrador and clients

System configuration object

The config table uses string keys. The 'general' key holds the system-wide operational settings:
// DEFAULT_CONFIG from src/utils/constants.js
export const DEFAULT_CONFIG = {
  cuotaMensual:      3.00,   // Monthly water fee in B/. (Balboas)
  permitirParciales: true,   // Allow partial payments
  mesesGraciaCorte:  3,      // Months of non-payment before service cut
};
initDB() seeds this key on first launch. Admins can update it at runtime via saveConfig(cfg), which performs an upsert (db.config.put).

The saldos composite key pattern

The saldos table uses a compound primary key [usuarioId+mes]:
saldos: '[usuarioId+mes], estado'
This means the combination of usuarioId and mes (formatted as YYYY-MM) uniquely identifies a single balance record. There is exactly one saldo row per user per month — no duplicates are possible at the database level. Why a composite key instead of auto-increment?
  • Idempotent upserts. db.saldos.put({ usuarioId, mes, ... }) either creates or updates the record without needing to look up an existing ID first.
  • Fast lookups. db.saldos.get([usuarioId, '2025-03']) is a single indexed read — no full-table scan.
  • Natural identity. A balance for user 42 in March 2025 has a stable, human-readable identity: [42, '2025-03'].
Multi-month payments work by iterating a range of months and upserting one saldo row per month in a single transaction.

The auditoria table

Every security-relevant action in SIMAP Digital is permanently recorded in auditoria. The table is append-only by convention — no record is ever updated or deleted.
auditoria: '++id, timestamp, accion, user_id, afectado_id'
FieldDescription
idAuto-increment local ID
timestampISO 8601 datetime of the action
accionString describing the operation (e.g. "PAGO_REGISTRADO", "USUARIO_APROBADO", "CONFIG_ACTUALIZADA")
user_idUUID of the authenticated user who performed the action
afectado_idUUID or local ID of the entity that was affected
The dev role has read-only access to this table via the Auditoría tab in /admin. No other role can view audit records. When auditoria data is synced to Supabase, it feeds the cloud-side audit trail which admins can query for compliance and incident review.

The archivos table

archivos stores metadata about files attached to payment records (e.g. photo receipts). The actual binary content is stored in Supabase Storage; only the reference is kept in IndexedDB:
archivos: '++id, pagoId, fileHash, fileName'
fileHash is a content hash used to detect duplicate uploads and prevent re-sending the same file during sync retries.

Soft-delete pattern

SIMAP Digital never physically deletes records from its operational tables. Instead, records are deactivated by setting estado: 'inactivo' (or equivalent flag fields). This applies to:
  • usuarios — deactivated community members remain in the database so historical payment records remain intact and auditable.
  • registeredUsers — suspended accounts (estado: 'suspendido') are retained for the audit trail.
  • juntas — water boards can be placed in estado: 'pendiente_global' or 'inactivo' without data loss.
Physical deletion only occurs in logout(), which clears db.usuarios, db.pagos, and db.saldos for security — not as a data management operation. All cloud data in Supabase is unaffected by the local clear.

localStorage migration

On first launch after upgrading from the legacy HTML5/vanilla-JS version of SIMAP, initDB() automatically runs migrateFromLocalStorage(). This one-time migration reads simap_users, simap_pagos, simap_gastos, and simap_usuarios from localStorage, imports them into the corresponding Dexie tables, and writes a ls_migrated config flag to prevent re-running. No data is lost during the upgrade path.

Build docs developers (and LLMs) love