Documentation

How the product works

This page describes the multi-tenant field-service experience: URLs, roles, main UX features, and how to run the frontend locally for development.

Problem we solve

Field service companies run on dispatched work, customer expectations, and proof of work — but generic tools scatter tickets, portals, and technician updates across inboxes and spreadsheets. Service Ticket SaaS gives each organization an isolated workspace with admin, customer, and technician surfaces under one path-based product, plus a separate platform plane for operators who run the multi-tenant service itself.

On this page

Jump to a section or use search below to filter feature cards and the route table.

System model

Each company is a tenant with its own data (isolated by company-id on the server). Users belong to one tenant per session. The browser URL uses a public slug (not a UUID) so links are shareable: /o/your-slug/….

  • Admin creates the org and manages tickets, customers, and settings.
  • Customer opens and follows service requests (My Tickets).
  • Technician sees assigned work (My Jobs).
  • Platform operators use a separate route (not under /o/) for SaaS-level tasks.

Core architecture (logical)

Browser and mobile clients talk to a single API namespace. Tenant context is enforced from verified JWT claims and tenant-id on every data row — never from client-supplied slugs alone.

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────────┐
│  Next.js (App)  │────▶│  Express /api/v1 │────▶│  PostgreSQL + RLS   │
│  /o/{slug}/…    │     │  + tenant ctx    │     │  tenant-id scoped   │
└────────┬────────┘     └────────┬─────────┘     └─────────────────────┘
         │                       │
         │              ┌──────────┴──────────┐
         │              │ Redis (sessions /   │
         └──────────────│  queues / rate cap) │
                        └─────────────────────┘

Platform plane: /platform/* UI → Bearer platform JWT → /api/v1/platform/* (separate from org tokens).

Representative user flows

  • Org admin registers at /register → lands in /o/slug/a/dashboard → invites staff and creates tickets.
  • Customer uses /o/slug/login /o/slug/c/my-tickets to open and track requests.
  • Technician signs in at the same org URL → /o/slug/t/my-jobs for field overview, filters, and assignments.
  • Platform operator uses /platform/login → overview metrics → tenant directory → per-tenant management; super admins add delegated operators.

Ticket workflow (overview)

Support tickets close without a billing step; billable tickets may pass through Billing before Closed. Staff can submit work for review; reviewers approve, resolve, or send back. The live product enforces transitions per template in the API.

Loading diagram…

UX features (what you get in the browser)

These are product-facing behaviors implemented or staged in the current frontend. APIs and tenancy rules live in the backend; this list is what users and testers interact with.

Path-based tenancy

Every organization has a public slug. All app URLs live under /o/{slug}/ with role segments: /a (admin), /c (customer), /t (technician). No subdomain setup required for v1.

Session & URL alignment

After sign-in, the client keeps your JWT org slug in sync with the address bar. Wrong slug or wrong role segment (/a vs /c vs /t) is corrected automatically when possible.

Multi-organization accounts

If your email belongs to more than one company, you can pick the active organization (and set a default in Settings). Switching updates the path under the new slug.

Role-specific shells

Admin, customer, and technician areas use dedicated layouts: sidebar or top nav, skip links for accessibility, and consistent semantic colors (design tokens).

Guided tours

First-time flows can launch in-page tours on key screens (admin queue, customer tickets, technician jobs). Completion is remembered in the browser.

Realtime updates

When tickets change (create, status, assignment), the UI can refresh lists and dashboards via a tenant-scoped socket connection.

Technician install shell (PWA)

Technician routes support a minimal offline-friendly install manifest and service worker for shell caching; full offline sync is a future iteration.

Invite & onboarding copy

Org admins get copy-paste registration and login URLs per role. Public org pages explain customer vs technician paths without exposing internal IDs.

RBAC & custom staff roles

A global permission template applies to Owner, Admin, Manager, Reviewer, Technician, and Customer—including tickets.review on staff roles for future workflow surfaces. The Owner can define named custom profiles (FR-040 Phase B) with their own permission sets; platform policy org_max_custom_roles caps how many exist (0 disables). Assign on Users; JWTs carry customRoleId—re-login after assignment.

Ticket workflow templates (admin)

Tickets have a type (support vs billable) that selects the workflow template: support skips billing states; billable may include Billing before close. Admin ticket detail uses Allowed next statuses with POST /tickets/{id}/workflow-transition (optional reason + reasonCode). Review queue nav appears when you have tickets.review (FR-041).

Platform operator shell

After /platform/login, Overview metrics, tenant directory, per-tenant management, delegated operators (super admin), and Legal sit in a sidebar shell: skip link, theme, sign-out, tenant log-in, and footer links to platform policies and product legal (FR-012 v1.2 / FR-045).

Ticket attachments (field capture)

Customer and technician ticket pages support uploads when the API is configured for S3 or local filesystem storage; otherwise the UI shows a stub. Downloads use a short-lived token on the file URL.

Governance & Auditability

Administrative identity changes (email, profile, role) and password resets require an approved Governance Ticket. The Governance Wizard facilitates ticket creation, authorization, and automatic resolution with a full audit summary.

Replace {slug}with your organization's slug. Use the global entry points when you do not yet have a slug bookmarked.

Authenticated workspaces are only under /o/{slug}/…. Legacy flat paths such as /dashboard, /my-tickets, or /my-jobs are not served — update bookmarks and integrations to the slugged URLs below.

Path or patternWhat it isTypical user
/Product homeEveryone
/how-it-worksThis guide (routes + dev setup)Everyone
/registerCreate organization (first admin)New SaaS customer
/register/customerCustomer signup with company ID (when no slug link)End customers
/register/agentTechnician signup with company ID (when no slug link)Field staff
/loginSign in (any role, org picker if needed)Returning users
/forgot-passwordPassword help stub (FR-037)Anyone locked out
/o/{slug}Organization welcomeVisitors with a slug
/o/{slug}/c/register · /o/{slug}/loginCustomer join & return (preferred)End customers
/o/{slug}/t/register · /o/{slug}/loginTechnician join & return (preferred)Field staff
/o/{slug}/loginSign-in for that org (all roles → right dashboard)Admins, techs, customers
/o/{slug}/a/dashboardAdmin dashboard & metricsAdmins
/o/{slug}/a/ticketsCompany ticket queue (list)Admins
/o/{slug}/a/tickets/{id}Field ticket detail + Allowed next statuses (FSM)Admins (tickets.read)
/o/{slug}/a/settings/requests/{id}Governance ticket detail (Gov workflow)Admins (tickets.read)
/o/{slug}/a/tickets/newCreate ticket (lazy form)Admins (tickets.create)
/o/{slug}/a/customersCustomer directoryAdmins
/o/{slug}/a/customers/newAdd customer (lazy form)Admins
/o/{slug}/a/settingsAdmin settings, invites, default orgAdmins
/o/{slug}/a/settings/permissionsPermission matrix (global + custom rows)Admins (settings.read)
/o/{slug}/a/settings/custom-rolesCustom staff roles (Owner edits)Admins
/o/{slug}/a/usersUsers directory + Owner custom-role assignAdmins
/o/{slug}/c/my-ticketsMy Tickets listCustomers
/o/{slug}/c/my-tickets/newNew service requestCustomers
/o/{slug}/c/my-tickets/{id}Ticket detail, comments, attachmentsCustomers
/o/{slug}/c/settingsCustomer account & default orgCustomers
/o/{slug}/t/my-jobsMy Jobs (assignments)Technicians
/o/{slug}/t/my-jobs/{id}Job detail & attachmentsTechnicians
/o/{slug}/t/settingsTechnician account & default orgTechnicians
/platform/loginPlatform operator sign-inSaaS staff only
/platformPlatform overview (tenant counts)Platform JWT
/platform/tenantsTenant directory (search, link to manage)Platform JWT
/platform/tenants/{companyId}Tenant management (lifecycle, flags)Platform JWT
/platform/operatorsDelegated platform users (super admin)Platform JWT
/platform/legalOperator policy placeholdersPlatform JWT
/o/{slug}/t/my-jobsTechnician jobs (overview + assigned list)Technicians

Quick start (frontend development)

From the repository root, install once, then run the API and the Next.js app. The UI expects the backend for sign-in, tickets, and public org lookup.

Commands

Node.js 20+ and npm 10+ recommended.

1. Install dependencies

npm install

2. Infrastructure (PostgreSQL + Redis)

docker compose up -d

3. Database schema + demo data (once per machine)

npm run prisma:migrate:deploy -w backend
npm run seed

See README.md for sandbox orgs sandbox-hvac / sandbox-it and the shared demo password.

4. Backend API (or run both apps together — step 5)

npm run dev:backend

Requires DATABASE_URL, JWT_SECRET, and Redis if enabled — see backend/README.md.

5. Frontend (or API + UI concurrently)

npm run dev:frontend
npm run dev

App URL is usually http://localhost:3000. Prefer NEXT_PUBLIC_API_BASE_URL=/api/v1 so Next proxies to the API (same-origin cookies). Set NEXT_PUBLIC_SOCKET_URL to the raw API origin for websockets — see frontend/.env.example and frontend/src/lib/api.ts.

6. Smoke-test flows

Register at /register or sign in with a seeded org under / → slugged /o/…/a/dashboard. Playwright specs live in e2e/tests/. From the repo root, quality gate: npm run verify.