# MT Ink Dashboard - Complete Technical Bible (VERSION 2)
**Last Updated:** February 3, 2026 03:33 CET  
**Current Version:** VERSION 2 — Dynamic Quarterly Projections  
**Status:** 🟢 Production (Stable)  
**URL:** https://dashboard.mt.ink  
**Server:** 143.110.165.195 (Digital Ocean - Ubuntu 24.04 LTS)

---

## Table of Contents
1. [Quick Access](#quick-access)
2. [What's New in VERSION 2](#whats-new-in-version-2)
3. [Architecture Overview](#architecture-overview)
4. [Complete Feature List](#complete-feature-list)
5. [Data Sources & Integration](#data-sources--integration)
6. [File Structure](#file-structure)
7. [Development Workflow](#development-workflow)
8. [Database Schema](#database-schema)
9. [API Endpoints](#api-endpoints)
10. [Frontend Components](#frontend-components)
11. [WebSocket Real-Time Updates](#websocket-real-time-updates)
12. [Deployment & Operations](#deployment--operations)
13. [Troubleshooting](#troubleshooting)
14. [Common Tasks](#common-tasks)

---

## Quick Access

### URLs
- **Production:** https://dashboard.mt.ink
- **API Backend:** Port 3003 (proxied via Nginx)
- **WebSocket:** wss://dashboard.mt.ink/ws
- **Upload Portal:** https://mcp-server.mt.ink (for Spotify data uploads)
- **Real-time Sales:** https://fuck.letsdoit.ai

### Credentials
- **Username:** dev
- **Password:** Givemelotsofdata13

### Server Locations
```bash
# Production (LIVE - DO NOT EDIT DIRECTLY)
/opt/mt-ink-dashboard/
├── server.js              # Express + WebSocket server
├── public/index.html      # VERSION 2 dashboard (54KB)

# Sandbox (ALL DEVELOPMENT HAPPENS HERE FIRST)
/opt/mt-ink-dashboard-sandbox/

# Related Services
/opt/mt-ink-mcp/              # MCP server (port 3000)
/opt/fuck-letsdoit/           # Real-time sales dashboard (port 3001)
/opt/mt-ink-upload/           # Spotify upload service
```

### Service Commands
```bash
# Dashboard API + WebSocket service
systemctl status mt-ink-dashboard.service
systemctl restart mt-ink-dashboard.service
systemctl stop mt-ink-dashboard.service
journalctl -u mt-ink-dashboard.service -f  # Follow logs

# Check WebSocket connections
journalctl -u mt-ink-dashboard.service | grep -i "client connected"

# Database health
journalctl -u mt-ink-dashboard.service | grep -i "DB HEALTH"
```

---

## What's New in VERSION 2

### Major Features Added (Since Nov 2025)

#### 1. Today vs Yesterday Real-Time Comparison
- Shows sales "as of current time" vs yesterday at same time
- Updates throughout the day for accurate tracking
- Full-day yesterday totals in dedicated card

#### 2. Average Order Value (AOV) Analysis
- Three-period comparison (Today, Week, Month)
- Real-time calculation from order count and revenue
- Highlighted metric card with 💰 icon
- Color-coded percentage changes

#### 3. Time-Based Period Comparisons
- This Week vs Last Week (same point)
- This Month vs Last Month (same point)
- Shows progression and percentage changes

#### 4. Spotify Pool Payment Breakdown
- Historical quarters (Q1-Q4 2025)
- Current pool rate display ($0.00841/min)
- Dynamic Q4+ projections
- Per-minute rate tracking

#### 5. Real-Time WebSocket Notifications
- Live order notifications
- Auto-refresh on new orders
- Top-right corner notifications with auto-dismiss

#### 6. Embedded Spotify Upload
- Upload CSV/XLSX directly from dashboard
- Multi-file support
- Real-time progress indicators

#### 7. Country-Level Analytics
- 8-week Spotify listening by country
- Week-over-week trend indicators (green/red)
- Total minutes per country

#### 8. Enhanced Responsive Design
- Full mobile/tablet optimization
- Touch-friendly interactions
- Landscape mode support

---

## Architecture Overview

### System Components
```
┌──────────────────────────────────────────────────────────────┐
│                     dashboard.mt.ink                          │
│              (Nginx HTTPS + Basic Auth)                       │
└───────────────────────┬──────────────────────────────────────┘
                        │
                        ▼
┌──────────────────────────────────────────────────────────────┐
│         Express API + WebSocket Server (Port 3003)            │
│  • REST API endpoints (revenue, metrics, comparisons)         │
│  • WebSocket server (real-time order notifications)           │
│  • Static file serving (index.html)                           │
│  • Database health monitoring                                 │
└──────────────────┬───────────────────────────────────────────┘
                   │
                   ▼
┌──────────────────────────────────────────────────────────────┐
│         PostgreSQL Database (Digital Ocean Managed)           │
│  • shopify_orders (real-time order data)                     │
│  • weekly_revenue (Spotify estimates)                         │
│  • monthly_revenue (Spotify actuals - AUTHORITATIVE)          │
│  • spotify_weekly_stats (detailed listening)                  │
│  • spotify_country_stats (country-level data)                 │
└──────────────────▲───────────────────────────────────────────┘
                   │
┌──────────────────┴───────────────────────────────────────────┐
│  Data Sources:                                                │
│  1. Shopify Webhook → /opt/fuck-letsdoit/ → Database        │
│  2. Spotify CSV Uploads → mcp-server.mt.ink → Database      │
│  3. Manual backfill via backfill.js script                   │
└───────────────────────────────────────────────────────────────┘
```

### Technology Stack
- **Frontend:** Vanilla JavaScript (ES6), Pure CSS
- **Backend:** Node.js 20, Express.js (ES6 modules)
- **Real-Time:** WebSocket (ws library)
- **Database:** PostgreSQL 14 (Digital Ocean managed)
- **Proxy:** Nginx with SSL (Let's Encrypt)
- **Process Manager:** systemd
- **Authentication:** Basic HTTP auth (nginx level)

---

## Complete Feature List

### 🎯 Core Metrics Dashboard

#### Today vs Yesterday Comparison
- Real-time tracking with current time display
- Order count comparison
- Revenue comparison
- Percentage changes (green/red arrows)
- Yesterday's full-day totals in highlighted card

#### Metrics Grid (6 Cards)
1. **Weekly Sales** - Current week revenue + % change
2. **Monthly Sales** - Current month revenue + % change  
3. **This Week Orders** - Order count + % change
4. **Average Order Value** - Highlighted with 💰 icon + % change
5. **Total Revenue** - 30-day rolling total
6. **Yesterday's Total** - Previous day complete (blue highlighted)

#### AOV Analysis (3 Comparisons)
- **Today vs Yesterday** - Current day AOV vs yesterday same time
- **This Week vs Last Week** - 7-day rolling AOV comparison
- **This Month vs Last Month** - Monthly AOV trends

Each shows:
- Current AOV value (e.g., £45.67)
- Previous period value (e.g., vs £42.31)
- Percentage change with color coding

### 📊 Spotify Analytics

#### Pool Payment Analysis
**Historical Data:**
- Q1 2025: $4,822 (812.8 completes, 429,977 mins, $0.01121/min)
- Q2 2025: $8,317 (1,045.6 completes, 553,075 mins, $0.01504/min)
- Q3 2025: $14,354 (1,707,582 mins, $0.00841/min)
- Q4 2025: $9,249 (894,829 mins) ✓ ACTUAL

**Current Pool Rate Display:**
- Rate: $0.00841/min
- Equivalent: $8.41 per 1,000 minutes
- Source: Q3 2025 actual results

**Dynamic Projections:**
- Live Q4+ projection calculation
- Based on actual weekly data
- Average weekly minutes tracking
- Growth % vs previous quarter

#### Last 12 Weeks Listening
- Weekly minutes streamed
- Estimated completes (minutes ÷ 530)
- Estimated revenue (minutes × $0.00841)
- Color-coded table with sortable columns

#### Country Breakdown (Last 8 Weeks)
- Minutes by country per week
- Week-over-week trend indicators
  - Green: Growth
  - Red: Decline
  - Gray: No data
- Country totals and weekly totals
- Fully responsive table with horizontal scroll

### 📈 Sales Analytics

#### Time-Based Comparisons
- **Week vs Last Week** - Same day-of-week comparison
- **Month vs Last Month** - Same day-of-month comparison
- Shows order progression (e.g., "15 → 23 orders")
- Revenue changes with percentages

#### Top Countries (Last 30 Days)
- Top 10 countries by order count
- Revenue per country
- Shopify shipping data

#### Platform Revenue Breakdown
- Shopify (active)
- Etsy (active)
- WooCommerce (active)
- Amazon (coming soon)
- eBay (coming soon)

#### AOV Trend Table (30 Days)
- Daily date, orders, revenue, AOV
- Day-over-day % change
- Color-coded changes
- Historical pattern analysis

### 🔔 Real-Time Features

#### WebSocket Order Notifications
- Live connection to wss://dashboard.mt.ink/ws
- Notifications appear top-right corner:
  - "✅ New Order: £45.67 (GB)"
- Auto-dismiss after 5 seconds
- Dashboard auto-refreshes data
- Auto-reconnect on disconnect

#### Auto-Refresh
- Dashboard polls API every 60 seconds
- Ensures data is always current
- Non-blocking (doesn't interrupt user)

### 📤 Data Management

#### Spotify Upload Portal (Embedded)
- **Location:** Bottom of dashboard
- **File Types:** CSV, XLSX
- **Report Types:**
  - Monthly Stats (CSV)
  - Weekly Stats (CSV)
  - Revenue Report (XLSX)
- **Features:**
  - Multi-file upload
  - Progress indicators
  - Success/error feedback
  - File validation

---

## Data Sources & Integration

### 1. Shopify Orders (Real-Time)
**Source:** Shopify webhook → fuck.letsdoit.ai  
**Method:** Automated webhook + manual backfill  
**Storage:** `shopify_orders` table  
**Frequency:** Real-time (webhooks) + backfill as needed

**Webhook Endpoint:**
```
POST https://fuck.letsdoit.ai/webhook/shopify
```

**Backfill Script:**
```bash
cd /opt/fuck-letsdoit/
node backfill.js 2025-01-01 2025-01-31
```

### 2. Spotify Weekly Stats (Estimates)
**Source:** Spotify weekly CSV reports  
**Method:** Manual upload via mcp-server.mt.ink  
**Storage:** `spotify_weekly_stats` table  
**Frequency:** Weekly  
**Note:** Estimates subject to revision by monthly actuals

**CSV Format:**
```csv
report_week_start,report_week_end,total_minutes,country
2025-10-06,2025-10-12,145000,GB
```

### 3. Spotify Monthly Revenue (AUTHORITATIVE)
**Source:** Spotify monthly confirmed reports  
**Method:** Manual upload via mcp-server.mt.ink  
**Storage:** `monthly_revenue` table  
**Frequency:** Monthly (2-3 weeks after month end)  
**Priority:** ALWAYS supersedes weekly estimates

**CSV Format:**
```csv
month,minutes_streamed,revenue
2025-10,622277,5233.35
```

### 4. Spotify Country Data
**Source:** Spotify country-level reports  
**Method:** Manual upload via mcp-server.mt.ink  
**Storage:** `spotify_country_stats` table  
**Frequency:** Weekly  

**CSV Format:**
```csv
report_week_start,country,minutes
2025-10-06,GB,85000
2025-10-06,US,45000
```

### Data Processing Pipeline

```
1. Upload → mcp-server.mt.ink (CSV/XLSX)
2. Parse → MCP server validates format
3. Store → PostgreSQL database insertion
4. Aggregate → Dashboard API computes metrics
5. Project → Calculate forward projections
6. Display → Frontend renders with real-time updates
```

---

## File Structure

### Production: `/opt/mt-ink-dashboard/`
```
mt-ink-dashboard/
├── server.js              # Express + WebSocket server (ES6)
├── package.json           # Dependencies
├── package-lock.json
├── public/
│   └── index.html        # VERSION 2 Dashboard (54KB)
├── logs/                 # Application logs (if configured)
└── MONITORING_DEPLOYMENT_SUMMARY.md
```

### Sandbox: `/opt/mt-ink-dashboard-sandbox/`
```
mt-ink-dashboard-sandbox/
├── server.js              # Copy of production server
├── package.json
├── public/
│   └── index.html        # DEVELOPMENT VERSION
├── DEPLOYMENT_INSTRUCTIONS.md
└── README.md
```

### Key Files Explained

#### `server.js` (Backend)
**Size:** ~500 lines  
**Language:** Node.js ES6 modules  

**Key Features:**
```javascript
// Database connection (PostgreSQL)
const pool = new Pool({
  host: 'db-postgresql-lon1-83676-do-user-18164855-0.l.db.ondigitalocean.com',
  port: 25060,
  database: 'mt_ink_sales',
  user: 'doadmin',
  password: [REDACTED],
  ssl: { rejectUnauthorized: true }
});

// WebSocket server for real-time updates
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ server });

// API endpoints
app.get('/api/comparison/today-vs-yesterday', async (req, res) => {
  // Current time comparison logic
  const now = new Date();
  const bstNow = new Date(now.toLocaleString('en-US', { timeZone: 'Europe/London' }));
  // ... query database for today vs yesterday at same time
});

// Health monitoring
setInterval(async () => {
  const result = await pool.query('SELECT NOW()');
  console.log('[DB HEALTH] Connection test passed, DB time:', result.rows[0].now);
}, 300000); // Every 5 minutes
```

#### `public/index.html` (Frontend)
**Size:** 54KB  
**Structure:** Single-page application  

**Sections:**
1. Version banner (top bar)
2. Header ("MT Ink Dashboard - Command Center")
3. Today vs Yesterday comparison (hero card)
4. Metrics grid (6 cards)
5. AOV analysis (3-period comparison)
6. Time-based comparisons (week/month)
7. Top countries table
8. AOV trend table (30 days)
9. Spotify pool payment analysis
10. Spotify listening (12 weeks)
11. Country-by-week table (8 weeks)
12. Platform revenue table
13. Spotify upload form

**JavaScript Features:**
```javascript
// Auto-refresh
setInterval(() => loadData(), 60000);

// WebSocket connection
const ws = new WebSocket('wss://dashboard.mt.ink/ws');
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'new_order') {
    showOrderNotification(message.data);
    loadData(); // Refresh dashboard
  }
};

// Dynamic Q4 projection rendering
function renderQ4Projection(qp) {
  // Dynamically update Q4 card with live projections
}
```

---

## Development Workflow

### ⚠️ MANDATORY Sandbox-First Workflow

**CRITICAL RULE:** Never edit `/opt/mt-ink-dashboard/` directly.

#### Step 1: Work in Sandbox
```bash
cd /opt/mt-ink-dashboard-sandbox/

# Frontend changes
nano public/index.html

# Backend changes
nano server.js
```

#### Step 2: Test Thoroughly
```bash
# If backend changed, restart sandbox (if configured)
# Or test locally
node server.js

# Test checklist:
# ✓ No console errors (F12)
# ✓ Data loads correctly
# ✓ Charts render
# ✓ Color coding works
# ✓ WebSocket connects
# ✓ Responsive on mobile
# ✓ No 404 errors
```

#### Step 3: Backup Production
```bash
cd /opt/mt-ink-dashboard/public/
cp index.html index.html.backup-$(date +%Y%m%d-%H%M%S)

# For backend
cd /opt/mt-ink-dashboard/
cp server.js server.js.backup-$(date +%Y%m%d-%H%M%S)
```

#### Step 4: Deploy
```bash
# Frontend
cp /opt/mt-ink-dashboard-sandbox/public/index.html \
   /opt/mt-ink-dashboard/public/index.html

# Backend
cp /opt/mt-ink-dashboard-sandbox/server.js \
   /opt/mt-ink-dashboard/server.js

# Restart
systemctl restart mt-ink-dashboard.service
```

#### Step 5: Verify
```bash
# Check service
systemctl status mt-ink-dashboard.service

# Check logs
journalctl -u mt-ink-dashboard.service -n 50 --no-pager

# Test live
curl -I https://dashboard.mt.ink
# Should return: HTTP/1.1 200 OK

# Check WebSocket
journalctl -u mt-ink-dashboard.service | grep "client connected"
```

---

## Database Schema

### Database: `mt_ink_sales` (Digital Ocean PostgreSQL)

#### Table: `shopify_orders`
```sql
CREATE TABLE shopify_orders (
  id BIGINT PRIMARY KEY,
  order_number INTEGER,
  email VARCHAR(255),
  created_at TIMESTAMP WITH TIME ZONE,
  financial_status VARCHAR(50),
  fulfillment_status VARCHAR(50),
  total NUMERIC(10,2),
  subtotal NUMERIC(10,2),
  total_tax NUMERIC(10,2),
  currency VARCHAR(10),
  shipping_country VARCHAR(100),
  customer_id BIGINT,
  line_items_count INTEGER,
  platform VARCHAR(50) DEFAULT 'shopify'
);

CREATE INDEX idx_shopify_created_at ON shopify_orders(created_at);
CREATE INDEX idx_shopify_country ON shopify_orders(shipping_country);
```

#### Table: `spotify_weekly_stats`
```sql
CREATE TABLE spotify_weekly_stats (
  id SERIAL PRIMARY KEY,
  report_week_start DATE NOT NULL,
  report_week_end DATE NOT NULL,
  total_minutes INTEGER NOT NULL,
  country VARCHAR(10),
  uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  UNIQUE(report_week_start, report_week_end, country)
);

CREATE INDEX idx_spotify_week_start ON spotify_weekly_stats(report_week_start);
CREATE INDEX idx_spotify_country ON spotify_weekly_stats(country);
```

#### Table: `monthly_revenue` (AUTHORITATIVE)
```sql
CREATE TABLE monthly_revenue (
  id SERIAL PRIMARY KEY,
  month DATE NOT NULL,
  minutes_streamed BIGINT NOT NULL,
  revenue NUMERIC(10,2) NOT NULL,
  uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  UNIQUE(month)
);

CREATE INDEX idx_monthly_month ON monthly_revenue(month);
```

#### Table: `spotify_country_stats`
```sql
CREATE TABLE spotify_country_stats (
  id SERIAL PRIMARY KEY,
  report_week_start DATE NOT NULL,
  country VARCHAR(10) NOT NULL,
  minutes INTEGER NOT NULL,
  uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  UNIQUE(report_week_start, country)
);

CREATE INDEX idx_country_week ON spotify_country_stats(report_week_start, country);
```

### Database Queries

```bash
# Connect
psql "postgresql://doadmin:[PASSWORD]@db-postgresql-lon1-83676-do-user-18164855-0.l.db.ondigitalocean.com:25060/mt_ink_sales?sslmode=require"

# Common queries
-- Today's orders
SELECT COUNT(*), SUM(total::decimal) FROM shopify_orders 
WHERE created_at >= CURRENT_DATE;

-- This week's revenue
SELECT COUNT(*), SUM(total::decimal) FROM shopify_orders 
WHERE created_at >= date_trunc('week', CURRENT_DATE);

-- Monthly Spotify revenue
SELECT * FROM monthly_revenue ORDER BY month DESC LIMIT 12;

-- Country breakdown
SELECT country, SUM(minutes) FROM spotify_country_stats 
WHERE report_week_start >= CURRENT_DATE - INTERVAL '8 weeks'
GROUP BY country ORDER BY SUM(minutes) DESC;
```

---

## API Endpoints

### Base URL: `https://dashboard.mt.ink/api`

#### Comparison Endpoints

**1. GET `/api/comparison/today-vs-yesterday`**

Returns today's sales vs yesterday at same time.

**Response:**
```json
{
  "today": {
    "orders": 23,
    "revenue": "1045.67"
  },
  "yesterday": {
    "orders": 19,
    "revenue": "892.34"
  },
  "yesterdayFullDay": {
    "orders": 45,
    "revenue": "2134.56"
  },
  "comparison": {
    "currentTime": "14:30",
    "ordersDiff": 4,
    "ordersPercent": "21.1",
    "revenueDiff": "153.33",
    "revenuePercent": "17.2"
  }
}
```

**2. GET `/api/comparison/week-vs-lastweek`**

Same point in week comparison.

**Response:**
```json
{
  "thisWeek": {
    "orders": 67,
    "revenue": "3045.89"
  },
  "lastWeek": {
    "orders": 58,
    "revenue": "2678.45"
  },
  "comparison": {
    "ordersDiff": 9,
    "ordersPercent": "15.5",
    "revenueDiff": "367.44",
    "revenuePercent": "13.7"
  }
}
```

**3. GET `/api/comparison/month-vs-lastmonth`**

Same point in month comparison.

#### Growth Endpoints

**4. GET `/api/growth/weekly`**

Weekly aggregated data.

**Response:**
```json
{
  "thisWeek": {
    "order_count": 67,
    "revenue": "3045.89"
  },
  "lastWeek": {
    "order_count": 58,
    "revenue": "2678.45"
  },
  "comparison": {
    "ordersPercent": "15.5",
    "revenuePercent": "13.7"
  }
}
```

**5. GET `/api/growth/monthly`**

Monthly aggregated data (similar structure).

**6. GET `/api/growth/daily?days=30`**

Daily breakdown for AOV trend analysis.

**Response:**
```json
[
  {
    "date": "2026-02-03",
    "order_count": 12,
    "revenue": "567.89"
  }
]
```

#### Spotify Endpoints

**7. GET `/api/spotify/minutes?weeks=12`**

Last 12 weeks of Spotify listening data.

**Response:**
```json
[
  {
    "report_week_start": "2025-12-23",
    "total_minutes": 145000
  }
]
```

**8. GET `/api/spotify/countries`**

Country-level breakdown for last 8 weeks.

**Response:**
```json
[
  {
    "report_week_start": "2025-12-23",
    "country": "GB",
    "minutes": 85000
  }
]
```

**9. GET `/api/spotify/quarterly-projection`**

Dynamic Q4+ projection calculation.

**Response:**
```json
{
  "quarter": "Q4",
  "year": 2025,
  "totals": {
    "revenue": 9249.00,
    "minutes": 894829
  },
  "months": [
    {
      "name": "October",
      "year": 2025,
      "revenue": 3456.78,
      "minutes": 410000,
      "status": "actual",
      "source": "Monthly confirmed data"
    }
  ],
  "comparison": {
    "previousQuarter": "Q3 2025",
    "growth": -35.6,
    "trend": "down"
  },
  "avgWeeklyMinutes": 74569,
  "lastUpdated": "2026-02-03T11:14:00.000Z"
}
```

#### Revenue & Countries

**10. GET `/api/revenue/platforms`**

Revenue by platform (last 30 days).

**Response:**
```json
[
  {
    "platform": "shopify",
    "order_count": 234,
    "revenue": "10456.78"
  },
  {
    "platform": "etsy",
    "order_count": 45,
    "revenue": "2134.56"
  }
]
```

**11. GET `/api/countries`**

Top countries by orders (last 30 days).

**Response:**
```json
[
  {
    "shipping_country": "United Kingdom",
    "order_count": 156,
    "revenue": "7234.56"
  }
]
```

#### Data Upload

**12. POST `/api/upload`**

Upload Spotify CSV/XLSX data.

**Request:**
```
Content-Type: multipart/form-data

file: [CSV/XLSX file]
reportType: "monthly" | "weekly" | "revenue"
```

**Response:**
```json
{
  "success": true,
  "message": "File uploaded successfully",
  "rowsInserted": 12
}
```

---

## WebSocket Real-Time Updates

### Connection
**URL:** `wss://dashboard.mt.ink/ws`

### Message Types

#### New Order Event
```javascript
{
  "type": "new_order",
  "data": {
    "id": 1234567890,
    "total": "45.67",
    "currency": "GBP",
    "country": "GB",
    "created_at": "2026-02-03T14:30:00Z"
  }
}
```

### Frontend Implementation

```javascript
let ws;
let reconnectInterval;

function connectWebSocket() {
  ws = new WebSocket('wss://dashboard.mt.ink/ws');
  
  ws.onopen = () => {
    console.log('WebSocket connected');
    clearInterval(reconnectInterval);
  };
  
  ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    if (message.type === 'new_order') {
      showOrderNotification(message.data);
      loadData(); // Refresh dashboard
    }
  };
  
  ws.onclose = () => {
    console.log('WebSocket disconnected - reconnecting...');
    reconnectInterval = setInterval(() => {
      connectWebSocket();
    }, 5000);
  };
}

function showOrderNotification(orderData) {
  const notification = document.createElement('div');
  notification.innerHTML = `✅ New Order: £${orderData.total} (${orderData.country})`;
  // Style and append to DOM
  document.body.appendChild(notification);
  setTimeout(() => notification.remove(), 5000);
}
```

### Backend Implementation

```javascript
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ server });

wss.on('connection', (ws) => {
  console.log('Dashboard client connected');
  
  ws.on('close', () => {
    console.log('Dashboard client disconnected');
  });
});

// Broadcast new order to all connected clients
export function broadcastNewOrder(orderData) {
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify({
        type: 'new_order',
        data: orderData
      }));
    }
  });
}
```

---

## Frontend Components

### Color Scheme
```css
/* Background gradient */
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 100%);

/* Growth indicators */
color-growth: #4ade80;     /* Green */
color-decline: #f87171;    /* Red */
color-neutral: #93c5fd;    /* Blue */
color-warning: #fbbf24;    /* Yellow */

/* Card backgrounds */
card-bg: rgba(255, 255, 255, 0.15);
card-bg-highlight: rgba(74, 222, 128, 0.15);
card-bg-blue: rgba(147, 197, 253, 0.15);
```

### Responsive Breakpoints
```css
/* Mobile */
@media (max-width: 640px) {
  /* Single column layout */
  /* Larger touch targets */
  /* Font size: 16px minimum */
}

/* Tablet */
@media (min-width: 640px) and (max-width: 1024px) {
  /* 2-column grid */
}

/* Desktop */
@media (min-width: 1024px) {
  /* Full feature grid */
  /* Max width: 1400px */
}
```

### Animation & Interactions
```css
/* Hover effects */
.metric-card:hover {
  transform: translateY(-5px);
  transition: transform 0.3s ease;
}

/* Loading states */
.loading {
  animation: pulse 1.5s ease-in-out infinite;
}

/* Notification slide-in */
@keyframes slideIn {
  from { transform: translateX(400px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}
```

---

## Deployment & Operations

### Nginx Configuration
**File:** `/etc/nginx/sites-available/dashboard.mt.ink`

```nginx
server {
    listen 80;
    listen [::]:80;
    server_name dashboard.mt.ink;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name dashboard.mt.ink;

    ssl_certificate /etc/letsencrypt/live/dashboard.mt.ink/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dashboard.mt.ink/privkey.pem;

    # Basic authentication
    auth_basic "MT Ink Dashboard";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://localhost:3003;
        proxy_http_version 1.1;
        
        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        
        # Standard headers
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}
```

### systemd Service
**File:** `/etc/systemd/system/mt-ink-dashboard.service`

```ini
[Unit]
Description=MT Ink Dashboard API + WebSocket
After=network.target postgresql.service

[Service]
Type=simple
User=root
WorkingDirectory=/opt/mt-ink-dashboard
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target
```

### SSL Certificate Renewal
```bash
# Certbot auto-renewal (configured)
certbot renew --dry-run

# Manual renewal if needed
certbot renew
systemctl reload nginx
```

---

## Troubleshooting

### Issue: Dashboard Not Loading

**Symptoms:** Browser shows connection error  

**Diagnosis:**
```bash
systemctl status mt-ink-dashboard.service
netstat -tlnp | grep 3003
systemctl status nginx
```

**Fix:**
```bash
systemctl restart mt-ink-dashboard.service
systemctl restart nginx
```

### Issue: 502 Bad Gateway

**Symptoms:** Nginx returns 502  

**Diagnosis:**
```bash
journalctl -u mt-ink-dashboard.service -n 50
```

**Common Causes:**
- Database connection failure
- Node process crashed
- Port 3003 already in use

**Fix:**
```bash
# Check for errors
journalctl -u mt-ink-dashboard.service | grep -i error

# Restart service
systemctl restart mt-ink-dashboard.service
```

### Issue: WebSocket Not Connecting

**Symptoms:** No real-time notifications  

**Diagnosis:**
```bash
# Check WebSocket logs
journalctl -u mt-ink-dashboard.service | grep -i "client connected"

# Test WebSocket (from browser console)
const ws = new WebSocket('wss://dashboard.mt.ink/ws');
ws.onopen = () => console.log('Connected!');
ws.onerror = (e) => console.error('Error:', e);
```

**Fix:**
```bash
# Restart service
systemctl restart mt-ink-dashboard.service

# Check Nginx WebSocket config
nginx -t
```

### Issue: Data Not Showing

**Symptoms:** Dashboard shows $0.00 or no data  

**Diagnosis:**
```bash
# Check database
psql "postgresql://..." -c "SELECT COUNT(*) FROM shopify_orders;"
psql "postgresql://..." -c "SELECT COUNT(*) FROM monthly_revenue;"

# Check API
curl https://dashboard.mt.ink/api/comparison/today-vs-yesterday | jq
```

**Fix:**
- Upload data via mcp-server.mt.ink
- Verify database connectivity
- Check browser console for JavaScript errors

---

## Common Tasks

### Task 1: Deploy New Feature

```bash
# 1. Develop in sandbox
cd /opt/mt-ink-dashboard-sandbox/
nano public/index.html

# 2. Test thoroughly
# Open in browser, check console, test functionality

# 3. Backup production
cd /opt/mt-ink-dashboard/public/
cp index.html index.html.backup-$(date +%Y%m%d-%H%M%S)

# 4. Deploy
cp /opt/mt-ink-dashboard-sandbox/public/index.html \
   /opt/mt-ink-dashboard/public/index.html

# 5. Restart (if backend changed)
systemctl restart mt-ink-dashboard.service

# 6. Verify
curl -I https://dashboard.mt.ink
journalctl -u mt-ink-dashboard.service -n 20
```

### Task 2: Upload Spotify Data

```bash
# 1. Go to upload portal
https://mcp-server.mt.ink

# 2. Select report type
# - Monthly Stats (CSV)
# - Weekly Stats (CSV)
# - Revenue Report (XLSX)

# 3. Upload file(s)
# System validates and inserts into database

# 4. Verify in dashboard
# Data should appear immediately
```

### Task 3: Backfill Missing Shopify Orders

```bash
cd /opt/fuck-letsdoit/
node backfill.js 2026-01-01 2026-01-31

# Verify
psql "postgresql://..." -c "SELECT COUNT(*) FROM shopify_orders WHERE created_at >= '2026-01-01';"
```

### Task 4: Check System Health

```bash
# Service status
systemctl status mt-ink-dashboard.service

# Recent logs
journalctl -u mt-ink-dashboard.service -n 50 --no-pager

# Database health
journalctl -u mt-ink-dashboard.service | grep "DB HEALTH" | tail -5

# WebSocket connections
journalctl -u mt-ink-dashboard.service | grep "client connected" | tail -10

# Nginx logs
tail -50 /var/log/nginx/access.log
tail -50 /var/log/nginx/error.log
```

### Task 5: Database Backup

```bash
# Export tables
psql "postgresql://..." -c "\COPY shopify_orders TO '/tmp/shopify_orders_backup.csv' CSV HEADER;"
psql "postgresql://..." -c "\COPY monthly_revenue TO '/tmp/monthly_revenue_backup.csv' CSV HEADER;"
psql "postgresql://..." -c "\COPY spotify_weekly_stats TO '/tmp/spotify_weekly_backup.csv' CSV HEADER;"

# Download locally
scp root@143.110.165.195:/tmp/*_backup.csv ~/backups/$(date +%Y%m%d)/
```

---

## Quick Reference

### Most Used Commands
```bash
# Restart dashboard
systemctl restart mt-ink-dashboard.service

# View logs
journalctl -u mt-ink-dashboard.service -f

# Check version
head -3 /opt/mt-ink-dashboard/public/index.html | grep VERSION

# Test endpoint
curl https://dashboard.mt.ink/api/comparison/today-vs-yesterday | jq

# Database connection
psql "postgresql://doadmin:[PASSWORD]@db-postgresql-lon1-83676-do-user-18164855-0.l.db.ondigitalocean.com:25060/mt_ink_sales?sslmode=require"

# Sandbox development
cd /opt/mt-ink-dashboard-sandbox/
```

### File Locations
```
Production:  /opt/mt-ink-dashboard/
Sandbox:     /opt/mt-ink-dashboard-sandbox/
Docs:        /opt/mt-ink-docs/
MCP:         /opt/mt-ink-mcp/
Real-time:   /opt/fuck-letsdoit/
Upload:      /opt/mt-ink-upload/
```

### URLs
```
Dashboard:   https://dashboard.mt.ink
Upload:      https://mcp-server.mt.ink
Real-time:   https://fuck.letsdoit.ai
```

---

## Version History

### VERSION 2 (Current) - January 16, 2026
- ✅ Today vs Yesterday real-time comparison
- ✅ AOV analysis (3-period)
- ✅ Time-based week/month comparisons
- ✅ Spotify pool payment breakdown
- ✅ WebSocket real-time notifications
- ✅ Embedded Spotify upload
- ✅ Country-level analytics (8 weeks)
- ✅ Enhanced responsive design
- ✅ Yesterday full-day card

### VERSION 1 - November 2025
- Basic dashboard with metrics
- Monthly/weekly revenue
- Platform breakdown
- Top countries

---

## Support & Contacts

**Server:** 143.110.165.195 (Digital Ocean)  
**Dashboard:** https://dashboard.mt.ink (dev / Givemelotsofdata13)  
**Service:** mt-ink-dashboard.service  

**Documentation:**
- Tech Bible: `/opt/mt-ink-docs/DASHBOARD-TECH-BIBLE.md` (this file)
- Project Status: `/opt/mt-ink-docs/PROJECT-STATUS.md`
- MT Ink Docs: `/opt/mt-ink-docs/MT-INK-TECHNICAL-DOCUMENTATION.md`

**Related Services:**
- MCP Server: https://mcp-server.mt.ink (port 3000)
- Real-time Sales: https://fuck.letsdoit.ai (port 3001)
- Shopify Webhook: Port 3001

---

**Last Updated:** February 3, 2026 03:33 CET  
**Current Version:** VERSION 2 — Dynamic Quarterly Projections  
**Maintained By:** Ian / Claude  
**Next Review:** March 3, 2026
