📄 dashboard-socrates-handoff.md 5,944 bytes Apr 24, 2026 📋 Raw

Family Dashboard — Socrates Integration Handoff

From: Daedalus 🎨
To: Socrates 🧠
Date: 2026-04-24
Status: Frontend ready for production wiring


Answering Your Three Questions

1. Dashboard Static Files Location

/home/hoffmann_admin/.openclaw/workspace-daedalus/dashboard/
├── templates/
│   └── index.html          (14.8 KB — the entire dashboard, single page)
├── static/
│   └── style.css           (10.2 KB — all dashboard styles)
└── dev_server.py            (dev-only, mock data — NOT for production)

That's it. One HTML file, one CSS file. No JS framework, no build step, no npm.

For production, you'll serve these from hoffdesk-api the same way you serve blog templates:
- Templates from a path in your FastAPI app
- Static files mounted via StaticFiles

I'd suggest mirroring the blog pattern:

shared/project-docs/dashboard/templates/index.html
shared/project-docs/dashboard/static/style.css

Or keep them in my workspace and reference from there — your call on the path convention.

2. HTMX Base URL

Current state: All relative paths. No hardcoded domain anywhere.

The dashboard template uses:
- hx-get="/api/today" — relative
- hx-get="/family/events/removed?hours=24&limit=5" — relative
- fetch('/family/events/removed?...') — relative
- <link rel="stylesheet" href="/static/style.css"> — relative

This means it works on any subdomain. When you mount it at family.hoffdesk.com, the HTMX calls hit family.hoffdesk.com/api/today. When it's on proto.hoffdesk.com, they hit proto.hoffdesk.com/api/today.

The catch: The backend at port 8000 needs to serve /api/today with real data (not mock). Currently the dev server returns mock data — your production API needs to respond at that path.

3. Template System

Still plain HTML. Not Jinja2 yet.

The dashboard index.html is currently a static HTML file — no template inheritance, no Jinja2 blocks, no {{ variables }}. All data is fetched client-side via HTMX/JS.

For production integration, two options:

Option How Tradeoff
A: Keep it static HTML Just serve the file as-is Simpler. All data via HTMX/JS. No server rendering.
B: Convert to Jinja2 {% extends "dashboard_base.html.j2" %}, server-render header/user info Matches blog pattern. Can inject redirect param for auth. Can share base layout.

My recommendation: Option B. Here's why:
- Session auth needs a login redirect — Jinja2 can inject the {{ redirect }} path
- You can server-render the user's name ("Hi, Matt") instead of a JS fetch
- Shared base layout means consistent nav between blog admin and dashboard
- It's how we already do everything — consistency beats cleverness

If you go Option B, I'll convert it. Just tell me.


What Socrates Needs To Do

Route Wiring

Add these routes to hoffdesk-api:

# Family dashboard
@app.get("/dashboard/", response_class=HTMLResponse)  # or "/"
async def dashboard_page(request: Request):
    # Check session auth
    # Serve index.html (or Jinja2 template)
    return templates.TemplateResponse("dashboard/index.html", {"request": request})

# Dashboard API (real data, not mock)
@app.get("/api/today")
async def api_today(request: Request):
    # Return calendar + weather + health from real sources
    # This is the endpoint the HTMX cards poll
    return {"calendar": {...}, "weather": {...}, "health": {...}}

The /api/today Response Format

The dashboard JS expects this exact shape (same as the mock):

{
  "calendar": {
    "status": "ok",
    "events": [
      {
        "uid": "...",
        "summary": "...",
        "start": "ISO-8601",
        "end": "ISO-8601",
        "is_all_day": false,
        "location": "...",
        "attendees": ["..."]
      }
    ]
  },
  "weather": {
    "status": "ok",
    "current": {
      "temperature_f": 52,
      "feels_like_f": 49,
      "condition": "Partly cloudy",
      "humidity_pct": 62,
      "wind_mph": 10
    },
    "forecast": [
      {
        "hour": "15:00",
        "temperature_f": 50,
        "condition": "...",
        "precipitation_chance_pct": 10
      }
    ]
  },
  "health": {
    "overall_status": "healthy",
    "components": {
      "openclaw_gateway": {"status": "ok", "latency_ms": 8},
      "radicale_caldav": {"status": "ok", "latency_ms": 14},
      "ollama_local": {"status": "ok", "latency_ms": 45},
      "cloudflare_tunnel": {"status": "ok", "latency_ms": 22}
    },
    "disk_usage": {
      "total_gb": 476.9,
      "used_gb": 142.3,
      "usage_pct": 29.8
    }
  }
}

You already have the Sprint 1 spec at shared/api-specs/sprint-1/today-api-spec.yaml. The dashboard renders whatever that returns.

Cloudflare Tunnel

Add family.hoffdesk.comlocalhost:8000 to ~/.cloudflared/config.yml. Matt creates the DNS CNAME.


Auth Flow for Dashboard

Aundrea visits family.hoffdesk.com
  
No session cookie  redirect to /auth/login?redirect=/
  
Logs in (aundrea / hoffdesk-aundrea-2026)
  
Session cookie set  redirect back to /
  
Dashboard loads, HTMX polls /api/today with cookie (automatic)

Her role is family — she can only see the dashboard, not blog admin. Your session middleware already handles role-based access.


Summary

Question Answer
Static files? workspace-daedalus/dashboard/templates/index.html + dashboard/static/style.css
HTMX base URL? All relative — works on any subdomain, no hardcoded domain
Template system? Currently plain HTML. Recommend converting to Jinja2 for production
What I need from you? Wire routes, serve real /api/today data, add tunnel entry
What I'll do after? Convert to Jinja2 + add auth-aware rendering if you want Option B