A full-stack analytics dashboard for Illinois Business Consulting (IBC) that visualizes consultant demographics, project staffing, semester trends, and staff transitions. Built with React, Express, PostgreSQL, and Microsoft Azure SSO.
- Overview
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- How Authentication Works
- API Reference
- Database Schema
- Frontend Pages & Components
- Data Flow
- NPM Scripts
The platform has two distinct parts:
| Part | Description | Location |
|---|---|---|
| Portal | Login screen + app launcher (SSO-gated) | platform/ |
| Data Dashboard | Full React SPA with charts and filters | src/ |
Users log in via the portal with their UIUC Microsoft account, get redirected to the portal home (platform/index.html) which links out to the Data Visualization dashboard, the Paul Dashboard, and the Staffing Tool (SD/SM roles only).
| Tool | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| Vite | 7.3 | Build tool & dev server |
| TypeScript | 5.9 | Type safety |
| Tailwind CSS | 4.1 | Utility styling |
| Ant Design | 6.3 | UI component library |
| Recharts | 3.7 | Charts & visualizations |
| React Router | 7.13 | Client-side routing |
| TanStack Query | 5.90 | Data fetching & caching |
| Axios | 1.13 | HTTP client |
| Lucide React | 0.564 | Icons |
| Tool | Version | Purpose |
|---|---|---|
| Node.js + Express | 5.2 | HTTP server |
| TypeScript | 5.9 | Type safety |
| PostgreSQL (pg) | 8 | Database client |
| Azure MSAL Node | 5.1 | Microsoft SSO |
| JSON Web Tokens | 9.0 | Session tokens |
| nodemon | 3.1 | Dev auto-restart |
- Database: PostgreSQL on GCP Cloud SQL (
34.60.188.54) - Auth Provider: Microsoft Azure AD (UIUC tenant)
ibc-data-visualization/
│
├── platform/ # Static portal (login + app launcher)
│ ├── login.html # Microsoft SSO login page
│ └── index.html # Post-login portal with app links
│
├── src/ # React frontend (data dashboard)
│ ├── main.tsx # App entry point
│ ├── App.tsx # Router + providers setup
│ ├── index.css # Global styles
│ │
│ ├── pages/
│ │ ├── Dashboard.tsx # Main charts & KPI dashboard
│ │ ├── Trends.tsx # Semester-over-semester trends
│ │ ├── Consultants.tsx # Consultant directory (card/table)
│ │ └── Settings.tsx # Theme settings
│ │
│ ├── components/
│ │ ├── dashboard/
│ │ │ └── Charts.tsx # All chart components
│ │ ├── trends/
│ │ │ ├── TrendCards.tsx # KPI change cards
│ │ │ ├── TrendCharts.tsx # Trend line/area charts
│ │ │ └── PromotionsDropsCards.tsx # Promotions, drops, deferrals
│ │ ├── filters/
│ │ │ └── FilterBar.tsx # Semester & project filter dropdowns
│ │ └── layout/
│ │ ├── MainLayout.tsx # Sidebar + main content wrapper
│ │ └── Sidebar.tsx # Navigation sidebar
│ │
│ ├── hooks/
│ │ └── useDashboardData.ts # React Query hooks for all API calls
│ │
│ ├── services/
│ │ ├── api.ts # Real API client (axios)
│ │ └── mockApi.ts # Mock data for development/testing
│ │
│ ├── types/
│ │ └── index.ts # TypeScript interfaces & types
│ │
│ └── context/
│ └── ThemeContext.tsx # Dark/light mode provider
│
├── server/ # Express backend
│ ├── src/
│ │ ├── index.ts # Server entry, route mounting, static serving
│ │ ├── routes/
│ │ │ ├── api.ts # All data API endpoints (~900 lines of SQL)
│ │ │ └── auth.ts # Azure SSO + JWT auth endpoints
│ │ └── config/
│ │ └── db.ts # PostgreSQL connection pool
│ ├── .env # Server environment variables (do not commit)
│ ├── package.json
│ └── tsconfig.json
│
├── index.html # React app HTML entry
├── vite.config.ts # Vite build configuration
├── package.json # Frontend dependencies & scripts
├── tsconfig.json
└── .env # Frontend environment variables
- Node.js 18+
- npm
- Access to the GCP Cloud SQL database (VPN or allowlisted IP may be required)
# Frontend
npm install
# Backend
cd server && npm installFill in the env files — see Environment Variables below for what each value does.
Open two terminals:
# Terminal 1: Backend (http://localhost:3000)
cd server
npm run dev
# Terminal 2: Frontend (http://localhost:5173)
npm run dev- Portal (login):
http://localhost:3000/login.html— served by the Express server - Data Dashboard:
http://localhost:5173— served by Vite dev server
The portal must be opened through the Express server (
localhost:3000) so that the/auth/loginand/auth/redirectSSO routes work correctly. Openinglogin.htmldirectly as a local file will break all API calls.
VITE_API_URL=http://localhost:3000/api# PostgreSQL (GCP Cloud SQL)
DB_USER=postgres
DB_PASSWORD=your_password
DB_HOST=34.60.188.54
DB_NAME=ibc
DB_PORT=5432
DB_SSL=true
# Microsoft Azure SSO
AZURE_CLIENT_ID=your_azure_client_id
AZURE_TENANT_ID=your_azure_tenant_id # UIUC tenant
AZURE_CLIENT_SECRET=your_azure_client_secret
REDIRECT_URI=http://localhost:3000/auth/redirect
# JWT
JWT_SECRET=your_256bit_hex_secret
# Optional
PORT=3000The platform uses Microsoft Azure SSO (UIUC tenant) with JWT session tokens.
1. User visits http://localhost:3000/login.html
2. Clicks "Continue with UIUC Microsoft"
3. GET /auth/login → server generates Azure auth URL via MSAL
4. Browser redirected to Microsoft login (UIUC account)
5. Microsoft redirects back to GET /auth/redirect?code=...
6. Server exchanges auth code for access token
7. Extracts email from token
8. Queries: SELECT * FROM users WHERE email = ?
- Not found → redirect to login.html?error=unauthorized
- Found → sign JWT (8hr expiry) with { email, name, role }
9. Redirect to platform/index.html?token=...
10. index.html stores JWT in localStorage, parses role
11. Role-based UI: SD/SM roles see the Staffing Tool link
{
"email": "user@illinois.edu",
"name": "First Last",
"role": "SC",
"exp": 1234567890
}| Role | Description | Staffing Tool Access |
|---|---|---|
| NC | New Consultant | No |
| EC | Experienced Consultant | No |
| SC | Senior Consultant | No |
| PM | Project Manager | No |
| SM | Senior Manager | Yes |
| SD | Senior Director | Yes |
Base URL: http://localhost:3000/api
Returns all available semesters.
Response:
[{ "semester_id": "S25", "display_name": "Spring 2025" }]Returns all projects, optionally filtered by semester.
Query Params:
| Param | Type | Required | Description |
|---|---|---|---|
semesterId |
string | No | Filter by semester (e.g. S25) |
Returns all consultants with demographics.
Response fields: user_id, email, name, gender, curr_role, year, major, college
Returns aggregated dashboard statistics for a given semester and optional project filter.
Query Params:
| Param | Type | Required | Description |
|---|---|---|---|
semesterId |
string | Yes | Semester to aggregate (e.g. S25) |
projects |
string | No | Comma-separated project IDs (e.g. 1,3,7) |
Response (DashboardStats):
{
"totalConsultants": 85,
"activeConsultants": 72,
"totalProjects": 12,
"roleDistribution": [{ "role": "SC", "count": 30 }],
"genderDistribution": [{ "gender": "Male", "count": 45 }],
"demographicDistribution": [{ "year": "Junior", "count": 25 }],
"majorDistribution": [{ "major": "Computer Science", "count": 20 }],
"collegeDistribution": [{ "college": "Grainger", "count": 35 }],
"projectStaffing": [{ "project_name": "Project A", "count": 8 }]
}Returns semester-over-semester comparison with deltas.
Query Params:
| Param | Type | Required | Description |
|---|---|---|---|
semesters |
string | Yes | Comma-separated semester IDs, min 2 (e.g. F24,S25) |
| Route | Description |
|---|---|
GET /mock/stats/compare |
Mock semester comparison (S24, F24, S25) |
GET /mock/promotions |
Mock role transitions between semesters |
GET /mock/drops |
Mock consultants who resigned or were let go |
GET /mock/deferrals |
Mock consultants who took time off |
| Method | Route | Description |
|---|---|---|
GET |
/auth/login |
Initiates Azure SSO redirect |
GET |
/auth/redirect |
SSO callback — exchanges code for JWT |
POST |
/api/auth/login |
Email-based login check (fallback) |
| Method | Route | Description |
|---|---|---|
GET |
/health |
Checks DB connectivity |
Host: GCP Cloud SQL at 34.60.188.54
Database: ibc
SSL: Required
| Column | Type | Description |
|---|---|---|
user_id |
PK | Unique user identifier |
email |
string | UIUC email (used for SSO match) |
name |
string | Full name |
gender |
string | Gender |
curr_role |
string | Current role (NC, EC, SC, PM, SM, SD) |
| Column | Type | Description |
|---|---|---|
user_id |
FK → users | Links to user record |
year |
string | Academic year (Freshman, Sophomore, Junior, Senior, Master's) |
major |
string | Major name |
college |
string | College (Grainger, Gies, LAS, etc.) |
| Column | Type | Description |
|---|---|---|
project_id |
PK | Unique project identifier |
project_name |
string | Project display name |
project_semester |
string | Semester the project ran (e.g. S25) |
client_name |
string | Client name |
| Column | Type | Description |
|---|---|---|
user_id |
FK → users | Consultant assigned |
project_id |
FK → projects | Project assigned to |
role |
string | Role on this specific project |
- A user can be assigned to multiple projects (many-to-many via
consultant_projects) - A consultant without a project assignment is still included in stats via
curr_role - Semester filtering is done through
projects.project_semester
| Page | Route | Description |
|---|---|---|
| Dashboard | / |
KPI cards + charts filtered by semester & project |
| Trends | /trends |
Semester-over-semester comparison & trend lines |
| Consultants | /consultants |
Searchable/filterable consultant directory |
| Settings | /settings |
Theme toggle |
| Component | Type | Shows |
|---|---|---|
RoleDistributionChart |
Bar chart | NC, EC, SC, PM, SM, SD counts |
GenderChart |
Donut chart | Male, Female, Other, Unknown |
DemographicChart |
Bar chart | Freshman → Master's distribution |
MajorDistributionChart |
Horizontal bar | Major categories (expandable to modal) |
CollegeDistributionChart |
Donut chart | College distribution |
ProjectStaffingChart |
Horizontal bar | Per-project headcount (click to filter) |
| Component | Description |
|---|---|
TrendCard |
KPI with % change vs previous semester |
ConsultantsTrendChart |
Area chart of consultant count over time |
ProjectsTrendChart |
Area chart of project count over time |
GenderTrendChart |
Grouped bar chart of gender over time |
RoleTrendChart |
Grouped bar chart of roles over time |
GradeTrendChart |
Multi-line chart for year levels over time |
PromotionsChart |
Stacked bar of role transitions |
DropsChart |
Donut chart of departures by reason |
DeferralsChart |
Bar chart of deferrals by role |
User selects semester in FilterBar
↓
URL params updated: ?semester=S25
↓
useDashboardStats('S25') hook triggered (React Query)
↓
GET /api/stats?semesterId=S25
↓
Express → PostgreSQL aggregation queries
↓
JSON response: DashboardStats
↓
Charts render with data
↓
User clicks a bar in ProjectStaffingChart
↓
URL updated: ?semester=S25&projects=1,3
↓
Stats refetched with project filter applied
↓
All charts update to show scoped data
React Query caches responses by [queryKey, semesterId, projectIds]. Default stale time: 5 minutes.
npm run dev # Start Vite dev server at http://localhost:5173
npm run build # TypeScript compile + Vite production build → dist/
npm run preview # Preview production build locally
npm run lint # Run ESLintnpm run dev # nodemon watch mode — auto-restarts on file changes
npm run build # Compile TypeScript → dist/
npm start # Run compiled production server (node dist/index.js)