Architecture

Last updated on Jun 12, 2026

Understanding the system design and component structure of CHRIS.

Overview

CHRIS follows a modern JAMstack architecture with React on the frontend and Supabase providing backend services.

┌─────────────────────────────────────────────────────────┐
│                     Frontend (React)                     │
├─────────────────────────────────────────────────────────┤
│  Pages  │  Components  │  Hooks  │  Contexts  │  Utils  │
└─────────────────────────────────────────────────────────┘
                           │
                           │ HTTPS
                           ▼
┌─────────────────────────────────────────────────────────┐
│                   Supabase Platform                      │
├──────────────┬──────────────┬──────────────┬────────────┤
│  PostgreSQL  │     Auth     │    Storage   │  Functions │
│   Database   │   (GoTrue)   │    (S3)      │   (Deno)   │
└──────────────┴──────────────┴──────────────┴────────────┘

Frontend Architecture

Technology Stack

Layer Technology Purpose
Framework React 18 UI rendering
Language TypeScript Type safety
Build Vite Fast development/builds
UI Components shadcn/ui Pre-built components
Styling Tailwind CSS Utility-first CSS
State TanStack Query Server state management
Routing React Router v6 Client-side routing
Forms React Hook Form + Zod Form handling/validation

Directory Structure

src/
├── components/           # Reusable UI components
│   ├── Contracts/       # Contract-related components
│   ├── Dashboard/       # Dashboard widgets
│   ├── Layout/          # App layout (nav, sidebar)
│   ├── Profile/         # Profile-related components
│   └── ui/              # shadcn/ui base components
├── contexts/            # React contexts
│   └── LanguageContext.tsx
├── hooks/               # Custom React hooks
│   ├── useAuth.tsx
│   ├── useTeamLeadership.tsx
│   └── use-toast.ts
├── integrations/        # External service integrations
│   └── supabase/
│       ├── client.ts    # Supabase client instance
│       └── types.ts     # Auto-generated types
├── pages/               # Route components
│   ├── Auth.tsx
│   ├── Dashboard.tsx
│   ├── Employees.tsx
│   ├── LeaveRequests.tsx
│   ├── Profile.tsx
│   ├── Settings.tsx
│   └── Teams.tsx
└── utils/               # Utility functions
    └── emailNotifications.ts

Component Design

Page Components

Pages are route-level components that:

  • Handle data fetching with TanStack Query
  • Compose smaller components
  • Manage page-level state
// Example: LeaveRequests.tsx
function LeaveRequests() {
  const { data: requests, isLoading } = useQuery({
    queryKey: ['leave-requests'],
    queryFn: fetchLeaveRequests
  });

  if (isLoading) return <LoadingSpinner />;

  return (
    <Layout>
      <LeaveRequestList requests={requests} />
    </Layout>
  );
}

Feature Components

Feature-specific components organized by domain:

components/
├── Contracts/
│   ├── ContractForm.tsx
│   ├── ContractList.tsx
│   └── ContractCard.tsx
├── Dashboard/
│   ├── LeaveBalanceCard.tsx
│   ├── PendingRequestsCard.tsx
│   └── TeamCalendar.tsx

UI Components

Base components from shadcn/ui, customized for CHRIS:

components/ui/
├── button.tsx
├── card.tsx
├── dialog.tsx
├── form.tsx
├── input.tsx
├── select.tsx
└── table.tsx

State Management

Server State (TanStack Query)

All server data is managed via TanStack Query:

// Fetching data
const { data, isLoading, error } = useQuery({
  queryKey: ['profiles'],
  queryFn: () => supabase.from('profiles').select('*')
});

// Mutations
const mutation = useMutation({
  mutationFn: (newRequest) =>
    supabase.from('leave_requests').insert(newRequest),
  onSuccess: () => {
    queryClient.invalidateQueries(['leave-requests']);
  }
});

Client State (Contexts)

Global client state via React Context:

  • LanguageContext: Current language, translations
  • AuthContext: User session, role
// Using language context
const { t, language, setLanguage } = useLanguage();

// Using auth context
const { user, isAdmin, isHR } = useAuth();

Backend Architecture

Supabase Services

Service Purpose
PostgreSQL Relational database
Auth (GoTrue) User authentication
Storage File storage (avatars, CVs)
Edge Functions Server-side logic
Realtime Live subscriptions (optional)

Database Design

The database follows these principles:

  1. Normalized structure - Minimal data duplication
  2. UUID primary keys - Globally unique identifiers
  3. Timestamps - created_at, updated_at on all tables
  4. Soft deletes - Status fields instead of deletion

Row Level Security (RLS)

All tables have RLS policies enforcing access control:

-- Example: Users can only see their own leave requests
CREATE POLICY "Users can view own requests"
ON leave_requests FOR SELECT
USING (auth.uid() = user_id);

-- HR can see all requests
CREATE POLICY "HR can view all requests"
ON leave_requests FOR SELECT
USING (
  EXISTS (
    SELECT 1 FROM user_roles
    WHERE user_id = auth.uid()
    AND role IN ('admin', 'hr_manager')
  )
);

Data Flow

Read Operations

User Action → React Component → TanStack Query → Supabase Client
    ↓
Database ← RLS Policy Check ← PostgREST ← Supabase
    ↓
Response → Cache Update → Re-render → UI Update

Write Operations

User Action → Form Submit → Validation (Zod) → Mutation
    ↓
Supabase Client → Edge Function (if needed) → Database
    ↓
Success → Cache Invalidation → Re-fetch → UI Update

Email Notifications

User Action → Frontend → Edge Function → SMTP Server → Recipient
               │
               └─→ Database (company_settings for SMTP config)

Key Design Decisions

Why Supabase?

  • PostgreSQL: Powerful relational database with JSON support
  • Built-in Auth: Secure authentication without custom code
  • Auto-generated API: PostgREST provides instant REST API
  • Edge Functions: Server-side logic when needed
  • Real-time: Optional live updates

Why shadcn/ui?

  • Customizable: Copy components into your project
  • Accessible: Built on Radix UI primitives
  • Tailwind: Integrates with existing styling
  • No lock-in: You own the component code

Why TanStack Query?

  • Caching: Automatic request deduplication
  • Background updates: Fresh data without blocking UI
  • Optimistic updates: Instant UI feedback
  • DevTools: Easy debugging

Deployment Architecture

Production Setup

┌─────────────────┐     ┌─────────────────┐
│   CDN (Vercel)  │     │    Supabase     │
│                 │     │   Platform      │
│  Static Assets  │────▶│  - Database     │
│  React App      │     │  - Auth         │
│                 │     │  - Functions    │
└─────────────────┘     └─────────────────┘

Scaling Considerations

Component Scaling Strategy
Frontend CDN edge caching
Database Supabase handles scaling
Auth Supabase handles scaling
Storage S3-compatible, unlimited
Functions Auto-scaling Deno Deploy

Security Architecture

Authentication Flow

  1. User submits credentials
  2. Supabase Auth validates
  3. JWT token issued
  4. Token sent with all requests
  5. RLS policies enforce access

Data Protection

  • Transport: HTTPS everywhere
  • At rest: Encrypted database storage
  • Secrets: Stored in Supabase vault
  • Passwords: bcrypt hashed