Jejak Penanaman
Geofenced selfie check-in app for IKN tree-planting events, with plant species tracking and QR ticketing
Fullstack Developer
Apr – Jul 2026
On this page
The problem
IKN Nusantara hosts recurring tree-planting ceremonies involving both government employees and the general public. Before this system, attendance was tracked manually with paper forms and spreadsheets — no way to verify that participants were actually on-site, no central record of who attended which event, no documentation of species planted, and no photographic evidence.
Two distinct user types needed the same system: IKN government employees who authenticate via Keycloak SSO, and members of the public who register with an email address — plus walk-up participants at open events who have no account at all. Admins needed to manage events, set geofenced locations, track registrations, and export attendance reports.
The approach
Geofenced selfie check-in
Attendance submission requires both a selfie and GPS coordinates within a configurable radius (50–5000m) of the event pin. The geofence is checked twice: client-side with a Haversine calculation for immediate feedback ("you are 340m away"), and server-side in the attendance submission view to prevent API-level spoofing, using the same formula against the event's stored coordinates.
The CameraCapture component uses the browser MediaDevices API, draws the captured frame to a hidden canvas, and compresses it to max 1024px / JPEG 0.7 before upload — keeping selfies under 1 MB even on high-resolution phone cameras. iOS Safari requires getUserMedia() to be triggered by a direct user gesture, so camera activation is wired to a button click, not page load.
Every photo carries its own proof
Selfies and plant photos get a timestamp-and-GPS overlay burned directly into the canvas before upload — date and time in WITA, coordinates to 7 decimal places — and the same GPS data is embedded into the JPEG's EXIF metadata client-side via piexifjs. No server-side image processing is involved; the proof travels with the file itself.
Three ways in, one attendance model
IKN employees authenticate via Keycloak OIDC, whose claims arrive genuinely messy — names in ALL CAPS, organisational units split inconsistently across several claim fields — normalised by a custom OIDCAuthenticationBackend. Public participants register with email and get an auto-generated password via SendGrid. For open public events, a third path skips accounts entirely: an anonymous registration endpoint creates a lightweight guest user on the fly so a walk-up participant isn't blocked by the auth flow, without weakening the permission model for anyone else.
Plant species tracking
Each attendance record links to multiple flora species planted, with per-species quantity, sapling age in months, and photos — added mid-project, outside the original PRD, to support IKN's reforestation biodiversity records without breaking the existing data model. A personal "Jejak Hijau" view lets a participant see everything they've planted across every event they've attended, with a photo lightbox.
QR tickets that scale to per-institution quotas
Public events generate a unique JP-YYMMDD-NNN ticket code per registration, assigned atomically to avoid collisions, and admins scan them at the gate with the browser's camera via html5-qrcode, with audio feedback per scan result. What started as one flat quota per event grew into opt-in segmentation: an event can split its quota by government institution, each with its own nullable cap (null = unlimited) and its own list of available t-shirt sizes — registration validation resolves the participant's institution and enforces that institution's specific rules, while simpler events keep working exactly as before. Twibbon selfie overlays — branded IKN frames — composite client-side over the captured photo and download as shareable images.
Attendance that bends for real-world connectivity, without going soft
Attendance isn't fully immutable. Admins can force-create a record on a participant's behalf — including overriding the timestamp via a time picker — for the on-site connectivity failures that inevitably happen at an outdoor event, and can add plant photos to an existing record after the fact. Above the admin/participant split sits a superadmin role that manages other admins, promotes public users to employee status (locking their organisational fields so a future SSO login can't silently overwrite a manual correction), and searches, filters, and deletes across the full user base.
Outcome
Deployed to production and used in real IKN tree-planting events since mid-2026. 78 commits across roughly three months, spanning 5 Django apps, 13 models, ~90 API endpoints, and 28 frontend pages. The system handles internal events (SSO-only, no registration) and public events (open registration, per-institution quotas, QR tickets, guest walk-ins) through a single codebase, with admin dashboards breaking down attendance by directorate and department for internal reporting.
- ~90
- 28
- 13
- 78
Have a project like this?
If you need a system built with the same care — clear scope, solid execution — let's talk.
Start a project