# Performance Investigation Report

**Date:** 2025-01-17
**Status:** Investigation Complete

---

## Executive Summary

The application is slow primarily due to **8 independent API calls on dashboard load**, with **no frontend caching** despite having React Query installed. Two of these calls invoke **OpenAI which blocks rendering**. The main JS bundle is **1.2MB** (too large for mobile-first).

---

## Baseline Measurements

### Bundle Size
| File | Size | Issue? |
|------|------|--------|
| `index-*.js` | **1.2 MB** | Too large - needs code splitting |
| `html2canvas.esm-*.js` | 197 KB | Lazy loadable |
| **Total JS** | **~1.4 MB** | Target: < 500KB for mobile |

### Dashboard API Calls (on single page load)

| # | Component | Endpoint | DB Queries | AI Call? | Blocks Render? |
|---|-----------|----------|------------|----------|----------------|
| 1 | Dashboard.jsx | `/api/dashboard/stats` | 15 parallel | No | Yes |
| 2 | AIStrategyCard | `/api/dashboard/strategy` | 6 | **Yes (OpenAI)** | Yes |
| 3 | PriorityActionCard | `/api/dashboard/priority-action` | 6+ | **Yes (OpenAI)** | Yes |
| 4 | RepliesWaiting | `/api/sms/conversations/prioritized` | 2-3 | No | Yes |
| 5 | SlippingLeads | `/api/dashboard/slipping` | 1 | No | Yes |
| 6 | LevelProgress | `/api/xp/user` | 1 | No | Yes |
| 7 | CoachingTips | `/api/coaching/tips` | 1 | No | Yes |
| 8 | WeeklyMiniCard | `/api/coaching/weekly` | 1 | No | Yes |
| **TOTAL** | | **8 calls** | **33+ queries** | **2 AI calls** | |

### Rate Limiting Analysis
- Dashboard rate limit: **30 requests/minute** per user
- Calls per dashboard load: **8**
- Maximum refreshes before rate limit: **~3-4 per minute**
- **Result:** Users can hit rate limits with normal usage

---

## Root Causes Identified

### 1. Too Many Independent API Calls (CRITICAL)
- **Evidence:** 8 separate fetch() calls on dashboard load
- **Impact:** HIGH - waterfall requests, rate limit hits
- **Each component fetches independently** instead of sharing data

### 2. React Query Installed But Not Used (CRITICAL)
- **Evidence:** `@tanstack/react-query` in package.json, zero imports in codebase
- **Impact:** HIGH - no caching, every page visit re-fetches everything
- **File:** `admin/package.json` line 14

### 3. OpenAI Calls Blocking Initial Render (HIGH)
- **Evidence:**
  - `AIStrategyCard` waits for `/api/dashboard/strategy` (calls OpenAI)
  - `PriorityActionCard` waits for `/api/dashboard/priority-action` (calls OpenAI for script)
- **Impact:** HIGH - adds 1-3 seconds to page load
- **Files:**
  - `src/routes/dashboard.js:887-964` (OpenAI strategy generation)
  - `src/routes/dashboard.js:1481-1516` (OpenAI script generation)

### 4. Redundant Database Queries (MEDIUM)
- **Evidence:** `/stats`, `/strategy`, and `/priority-action` all query similar lead data
- **Impact:** MEDIUM - unnecessary database load
- Same queries for hot leads, callbacks, SMS run 3 times

### 5. Large Bundle Size (MEDIUM)
- **Evidence:** 1.2 MB main bundle
- **Impact:** MEDIUM - slow initial load on mobile
- No code splitting configured
- No lazy loading of routes

### 6. No Data Sharing Between Components (MEDIUM)
- **Evidence:** Dashboard.jsx passes `stats` to TodayStats, but other components fetch their own
- **Impact:** MEDIUM - could reduce API calls by 50%

---

## Recommended Fixes (Priority Order)

### Fix 1: Add React Query Caching (CRITICAL)
**Problem:** Every page load fetches fresh data
**Solution:** Use the already-installed React Query
**Files to change:**
- `admin/src/main.jsx` - Add QueryClientProvider
- All components using fetch - Convert to useQuery

**Implementation:**
```javascript
// admin/src/main.jsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 30 * 1000, // 30 seconds
      gcTime: 5 * 60 * 1000, // 5 minutes
    },
  },
});

// Wrap App with QueryClientProvider
```

**Effort:** 2-3 hours
**Impact:** 50-70% reduction in API calls

---

### Fix 2: Consolidate Dashboard API Into Single Call (HIGH)
**Problem:** 8 separate API calls
**Solution:** Create `/api/dashboard/all` that returns everything

**New endpoint returns:**
```javascript
{
  stats: { ... },      // from /stats
  strategy: { ... },   // from /strategy (without AI - defer)
  priorityLead: { ... },  // from /priority-action (without AI script)
  slipping: { ... },   // from /slipping
  smsReplies: { ... }, // from /sms/prioritized
  xp: { ... },         // from /xp/user
  coaching: { ... },   // from /coaching/tips
  weekly: { ... },     // from /coaching/weekly
}
```

**Effort:** 3-4 hours
**Impact:** Reduce 8 calls to 1 call

---

### Fix 3: Defer AI Content (HIGH)
**Problem:** OpenAI calls block initial render
**Solution:** Show page immediately, load AI content after

**Implementation:**
```javascript
// AIStrategyCard.jsx - Show skeleton immediately
// Fetch strategy in background
// PriorityActionCard.jsx - Show lead info first
// Fetch script opener in background (or on click)
```

**Effort:** 1-2 hours
**Impact:** 1-3 second faster initial render

---

### Fix 4: Add Code Splitting (MEDIUM)
**Problem:** 1.2 MB bundle
**Solution:** Lazy load routes

**Implementation:**
```javascript
// admin/src/App.jsx
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const LeadProfile = lazy(() => import('./pages/LeadProfile'));
const Inbox = lazy(() => import('./pages/Inbox'));
// etc.
```

**Also lazy load:**
- `html2canvas` (only needed for screenshots)
- Heavy components like LiveCopilot

**Effort:** 2-3 hours
**Impact:** 50%+ reduction in initial bundle

---

### Fix 5: Increase Rate Limits (QUICK WIN)
**Problem:** 30/min too tight for 8-call dashboard
**Solution:** Increase to 60/min

**File:** `src/middleware/rateLimiter.js:126-134`
```javascript
const dashboardLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 60, // Increase from 30 to 60
  // ...
});
```

**Effort:** 5 minutes
**Impact:** Prevents rate limit errors

---

### Fix 6: Pre-fetch Dashboard Data on Auth (NICE TO HAVE)
**Problem:** Dashboard always cold on first load
**Solution:** Start fetching dashboard data when user logs in

**Effort:** 1 hour
**Impact:** Perceived instant dashboard load

---

## Quick Wins (< 1 hour each)

1. **Increase dashboard rate limit** to 60/min (5 min)
2. **Add loading skeletons** for all dashboard cards (already done, but ensure consistent)
3. **Defer script generation** in PriorityActionCard - fetch on "Call" click instead (30 min)
4. **Add staleTime to any existing useEffect fetches** as stopgap (30 min)

---

## Implementation Plan

| Priority | Fix | Effort | Impact | Dependencies |
|----------|-----|--------|--------|--------------|
| 1 | Increase rate limit | 5 min | Low | None |
| 2 | Add React Query provider | 1 hr | Medium | None |
| 3 | Convert Dashboard to useQuery | 2 hrs | High | #2 |
| 4 | Defer AI calls | 2 hrs | High | None |
| 5 | Consolidate dashboard API | 4 hrs | High | #3 |
| 6 | Add code splitting | 2 hrs | Medium | None |

**Recommended Order:** 1 → 2 → 4 → 3 → 5 → 6

---

## Architecture Recommendation

### Current Flow (Slow)
```
Dashboard Load
    ├── fetch /stats (15 queries)
    ├── fetch /strategy (6 queries + OpenAI)
    ├── fetch /priority-action (6 queries + OpenAI)
    ├── fetch /slipping (1 query)
    ├── fetch /sms/prioritized (2 queries)
    ├── fetch /xp/user (1 query)
    ├── fetch /coaching/tips (1 query)
    └── fetch /coaching/weekly (1 query)
= 8 API calls, 33+ DB queries, 2 OpenAI calls
```

### Recommended Flow (Fast)
```
Dashboard Load
    └── fetch /dashboard/all (cached via React Query)
        ├── Returns: stats, slipping, sms, xp, coaching, weekly
        └── 1 API call, ~20 DB queries (parallel)

After Initial Render
    ├── fetch /strategy (AI content)
    └── fetch /priority-action (AI script on demand)
```

---

## Monitoring Recommendations

After implementing fixes, track:
1. **Time to First Contentful Paint (FCP)** - Target: < 1.5s
2. **Time to Interactive (TTI)** - Target: < 3s
3. **API response times** - Target: < 500ms for dashboard
4. **Rate limit hits** - Target: 0

---

## Files to Modify

| File | Changes |
|------|---------|
| `admin/src/main.jsx` | Add QueryClientProvider |
| `admin/src/pages/Dashboard.jsx` | Convert to useQuery, pass data to children |
| `admin/src/components/dashboard/*.jsx` | Receive data as props instead of fetching |
| `admin/src/App.jsx` | Add lazy loading |
| `admin/vite.config.js` | Configure code splitting |
| `src/routes/dashboard.js` | Add consolidated endpoint |
| `src/middleware/rateLimiter.js` | Increase limit |

---

## Summary

The app feels slow because:
1. **8 API calls** happen on every dashboard load
2. **No caching** - React Query is installed but unused
3. **AI calls block render** - waiting for OpenAI before showing anything
4. **1.2MB bundle** - no code splitting

**Estimated time to fix:** 8-12 hours for all fixes
**Expected improvement:** 60-80% faster perceived load time
