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.com → localhost: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 |