# Codebase Navigation — WebGIS Poverty Mapping > Quick reference for navigating the project. Updated: 2026-06-03. --- ## Directory Tree at a Glance ``` WebgisPovertyMapping/ ├── index.php ← Main app shell (auth-aware) ├── index.html ← Redirect/legacy entry from older prototype ├── dashboard.php ← Authenticated admin/operator shell ├── papan-kebutuhan.php ← Public needs board page ├── config.php ← All app constants (DB, session, map center) ├── koneksi.php ← DB connection + Haversine + calc_proximity() ├── setup_database.sql ← Full schema (idempotent, run to init DB) │ ├── auth/ │ ├── helper.php ← RBAC core: is_logged_in(), require_auth(), has_role() │ ├── login.php ← Login page │ └── change_password.php ← Forced password-change page │ ├── pages/ │ ├── map.php ← Dashboard wrapper embedding index.php?embedded=1 │ ├── penduduk.php ← Operator/admin penduduk management │ ├── kebutuhan.php ← Operator/admin kebutuhan workflow │ ├── status-bantuan.php ← Operator/admin aid status workflow │ ├── ibadah.php ← Admin rumah ibadah management │ ├── users.php ← Admin user management │ ├── import.php ← Admin CSV import │ ├── laporan.php ← Admin export/report page │ └── analisis.php ← Admin analytics/recalculation page │ ├── modules/ ← Frontend JS modules (loaded by index.php) │ ├── ibadah.js ← Rumah Ibadah layer: markers, circles, CRUD │ ├── penduduk.js ← Penduduk layer: proximity coloring, CRUD, filters │ ├── stats.js ← Dashboard modal: cards + chart + rekap table │ ├── heatmap.js ← Heatmap overlay (leaflet.heat, weighted by jiwa) │ └── kebutuhan.js ← Kebutuhan bantuan popup/actions │ ├── css/ │ └── admin.css ← Shared dashboard/page styling │ ├── includes/ │ ├── page-start.php ← Authenticated page shell start │ └── page-end.php ← Authenticated page shell end │ ├── api/ │ ├── auth/ ← login.php · logout.php · change_password.php │ ├── users/ ← ambil · simpan · update · reset_password · toggle_active │ ├── ibadah/ ← ambil · simpan · update · hapus · update_posisi · update_kontak · update_radius · recalculate │ ├── penduduk/ ← ambil · simpan · verifikasi · hapus · nonaktifkan · update_posisi · update_status · riwayat · arsip │ ├── stats/ ← ambil.php (aggregations) │ ├── kebutuhan/ ← ambil · simpan · update_status │ ├── papan/ ← public board + donor contact endpoints │ ├── import/ ← csv.php · template.php │ └── export/ ← csv.php · pdf.php │ ├── tests/ │ ├── test_db.php ← Schema smoke test │ ├── test_auth_helper.php ← Auth helper unit tests │ ├── test_ibadah.php ← Ibadah column + radius tests │ ├── test_penduduk.php ← Penduduk columns + NIK index tests │ ├── test_kebutuhan.php ← Kebutuhan schema/query smoke tests │ ├── test_proximity.php ← Haversine + tie-breaking unit tests │ ├── test_verifikasi_binding.php ← Verification endpoint bind order guard │ ├── test_update_posisi_scope.php ← Penduduk move scope guard │ ├── test_operator_scope_endpoints.php ← Operator endpoint scope checks │ ├── test_public_verification_filters.php← Public verification filters │ ├── test_frontend_module_loader.php ← Module loader cache-busting guard │ ├── test_frontend_reliability_guards.php ← Loader/heatmap/recalc guard checks │ ├── test_no_legacy_poi.php ← Legacy POI removal guard │ ├── test_viewer_public_filters.php ← Viewer public-data leakage guard │ ├── test_operator_scope_strict.php ← Strict operator ibadah scope guard │ ├── test_penduduk_binding_types.php ← Penduduk/import bind type guard │ ├── test_csrf_enforcement.php ← CSRF helper/endpoint/frontend wiring guard │ ├── test_session_cookie_security.php ← Session cookie security guard │ ├── test_operator_ibadah_highlight.php ← Operator assigned-ibadah highlight guard │ └── test_viewer_account_ui_hidden.php ← Viewer account creation UI guard │ ├── Tugas/ ← Assignment docs (SRS, use-case diagram) └── docs/ ├── codebase-navigation.md ← This file └── superpowers/ ├── specs/ ← PRD and design specs └── plans/ ← Phase implementation plans ``` --- ## Essential Files — Start Here | File | Why You Need It | |------|-----------------| | [config.php](../config.php) | All configurable constants — DB credentials, map center (CENTER_LAT/LNG), SESSION_TIMEOUT, MAX_LOGIN_ATTEMPTS | | [koneksi.php](../koneksi.php) | DB connection + the two core functions: `haversine_distance()` and `calc_proximity()` — the heart of the proximity engine | | [setup_database.sql](../setup_database.sql) | Authoritative schema — run this to initialize or reset the DB | | [auth/helper.php](../auth/helper.php) | RBAC foundation used by every endpoint: `require_auth()`, `has_role()`, `is_logged_in()`, session timeout logic | | [index.php](../index.php) | Main SPA shell — role detection, toolbar rendering, all modals, map init, utility functions (`showToast`, `showDeleteConfirm`, etc.) | ## Related Documentation | File | Purpose | |------|---------| | [codebase-audit-report.md](codebase-audit-report.md) | End-to-end business process, backend/frontend wiring, data lifecycle, current logic concerns, fix priorities, and development suggestions. | | [business-process-sop.md](business-process-sop.md) | SOP proses bisnis aktif untuk administrator, operator, publik, donatur, verifikasi, proximity, dan catatan audit. | | [deployment-docker.md](deployment-docker.md) | Docker/Coolify deployment, reverse proxy routing, database container, and troubleshooting. | | [deployment-xampp.md](deployment-xampp.md) | Legacy XAMPP setup if the app must run without Docker. | | [superpowers/README.md](superpowers/README.md) | Index PRD, implementation plans, and the latest superpowers audit refresh. | | [superpowers/plans/2026-06-03-codebase-audit-refresh.md](superpowers/plans/2026-06-03-codebase-audit-refresh.md) | Latest codebase audit snapshot after hardening. | | [../README.md](../README.md) | Setup, stack, default account, roles, features, and test commands. | | [../setup_database.sql](../setup_database.sql) | Authoritative database schema and default admin seed. | --- ## Database Tables ### Core v1.0 Tables **`rumah_ibadah`** — Worship places with service radius | Key Columns | Notes | |-------------|-------| | `id`, `nama`, `jenis` (ENUM 6 types), `lat/lng` | Core identity | | `radius_m` INT DEFAULT 500 | Validated 100–5000 | | `deleted_at` TIMESTAMP NULL | Soft delete — `WHERE deleted_at IS NULL` for all queries | **`penduduk_miskin`** — Poor residents (core data) | Key Columns | Notes | |-------------|-------| | `id`, `nama_kk`, `nik` VARCHAR(16) UNIQUE | NIK nullable but unique | | `lat/lng`, `ibadah_id` FK, `jarak_m`, `is_blank_spot` | Proximity results — computed by `calc_proximity()` | | `kategori` ENUM 3 levels, `status_bantuan` ENUM 3 states | Classification + aid tracking | | `status_verifikasi` ENUM('Pending','Terverifikasi','Ditolak') | Public visibility gate | | `is_active` TINYINT(1) | 0 = deceased/relocated (archived) | | `deleted_at` TIMESTAMP NULL | Admin error correction (soft delete) | > **`is_active=0` vs `deleted_at IS NOT NULL`:** Both hide the record from all active queries. `is_active=0` = real person, archived. `deleted_at` = data entry error, logically cancelled. **`users`** — System accounts | Key Columns | Notes | |-------------|-------| | `role` ENUM('administrator','operator','viewer') | RBAC basis | | `ibadah_id` FK | Operator's assigned worship place | | `must_change_password` TINYINT(1) | Forces redirect to change_password.php on login | | `login_attempts`, `locked_until` | Brute-force protection state | **`riwayat_bantuan`** — Aid status audit log (append-only) | Key Columns | Notes | |-------------|-------| | `penduduk_id`, `operator_id` FK | Who changed what | | `status_lama`, `status_baru`, `catatan` | Change record | | No `deleted_at` | Intentionally — immutable audit trail | **`kebutuhan`** — Assistance needs per resident | Key Columns | Notes | |-------------|-------| | `penduduk_id`, `kategori`, `deskripsi`, `status` | Need details and fulfillment state | | `created_by`, `updated_by` | Operator/admin audit references | **`riwayat_kebutuhan`** — Kebutuhan status audit log (append-only) | Key Columns | Notes | |-------------|-------| | `kebutuhan_id`, `operator_id` FK | Which need changed and who changed it | | `status_lama`, `status_baru`, `catatan` | Status transition record | | `created_at` | Append-only timestamp for audit chronology | **`kontak_donatur`** — Public donor contact submissions | Key Columns | Notes | |-------------|-------| | `nama`, `kontak`, `kategori_minat`, `pesan` | Public contact form data | | `is_read` | Admin dashboard notification state | ## API Endpoint Map ### Public (no auth required) | Endpoint | Method | Purpose | |----------|--------|---------| | `api/ibadah/ambil.php` | GET | All active ibadah + KK/jiwa counts | | `api/penduduk/ambil.php` | GET | Active penduduk — PII masked for non-auth | | `api/stats/ambil.php` | GET | Aggregate stats; rekap_ibadah only for admin | | `api/papan/ambil.php` | GET | Public verified kebutuhan board aggregation | | `api/papan/kontak_donatur.php` | POST | Public donor contact submission | ### Operator+ (operator or admin) | Endpoint | Method | Purpose | |----------|--------|---------| | `api/penduduk/simpan.php` | POST | Add penduduk (auto-calls calc_proximity) | | `api/penduduk/update_posisi.php` | POST | Move marker (recalcs proximity) | | `api/penduduk/update_status.php` | POST | Change aid status + write audit log | | `api/penduduk/riwayat.php` | GET | Aid history for a penduduk (scoped to own ibadah for operators) | | `api/kebutuhan/ambil.php` | GET | List kebutuhan for a scoped penduduk | | `api/kebutuhan/simpan.php` | POST | Add kebutuhan for a scoped penduduk | | `api/kebutuhan/update_status.php` | POST | Update kebutuhan status + write audit log | ### Admin only | Endpoint | Method | Purpose | |----------|--------|---------| | `api/ibadah/simpan.php` | POST | Create ibadah | | `api/ibadah/update.php` | POST | Update ibadah master data from admin page | | `api/ibadah/hapus.php` | POST | Soft-delete ibadah (warns if operators linked) | | `api/ibadah/update_posisi.php` | POST | Move ibadah → triggers full recalc | | `api/ibadah/update_radius.php` | POST | Change radius → triggers full recalc | | `api/ibadah/update_kontak.php` | POST | Update contact only | | `api/ibadah/recalculate.php` | POST | Bulk recalculate all proximity | | `api/penduduk/verifikasi.php` | POST | Approve/reject pending penduduk records | | `api/penduduk/hapus.php` | POST | Soft-delete penduduk (data entry error) | | `api/penduduk/nonaktifkan.php` | POST | Set is_active=0 (deceased/relocated) | | `api/penduduk/arsip.php` | GET | List inactive/deleted warga for audit (up to 200 rows) | | `api/papan/donatur.php` | GET/POST | GET lists donor contacts; POST marks unread entries as read with CSRF | | `api/users/ambil.php` | GET | List all accounts | | `api/users/simpan.php` | POST | Create account (must_change_password=1 forced) | | `api/users/update.php` | POST | Edit nama/role/ibadah_id | | `api/users/reset_password.php` | POST | Generate temp password + must_change=1 | | `api/users/toggle_active.php` | POST | Enable/disable account (cannot self-deactivate) | | `api/import/csv.php` | POST | CSV import (mode=preview or mode=import) | | `api/import/template.php` | GET | Download CSV template | | `api/export/csv.php` | GET | Export warga binaan as CSV (no NIK) | | `api/export/pdf.php` | GET | Print-ready HTML report (no NIK) | --- ## Frontend Module Map ### `modules/ibadah.js` - **Entry:** `window.initIbadah()` — called by index.php on load - **Shared state:** `window._ibadahList[]` — read by penduduk.js for client-side proximity preview - **Key functions:** - `addToMap(data)` — creates draggable marker + radius circle - `buildInfoPopup(data)` — admin: editable kontak/radius/export links; viewer: read-only - `_ibadahHapus(id)` — two-step: operator-warning check → confirm → soft-delete - `_recalcAll()` — calls recalculate.php, disables all CRUD buttons during operation - `window._ibadahToggleLayer(bool)` / `_setBufferVisible(bool)` — layer controls ### `modules/penduduk.js` - **Entry:** `window.initPenduduk()` — called by index.php on load - **Shared state:** `window._pendudukList[]` — read by stats.js and heatmap.js - **Key functions:** - `buildInfoPopup(data, ibadah)` — proximity section + NIK (auth only) + status buttons (operator) + riwayat - `findNearestIbadah(lat, lng)` — client-side Haversine mirror of PHP `calc_proximity()` - `applyFilters()` — AND-logic filter across 4 dimensions + search - `window._pendudukRecalcAll()` — client-side repaint after ibadah change (no server call) - `_updateBlankSpotCounter()` / `_filterBlankSpot()` — blank spot counter + click-to-filter ### `modules/stats.js` - **Entry:** `window._showDashboard()` — opens modal - `window._statsUpdate()` — re-fetches and re-renders (only if modal visible) - Destroys and recreates Chart.js instance on each update to avoid memory leaks ### `modules/heatmap.js` - **Entry:** `window._toggleHeatmap(bool)` — creates/destroys heatmap layer - Intensity = `0.2 + 0.8 * (jumlah_jiwa / maxJiwa)` — proportional to household size - `window._heatmapSetKategori(k)` — filter heatmap by category ### `modules/kebutuhan.js` - **Entry:** loaded for authenticated users through `window.MAP_MODULES` - Builds kebutuhan controls inside penduduk popups - Calls `api/kebutuhan/ambil.php`, `api/kebutuhan/simpan.php`, and `api/kebutuhan/update_status.php` --- ## Core Algorithm: Proximity Engine **Location:** [koneksi.php](../koneksi.php) — `calc_proximity($conn, $lat, $lng)` **Logic:** 1. Query all `rumah_ibadah WHERE deleted_at IS NULL ORDER BY id ASC` 2. For each, compute Haversine distance 3. Track two candidates: `best_in_radius` (nearest within radius) and `best_overall` (nearest regardless) 4. Winner = `best_in_radius` if exists; else `best_overall` (blank spot) 5. Tie-breaking: `ORDER BY id ASC` + strict `<` comparison → lowest id wins on equal distance **Triggers:** - `api/penduduk/simpan.php` — on each new penduduk - `api/penduduk/update_posisi.php` — when penduduk moved - `api/ibadah/update_posisi.php` — after ibadah position change (loops ALL penduduk) - `api/ibadah/update_radius.php` — after radius change (loops ALL penduduk) - `api/ibadah/hapus.php` — after soft-delete (loops ALL penduduk) - `api/ibadah/recalculate.php` — manual bulk trigger (admin "Hitung Ulang Semua") - `api/import/csv.php` — per-row during import **Client-side mirror:** `modules/penduduk.js` — `findNearestIbadah()` for instant visual feedback during drag/radius resize (no server call). --- ## RBAC Quick Reference ``` viewer (0) → public/fallback read-only map, aggregated stats, PII masked operator (1) → + add/move penduduk, update aid status (own ibadah only) administrator (2) → + everything: CRUD ibadah, manage users, import/export, recalculate ``` Operators are created by administrators in `pages/users.php` and must be linked to one `rumah_ibadah`. The shared admin shell renders navigation by role: administrators see dashboard/master/import/export/user management, while operators see map, penduduk, kebutuhan, and status bantuan only. Unauthenticated visitors are treated as `viewer`, so the admin user-management UI does not offer `viewer` for new accounts. The backend role remains for fallback RBAC and existing read-only accounts. **Session flow:** 1. Unauthenticated → `index.php` renders as `viewer` (no redirect) 2. Login → `api/auth/login.php` → session set → if `must_change_password=1`, redirect to `auth/change_password.php` 3. Every page/API: `auth/helper.php:is_logged_in()` validates session + renews `last_activity` 4. Idle 2hr → session expired → treated as viewer on next request --- ## Legacy Status The obsolete road/polyline, parcel/polygon, and business-location prototype features have been removed from the active app. Active core: - `rumah_ibadah` - `penduduk_miskin` - `users` - `kebutuhan` - `riwayat_bantuan` - `riwayat_kebutuhan` - `kontak_donatur` --- ## Running Tests Tests are intended to run from CLI/local PHP. Browser access to `/tests/` is blocked by `.htaccess` for deployment safety. Run selected static/smoke tests from the project root: ```powershell & 'C:\Program Files\Xampp\php\php.exe' tests\run_all.php ``` Some database smoke tests load `../config.php` or `../koneksi.php` and should be run from the `tests` directory after importing `setup_database.sql`. --- ## Configuration Quick-Start Edit [config.php](../config.php) to set: ```php define('CENTER_LAT', -0.0557); // Map center latitude define('CENTER_LNG', 109.3487); // Map center longitude define('ZOOM_LEVEL', 15); // Initial zoom define('DB_HOST', 'localhost'); define('DB_PORT', 3307); // Local configured MySQL port define('DB_NAME', 'db_webgis'); ``` Default admin credentials (must change on first login): `admin` / `Admin1234`