Skip to content
AFM Studio
  • Pages

    AFM Studio
  • Work
  • Lab
  • About
  • CV
  • Contact
  • Projects

    AksaraA searchable library for Otorita IKN's knowledge, where the server checks every reader's access across three levels
  • Daily TaqwaTamper-resistant prayer attendance system for ~2,000 OIKN government employees at Masjid Negara IKN
  • Lantara v2A permit engine for Otorita IKN where administrators add new permit types from a screen, without a developer
  • eKiosk IKNTwo control rooms for Nusantara: kiosk content management and live bus tracking with arrival times that grade themselves
  • E-Monev IKNA monitoring platform for Nusantara's 23-year build, tracking every unit's quarterly progress against 24 national KPIs
  • JDIH OIKNOIKN's public legal document portal, with a controlled publishing workflow and AI summaries that never leave the server
  • Jejak PenanamanA check-in app for IKN tree-planting events that proves each participant was on site with a geofenced selfie
  • Dashboard SAKTIA nightly pipeline and dashboard that replaces OIKN's manual budget downloads for all six of its budget units
  • DoseRxA bedside dose calculator for 127 drugs that rounds each dose to tablets and syrups Indonesian pharmacies sell
  • CubiqA speedcubing trainer with competition-grade timing and a solver I wrote for each of the 8 major puzzles
  • ScimotionAn interactive science library of 86 articles, each explained next to animations the reader can control
  • WA Daily Scrum BotA WhatsApp bot that chases missing standup reports for a 14-person team and flags tasks stuck for days

↑ ↓ to move · Enter to open · Esc to close

Montara

Construction monitoring for IKN where every change to a project is approved by the right officer and logged

Screenshots
role-gated dashboards with CSV and PDF export
8
roles, each with its own rights
5
kinds of change that need approval
4
On this page

The problem

Nusantara, Indonesia's new capital, is being built by many contractors at the same time. The people who track that work need different things, and they are trusted with different things. Contractors report physical and financial progress on their own projects. The contracting officer, the PPK, approves or rejects those reports, but only for projects in their scope. They need an audit trail, because these are government commitments. The compliance team and executives want dashboards they can trust without reading every project's raw data.

Before Montara, this ran through spreadsheets and email. Nothing enforced who could change what, and nothing recorded who approved it.

What I built

I built the frontend first and made it the source of truth

I built a complete working prototype in the browser before any server existed. Every role, permission rule and workflow ran end to end, with data saved in the browser. It then went through a formal code review, and I worked through the items it raised. Only after that did I build the backend, as a server-side copy of rules that were already proven. The old spec had four roles and left contractors out. The prototype had five, with contractors as users. Where the two disagreed, I wrote down that the prototype wins.

Every change is a request someone approves

A project doesn't carry a single status that flips from draft to approved. Every change is filed as its own approval request, whether it creates a project, edits it, reports progress or deletes it. The project's status comes from its latest request. That gives a full record of who proposed each change and who decided it. The PPK and the super-admin can edit directly, and those edits are logged separately so they stay visible.

Stale approvals are rejected

Each project keeps a revision number. When someone files a request, the request records that number. At approval time the server checks it again. If the project changed in between, the approval is rejected and the newer data stays. This works without locking any tables.

Deleting a project can be undone until the last step

Archiving a project closes its open issues and remembers what state they were in, so a restore puts them back. A permanent delete first shows how many records of each kind it will remove. For a government record, the people deleting it should see that before anything is gone.

The backend improves on the prototype in two places

The prototype recalculated notifications every time a screen drew. On the server, I store each notification when the event that triggers it happens. Background workers check for overdue projects and prepare scheduled summaries. The spec had also left the 3D building-model viewer as an open question. I resolved it by connecting the viewer to Autodesk Platform Services. The labelled simulated viewer stays for projects without a model.

Result

Montara is a private system, past its integration milestone and before production launch. It has eight dashboards, each limited by role: summary, map, budget, progress, risk, compliance, portfolio and contractor performance, with CSV and PDF export. Five roles are enforced in the browser and again on the server. The approval workflow follows a rejected request through resubmission. Automated frontend tests check, among other things, that one role can't see another role's data. I built the prototype over about six weeks from late May 2026 and the backend on 5 July. The frontend now talks to the server behind a switch, so it can fall back to prototype mode during the cutover.

Under the hoodTechnical detail for engineers
  • Frontend: Next.js App Router, React 18, TypeScript, shadcn/Radix UI, Tailwind CSS v4, react-hook-form, Leaflet and Recharts. The prototype ran entirely on localStorage.
  • The prototype's types.ts and businessRules.ts became the contract for the Django models and DRF permission classes, so the two sides can't drift apart.
  • Roles: contractor, ppk, mki, executive and super_admin. PPK approval rights are limited to an explicit per-project scope. Client-side checks are for the interface; the server is the security boundary.
  • ApprovalRequest types are CREATE, UPDATE, PROGRESS_UPDATE and DELETE. Project.approvalStatus is derived from the latest request. Rejections require a comment.
  • Optimistic concurrency uses a revision integer on Project, captured when the request is filed and re-validated on approval.
  • Progress is tracked against a planned S-curve, with submission windows, no-regression checks, duplicate-pending detection and a configurable reporting cadence held in a SystemSettings singleton.
  • Issues have status and priority transitions, follow-ups, file attachments, and escalation from the compliance team to executives.
  • Deletions write a ProjectDeletionLog record.
  • Notifications are rows written in the same transaction as the triggering event. Celery and Redis run overdue detection and scheduled aggregation.
  • The frontend review tracked fixes including component splitting, memoization, centralised authorisation logic, debounced autosave and cross-tab sync.
  • Backend: Django 5.1, DRF with a camelCase API, django-filter, drf-spectacular for OpenAPI docs, and PostgreSQL. 11 domain apps and 37 models.
  • 18 routed pages. At least 111 automated frontend tests in about 10 test files, in Vitest and Testing Library.
  • Frontend: 53 commits from 25 May to 5 July 2026, about 27,300 lines of TypeScript. Backend: 20 commits on 5 July 2026, about 4,150 lines of Python.

Hiring, or building something similar?

I'm open to full-time roles and select freelance work.