Skip to content
Back to all work
Full-Stack / SaaS2026

OpsPilot

A multi-tenant operations dashboard where the permission matrix is enforced by the server, not drawn by the client.

Role
Architect & sole engineer
Next.js 16React 19TypeScriptPrismaPostgreSQLRechartsServer Actions

The application

Screens, captured from the running app

Booted against its seeded database and walked route by route — these are the real pages, not mockups.

OpsPilot dashboard with active client and project counts, task completion trend and recent activity.
Workspace dashboard — everything scoped to the tenant you are signed into.
OpsPilot client list with engagement status and pipeline value per client.
Clients, with the engagements and pipeline value hanging off each one.
OpsPilot task board grouped by status across projects.
Task board across projects, driven by server actions rather than client state.
OpsPilot reports view with utilisation and delivery charts.
Reporting over the same normalized model — no separate analytics store.
OpsPilot settings screen showing the Owner/Admin/Member permission matrix.
The permission matrix. It is enforced on the server; this screen only describes what the server already refuses.

The problem with most "multi-tenant" dashboards

They are single-tenant apps with a workspaceId column bolted on, and a UI that hides the buttons a Member shouldn't press. Both halves of that are wrong. A column you have to remember to filter on will eventually not be filtered on, and a hidden button is not a permission — it is a suggestion.

OpsPilot was built to get those two things right first and add features second.

Workspace isolation in the data model

Every queryable entity — client, project, task, comment, activity record — hangs off a workspace, and every read path goes through a helper that takes the session's workspace as a required argument rather than an optional filter. There is no code path that can construct a query without one, because the query builder will not compile without it.

That constraint is unglamorous and it is the entire point. It means a new feature cannot accidentally leak across tenants; the type system refuses before a reviewer has to notice.

RBAC that lives on the server

Three roles — Owner, Admin, Member — over a matrix of resource/action pairs. The rules are declared once and consulted in two places:

  • Server actions, which refuse before touching the database and return a typed error.
  • The settings screen, which renders the same matrix so an Owner can see exactly what each role can do.

The UI never decides anything. It reads the rules and describes them. Hiding a control is a courtesy to the user, not a security boundary — so the control being hidden and the action being refused are derived from one source rather than kept in sync by hand.

Server actions over an API layer

There is no REST layer between the pages and the database. Mutations are server actions: the permission check, the write and the revalidation happen in one function, on the server, in one round trip. It removes an entire class of bug where an endpoint and its caller disagree about who is allowed to do what.

The eight screens

Dashboard, clients, client detail, projects, project detail, tasks, team, reports, settings — seeded with realistic data so the charts have shapes worth looking at and the tables have enough rows to expose layout problems. Recharts throughout; the reporting view runs over the same normalized tables rather than a separate analytics store, which keeps the numbers honest at the cost of some query complexity.

What it demonstrates

Multi-tenant data modelling that fails closed; authorization designed as a single source of truth consulted by both server and UI; Next.js App Router with server actions and React Server Components; Prisma schema design with cascade relations; and enough seeded realism that the screens are worth showing.

You have a process thatshould be a system.

Tell me what arrives, who has to act on it, and where it currently falls over. That conversation is usually enough to scope the build.