Case Study

ISAK FIRE OPS

Enterprise operations platform. 161 database models. 102 pages. One engineer.

161
DB Models
102
Pages
10
Route Groups
4,219
Schema Lines

The Problem

A fire protection company running its entire operations on a vanilla JavaScript monolith. No authentication — anyone with the URL could access everything. No type safety — runtime errors in production were the norm. No testing — changes were deployed and prayed over.

The business had outgrown its tools. CRM deals, project scheduling, HR records, fleet management, financial reporting, and field operations were all jammed into a single codebase with no separation of concerns, no role-based access, and no audit trail.

The ask: replace everything. Build a platform that a 50+ person company could actually run on — with real auth, real data integrity, and real visibility into operations across 9 business domains.

System Architecture

Overview

6 modules
DashboardCompany OverviewCountry OverviewLocation OverviewIssue TrackerSprint Dashboard

Revenue

7 modules
CRMEstimatingBuilderForecastingMarketingAcquisitionsSales Forecast

Projects

7 modules
ProjectsDispatchEngineeringITMPurchasingSchedulingVendors

Finance

1 modules
Revenue Core

People

6 modules
HRPayrollTimeFleetLicensingLocations

Intelligence

3 modules
KPIsReportsCEO Dashboard

Knowledge

5 modules
DocsSOPOps CadenceAI CenterAI Automation

Portals

3 modules
Field PortalOffice PortalCustomer Portal

Admin

1 modules
Role Management

Technical Decisions

Why Next.js 16 App Router for an enterprise app?

Server Components by default means the client ships minimal JavaScript. Each of the 70+ modules is a server component that fetches its own data — no global state management, no loading spinners, no waterfall requests. The App Router's nested layouts let 10 route groups share auth and navigation without prop drilling.

Why 161 Prisma models instead of a headless CMS?

The business logic IS the schema. Deals have stages, milestones, inspections, activities, documents, and cost breakdowns — all with referential integrity. A CMS would have required bolting relational logic onto a document store. Prisma's type-safe client catches schema drift at compile time, not in production.

How does role-based access scale to 70+ modules?

A permission matrix maps 8 role tiers (super_admin through viewer) to module access. Every server action calls requireRole() before touching the database. The sidebar navigation filters itself based on the logged-in user's role — no hidden admin links, no client-side guards.

How was this built by one person?

Convention over configuration. One server action file per domain (22 files), one page per route, shared UI components (ActionModal, DataTable, KanbanBoard). Playwright E2E tests catch regressions. The architecture is repetitive by design — adding a new module is scaffolding, not invention.

Next.js 16 · React 19 · TypeScript · Prisma 7 · PostgreSQL · GCP Cloud Run · Tailwind 4 · Playwright