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:
- Normalized structure - Minimal data duplication
- UUID primary keys - Globally unique identifiers
- Timestamps - created_at, updated_at on all tables
- 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
- User submits credentials
- Supabase Auth validates
- JWT token issued
- Token sent with all requests
- RLS policies enforce access
Data Protection
- Transport: HTTPS everywhere
- At rest: Encrypted database storage
- Secrets: Stored in Supabase vault
- Passwords: bcrypt hashed