# Dashboard Reference

## Stack

- React 19 + Vite 6 + Tailwind CSS 4
- React Router 7 (SPA routing)
- Socket.IO Client (real-time updates)
- Light theme (white background, dark text, vibrant accents)

## Vite Config

```javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
  server: {
    proxy: { '/api': 'http://localhost:3002', '/socket.io': { target: 'http://localhost:3002', ws: true } }
  }
})
```

## App.jsx Pattern

```jsx
import { BrowserRouter, Routes, Route, NavLink } from 'react-router-dom'
import { io } from 'socket.io-client'
import { useEffect, useState } from 'react'

const API = import.meta.env.DEV ? 'http://localhost:3002' : ''
const socket = io(API)

export default function App() {
  const [status, setStatus] = useState({ status: 'disconnected' })

  useEffect(() => {
    socket.on('status', setStatus)
    socket.on('ready', setStatus)
    socket.on('disconnected', () => setStatus({ status: 'disconnected' }))
    return () => { socket.off('status'); socket.off('ready'); socket.off('disconnected') }
  }, [])

  return (
    <BrowserRouter>
      <div className="flex h-screen bg-white">
        <Sidebar status={status} />
        <main className="flex-1 overflow-auto p-6">
          <Routes>
            <Route path="/" element={<DashboardPage />} />
            <Route path="/connect" element={<ConnectPage status={status} />} />
            <Route path="/conversations" element={<ConversationsPage />} />
            <Route path="/conversations/:phone" element={<ConversationDetail />} />
            <Route path="/settings" element={<SettingsPage />} />
          </Routes>
        </main>
      </div>
    </BrowserRouter>
  )
}
```

## Essential Pages

### ConnectPage (QR Scanner)
- Listen to `socket.on('qr', setQrCode)`
- Show QR via: `https://api.qrserver.com/v1/create-qr-code/?size=280x280&data=${encodeURIComponent(qr)}`
- Show green status when connected with phone number
- "Disconnect" button → `POST /api/logout`

### DashboardPage (Stats + Charts)
- Fetch `GET /api/stats` on mount
- Fetch `GET /api/stats/daily?days=14` for chart
- Listen to `socket.on('generation_complete', reloadStats)`
- Stat cards: Total Users, Today's Tasks, Total Results, Errors
- Bar chart: results per day (last 14 days)

### ConversationsPage (User List)
- Fetch `GET /api/conversations?page=1&limit=30`
- Table: Name, Phone, API Key status, State (colored badge), Last Activity
- Click row → navigate to `/conversations/:phone`
- Listen to `socket.on('state_change', reload)`

### ConversationDetail (User History)
- Fetch `GET /api/conversations/:phone`
- Session info card (name, phone, state, created)
- Task history list with status badges
- Result badges per task (sent/failed)

### SettingsPage (Admin Config)
- Fetch `GET /api/settings` on mount
- Form sections: API Key, Bot toggle, Limits, Messages
- Save → `PUT /api/settings` with changed fields
- Show success feedback with auto-hide

## Socket.IO Events to Listen

```javascript
socket.on('qr', (qr) => ...)                // QR code data string
socket.on('status', (s) => ...)              // { status, phone? }
socket.on('ready', (s) => ...)               // { status: 'connected', phone }
socket.on('disconnected', () => ...)         // WhatsApp lost
socket.on('state_change', (d) => ...)        // { phone, state }
socket.on('generation_complete', (d) => ...) // { phone, count }
```

## Theme: Light Mode (mandatory)

```
Background: white / slate-50
Text: slate-800 / slate-900
Secondary text: slate-500
Cards: white with slate-200 border
Accent: amber-500 / emerald-500 (or project-specific)
Status badges: emerald (done), amber (processing), red (error), gray (idle)
```
