Currently building: himasjid · Week 16
Perth, AU · GMT+8 00:00:00
Back to work
Client: Peopleshift Group· Role: Software developer, part-time· Timeframe: Nov 2025 — Present

Peopleshift ERP.

An ERP for project operations at Peopleshift Group, covering budgets, cash advances, learning-partner bookings, receipts, invoices, and payments across every training engagement that Squadgames, ShiftAcademy, and Peopleshift run.

Status

Active, in production

Stack

Next.js · TypeScript · Supabase

PS $ A Find... F Total Projects 142 Open 38 38 Closed 59 59 At Risk 4 4 Total Allocated Budget Rp5.251.084.901 Total Actual Spend Rp3.144.371.630 ⌕ Search: All Statuses Search project name, client, ID... Select date range Apply Date Reset Date Refresh Data CSV Add Project Status Project ID Project Name Client Budgeted Actual Spend Project Officer Start Date Open Open Open Open Open Open Open Open Open Open PRJ-0001 PRJ-0002 PRJ-0003 PRJ-0004 PRJ-0005 PRJ-0006 PRJ-0007 PRJ-0008 PRJ-0009 PRJ-0010 Initiative Alpha Quarterly Review Cycle Field Operations Rollout Manager Onboarding Path Self-Directed Learning Coaching Programme... Coaching Programme... Coaching Programme... Coaching Programme... Coaching Programme... Acme Holdings Northwind Bank Globex Industries Initech Finance Northwind Bank Helios Energy Group Helios Energy Group Helios Energy Group Helios Energy Group Helios Energy Group Rp7.850.000 Rp14.250.000 Rp27.500.000 Rp26.000.000 Rp20.137.500 Rp25.500.000 Rp25.500.000 Rp25.500.000 Rp25.500.000 Rp25.500.000 Rp2.140.000 Rp9.875.500 Rp18.420.700 Rp0 Rp5.324.215 Rp4.612.000 Rp11.893.000 Rp11.826.000 Rp7.250.000 Rp0 amelia noah noah soren noah soren soren soren soren noah 12 May 2026 12 May 2026 28 Apr 2026 28 Apr 2026 24 Apr 2026 13 Apr 2026 13 Apr 2026 13 Apr 2026 13 Apr 2026 13 Apr 2026 Showing 1 - 20 of 142 projects ‹ Previous 1 2 ··· 8 Next ›
§ 01 — Context

PeopleShift Group is an Indonesian corporate-training company offering leadership programs, team-building games, and academy cohorts, delivered under three sister brands: PeopleShift, Squadgames, and ShiftAcademy. Over twelve hundred sessions delivered, more than five hundred companies served, with clients including PLN, Pertamina, Toyota, Bukalapak, BRI, and Prudential.

Every engagement is a project, and a project is a small economy of its own. A budget is agreed with the client. Learning partners, external facilitators, are booked to run the session. The ops team on the ground buys materials, pays venues, and collects receipts. Finance, on the other side, issues the invoice and tracks whether the client has paid and whether the facilitators have been paid. Multiply that by hundreds of projects a year across three brands, and the flow is not something a tool designed for a single SaaS company handles well.

When I started, the finance and ops teams were running the whole operation on a mix of Google Sheets, Discord threads, PDF invoices, and Word documents. They had looked at the usual off-the-shelf ERPs. Nothing fit. The shape of a training-and-events business is specific enough that "configurable" software still forces you to bend your process to theirs. The ask was blunt: build us something that fits us, keep it cheap, and ship it this quarter.

Three weeks later, a usable prototype was live for the ops and finance teams, built end-to-end by a single developer working heavily with AI tooling. It has been in production and under continuous development ever since.

Three brands, hundreds of projects a year, running on Google Sheets, Discord, PDFs, and Word.
§ 02 — Constraints

This wasn’t the kind of project where you write a big architecture document and then hire a team. It was the kind where you ship something in three weeks or the ops team quietly gives up and keeps emailing PDFs. One developer, part-time, alongside the rest of my work. The budget was small by design: the whole point of building custom was to beat the cost of an off-the-shelf tool that wouldn’t have fit anyway.

Timeline
Usable prototype in 3 weeks. Continuous delivery since.
Team
Solo developer, part-time. Direction set with the finance and ops leads.
Users
~20 across finance, ops, project managers, and leadership.
Legacy workflow
Google Sheets, Discord threads, PDF invoices, Word docs. Rebuilt the same tables with the same column names and wrote an importer, the teams moved in without re-typing anything.
Devices
Ops uploads receipts from the field, where they forget if it isn’t immediate. Shipped as a PWA, mobile-friendly from day one, no second codebase.
Exit
Every data shape in the ERP is exportable. The tool has to earn its keep by being better than the spreadsheets, not by trapping the data.
§ 03 — Architecture

The architecture question I spent the most time on was not which framework, a Next.js monolith was obvious from the first week, but how to keep the surface area small enough for one developer to own end-to-end, for years, while the scope kept expanding.

The shape settled quickly. Next.js 16 on the App Router handles rendering, routing, and the API. Supabase manages Postgres and storage. NextAuth v5 handles authentication, deliberately decoupled from the database layer so that swapping hosting providers later would not also mean rewriting auth. Data on the client is TanStack Query and TanStack Table. The entire ERP is effectively a large set of filterable, editable tables, and TanStack is what makes that boring in a good way. The mobile experience is the same codebase served as a PWA, so ops can upload a receipt from the field the moment it is in their hand.

Underneath everything is a single spine: the Project. Budgets, cash advances, learning-partner bookings, receipts, invoices, and payments all belong to a project. There is no separate "CRM" or "finance" module in the code, there are just views over the same relational model from different angles. That shape was chosen early and has absorbed every new requirement since without a rewrite.

The two decisions I am most pleased with did not live in the framework layer. The first was the import system: the ERP's tables and column names deliberately mirror the Google Sheets the finance and ops teams had been using, so migration was a paste-and-check, not a rewrite. Nobody had to re-learn their data to move in.

The second is the error pipeline. With one developer in production, I cannot afford to find out about a broken invoice form on Monday morning from a Discord message that starts with "hey, quick one". Every unhandled error, client and server, flows through a classifier that tags it Critical, High, Medium, or Low. Critical errors fire a Discord webhook and open a Jira ticket automatically with the full context. Everything is logged to Postgres with dedup, so repeat errors count up instead of spamming. It is not glamorous, but it is the thing that lets me sleep.

BRANDS PeopleShift TRAINING Squadgames TEAM-BUILDING ShiftAcademy ACADEMY SPINE PROJECT CLIENT · BRAND · BUDGET · STATUS LIFECYCLE Setup BUDGET BOOKINGS Execution CASH ADVANCES OUT RECEIPTS IN Billing INVOICES OUT PAYMENTS IN / OUT CLOSE Close · P&L · archive LIME = PROJECT LIFECYCLE — SETUP · EXECUTION · BILLING · CLOSE
Fig. 01 — Project as the system spine. Every brand feeds projects through one lifecycle: setup, execution, billing, close.
§ 04 — Stack & reasoning
Next.js 16
One codebase for routing, rendering, and the API. Server actions let me skip an entire REST surface for internal CRUD. Turbopack keeps the dev feedback loop tight when it’s just me.
Supabase
Managed Postgres and storage, nothing more. I deliberately don’t use its auth, so the hosting layer stays a commodity I can swap out later.
NextAuth v5
Auth follows the app, not the database vendor. That’s what makes “move off Vercel eventually” a one-week problem instead of a three-month one.
TanStack
Query and Table together. The ERP is, at heart, a pile of tables sorting, filtering, paging, caching, invalidation, optimistic updates. TanStack turns every screen into the same pattern.
Observability
Pino for structured logs, feeding a classifier that fans out to Discord, Jira, and a Postgres error log. Solo-dev on-call solved at the observability layer, not by adding paid tooling.
§ 05 — Outcome

A usable prototype went live in three weeks, with finance and ops piloting it while the spreadsheets remained on standby for a few weeks longer. Migration was quiet. Because the ERP's tables mirrored the existing sheets, the move was a paste-and-check: the finance lead imported her own budget tracker on the second day without me present.

Ops changed the fastest. Receipts used to arrive as PDFs built in Word, emailed in weekly batches when they arrived at all. With the PWA, someone on the ops team can pay a venue, photograph the receipt, and have it attached to the correct project before walking back to the car. The weekly ritual of chasing missing paperwork largely disappeared, and ops estimate their administrative load dropped by around 80%. HR and GA began using the system as well, though neither was in the original scope, which suggested the thing was well-shaped enough to absorb adjacent work.

Finance changed next. Weekly transfers, checking what ops had spent, matching invoices, approving payouts to learning partners, used to take hours and now take minutes. Monthly close went from a multi-day reconciliation across half a dozen sheets to a filterable view of the same relational model. Finance put their saving at around 60%.

The ERP now tracks hundreds of projects across the three brands, with several thousand transactions flowing through it every month. Leadership has a live project budget summary, green, yellow, red, filterable by brand, that did not exist before. The system has been in production and under continuous development since it shipped. The spreadsheets still exist; they are archives now.

§ 06 — Reflection

The decision I got right was refusing to redesign the finance team's data model. The tables, the column names, the way a project's budget is broken down into line items, all of that was already working for them, just trapped in spreadsheets. I copied it verbatim into Postgres and wrote an importer that took their own sheets as input. Moving in was not a training exercise; it was a paste. Most of the adoption wins above came from that one decision.

What I would do differently is set up CI and Playwright from the start, rather than waiting until several months in. In that interval I shipped more than a few bugs into production that a five-minute end-to-end test would have caught: small regressions that cost finance and ops real time before I noticed. The error pipeline catches problems after they occur. Playwright would have stopped them from occurring. The two should have been paired from day one.

Next case study

Himasjid

A SaaS for mosque administration. My own product — real users, pre-revenue, running to learn.