NDSS CRM Manual / Chapter 3: Architecture & Technical Stack
V3.8 · 2024/2025

Chapter 3: Architecture & Technical Stack

A comprehensive technical reference covering the full-stack architecture, database schema, state management patterns, component library, and directory structure of the NDSS CRM platform built by Newdawn Support Services.

3.1 System Architecture Overview

NDSS CRM follows a modern, layered full-stack architecture designed for performance, scalability, and maintainability. The platform is composed of four primary tiers: a Next.js frontend served via server-side rendering and static generation, a Supabase / Oracle backend-as-a-service layer providing authentication and real-time database access, Python microservices for data processing and analytics workloads, and PHP integration modules for legacy system compatibility with existing Newdawn Support Services infrastructure.

The architecture is designed to support the demanding requirements of NDIS disability care organisations, where data integrity, audit compliance, role-based security, and real-time collaboration are critical operational needs.

3.1.1 High-Level Architecture Diagram

The following wireframe diagram illustrates the four-tier architecture of the NDSS CRM platform and the data flow between each layer.

NDSS CRM - Four-Tier System Architecture
TIER 1: Client Layer (Browser)
Next.js 16 App Router
React 19
TypeScript 5.x
TailwindCSS 4.x
Radix UI
React Query
Zustand
TIER 2: Backend-as-a-Service (Supabase / Oracle)
Supabase / Oracle Auth
PostgreSQL 15
Row Level Security (RLS)
Realtime Subscriptions
Edge Functions
Storage Buckets
TIER 3: Microservices (Python & PHP)
Python 3.12 - Data Processing
Python - Report Generation
Python - NDIS Claim Engine
PHP 8.3 - Legacy API Bridge
PHP - Payroll Integration
PHP - Document Converter
TIER 4: Data & Storage
PostgreSQL (Primary DB)
Supabase / Oracle Storage (Files)
Redis (Session Cache)
S3-Compatible (Backups)
Fig 3.1 - The four-tier architecture of NDSS CRM: Client Layer (Next.js/React), Backend-as-a-Service (Supabase / Oracle), Microservices (Python/PHP), and Data/Storage.

3.1.2 Architecture Summary Table

Tier Technologies Responsibilities
Client Layer Next.js 16, React 19, TypeScript, TailwindCSS Server-side rendering (SSR), static site generation (SSG), client-side routing, UI rendering, form validation, state management, and real-time UI updates via subscriptions.
BaaS Layer Supabase / Oracle, PostgreSQL 15 Authentication, authorization (RLS), database CRUD operations, real-time data subscriptions, file storage, and edge function execution for lightweight serverless logic.
Microservices Python 3.12, PHP 8.3 Data processing pipelines, NDIS claim generation, report compilation, PDF generation, legacy system integrations, payroll data synchronisation, and batch data transformations.
Data & Storage PostgreSQL, Supabase / Oracle Storage, Redis Persistent data storage, file/document storage, session caching, query result caching, and automated backup management.
Why a Polyglot Architecture?

NDSS CRM uses multiple programming languages (TypeScript, Python, PHP) because each language excels in its domain. TypeScript powers the interactive frontend with type safety. Python handles data-intensive processing, analytics, and report generation with its rich ecosystem of libraries (Pandas, ReportLab). PHP provides robust integration with existing Newdawn Support Services legacy systems and third-party payroll providers that expose PHP-compatible APIs.

3.2 Frontend Architecture

The NDSS CRM frontend is built on Next.js 16 using the App Router paradigm introduced in Next.js 13 and matured through subsequent releases. The App Router provides file-system-based routing, React Server Components (RSC), streaming server-side rendering, and fine-grained layout nesting - all of which are leveraged extensively throughout the platform.

3.2.1 Next.js 16 App Router

The App Router organises routes as directories within the src/app/ folder. Each directory can contain a page.tsx file (the rendered page), a layout.tsx file (shared layout wrapper), a loading.tsx file (suspense fallback), and an error.tsx file (error boundary). This convention-based approach eliminates the need for manual route configuration.

File Convention Purpose Example
page.tsx Page Component src/app/(dashboard)/clients/page.tsx renders at /clients
layout.tsx Shared Layout src/app/(dashboard)/layout.tsx wraps all dashboard pages with sidebar and topbar
loading.tsx Suspense Fallback Displays skeleton loaders while server components stream data
error.tsx Error Boundary Catches and displays runtime errors with recovery options
not-found.tsx 404 Handler Custom "Page Not Found" display per route segment
template.tsx Re-rendered Layout Similar to layout but creates a new instance on navigation (used for animations)

3.2.2 React 19 Features Used

NDSS CRM leverages several React 19 features throughout the codebase:

  • React Server Components (RSC): Data-fetching components that execute on the server, reducing client-side JavaScript bundle size. Used extensively for list pages, detail views, and report rendering.
  • Server Actions: Server-side functions invoked directly from client components for form submissions (e.g., creating a new client, submitting a progress note, approving a timesheet).
  • useOptimistic: Provides instant UI feedback for mutations before the server confirms the change. Used in shift status updates and message sending.
  • useFormStatus: Tracks the pending state of form submissions to display loading indicators on submit buttons.
  • Suspense Boundaries: Wrap async components to show loading skeletons while data streams from the server.

3.2.3 Component Hierarchy

The frontend component tree follows a strict hierarchy with clearly separated concerns:

NDSS CRM - Component Hierarchy
RootLayout (src/app/layout.tsx)
ThemeProvider
QueryClientProvider
AuthProvider
DashboardLayout (src/app/(dashboard)/layout.tsx)
Sidebar
Topbar
BreadcrumbBar
Page Content (page.tsx)
StatCards
DataTable
FilterBar
ActionButtons
Modals/Dialogs
Fig 3.2 - Nested component hierarchy from RootLayout down to individual page content components.

3.2.4 Route Groups & Routing Structure

NDSS CRM uses Next.js route groups (parenthesised folders) to organise routes without affecting the URL path. The three primary route groups are:

Route Group Layout Contains
(auth) Minimal layout (no sidebar) Login, Register, Forgot Password, Reset Password, Verify Email
(dashboard) Full layout (sidebar + topbar) All authenticated platform pages: Dashboard, Clients, Staff, Rostering, Finance, Compliance, Intake, Clinical, Learning, Messaging, Reports, Admin, SIL
(client-portal) Portal layout (simplified sidebar) Client-facing portal: My Services, My Plan, Messages, Documents, Profile
(public) Public layout (no auth required) Landing page, Terms of Service, Privacy Policy, Demo Request

3.3 Backend Architecture

The backend of NDSS CRM is built on a hybrid architecture combining Supabase / Oracle (PostgreSQL-based Backend-as-a-Service), Python microservices for data-intensive operations, and PHP integration modules for legacy system compatibility. This approach provides the rapid development benefits of a BaaS while retaining the flexibility to implement complex business logic in purpose-built services.

3.3.1 Supabase / Oracle (PostgreSQL)

Supabase / Oracle serves as the primary backend layer and provides the following capabilities:

  • PostgreSQL 15 Database: All application data is stored in a managed PostgreSQL instance with full SQL support, including advanced features such as CTEs, window functions, JSONB columns, and full-text search.
  • Row Level Security (RLS): Every table has RLS policies that enforce role-based data access at the database level. This means that even if application-level security is bypassed, the database itself prevents unauthorised data access.
  • Supabase / Oracle Auth: Handles user authentication (email/password, OAuth), session management, password resets, and email verification. See Chapter 4 for detailed authentication documentation.
  • Realtime: Supabase / Oracle Realtime enables WebSocket-based subscriptions to database changes. NDSS CRM uses this for live shift updates, message notifications, and dashboard data refreshes.
  • Edge Functions: Deno-based serverless functions deployed at the edge for lightweight operations such as webhook handlers, email dispatch triggers, and NDIS price guide lookups.
  • Storage: Supabase / Oracle Storage manages file uploads including client documents, staff qualification certificates, incident report attachments, and profile photos.

3.3.2 Python Microservices

Python 3.12 microservices handle data processing workloads that require computational libraries not available in the JavaScript/TypeScript ecosystem:

Service Key Libraries Responsibilities
ndss-report-engine Pandas, ReportLab, Matplotlib Generates PDF and Excel reports for financial summaries, compliance audits, client progress reviews, and organisational KPI dashboards.
ndss-claim-processor Pandas, psycopg2, httpx Processes NDIS claims by validating service records against the NDIS Price Guide, calculating line item amounts, and preparing bulk claim submissions.
ndss-data-pipeline Pandas, SQLAlchemy, Celery Runs scheduled data transformations including shift aggregation, budget utilisation calculations, staff compliance status updates, and historical data archival.
ndss-notification-worker httpx, Jinja2, smtplib Dispatches email and SMS notifications based on system events: shift reminders, qualification expiry warnings, incident escalations, and billing alerts.

3.3.3 PHP Integration Modules

PHP 8.3 modules provide compatibility bridges with legacy systems and third-party services that use PHP-based APIs:

Module Key Libraries Responsibilities
ndss-payroll-bridge Guzzle, Carbon, League\Csv Synchronises timesheet data with external payroll systems (MYOB, Xero, KeyPay) that provide PHP SDK clients. Handles award interpretation, leave accrual calculations, and superannuation reporting.
ndss-legacy-api Laravel Components, Doctrine DBAL Provides a REST API bridge to legacy Newdawn databases that store historical client records, pre-migration financial data, and archived compliance documents.
ndss-doc-converter PhpSpreadsheet, TCPDF, PHPWord Converts documents between formats (DOCX to PDF, CSV to XLSX), processes bulk document uploads, and generates templated letters, service agreements, and compliance certificates.
Microservice Communication

Python and PHP microservices communicate with the main Supabase / Oracle database using direct PostgreSQL connections (via connection pooling with PgBouncer). They also expose REST endpoints that the Next.js frontend can call via internal API routes. All inter-service communication uses HTTPS with bearer token authentication.

3.4 Database Schema

The NDSS CRM database is a relational PostgreSQL schema containing over 40 tables. This section documents the 10 primary entities that form the core of the data model. Every table uses UUID primary keys, includes created_at and updated_at timestamp columns, and is protected by Row Level Security policies.

3.4.1 Profile (User Accounts)

The profiles table extends the Supabase / Oracle auth.users table with application-specific user data. Every authenticated user has exactly one profile record.

Column Type Nullable Description
idUUID (PK)NoReferences auth.users.id
emailTEXTNoUser email address (unique)
full_nameTEXTNoFull display name
roleTEXTNoOne of 24 role identifiers (e.g., master_admin, support_worker)
avatar_urlTEXTYesURL to profile photo in Supabase / Oracle Storage
phoneTEXTYesContact phone number
is_activeBOOLEANNoWhether the account is active (default: true)
last_login_atTIMESTAMPTZYesTimestamp of most recent login
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.2 Client (NDIS Participants)

The clients table stores all NDIS participant records, including personal details, NDIS identifiers, and service status information.

Column Type Nullable Description
idUUID (PK)NoPrimary key
ndis_numberTEXTNoNDIS participant number (unique, 9 digits)
first_nameTEXTNoParticipant first name
last_nameTEXTNoParticipant last name
date_of_birthDATENoDate of birth
genderTEXTYesGender identity
emailTEXTYesEmail address
phoneTEXTYesContact phone number
addressJSONBYesStructured address object (street, suburb, state, postcode)
statusTEXTNoOne of: active, inactive, waitlist, exited
primary_disabilityTEXTYesPrimary disability type
emergency_contactJSONBYesEmergency contact details (name, phone, relationship)
assigned_coordinator_idUUID (FK)YesReferences profiles.id for the assigned service coordinator
plan_start_dateDATEYesCurrent NDIS plan start date
plan_end_dateDATEYesCurrent NDIS plan end date
notesTEXTYesGeneral notes about the participant
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.3 Goal (Client Goals)

The goals table tracks NDIS participant goals as defined in their NDIS plan and service agreements.

Column Type Nullable Description
idUUID (PK)NoPrimary key
client_idUUID (FK)NoReferences clients.id
titleTEXTNoShort goal title
descriptionTEXTYesDetailed goal description
categoryTEXTNoGoal category (e.g., daily_living, social_participation, employment)
statusTEXTNoOne of: not_started, in_progress, achieved, discontinued
target_dateDATEYesTarget completion date
progress_percentageINTEGERNoCurrent progress (0-100)
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.4 ProgressNote

The progress_notes table stores shift-level notes recorded by support workers after each service delivery session.

Column Type Nullable Description
idUUID (PK)NoPrimary key
client_idUUID (FK)NoReferences clients.id
shift_idUUID (FK)YesReferences shifts.id (optional if not linked to a specific shift)
author_idUUID (FK)NoReferences profiles.id for the note author
note_dateDATENoDate of the service/note
contentTEXTNoProgress note content (rich text)
goals_addressedUUID[]YesArray of goal IDs addressed during this session
mood_ratingINTEGERYesClient mood rating (1-5 scale)
is_signedBOOLEANNoWhether the note has been signed/approved
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.5 ClientFunding

The client_funding table tracks NDIS plan funding allocations and budget utilisation for each participant.

Column Type Nullable Description
idUUID (PK)NoPrimary key
client_idUUID (FK)NoReferences clients.id
support_categoryTEXTNoNDIS support category code (e.g., core_daily_activities)
total_budgetNUMERIC(12,2)NoTotal allocated budget amount in AUD
used_budgetNUMERIC(12,2)NoAmount of budget consumed to date
plan_start_dateDATENoFunding period start date
plan_end_dateDATENoFunding period end date
management_typeTEXTNoOne of: ndia_managed, plan_managed, self_managed
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.6 StaffProfile

The staff_profiles table extends the profiles table with employment-specific fields for all staff members.

Column Type Nullable Description
idUUID (PK)NoPrimary key
profile_idUUID (FK)NoReferences profiles.id
employee_idTEXTNoInternal employee identifier (e.g., NDS-001)
departmentTEXTYesDepartment assignment
position_titleTEXTNoJob title
employment_typeTEXTNoOne of: full_time, part_time, casual, contractor
start_dateDATENoEmployment start date
ndis_worker_screeningJSONBYesScreening status object (number, expiry_date, status)
working_with_children_checkJSONBYesWWC check object (number, expiry_date, status)
hourly_rateNUMERIC(8,2)YesBase hourly pay rate
availabilityJSONBYesWeekly availability schedule (day-by-day time ranges)
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.7 Qualification

The qualifications table records certifications, qualifications, and training completions for staff members.

Column Type Nullable Description
idUUID (PK)NoPrimary key
staff_profile_idUUID (FK)NoReferences staff_profiles.id
nameTEXTNoQualification or certification name
issuing_bodyTEXTYesOrganisation that issued the qualification
issue_dateDATENoDate the qualification was issued
expiry_dateDATEYesExpiration date (null if no expiry)
document_urlTEXTYesURL to the uploaded certificate document
statusTEXTNoOne of: current, expired, pending_renewal
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.8 Shift

The shifts table is the core scheduling entity, representing individual service delivery appointments between staff and clients.

Column Type Nullable Description
idUUID (PK)NoPrimary key
client_idUUID (FK)NoReferences clients.id
staff_idUUID (FK)YesReferences staff_profiles.id (null if unallocated)
shift_dateDATENoDate of the shift
start_timeTIMENoShift start time
end_timeTIMENoShift end time
service_typeTEXTNoNDIS support item category
statusTEXTNoOne of: scheduled, confirmed, in_progress, completed, cancelled, no_show
locationTEXTYesService delivery location
notesTEXTYesShift-specific notes
clock_in_timeTIMESTAMPTZYesActual clock-in timestamp
clock_out_timeTIMESTAMPTZYesActual clock-out timestamp
is_recurringBOOLEANNoWhether this shift is part of a recurring series
recurrence_idUUID (FK)YesReferences parent recurrence record
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.9 Invoice

The invoices table tracks all financial documents generated for NDIS claims and client billing.

Column Type Nullable Description
idUUID (PK)NoPrimary key
invoice_numberTEXTNoAuto-generated invoice number (e.g., INV-2024-0001)
client_idUUID (FK)NoReferences clients.id
billing_period_startDATENoStart of billing period
billing_period_endDATENoEnd of billing period
total_amountNUMERIC(12,2)NoTotal invoice amount in AUD
gst_amountNUMERIC(12,2)NoGST component (typically $0 for NDIS services)
statusTEXTNoOne of: draft, submitted, paid, overdue, cancelled
line_itemsJSONBNoArray of line item objects (service_type, quantity, unit_price, total)
submitted_atTIMESTAMPTZYesTimestamp when invoice was submitted for payment
paid_atTIMESTAMPTZYesTimestamp when payment was received
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.10 Incident

The incidents table records all safety incidents, near-misses, and complaints as required by NDIS Practice Standards.

Column Type Nullable Description
idUUID (PK)NoPrimary key
incident_numberTEXTNoAuto-generated reference (e.g., INC-2024-0042)
client_idUUID (FK)YesReferences clients.id (null if not client-related)
reported_byUUID (FK)NoReferences profiles.id for the reporter
incident_dateTIMESTAMPTZNoDate and time the incident occurred
categoryTEXTNoIncident category (e.g., injury, medication_error, behaviour, property_damage)
severityTEXTNoOne of: low, medium, high, critical
descriptionTEXTNoDetailed description of the incident
immediate_actionsTEXTYesActions taken immediately following the incident
statusTEXTNoOne of: open, investigating, resolved, closed
is_reportableBOOLEANNoWhether this incident must be reported to the NDIS Commission
attachmentsTEXT[]YesArray of file URLs for supporting documents/photos
created_atTIMESTAMPTZNoRecord creation timestamp
updated_atTIMESTAMPTZNoLast modification timestamp

3.4.11 Entity Relationship Overview

The following diagram summarises the primary relationships between core database entities:

NDSS CRM - Entity Relationship Diagram (Simplified)
profiles
id, email, role
staff_profiles
profile_id (FK)
qualifications
staff_profile_id (FK)
clients
id, ndis_number
goals
client_id (FK)
client_funding
client_id (FK)
shifts
client_id, staff_id (FK)
progress_notes
client_id, shift_id (FK)
invoices
client_id (FK)
incidents
client_id, reported_by (FK)
Fig 3.3 - Simplified entity relationship diagram showing FK connections between the 10 core tables.

3.5 Authentication Flow

NDSS CRM uses Supabase / Oracle Auth for all authentication operations. The authentication flow is protected by Next.js middleware that intercepts every request and verifies the user session before allowing access to protected routes. For a complete guide to authentication features and user management, refer to Chapter 4.

3.5.1 Authentication Sequence

The following describes the step-by-step authentication flow when a user accesses NDSS CRM:

  1. Request Interception: Next.js middleware (src/middleware.ts) intercepts all incoming HTTP requests.
  2. Session Check: The middleware calls supabase.auth.getSession() to check for a valid session cookie.
  3. Unauthenticated Redirect: If no valid session exists and the requested route is protected, the user is redirected to /login.
  4. Credential Submission: The user submits their email and password via the login form.
  5. Supabase / Oracle Verification: Supabase / Oracle Auth verifies the credentials against the auth.users table and returns a JWT access token and refresh token.
  6. Session Cookie: The tokens are stored in an HTTP-only, secure, SameSite cookie managed by the Supabase / Oracle SSR helper library.
  7. Profile Fetch: The application fetches the user profile from the profiles table to determine the user role.
  8. Role-Based Redirect: The user is redirected to their role-specific dashboard (e.g., /dashboard for admins, /portal for clients).

3.5.2 Middleware Protection

The middleware configuration defines which routes are public (accessible without authentication) and which are protected:

// src/middleware.ts - Route Protection Configuration

const publicRoutes = [
  '/login',
  '/register',
  '/forgot-password',
  '/reset-password',
  '/verify-email',
  '/terms',
  '/privacy',
  '/demo'
];

const protectedRouteGroups = [
  '/(dashboard)',    // All internal platform pages
  '/(client-portal)' // Client self-service portal
];

// Middleware function
export async function middleware(request: NextRequest) {
  const session = await getSession(request);

  if (!session && isProtectedRoute(request.pathname)) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  if (session && isPublicOnlyRoute(request.pathname)) {
    return NextResponse.redirect(new URL('/dashboard', request.url));
  }

  return NextResponse.next();
}

3.5.3 Session Management

Sessions are managed using Supabase / Oracle's cookie-based approach with the @supabase/ssr library:

Parameter Value Description
Session Duration1 hour (access token)Access tokens expire after 1 hour and are automatically refreshed
Refresh Token Lifetime7 daysRefresh tokens allow silent token renewal for up to 7 days
Cookie TypeHTTP-only, Secure, SameSite=LaxPrevents JavaScript access to session tokens and mitigates CSRF
Auto RefreshEnabledThe Supabase / Oracle client automatically refreshes tokens before expiry
Idle Timeout30 minutesUsers are logged out after 30 minutes of inactivity

3.6 State Management

NDSS CRM employs a deliberate separation between server state (data from the database) and client state (UI state, form state, user preferences). This separation prevents the common pitfalls of mixing remote data with local UI state and ensures predictable data flow throughout the application.

3.6.1 React Query (TanStack Query) - Server State

React Query manages all data fetching, caching, synchronisation, and background updates for data originating from the Supabase / Oracle database or microservice APIs:

  • Automatic Caching: Query results are cached by unique keys and served from cache on subsequent requests until the cache expires or is invalidated.
  • Background Refetching: Stale data is automatically refetched in the background when the user refocuses the browser tab or reconnects to the network.
  • Optimistic Updates: Mutations (create, update, delete) immediately update the UI cache before the server confirms the change, providing instant feedback.
  • Query Invalidation: After a successful mutation, related queries are invalidated to trigger a refetch with the latest data.
// Example: Fetching client list with React Query
const { data: clients, isLoading, error } = useQuery({
  queryKey: ['clients', { status: 'active', page: 1 }],
  queryFn: () => supabase
    .from('clients')
    .select('*')
    .eq('status', 'active')
    .range(0, 24)
    .order('last_name', { ascending: true }),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

3.6.2 Zustand - Client State

Zustand manages lightweight client-side state that does not originate from the database:

  • Sidebar State: Whether the sidebar is collapsed or expanded, and on mobile, whether it is open or closed.
  • Theme Preferences: Light mode, dark mode, or system preference.
  • Filter Selections: Active filter chips, search queries, and sort preferences for data tables.
  • Modal State: Which dialogs are currently open and their associated context data.
  • Notification Queue: Toast notifications waiting to be displayed.
// Example: Zustand store for sidebar state
import { create } from 'zustand';

interface SidebarStore {
  isCollapsed: boolean;
  isMobileOpen: boolean;
  toggleCollapse: () => void;
  setMobileOpen: (open: boolean) => void;
}

export const useSidebarStore = create<SidebarStore>((set) => ({
  isCollapsed: false,
  isMobileOpen: false,
  toggleCollapse: () => set((s) => ({ isCollapsed: !s.isCollapsed })),
  setMobileOpen: (open) => set({ isMobileOpen: open }),
}));

3.6.3 React Hook Form - Form State

All forms across the platform use React Hook Form with Zod schema validation:

// Example: Client creation form with Zod validation
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const clientSchema = z.object({
  ndis_number: z.string().regex(/^\d{9}$/, 'Must be 9 digits'),
  first_name: z.string().min(1, 'Required'),
  last_name: z.string().min(1, 'Required'),
  date_of_birth: z.string().min(1, 'Required'),
  status: z.enum(['active', 'inactive', 'waitlist']),
  email: z.string().email().optional().or(z.literal('')),
  phone: z.string().optional(),
});

type ClientFormData = z.infer<typeof clientSchema>;

const { register, handleSubmit, formState: { errors } } = useForm<ClientFormData>({
  resolver: zodResolver(clientSchema),
});

3.7 API Layer

The NDSS CRM API layer consists of three complementary interfaces: Supabase / Oracle Client SDK for direct database operations, Next.js API Routes for custom server-side logic, and Supabase / Oracle Realtime for live data subscriptions.

3.7.1 RESTful Endpoints (Next.js API Routes)

Custom API routes are defined in src/app/api/ and handle operations that require server-side processing beyond simple CRUD:

Method Endpoint Description
POST/api/claims/generateTriggers the Python claim processor to generate NDIS claims for a billing period
POST/api/reports/buildTriggers the Python report engine to compile a specified report
POST/api/payroll/syncTriggers the PHP payroll bridge to synchronise timesheet data
GET/api/dashboard/statsReturns aggregated dashboard statistics for the authenticated user role
POST/api/shifts/bulk-createCreates multiple shifts at once (used for recurring shift generation)
POST/api/documents/convertTriggers the PHP document converter for format transformations
GET/api/ndis/price-guideReturns NDIS price guide data for a given support category and region
POST/api/notifications/sendDispatches email/SMS notifications via the Python notification worker

3.7.2 Supabase / Oracle Client SDK

The majority of CRUD operations are performed directly via the Supabase / Oracle JavaScript client, which translates method calls into PostgREST API requests:

// Direct Supabase / Oracle client operations
// SELECT
const { data } = await supabase.from('clients').select('*').eq('status', 'active');

// INSERT
const { data } = await supabase.from('clients').insert({ ... }).select().single();

// UPDATE
const { data } = await supabase.from('clients').update({ status: 'inactive' }).eq('id', clientId);

// DELETE
const { error } = await supabase.from('incidents').delete().eq('id', incidentId);

3.7.3 Supabase / Oracle Realtime Subscriptions

Real-time subscriptions are used for features that require live data updates without page refresh:

Subscription Table Use Case
Shift UpdatesshiftsRostering page updates in real-time when shifts are created, assigned, or status-changed by other users
New MessagesmessagesMessaging module receives new messages instantly without polling
Incident AlertsincidentsCompliance officers receive immediate alerts when critical incidents are reported
Dashboard CountersMultiple tablesDashboard stat cards update in real-time as data changes across the system

3.8 Component Library

NDSS CRM uses a custom component library built on top of Radix UI unstyled primitives, styled with TailwindCSS utility classes. This approach provides accessible, keyboard-navigable UI components with full design control.

3.8.1 Radix UI Primitives

The following Radix UI primitives are used across the platform:

Primitive Package Usage in NDSS CRM
Dialog@radix-ui/react-dialogModal dialogs for forms (create client, create shift, edit profile), confirmation prompts, and detail views
DropdownMenu@radix-ui/react-dropdown-menuAction menus on table rows, user profile dropdown, bulk action menus
Select@radix-ui/react-selectAll dropdown selects (role selector, status filter, service type picker)
Tabs@radix-ui/react-tabsTab navigation on detail pages (client profile tabs, staff profile tabs)
Toast@radix-ui/react-toastSuccess/error/info toast notifications
Tooltip@radix-ui/react-tooltipContextual tooltips on icons, badges, and truncated text
Popover@radix-ui/react-popoverDate pickers, filter panels, quick-view popovers
Switch@radix-ui/react-switchToggle switches for boolean settings (active/inactive, enable/disable)
Checkbox@radix-ui/react-checkboxMulti-select checkboxes in tables, form checkboxes
Avatar@radix-ui/react-avatarUser avatars with initials fallback throughout the platform

3.8.2 Custom Components

The src/components/ directory contains custom components built on top of Radix primitives:

Component File Path Description
DataTablecomponents/ui/data-table.tsxReusable sortable, filterable, paginated table with column visibility controls
StatCardcomponents/ui/stat-card.tsxDashboard metric card with icon, value, label, and trend indicator
FilterBarcomponents/ui/filter-bar.tsxSearch input with filter chip selectors for data tables
FormFieldcomponents/ui/form-field.tsxWrapper for React Hook Form fields with label, input, and error display
StatusBadgecomponents/ui/status-badge.tsxColoured badge displaying entity status (active, inactive, pending, etc.)
Sidebarcomponents/layout/sidebar.tsxCollapsible navigation sidebar with role-based menu items
Topbarcomponents/layout/topbar.tsxTop navigation bar with breadcrumb, search, notifications, and user menu
EmptyStatecomponents/ui/empty-state.tsxPlaceholder displayed when a list or table has no records
ConfirmDialogcomponents/ui/confirm-dialog.tsxReusable confirmation modal for destructive actions
FileUploadercomponents/ui/file-uploader.tsxDrag-and-drop file upload component with progress indicator

3.8.3 TailwindCSS Configuration

TailwindCSS is configured with a custom design system that includes NDSS CRM brand colours, spacing scale, and responsive breakpoints:

// tailwind.config.ts - Key customisations
export default {
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f0f7ff',
          100: '#e0effe',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
          900: '#1e3a5f',
        },
        success: '#22c55e',
        warning: '#f59e0b',
        danger: '#ef4444',
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
};

3.9 Directory Structure

The following is the complete project directory tree for the NDSS CRM codebase, showing all major directories and their purposes:

ndss-crm/
+-- src/
|   +-- app/
|   |   +-- (auth)/
|   |   |   +-- login/page.tsx
|   |   |   +-- register/page.tsx
|   |   |   +-- forgot-password/page.tsx
|   |   |   +-- reset-password/page.tsx
|   |   |   +-- verify-email/page.tsx
|   |   |   +-- layout.tsx
|   |   +-- (dashboard)/
|   |   |   +-- dashboard/page.tsx
|   |   |   +-- clients/
|   |   |   |   +-- page.tsx
|   |   |   |   +-- [id]/page.tsx
|   |   |   |   +-- new/page.tsx
|   |   |   +-- staff/
|   |   |   |   +-- page.tsx
|   |   |   |   +-- [id]/page.tsx
|   |   |   +-- rostering/
|   |   |   |   +-- page.tsx
|   |   |   |   +-- calendar/page.tsx
|   |   |   +-- finance/
|   |   |   |   +-- page.tsx
|   |   |   |   +-- invoices/page.tsx
|   |   |   +-- compliance/
|   |   |   |   +-- page.tsx
|   |   |   |   +-- incidents/page.tsx
|   |   |   +-- intake/page.tsx
|   |   |   +-- clinical/page.tsx
|   |   |   +-- learning/page.tsx
|   |   |   +-- messaging/page.tsx
|   |   |   +-- reports/page.tsx
|   |   |   +-- admin/
|   |   |   |   +-- page.tsx
|   |   |   |   +-- settings/page.tsx
|   |   |   |   +-- users/page.tsx
|   |   |   +-- sil/page.tsx
|   |   |   +-- layout.tsx
|   |   +-- (client-portal)/
|   |   |   +-- portal/page.tsx
|   |   |   +-- my-services/page.tsx
|   |   |   +-- my-plan/page.tsx
|   |   |   +-- layout.tsx
|   |   +-- (public)/
|   |   |   +-- page.tsx
|   |   |   +-- terms/page.tsx
|   |   |   +-- privacy/page.tsx
|   |   +-- api/
|   |   |   +-- claims/route.ts
|   |   |   +-- reports/route.ts
|   |   |   +-- payroll/route.ts
|   |   |   +-- dashboard/route.ts
|   |   |   +-- shifts/route.ts
|   |   |   +-- documents/route.ts
|   |   |   +-- notifications/route.ts
|   |   +-- globals.css
|   |   +-- layout.tsx
|   +-- components/
|   |   +-- layout/
|   |   |   +-- sidebar.tsx
|   |   |   +-- topbar.tsx
|   |   |   +-- breadcrumb.tsx
|   |   |   +-- footer.tsx
|   |   +-- ui/
|   |   |   +-- button.tsx
|   |   |   +-- input.tsx
|   |   |   +-- select.tsx
|   |   |   +-- data-table.tsx
|   |   |   +-- stat-card.tsx
|   |   |   +-- filter-bar.tsx
|   |   |   +-- status-badge.tsx
|   |   |   +-- form-field.tsx
|   |   |   +-- confirm-dialog.tsx
|   |   |   +-- file-uploader.tsx
|   |   |   +-- empty-state.tsx
|   |   |   +-- toast.tsx
|   |   +-- clients/
|   |   |   +-- client-form.tsx
|   |   |   +-- client-table.tsx
|   |   |   +-- client-profile.tsx
|   |   +-- staff/
|   |   |   +-- staff-form.tsx
|   |   |   +-- staff-table.tsx
|   |   +-- rostering/
|   |   |   +-- shift-form.tsx
|   |   |   +-- shift-calendar.tsx
|   |   |   +-- shift-table.tsx
|   |   +-- finance/
|   |   |   +-- invoice-form.tsx
|   |   |   +-- invoice-table.tsx
|   +-- lib/
|   |   +-- supabase/
|   |   |   +-- client.ts
|   |   |   +-- server.ts
|   |   |   +-- middleware.ts
|   |   +-- utils.ts
|   |   +-- constants.ts
|   |   +-- roles.ts
|   +-- providers/
|   |   +-- query-provider.tsx
|   |   +-- theme-provider.tsx
|   |   +-- auth-provider.tsx
|   +-- types/
|   |   +-- database.ts
|   |   +-- roles.ts
|   |   +-- forms.ts
|   +-- middleware.ts
+-- supabase/
|   +-- migrations/
|   |   +-- 001_create_profiles.sql
|   |   +-- 002_create_clients.sql
|   |   +-- 003_create_goals.sql
|   |   +-- 004_create_shifts.sql
|   |   +-- 005_create_invoices.sql
|   |   +-- 006_create_incidents.sql
|   +-- seed.sql
|   +-- config.toml
+-- services/
|   +-- python/
|   |   +-- report-engine/
|   |   +-- claim-processor/
|   |   +-- data-pipeline/
|   |   +-- notification-worker/
|   +-- php/
|       +-- payroll-bridge/
|       +-- legacy-api/
|       +-- doc-converter/
+-- public/
|   +-- ndss-crm-logo.svg
|   +-- favicon.ico
+-- package.json
+-- tsconfig.json
+-- tailwind.config.ts
+-- next.config.ts
+-- .env.local

3.10 Design Patterns

NDSS CRM employs several design patterns consistently across the codebase to ensure maintainability, reusability, and developer productivity.

3.10.1 Composable Components

Components are designed to be composable rather than monolithic. Instead of building a single large "ClientPage" component, the page is composed of smaller, focused components that can be reused independently:

// Composable page structure example
export default function ClientsPage() {
  return (
    <PageShell title="Clients" subtitle="Manage NDIS participants">
      <PageHeader>
        <FilterBar
          searchPlaceholder="Search clients..."
          filters={[
            { key: 'status', label: 'Status', options: statusOptions },
            { key: 'coordinator', label: 'Coordinator', options: staffOptions },
          ]}
        />
        <Button onClick={openCreateModal}>+ New Client</Button>
      </PageHeader>

      <DataTable
        columns={clientColumns}
        data={clients}
        pagination={pagination}
        onRowClick={navigateToClient}
      />

      <CreateClientDialog open={isCreateOpen} onClose={closeCreateModal} />
    </PageShell>
  );
}

3.10.2 Role-Based Rendering

A custom <RoleGate> component conditionally renders UI elements based on the current user role. This pattern is used throughout the platform to show or hide features, navigation items, action buttons, and data columns:

// Role-based rendering pattern
import { RoleGate } from '@/components/auth/role-gate';

// Only visible to admin roles
<RoleGate allowedRoles={['master_admin', 'administrator']}>
  <Button variant="danger" onClick={deleteClient}>
    Delete Client
  </Button>
</RoleGate>

// Visible to coordinators and above
<RoleGate allowedRoles={['master_admin', 'administrator', 'service_coordinator']}>
  <Tabs.Tab value="funding">Funding</Tabs.Tab>
</RoleGate>

// Visible to everyone except clients
<RoleGate excludeRoles={['client', 'family_member']}>
  <DataTable.Column header="Internal Notes" />
</RoleGate>

3.10.3 Server-First Data Fetching

Data fetching follows a "server-first" pattern where React Server Components fetch data on the server whenever possible, reducing client-side JavaScript and eliminating loading spinners for initial page loads:

// Server Component - data fetched on server, no client JS needed
export default async function ClientDetailPage({ params }: { params: { id: string } }) {
  const supabase = createServerClient();
  const { data: client } = await supabase
    .from('clients')
    .select('*, goals(*), client_funding(*)')
    .eq('id', params.id)
    .single();

  return <ClientProfile client={client} />;
}

3.10.4 Error Boundary Pattern

Every route segment includes an error.tsx file that catches runtime errors and provides a user-friendly error recovery experience:

// src/app/(dashboard)/clients/error.tsx
'use client';

export default function ClientsError({ error, reset }: {
  error: Error;
  reset: () => void;
}) {
  return (
    <ErrorState
      title="Failed to load clients"
      message={error.message}
      onRetry={reset}
    />
  );
}

3.10.5 Type Safety Throughout

TypeScript is enforced across the entire codebase with strict mode enabled. Database types are auto-generated from the Supabase / Oracle schema using supabase gen types typescript, ensuring that all database queries and responses are type-checked at compile time:

// Auto-generated database types
export type Database = {
  public: {
    Tables: {
      clients: {
        Row: {
          id: string;
          ndis_number: string;
          first_name: string;
          last_name: string;
          date_of_birth: string;
          status: 'active' | 'inactive' | 'waitlist' | 'exited';
          // ... all other fields
        };
        Insert: { /* ... */ };
        Update: { /* ... */ };
      };
      // ... all other tables
    };
  };
};
Further Reading

For API endpoint details, see Chapter 19: API & Integration. For security policies and data protection measures, see Chapter 20: Security & Data. For troubleshooting build and deployment issues, see Chapter 21: Troubleshooting & FAQ.