Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

02 — User Personas (Organizations Module)

Roles that touch the Organizations surface. Permissions are exact from permissions.constants.ts + role.schema.ts. Note: no organization.create permission exists — provisioning is a platform-admin responsibility by role design (OQ-7 in 01). The client hides/denies surfaces per permission; server remains authoritative.


P1 — Super Admin (platform)

AttributeValue
Real-world rolePlatform operator / SaaS vendor operations
System roleplatform_admin (role.schema.ts:9-16) — priority 1000, permissions: [], operates via isPlatformAdmin bypass (jwt-auth.guard.ts:54; base.repository.ts:21-23)
FrequencyDaily (onboarding queue), bursts during migration/ops
Entry/admin/tenants (platform surface, hidden from tenant admins)
Permissions (exact)No explicit organization.* needed — platform bypass; ALL_PERMISSIONS available if role grants (not granted by default)

Goals

  • Provision a school in one call: name + optional slug/domain/contact/address/timezone/currency/plan/branding/metadata (create-organization.dto.ts:57-112).
  • Oversee the whole tenant fleet: search by name/slug (organizations.service.ts:78-83), sort, paginate.
  • Suspend problem tenants (PATCH :idstatus: suspended, update-organization.dto.ts:130-133) and offboard departed customers (DELETE :id, soft delete).

Pain points

  • OQ-1: no self-org endpoint exists, but platform admin does have full _id visibility — pain is on the tenant side, not theirs.
  • Offboarding looks final but data survives 30 days (worker retention) — must communicate to customer.
  • Slug/domain conflicts surface as raw 409 messages ("Organization with slug X already exists." — organizations.service.ts:41-43).

Needs / restrictions

  • Needs create/update/delete + list; must not operate tenant data from this surface.
  • Restrictions: none enforced in code today for this controller (OQ-4) — client still gates by isPlatformAdmin.

Mental model: "I open tenants, I create a tenant, I hand the slug to the customer for their admin registration." Expected behaviour: every lifecycle action immediate + reversible (soft delete); nothing hard-deleted for 30 days.

P2 — Org Admin

AttributeValue
Real-world rolePrincipal / Director / IT admin of one institution
System roleorg_admin — priority 100, permissions: ALL_PERMISSIONS (role.schema.ts:17-24), including all organization.*, settings.*, feature-flags.*
FrequencyDaily (settings rarely; feature flags occasionally; profile quarterly)
Entry/organization (self surface)

Permissions (exact subset relevant to this module)permissions.constants.ts:

PermissionLineScreens
organization.read:2Org Overview, Branding read
organization.update:3Org Edit, Branding, Academic year, Subscription
organization.delete:4Offboarding (self-delete — destructive, gated)
organization.settings.update:5Settings tabs (embedded settings path)
settings.read / settings.update / settings.delete:75-77Settings tabs (standalone settings collection path)
feature-flags.read / feature-flags.update / feature-flags.delete:78-80Feature flags screen
user.create:8Invite admin (user creation)
rbac.member.create / rbac.member.read:15-16Invite admin (membership)

Goals

  • Understand at a glance what the institution is configured as (timezone, currency, academic year, plan, status).
  • Fix a mistake fast: wrong timezone, wrong working days, wrong late threshold — before the next attendance run.
  • Make the app "feel like the school": logo + brand colors.
  • Turn on/off features (e.g., biometric, WhatsApp) as contracts/priorities change.

Pain points

  • OQ-6: settings exist in two places (embedded + settings collection) — risk of seeing stale/duplicated config.
  • OQ-1: no "my organization" endpoint — today the app cannot fetch the org profile without the org _id.
  • Full-replace semantics: saving a partial settings/flag form silently wipes sibling groups/keys (organizations.service.ts:134,150).
  • PATCH :id/feature-flags accepts Record<string,boolean> — no key whitelist, no label; the UI must maintain its own flag catalog.

Needs / restrictions

  • Needs full read+write on self profile; delete is destructive → typed confirm.
  • Must not see other tenants; must not see platform surface.
  • Timezone/currency pickers over free text (server only validates IsStringcreate-organization.dto.ts:86-94).

Mental model: "This is my school's identity page — like a settings app." Expected: save → instantly reflected app-wide (theme color, logo). Feature flags = light switches that affect whole modules.

P3 — Invited Admin

AttributeValue
Real-world roleSecond administrator (e.g., vice principal, office manager)
System roleorg_admin again, or a custom role with a permission subset (rbac.controller.ts:39-43)
FrequencyDaily; configure rarely
EntryInvitation link/email → POST /auth/register (register.dto.ts:4-29) or login
PermissionsWhatever the org admin granted at invite (roles), evaluated server-side via getPermissionsForUser (rbac.service.ts:44-73)

Goals

  • Get in with zero friction: invite email → register (first/last name, email, phone, password ≥ 8) → usable session (register returns access+refresh pair — auth.service.ts:95-99).
  • Be able to act on the org surface only where granted; see the same screens as P2 minus denied actions.

Pain points

  • No invitation token mechanism exists in code (register is open; OQ-2) — invite = create user + add member role, then hand over credentials/registration.
  • If registered with a mistyped tenant slug, they land in the wrong/no tenant silently.

Needs / restrictions

  • Needs register form with tenant slug; needs user.create + rbac.member.create from the inviting admin.
  • Restricted by their role's permission set — the client mirrors it and re-checks on 403.

Mental model: "The boss invited me; I just register and everything is already set up for me." Expected: onboarding state visible on first login if org is still onboarding.

P4 — Platform Ops (secondary)

AttributeValue
Real-world roleSupport/DevOps
System roleAny platform role or direct DB/queue access
FrequencyIncident-driven
EntryBullMQ dashboard (/admin/queues, main.ts:65-70) + tenant detail
Permissionsn/a (observability)

Goals

  • Verify purge timing: worker deletes only isDeleted && deletedAt < now-30d (tenant-purge.worker.ts:32-42).
  • Replay OrganizationCreated notifications from the in-app queue (event-queue-map.ts:13).
  • Diagnose "tenant disappeared" — soft-deleted orgs are invisible to every query (organizations.repository.ts:15-19).

Persona × permission × surface matrix

SurfaceP1 Super AdminP2 Org AdminP3 Invited Admin
Tenants list (/admin/tenants)✅ (platform flag)❌ hidden❌ hidden
Tenant detail + edit + suspend❌ (own org only)
Org Overview (self)✅ (via list)organization.read✅ if role has it
Org Edit / Brandingorganization.updaterole-gated
Settings tabs✅ (any tenant via :id)organization.settings.update + settings.*role-gated
Feature flagsfeature-flags.*role-gated
Invite adminuser.create + rbac.member.create❌ (unless custom)
Offboard (delete)organization.delete (typed confirm)

Server remains authoritative: 403 PERMISSION_DENIED / 404 RESOURCE_NOT_FOUND handling per 00-shared/06 §5.