# EVE Market Calculator — UI Spec

**Version:** 0.1
**Date:** 2026-04-11
**Status:** Design Phase
**Reference:** [vs-industry-frontend](https://github.com/maurerit/vs-industry-frontend)

---

## 1. Tech Stack

Matches vs-industry-frontend:

| Layer | Tech | Version |
|-------|------|---------|
| Framework | React | 19.x |
| Build | Vite | 6.x |
| Language | TypeScript | ~5.8 |
| UI Library | MUI (Material UI) | 7.x |
| Icons | @mui/icons-material | 7.x |
| Routing | react-router-dom | 7.x |
| Charts | echarts + echarts-for-react | 5.x / 3.x |
| Dates | date-fns | 4.x |
| SSO Backend | Koa + koa-router | 3.x / 13.x |
| JWT | jsonwebtoken + jwks-rsa | 9.x / 3.x |

---

## 2. Layout

### 2.1 Shell

```
┌─────────────────────────────────────────────────────────┐
│ AppBar                                        [Avatar ▾]│
├────┬────────────────────────────────────────────────────┤
│ N  │                                                    │
│ A  │                                                    │
│ V  │              Main Content Area                     │
│    │                                                    │
│ R  │                                                    │
│ A  │                                                    │
│ I  │                                                    │
│ L  │                                                    │
│    │                                                    │
│ ☰  │                                                    │
└────┴────────────────────────────────────────────────────┘
```

### 2.2 AppBar (Top)

- **Left:** Hamburger icon (toggles sidebar collapse) + App title "EVE Market Calc"
- **Right (unauthenticated):** "Login with EVE" button (EVE SSO branding)
- **Right (authenticated):** Character portrait (32px circle) + character name → dropdown menu:
  - Settings
  - Divider
  - Logout

### 2.3 Sidebar (Left Nav Rail)

Collapsible left panel using MUI `Drawer` (permanent variant):

- **Expanded:** Icon + label (240px width)
- **Collapsed:** Icon only (64px width), tooltips on hover
- **Toggle:** Hamburger in AppBar OR chevron at bottom of sidebar
- **Persist state:** localStorage key `sidebar_collapsed`

**Navigation items (top-aligned):**

| Icon | Label | Route | Description |
|------|-------|-------|-------------|
| `Dashboard` | Dashboard | `/` | Overview with key metrics |
| `TrendingUp` | Opportunities | `/opportunities` | Profitable items list |
| `Assignment` | Assignments | `/assignments` | Corp member task board |
| `ShowChart` | Market Data | `/market` | Raw market order explorer |
| `History` | History | `/history` | Historical build cost trends |

**Bottom-aligned:**

| Icon | Label | Route |
|------|-------|-------|
| `Settings` | Settings | `/settings` |

---

## 3. Pages / Modules

### 3.1 Dashboard (`/`)

Summary view. No interaction needed beyond viewing.

**Cards (MUI `Card` in a responsive grid):**

- **Active Opportunities** — count of items in pruned_list today, with delta vs yesterday
- **Avg Margin** — average margin across today's pruned_list
- **Top Region** — region with most opportunities
- **Assigned Items** — count of active assignments for logged-in user

**Below cards:**

- **Top 10 Opportunities** — mini table (item, region, margin, profit) sorted by margin desc. Clickable rows → `/opportunities?item_id=X`
- **Margin Distribution** — echarts histogram of today's margins (buckets: 20-30%, 30-40%, etc.)

### 3.2 Opportunities (`/opportunities`)

Primary workhorse page.

**Controls:**
- Region filter (dropdown: All / Forge / Sinq Laison)
- Min margin slider (default 20%, range 20-80%)
- Search box (item name filter, debounced 300ms)
- Sort by: margin (default), profit, build cost, item name

**Table (MUI `DataGrid` or custom):**

| Column | Notes |
|--------|-------|
| Item Icon | 32px EVE item icon from ESI image server |
| Item Name | Sortable |
| Region | Chip/badge |
| Build Cost | ISK formatted |
| Sell Price | ISK formatted |
| Profit | ISK formatted, green text |
| Margin | Percentage, color-coded (green ≥40%, yellow 20-40%) |
| Actions | "Assign to Me" button (authenticated only) |

**Pagination:** Server-side, 50 per page (matches API `/opportunities` endpoint).

### 3.3 Assignments (`/assignments`)

**Views:**
- **My Assignments** (default) — items assigned to logged-in user
- **Corp Assignments** (admin/officer only, future iteration) — all assignments

**Table:**

| Column | Notes |
|--------|-------|
| Item Icon + Name | |
| Region | |
| Assigned At | Relative time (date-fns `formatDistanceToNow`) |
| Current Margin | Live from latest build_costs |
| Actions | "Unassign" button with confirmation dialog |

### 3.4 Market Data (`/market`)

Raw market order explorer for power users.

**Controls:**
- Item search (autocomplete from EVE item DB)
- Region selector

**Display:**
- Buy/sell price history chart (echarts line chart, time series from market_orders hypertable)
- Current best buy / best sell
- Order book depth (stretch goal)

### 3.5 History (`/history`)

Build cost trends over time.

**Controls:**
- Item search
- Region selector
- Date range picker (date-fns)

**Display:**
- Build cost vs sell price over time (dual-axis echarts line chart)
- Margin trend line
- Annotation markers when item entered/left pruned_list

### 3.6 Settings (`/settings`)

**Sections:**
- **Profile** — Character name, corp (read-only from SSO token)
- **Preferences:**
  - Default region (Forge / Sinq Laison)
  - Default min margin threshold
  - Theme (light/dark — MUI `ThemeProvider`, persisted in localStorage)
- **Notifications (future):**
  - Discord webhook URL for opportunity alerts

---

## 4. Authentication — EVE SSO

### 4.1 Flow

Identical pattern to vs-industry-frontend's Koa SSO backend:

1. User clicks "Login with EVE" → redirect to `https://login.eveonline.com/v2/oauth/authorize`
2. EVE redirects back to `/callback` with auth code
3. Koa backend exchanges code for tokens at `https://login.eveonline.com/v2/oauth/token`
4. Backend validates JWT via EVE's JWKS endpoint (`https://login.eveonline.com/oauth/jwks`)
5. Backend issues session cookie or returns JWT to frontend
6. Frontend stores token, includes in API requests as `Authorization: Bearer <token>`

### 4.2 SSO Backend (Koa)

Same structure as vs-industry-frontend's `backend/`:

```
sso-backend/
├── src/
│   ├── index.ts          # Koa app setup, CORS, routes
│   ├── config.ts          # EVE_CLIENT_ID, EVE_CLIENT_SECRET, CALLBACK_URI
│   ├── routes/
│   │   ├── auth.ts        # /login, /callback, /logout, /me
│   │   └── proxy.ts       # Proxy API requests to Go API server (with auth)
│   └── middleware/
│       └── auth.ts        # JWT validation middleware
├── package.json
└── tsconfig.json
```

**Routes:**
- `GET /login` — Redirect to EVE SSO authorize URL
- `GET /callback` — Exchange code → token, set session, redirect to frontend
- `GET /logout` — Clear session, redirect to `/`
- `GET /me` — Return character info from JWT (name, character_id, corp_id)

### 4.3 ESI Scopes

Minimum scopes needed:
- `publicData` — Character identity (name, portrait)

Future scopes (if we add corp features):
- `esi-corporations.read_corporation_membership.v1`

### 4.4 Frontend Auth State

```typescript
interface AuthContext {
  isAuthenticated: boolean;
  character: {
    id: number;
    name: string;
    corpId: number;
    corpName: string;
    portrait: string; // ESI image URL
  } | null;
  login: () => void;   // redirect to /login
  logout: () => void;  // call /logout
}
```

React context provider wrapping the app. On mount, calls `GET /me` — if 401, user is unauthenticated.

---

## 5. API Integration

Frontend talks to the Koa SSO backend, which proxies to the Go API server.

```
Browser → Koa SSO Backend (port 3001) → Go API Server (K8s service)
                                          ↓
                                     TimescaleDB
```

**Proxy routes in Koa:**

| Frontend calls | Koa proxies to | Go API endpoint |
|----------------|----------------|-----------------|
| `GET /api/opportunities` | → | `GET /opportunities` |
| `POST /api/assign` | → | `POST /assign` |
| `GET /api/assignments/:id` | → | `GET /assignments/:id` |
| `DELETE /api/assign/:member/:item/:region` | → | `DELETE /assign/:member/:item/:region` |
| `GET /api/market/:item/:region` | → | (new) market data endpoint |
| `GET /api/history/:item/:region` | → | (new) historical build costs |
| `GET /api/dashboard` | → | (new) dashboard aggregates |

**Note:** Go API will need 3 new endpoints for dashboard, market data, and history. Add to backend spec as needed.

---

## 6. Theming

MUI ThemeProvider with light/dark toggle.

**EVE-inspired dark theme (default):**
- Background: `#0a0e17` (deep space dark)
- Surface: `#141a2a`
- Primary: `#00b0ff` (cyan, EVE UI teal)
- Secondary: `#ff9100` (amber, profit/warning)
- Success: `#4caf50` (green, good margins)
- Error: `#f44336`
- Text primary: `#e0e0e0`
- Text secondary: `#9e9e9e`

**Light theme:** MUI defaults with primary/secondary carried over.

---

## 7. Project Structure

```
eve-market-calc-ui/
├── frontend/
│   ├── public/
│   │   └── favicon.ico        # EVE-themed icon
│   ├── src/
│   │   ├── main.tsx
│   │   ├── App.tsx             # Router + AuthProvider + ThemeProvider + Layout
│   │   ├── theme.ts            # MUI theme definitions
│   │   ├── components/
│   │   │   ├── Layout.tsx      # AppBar + Sidebar + Outlet
│   │   │   ├── Sidebar.tsx     # Collapsible nav rail
│   │   │   ├── AppBar.tsx      # Top bar with login/avatar
│   │   │   ├── UserMenu.tsx    # Avatar dropdown
│   │   │   └── ISKFormat.tsx   # ISK number formatter component
│   │   ├── pages/
│   │   │   ├── Dashboard.tsx
│   │   │   ├── Opportunities.tsx
│   │   │   ├── Assignments.tsx
│   │   │   ├── MarketData.tsx
│   │   │   ├── History.tsx
│   │   │   ├── Settings.tsx
│   │   │   └── Login.tsx       # Login landing / callback handler
│   │   ├── context/
│   │   │   ├── AuthContext.tsx
│   │   │   └── ThemeContext.tsx
│   │   ├── hooks/
│   │   │   ├── useAuth.ts
│   │   │   ├── useOpportunities.ts
│   │   │   └── useMarketData.ts
│   │   ├── api/
│   │   │   └── client.ts       # Fetch wrapper with auth headers
│   │   └── types/
│   │       ├── opportunity.ts
│   │       ├── assignment.ts
│   │       └── market.ts
│   ├── index.html
│   ├── vite.config.ts          # Dev proxy to Koa backend
│   ├── tsconfig.json
│   └── package.json
├── sso-backend/
│   ├── src/
│   │   ├── index.ts
│   │   ├── config.ts
│   │   ├── routes/
│   │   │   ├── auth.ts
│   │   │   └── proxy.ts
│   │   └── middleware/
│   │       └── auth.ts
│   ├── package.json
│   └── tsconfig.json
├── docker-compose.yaml         # Frontend + SSO backend + dev proxy
└── README.md
```

---

## 8. Responsive Behavior

- **≥1200px:** Full sidebar expanded, all columns visible
- **900-1199px:** Sidebar collapsed by default, table columns prioritized
- **<900px:** Sidebar becomes temporary drawer (overlay), simplified table (hide less critical columns)

No mobile-first requirement — this is a desktop tool — but it shouldn't break on tablets.

---

## 9. Implementation Order

1. **Scaffold** — Vite + React + MUI + Router, empty pages, Layout shell
2. **Auth** — Koa SSO backend, AuthContext, login/logout flow
3. **Opportunities page** — Primary table with mock data → wire to API
4. **Dashboard** — Aggregate cards + top 10 mini table
5. **Assignments** — Assign/unassign flow
6. **Market Data** — Charts + item search
7. **History** — Time series charts
8. **Settings** — Preferences + theme toggle
9. **Polish** — Loading states, error boundaries, empty states

---

## 10. New Go API Endpoints Needed

The backend spec currently has `/opportunities`, `/assign`, `/assignments`. The UI needs:

| Endpoint | Purpose |
|----------|---------|
| `GET /dashboard` | Aggregated stats (opp count, avg margin, top region, user assignment count) |
| `GET /market/:item_id/:region` | Market order history for charting |
| `GET /history/:item_id/:region?from=&to=` | Build cost history for charting |
| `GET /items/search?q=` | Item name autocomplete |

These should be added to the Go API spec before or during UI implementation.

---

## 11. Notes

- EVE item icons: `https://images.evetech.net/types/{type_id}/icon?size=32`
- Character portraits: `https://images.evetech.net/characters/{character_id}/portrait?size=64`
- ISK formatting: Use `Intl.NumberFormat` with custom suffix (e.g., "1.2M ISK", "3.4B ISK")
- All times displayed in user's local timezone (browser default)
- Vite dev proxy config should forward `/api/*` to Koa backend on port 3001
