Montara
Construction monitoring for IKN where every change to a project is approved by the right officer and logged
- 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.tsandbusinessRules.tsbecame the contract for the Django models and DRF permission classes, so the two sides can't drift apart. - Roles:
contractor,ppk,mki,executiveandsuper_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. ApprovalRequesttypes areCREATE,UPDATE,PROGRESS_UPDATEandDELETE.Project.approvalStatusis derived from the latest request. Rejections require a comment.- Optimistic concurrency uses a
revisioninteger onProject, 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
SystemSettingssingleton. - Issues have status and priority transitions, follow-ups, file attachments, and escalation from the compliance team to executives.
- Deletions write a
ProjectDeletionLogrecord. - 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-spectacularfor 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.