SIMAP Digital uses a dual-database architecture: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.
- IndexedDB (via Dexie.js) — the local, offline-first source of truth living in the user’s browser. Every read and write in the application hits IndexedDB first.
- Supabase (PostgreSQL) — the cloud sync target. Records queued in
pendingSyncare pushed to Supabase whenever the device has an internet connection.
IndexedDB Schema (Dexie.js)
The database is opened under the nameSIMAPDigital and is defined across six sequential schema versions. Dexie applies migrations incrementally — adding a version only ever adds new tables or new indexes; existing data is never destroyed during an upgrade.
Complete Table Inventory
| Table | Version introduced | Primary key | Indexed fields | Purpose |
|---|---|---|---|---|
usuarios | 1 | ++id (auto-int) | nombre, sector | Registered community households / residents |
pagos | 1 | ++idPago (auto-int) | usuarioId, tipo, mesTarget, fecha | Payment transaction ledger |
saldos | 1 | [usuarioId+mes] (composite) | estado | Per-household monthly balance entries |
gastos | 1 | ++id (auto-int) | fecha | Board expenses and purchases |
jornales | 1 | ++id (auto-int) | miembroId, fecha | Community work session attendance |
pendingSync | 1 | ++id (auto-int) | type, timestamp | Outbound cloud sync queue |
config | 1 | key (string) | — | System configuration key-value store |
registeredUsers | 1 | user (string) | rol, estado | Local login accounts (RBAC) |
foro | 2 | ++id (auto-int) | fecha, autor | Community notice board posts |
juntas | 3 | ++id (auto-int) | nombre, ruc, estado | Water board entity registry (B2B) |
auditoria | 3 | ++id (auto-int) | timestamp, accion, user_id, afectado_id | Immutable audit log |
archivos | 3 | ++id (auto-int) | pagoId, fileHash, fileName | Payment receipt file metadata |
puntos_historial | 4 | ++id (auto-int) | usuarioId, fecha, tipo_transaccion | Loyalty points transaction history |
notificaciones | 5 | ++id (auto-int) | usuarioId, fecha, leido | In-app notifications per user |
mensajes | 6 | ++id (auto-int) | conversacionId, de, para, fecha, leido | Direct messages between users |
Dexie Version Definitions
The full schema as written insrc/services/db.js:
Dexie’s store syntax only lists indexed fields — not every property of the stored object. You may store additional unindexed fields on any record; they are persisted but cannot be used in
where() queries. For example, pagos records carry a monto (amount) field that is not indexed because payments are always retrieved by usuarioId or mesTarget, never filtered by amount alone.Table Field Descriptions
usuarios — Resident records
| Field | Type | Description |
|---|---|---|
id | auto-int PK | Auto-incrementing local identifier |
nombre | string | Full household or family name (e.g. 'Sanchez Maylene') |
sector | string | Geographic sector within the community (e.g. 'Caballero Centro') |
pagadoEsteMes | boolean | Denormalised flag: whether the household paid in the current month |
mesesSinPagar | number | Cached count of consecutive unpaid months; drives estado transitions |
pagos — Payment transactions
| Field | Type | Description |
|---|---|---|
idPago | auto-int PK | Auto-incrementing payment identifier |
usuarioId | number FK → usuarios.id | Household that made the payment |
tipo | string | Payment type (see TIPOS_PAGO constants) |
mesTarget | string | Target month in 'YYYY-MM' format |
fecha | string/date | Date the payment was recorded |
monto | number | Amount paid in balboas (B/.) |
tipo (defined in src/utils/constants.js):
| Value | Label | Meaning |
|---|---|---|
mensual | Cuota Mensual | Standard full monthly quota |
diario | Pago Diario | Day-rate payment (partial period) |
multi_mes | Multi-Mes | Single payment covering multiple months |
parcial | Parcial | Partial payment; requires permitirParciales: true in config |
adelanto | Adelanto | Advance payment for future months |
puesta_al_dia | Puesta al Día | Catch-up payment clearing outstanding debt |
saldos — Balance ledger
| Field | Type | Description |
|---|---|---|
[usuarioId+mes] | composite PK | Compound key: household ID + month string |
usuarioId | number | References usuarios.id |
mes | string | Month in 'YYYY-MM' format |
estado | string | Payment state for this household/month record |
estado (set by pagosService when writing saldo records):
| Value | Meaning |
|---|---|
pendiente | Month record has been initialised but no payment has been applied yet |
parcial | A partial payment has been applied; outstanding balance remains |
pagado | The full monthly quota has been received; month is settled |
These are the saldo-record states written directly by
pagosService. They differ from the user-level status values (activo, moroso, corte) returned by calcularEstado(), which are derived by inspecting multiple saldo records across months and comparing against the grace-period threshold.pendingSync — Cloud sync queue
| Field | Type | Description |
|---|---|---|
id | auto-int PK | Queue entry identifier |
type | string | Operation type, e.g. 'pago', 'gasto', 'jornal' |
data | object | Full serialised record to push to Supabase |
timestamp | string | ISO 8601 timestamp (new Date().toISOString()) of when the entry was enqueued |
config — System configuration
| Field | Type | Description |
|---|---|---|
key | string PK | Configuration namespace (e.g. 'general', 'ls_migrated') |
...fields | mixed | Flattened config fields stored alongside key on the same record |
'general' row holds cuotaMensual, permitirParciales, and mesesGraciaCorte. See Runtime System Config for the full field reference.
registeredUsers — Login accounts
| Field | Type | Description |
|---|---|---|
user | string PK | Username (login identifier) |
pass | string | Password (hashed in production; plaintext in demo seeds) |
rol | string | Role: admin, cobrador, minsa, cliente, or dev |
nombre | string | Display name |
estado | string | Account state: 'activo' or 'inactivo' |
auditoria — Audit log
| Field | Type | Description |
|---|---|---|
id | auto-int PK | Log entry identifier |
timestamp | number/date | When the action occurred |
accion | string | Human-readable action label (e.g. 'PAGO_REGISTRADO') |
user_id | string | Username of the actor |
afectado_id | number | Primary key of the record that was affected |
archivos — Payment receipt metadata
| Field | Type | Description |
|---|---|---|
id | auto-int PK | File record identifier |
pagoId | number FK → pagos.idPago | Payment this receipt belongs to |
fileHash | string | SHA-256 hex digest of the uploaded file (computed client-side before upload) |
fileName | string | Original filename |
Supabase Migration (00001_init.sql)
The file supabase/migrations/00001_init.sql creates the entire cloud schema in a single migration. It is applied by running supabase db push (or via the Supabase dashboard’s SQL editor for manual setup).
profiles — Extended auth user data
profiles is the bridge between Supabase Auth and the application’s RBAC system. When a user signs up, the on_auth_user_created trigger (see below) automatically inserts a matching profiles row, copying email and the nombre value from raw_user_meta_data. The application then reads profiles.rol to determine which routes and UI sections are accessible.
Auth → profiles relationship:
Automatic profile creation trigger
SECURITY DEFINER, meaning it executes as the function owner (a Postgres superuser role) rather than the signing-up user. This is required because auth.users is in a restricted schema that the anon role cannot write to directly.
Core application tables
Collaboration and audit tables
Row Level Security (RLS)
RLS is enabled on every table. The migration ships with permissive baseline policies that allow any authenticated user to read and write most operational data. These policies should be tightened before a production rollout to enforce role-based access (matching theROLES constants in src/utils/constants.js).
Personal data tables (notificaciones, mensajes) already enforce owner-only access:
Realtime subscriptions
mensajes and notificaciones are added to the Supabase Realtime publication so the React client can subscribe to live updates without polling:
Key Data Patterns
Saldo composite key
Thesaldos table uses a compound primary key of [usuarioId + mes], where mes is always a 'YYYY-MM' string (e.g. '2024-03'). This guarantees exactly one balance record per household per calendar month and allows efficient range queries:
UNIQUE(usuario_id, mes).
Soft-delete pattern
Never issue aDELETE on the usuarios table. Removing a household record would cascade-delete all associated pagos and saldos, destroying the payment history. Instead, set the household’s estado to 'inactivo':
Pending sync queue
Every write that should eventually reach Supabase must enqueue a corresponding entry inpendingSync:
online events, reads all rows from pendingSync, pushes them to Supabase in order of timestamp, and removes each entry after a successful 201 response.
Audit log
Every action classified as critical (payment registration, user approval, config change, etc.) must append a row toauditoria:
File integrity with SHA-256
Before uploading a payment receipt to Supabase Storage, the client computes a SHA-256 digest of the file using the Web Crypto API and stores it inarchivos.fileHash:
archivos.fileHash to verify that the file has not been altered since upload — important for audit and dispute resolution in MINSA inspections.