Survey Dashboard: Multi-Tenant Survey Management Portal

Tech Stack:
ReactTypeScriptSpring BootPostgreSQLSTOMPWebSocketTailwind CSS

A multi-tenant web application for authoring surveys, assigning them to schools, collecting submissions, and verifying responses across educational organizations with realtime notifications

Survey Dashboard: Multi-Tenant Survey Management Portal

Project Overview

Survey Dashboard is a multi-tenant web application that manages surveys across educational organizations. Platform operators onboard organizations, survey authors assign task-based surveys to schools, and school staff submit responses through a structured verification workflow.

I built the application as the sole Full-stack Developer. The work includes the React single-page application, Spring Boot REST API, STOMP/WebSocket notification layer, PostgreSQL data model, and the operational handover documentation.

Survey author home page with assigned surveys and progress overview

Technology Stack

%% caption: System architecture across React SPA, Spring Boot API, PostgreSQL, and Resend
flowchart LR
    subgraph Client ["Frontend"]
        UI["React 18 / TypeScript SPA"]
        Query["TanStack Query"]
        STOMP["STOMP WebSocket Client"]
    end

    subgraph Backend ["Spring Boot 4 API"]
        REST["REST Controllers"]
        Svc["Domain Services"]
        WS["WebSocket Broker"]
    end

    subgraph Storage ["Data & Integrations"]
        DB[("PostgreSQL")]
        FS["Local File Storage"]
        Email["Resend Email API"]
    end

    UI <-->|HTTPS / JWT| REST
    STOMP <-->|WSS| WS
    REST --> Svc
    WS --> Svc
    Svc --> DB
    Svc --> FS
    Svc --> Email
%% caption: Notification flow from school submission through verification to STOMP delivery
sequenceDiagram
    participant School as School Author
    participant UI as React App
    participant API as Spring Boot API
    participant DB as PostgreSQL
    participant Author as Survey Author
    participant WS as STOMP Broker

    School->>UI: Submit task item response
    UI->>API: PUT /api/v1/surveys/{id}/responses
    API->>DB: Save response + set reviewStatus PENDING
    API->>DB: Insert notification rows
    API->>WS: Push AFTER_COMMIT notification event
    WS-->>Author: STOMP /user/queue/notifications
    Author->>UI: Review submission
    UI->>API: POST verify or reject
    API->>DB: Update reviewStatus
    API->>WS: Push notification to school
    WS-->>School: STOMP notification + toast
Stack AreaTechnologies & Tools
FrontendReact 18, TypeScript, Vite, Tailwind CSS, Radix UI, TanStack Query, Zustand, react-hook-form, zod, @stomp/stompjs
BackendJava, Spring Boot 4, Spring Web MVC, Spring Data JPA, Spring Security, STOMP over WebSocket
DatabasePostgreSQL with Flyway migrations
RealtimeSTOMP simple broker with JWT-scoped /user/queue/notifications destinations
EmailResend API for invitation and notification delivery
Build & DeployBun (frontend), Maven (backend), Docker

Product Context

Educational organizations struggle to collect standardized data from multiple schools while tracking completion across platform, organization, school, and class levels. Spreadsheets and email chains fail when surveys require prerequisite tasks, file uploads, multi-step verification, and strict role-based access.

Survey Dashboard solves this problem by moving the workflow into a centralized system. Survey authors define task items, assign surveys to schools, and review responses. School authors complete tasks, resubmit rejected work, and receive real-time feedback when their review status changes.

Tenant and Role Model

%% caption: Role hierarchy from platform admin down to school author
flowchart TD
    P["PLATFORM_ADMIN<br/>platform-wide"]
    O["SURVEY_AUTHOR / ORG_ADMIN<br/>scoped to Organization"]
    S["SCHOOL_AUTHOR / SCHOOL_ADMIN<br/>scoped to School"]
    P --> O --> S
RoleScopePrimary responsibilities
PLATFORM_ADMINPlatformManage organizations, send platform-level invitations, oversee tenants
SURVEY_AUTHOR (ORG_ADMIN)One OrganizationAuthor surveys, invite school authors, verify submissions, reject or approve task items
SCHOOL_AUTHOR (SCHOOL_ADMIN)One SchoolRun assigned surveys, submit task items, respond to verification rejections

Tenancy relies on the authenticated principal’s organizationId and schoolId instead of URL path parameters. The data layer uses repository methods like findByOrganization_IdAndRoleAndIsActiveTrue to make tenant boundaries explicit.

What I Built

Survey editor with task item configuration

  • Multi-tenant domain model: A platform, organization, school, and class hierarchy provides role-scoped data access across the survey lifecycle.
  • Survey and submission pipeline: Survey authors use typed field templates with prerequisite rules to assign work. School responses move through pending, submitted, verified, and rejected states, preserving reviewer history on the same database row.
  • Real-time notification system: STOMP/WebSocket delivery relies on JWT handshakes to scope messages per user. An AFTER_COMMIT event bridge ensures notifications only push after database commits finish.
  • Authentication and routing: The system uses JWT access tokens with HttpOnly refresh cookies, silent refresh scheduling, and automatic retry logic. React route trees isolate platform admins, survey authors, and school authors.
  • Operational documentation: Docusaurus documentation details the architecture, survey lifecycle, deployment topology, and day-two runbooks for the client team.

Survey Lifecycle

%% caption: Survey object hierarchy from survey down to verification
flowchart LR
    TaskItem --> Response["School Response"]
    Response --> Verification
%% caption: Review states for a school response from pending through verification or rejection
stateDiagram-v2
    [*] --> Pending
    Pending --> Submitted : SCHOOL_AUTHOR submits
    Submitted --> Verified : SURVEY_AUTHOR approves
    Submitted --> Rejected : SURVEY_AUTHOR rejects
    Rejected --> Submitted : SCHOOL_AUTHOR resubmits
    Verified --> [*]

A school submitting a response marks a task item as complete, but completion does not mean verification. Survey authors can reject the work and require a new submission. Prerequisite logic blocks subsequent task items until the earlier ones receive verification. This keeps multi-step surveys ordered without manual intervention.

Key Engineering Decisions

1. Enforce Tenancy in the Repository Layer

Multi-tenant systems often leak data when controllers trust client-supplied organization or school IDs. Survey Dashboard scopes every database query through the authenticated principal’s tenant context. This approach maintains consistent authorization even as developers add new endpoints.

2. Deliver Notifications After Database Commit

The application produces notification rows and STOMP pushes together, but the WebSocket bridge uses an AFTER_COMMIT transactional event listener. If a transaction rolls back, the system sends no push notification. This prevents the interface from showing alerts for data that never persisted in the database.

3. Separate Access and Refresh Tokens

The access token lives in Zustand and localStorage for API calls. The refresh token operates as an HttpOnly cookie set by the backend. The client schedules a silent refresh one minute before expiry and retries failed requests once. This keeps long survey sessions stable without exposing the refresh token to JavaScript.

4. Use a Simple In-Memory STOMP Broker

The initial deployment assumes a single backend instance. Spring’s simple broker keeps real-time delivery straightforward for the first version. The documentation outlines the scaling path: the client must switch to an external STOMP relay before horizontally scaling the API tier.

Outcome

Survey Dashboard provides the client with a production-ready portal for survey administration, structured verification, and real-time status updates across schools. The handover documentation records domain rules, architecture decisions, and operational runbooks. A future team can maintain the system using this documentation instead of relying on the original developer.

Lessons Learned

  • Verification workflows with prerequisites are easier to manage when the review state lives directly on the response row, rather than on a separate verification entity.
  • Real-time features require the same strict tenancy discipline as REST endpoints. This is particularly true for STOMP destinations that rely on JWT identity during the WebSocket handshake.
  • The quality of a client handover depends as much on documenting business rules as it does on code comments. Clear documentation prevents confusion over mismatched terms like ORG_ADMIN and SURVEY_AUTHOR.
  • Separating access tokens in memory from refresh tokens in HttpOnly cookies provides strong session stability for users taking long surveys, without sacrificing security.
  • Binding external system events to database transaction commits prevents race conditions. Notifications should only reach the user after the database confirms the write.

Attachments

Survey Author Home

Survey author home page

Survey List

List of surveys assigned to schools

School List

Organization school list with assignment status

Create Survey

Create new survey form

Invite School Author

Invite school author form

School Detail

School detail view for survey authors

School Survey Response

School author survey response workspace

Document Management

Document management for survey file uploads

Notification Panel

In-app notification panel