SIMAP Digital is built around a write-local-first, sync-later model. Every action a collector takes — registering a payment, logging a gasto, recording a jornal — is saved immediately to the browser’s IndexedDB via Dexie.js. The app works identically with or without an internet connection. When connectivity is available, queued records are pushed to Supabase PostgreSQL in the background. This architecture solves the core problem described inDocumentation 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.
docs/arquitectura.md:
“When a collector goes house to house to collect water payments, they are usually in rural areas where there is no internet coverage or cell signal.”
The Sync Model
Why IndexedDB?
| Feature | IndexedDB (via Dexie.js) | localStorage |
|---|---|---|
| Storage capacity | Hundreds of MB | ~5–10 MB |
| Structured queries | ✅ Full query API | ❌ Key-value only |
| Async non-blocking | ✅ Promise-based | ❌ Synchronous |
| Works offline | ✅ | ✅ |
The pendingSync Queue
Every local write that needs to reach the cloud creates an entry in the pendingSync store within IndexedDB. This acts as the outbox for all offline work. The entry shape varies slightly by service:
The useOnline Hook
Network status is tracked application-wide by src/hooks/useOnline.js, which registers listeners on the browser’s native online and offline events:
isOnline is a reactive boolean. Any component that needs to conditionally enable sync or display the connectivity badge consumes this hook.
Sync Triggers
Synchronization is initiated in two situations:| Trigger | When | Condition |
|---|---|---|
| App load | On initial render / page refresh | A valid session must exist in authService |
| Network recovery | When useOnline transitions from false → true | Any pending records exist in the queue |
Upload Sync (Device → Cloud)
syncService.js exposes pushToSupabase(pago), which maps a local payment record to Supabase column names and inserts it:
error is null the record can be removed from pendingSync. If the insert fails (network drop, Supabase timeout), the record stays in the queue and will be retried on the next sync trigger.
Download Sync (Cloud → Device)
syncService.js also pulls canonical data from Supabase down to the local cache via syncFromSupabase(). This runs on app load when online and populates the local tables with the latest server state:
usuarios, pagos, saldos, gastos.
Sync Flow Diagram
What Gets Synced
| Data Type | Local Table | Supabase Table | pendingSync type | Direction |
|---|---|---|---|---|
| Payments | db.pagos | pagos | pago | Both ↕ |
| Expenses | db.gastos | gastos | ADD_GASTO | Both ↕ |
| Work jornals | db.jornales | jornales | ADD_JORNAL | Push ↑ |
| Forum posts | db.foro | foros | ADD_POST | Push ↑ |
| User data | db.usuarios | usuarios | — | Pull ↓ |
| Account balances | db.saldos | saldos | — | Pull ↓ |
Jornales, forum posts, and gastos are written to
pendingSync locally but the current syncFromSupabase() download pass only fetches usuarios, pagos, saldos, and gastos. Jornals and forum posts are upload-only at this time.The Sync Indicator in the Header
The app header displays live connectivity and sync status:- 🔴 Sin Red badge — shown when
useOnline()returnsfalse - Pending count — number of records in
pendingSyncwaiting to upload
Data Security on Logout
When a user logs out, sensitive local tables are cleared to prevent another person picking up the device and accessing resident financial data:Conflict Resolution
SIMAP uses a last-write-wins strategy for most records. Because the offline client is the single collector device for a given community, true write conflicts are rare. Each payment is assigned a unique
idPago composed of a timestamp plus a random component (e.g., 1710499920000_r4f2a), ensuring that concurrent inserts never collide on the primary key — even if two devices happen to sync simultaneously.