Internal Management System
An internal operations system for a wireline company — incident reporting, crews, equipment, attendance and medical readiness — held together by one permission model and the test suite that guards it.
- Relational tables
- 26
- Permissions
- 32 · 7 baseline profiles
- Feature modules
- 13, sliced by domain
- Committed test files
- 33
- Context
- Well Logging Energy · Professional work
- Replaces
- Printed incident forms, planning sheets and monthly paper registers
- Period
- July 2026 — Present
- Role
- Requirements, architecture, constraints, direction and review
- Shape
- Nine workspaces over one schema and one permission model
- Status
- Deployed internally · under active development
- Visibility
- Private repository · no records, routes or screens shown
Covers
Stack
- Next.js
- React
- TypeScript
- PostgreSQL
- Drizzle ORM
- Zod
- Cloudflare Turnstile
The problem
The company ran its operations on paper. Incident and hazard reports on printed forms, field work on a standard operations planning sheet, attendance and equipment utilisation on monthly grids, medical fitness on certificates in a folder.
Each of those has a different audience and a different rule about who may see it. A safety report is not an attendance register is not a medical certificate, and the people allowed near one are frequently not allowed near another. On paper that separation is enforced by which filing cabinet a document sits in.
So the replacement could not be one dashboard over one table. It had to be several workspaces sharing an organization, a permission model and an audit trail, without leaking across the boundaries between them — which is the part that actually determines whether a system like this can be trusted with medical records at all.
- ORGANIZATION
- PEOPLE
- CLIENTS
- ACCESS
- MEDICAL
- JOBS
- ATTENDANCE
- INCIDENTS
- ASSETS
The system
The code is sliced by feature rather than by technical layer. Each workspace owns its domain rules, its validation, its permission checks and its components in one place, so a change to how attendance works stays inside attendance instead of rippling through a shared controllers folder.
Underneath, everything leans on one organization module — people, departments, teams — because every other workspace is ultimately about someone or something that belongs to the company. Getting that spine right early is what let the later workspaces be small.
Writes go through a single wrapper that resolves the caller, checks authority, runs the mutation and records the audit event inside one transaction. A state change and the record of that change either both land or neither does. An audit trail that can silently disagree with the data it describes is worse than no audit trail, because it is trusted.
Intake form
Unauthenticated
Staff portal
Signed in
Self-service
Own records only
Route gating
First boundary
Authorization module
Permissions resolved
Feature modules
Sliced by domain
Transactional actions
Write and audit together
PostgreSQL
26 relational tables
Organization module
People, departments, teams
Audit log
Per-record timelines
Private media
Access-checked routes
What it actually does
Nine workspaces are in internal use, each replacing something that used to be printed, photocopied or kept in a spreadsheet on one person's machine.
Two details shaped more of the build than their size suggests. Intake is bilingual, Urdu and English, because the people closest to a hazard are not always the people most comfortable reporting it in English — and a safety system that quietly filters by language is not collecting the reports that matter most.
The second is that spreadsheets had to keep working. Registers import from and export to workbooks, and reports print to a clean A4 page, because the system has to interoperate with the paper process it is replacing rather than demand everyone abandon it on the same day.
Safety & compliance
- Incident and hazard reporting
- Bilingual intake, Urdu and English
- Triage, routing, investigation, closure
- Medical readiness and expiry tracking
Operations
- Field job planning
- Crew assignment
- Stage progression
- Customer directory
Assets
- Equipment register
- Monthly utilisation grid
- Fleet register and status logs
- Odometer readings
Workforce
- Employee records
- Self-service profiles
- Departments and teams
- Monthly attendance register
Administration
- Access control
- Permission profiles
- Branding held in the database
- Activity log
In and out
- Workbook import
- Workbook export
- Print-ready reports
- Field photographs normalized on upload
Every workspace listed here is in internal use. Nothing is named at record, route, client or role level, and no screen is shown.
A month at a glance
The attendance and utilisation workspaces are the same shape: one entry per subject per day, folded into a month-wide grid an operations lead can read across in a single pass.
The grid deliberately stops at recording. It says which state an asset or a person was in on each day; it does not compute a utilisation percentage, an efficiency score or a ranking. Those numbers would be easy to add and easy to misread, and nobody has asked for a measure whose definition has not been agreed.
One status entry per subject, per day
Subject × calendar month
- One code per cell
- Up to 31 day columns
- One row per person or asset
A month-wide grid, exportable as a workbook
A register, not a score
The grid records which state something was in on a given day. Turning that into a percentage would mean agreeing what counts as available, what counts as productive and what a good number looks like — decisions that belong to the operations team, not to the schema.
subject × day → stateWhat the tests hold
There are 33 committed test files, and they sit beside the code they cover rather than in a separate tree — a workspace's domain rules, validation, permissions and workflow tests live in the workspace.
They run on Node's own test runner, so the suite adds no test framework to the dependency list. That was a deliberate constraint on a system one person maintains: every dependency is something that will eventually need upgrading, and a test suite is not worth a second toolchain.
The shapes worth naming are the permission tests on every workspace that has a boundary, the round-trip tests on workbook import and export, and one end-to-end test that walks a record through its whole lifecycle. Coverage is not measured, so no coverage figure appears here.
- 0133 test files committed alongside the code they cover
- 02Authorization carries both a core suite and a regression suite
- 03Every workspace with a permission boundary has a permissions test
- 04Workbook import and export are tested as a round trip
- 05One end-to-end test walks a record through its whole lifecycle
- 06Node's built-in test runner — no test framework dependency
Known-data KPI test
- Sign-in
- Permissions resolved
- Workspace entered
- Record created
- State advanced
- Audit event written
The chain names the stages one end-to-end test walks, not its assertions, fixtures or data. No coverage percentage is reported here, because none has been measured.
How it is served
The application sits behind a reverse proxy that terminates TLS, and talks to PostgreSQL through a bounded connection pool. Sessions are database-backed, sign-in is rate-limited and bot-checked, and passwords are stored with a salted key-derivation function rather than a plain hash.
Private media never has a public URL. Field photographs, portraits and medical certificates are held in a private bucket and reached only through in-app routes that re-check the caller's permissions on the way past — a link that leaks is a link that stops working for whoever it leaked to.
Photographs arrive from phones, which means they arrive large and occasionally in formats a browser will not display. They are converted and resized on upload, so the storage cost and the load time are settled once at intake rather than paid on every read.
Client
Reverse proxy
TLS termination
Next.js application
Server Components
Connection pool
Bounded
PostgreSQL
Private object storage
Reached only through the app
How it grew
The order was driven by which piece of paper was costing the most, not by which module was most interesting to build.
Reporting came first because an incident form that goes missing is the one failure with a genuine safety cost. The organization spine came second, because every workspace after it needed somewhere for a person to belong. The permission model was hardened once there was enough in the system to be worth protecting properly.
- 01
Reporting core
Structured incident and hazard reporting, replacing the printed form.
- 02
Organization spine
People, departments and teams — something for every later workspace to hang off.
- 03
Job planning
The operations planning sheet digitized, with crews and lifecycle stages.
- 04
Assets and workforce
Equipment, fleet, monthly utilisation and attendance, with workbooks in and out.
- 05
Permission model
Named permissions, baseline profiles and direct grants, resolved server-side.
- 06
Test suite
Domain, validation, permission and workflow tests committed beside the code.
The tradeoff
My strongest engineering evidence on this project sits in exactly the places I cannot show: domain rules, permission resolution, transactional behaviour and the relationships between records.
On this page I would rather describe those honestly and abstractly than manufacture a realistic-looking screen full of invented employees. A fake dashboard would show more and prove less, and anyone who has built one of these can tell the difference.
Where the engineering is
- Domain rules
- Permission resolution
- Transactional writes
- Audit behaviour
- Data relationships
What may be published
- Sanitized diagrams
- Architecture only
- No records, routes or clients
- Approved screenshots when available
- Private system
- Public case study
- Safe abstraction
- Engineering evidence
What holds, and what does not
The previous version of this case study said the repository had no committed tests. That is no longer true — there are 33 files — so the claim has been replaced rather than quietly dropped.
What is genuinely outstanding is narrower and less flattering. Schema reaches the database by push rather than through committed, versioned migrations, which means there is no migration history to review or roll back. The tests run when someone runs them; no continuous integration runs them on a change. Both are known, both are mine, and neither is hard — they are simply not done.
Verified
- Domain rules and validation, by committed tests
- Permission resolution, by a dedicated regression suite
- Workbook import and export, as a round trip
- Type-checking and a production build, on every change
Not verified
- Schema history — the database is pushed, not migrated
- Continuous integration — tests run on demand, not automatically
- Behaviour under load or real concurrency
- Any security review by someone other than me
Next proof
- Versioned migrations
- CI on every push
- A restore rehearsal
- An external review
Current status
The system is deployed internally and in daily use across its nine workspaces, and remains under active development.
The rows below separate what is in place from what is not, on the same terms as the section above: nothing planned is listed as though it were delivered.
- Nine workspaces
- In internal use
- Permission model
- 32 permissions · 7 profiles
- Automated tests
- 33 files committed
- Backup & restore procedure
- Documented
- Versioned migrations
- Not yet — schema is pushed
- Continuous integration
- Not configured
Access & audit model
- Authority derived from named permissions, not from job titles
- Baseline profiles, with direct grants for genuine exceptions
- Server-side checks on every request, not only at the route
- Database-backed sessions, rate-limited and bot-checked sign-in
- Transactional mutations that write the record and its audit event together
- Per-record audit timelines, plus a separate administrative activity log
- Private media reachable only through access-checked in-app routes
Described at concept level only. Permission matrices, role identifiers, route structures and implementation detail are deliberately withheld.
Evidence boundary
Supported
I can show explicit feature boundaries, a permission model of 32 named permissions with baseline profiles and direct grants, server-side authorization carrying its own regression suite, transactional writes paired with per-record audit timelines, private media delivery behind access checks, and 33 committed test files covering domain rules, validation, permissions, workflows and workbook import and export.
Not overstated
This is a live internal system, so no records, routes, screens, client names or role identifiers appear here. I do not claim production scale, quantified business impact, or a security review by anyone other than me. Schema is applied by push rather than by committed migrations, and the tests are not yet run by continuous integration.
Technical notes
The system runs as one Next.js application over PostgreSQL, with private S3-compatible object storage reachable only through in-app routes that re-check permissions per request. Data moves in and out as spreadsheet workbooks, field photographs are normalized on upload, and every write that changes a record's state is transactional and audited.
Core stack
- Next.js (App Router)
- React Server Components
- TypeScript
- PostgreSQL
- Drizzle ORM
Domain & safety
- Schema validation at every boundary
- Transactional server actions
- Typed domain errors
Data in & out
- Workbook import
- Workbook export
- Print-ready reports
Media
- Private S3-compatible object storage
- Access-checked media routes
- Conversion and resizing on upload
Security
- Salted key-derivation password hashing
- Database-backed sessions
- Rate-limited sign-in
- Cloudflare Turnstile
Testing
- Node's built-in test runner
- 33 committed test files
- Authorization regression suite
- Repository
- Private
- Public artifacts
- Approved public artifacts will be added after redaction and review.