SPEC: RTSport Coach Dashboard — Status & Messages Tabs
Agent Collaboration Protocol v1.3.0
Build Date: 2026-05-06
Overview
The Coach Dashboard has three bottom-nav tabs. Roster is fully built and polished. This build fleshes out the remaining two: Status (team-wide injury/recovery analytics) and Messages (secure coach-AT communication).
Contract
| Field | Value |
|---|---|
| API Base Path | https://hoffdesk.com/api/v1 |
| Auth Scheme | Bearer JWT (localStorage rtsport_token) |
| Content Type | application/json |
| Error Format | { "error": "...", "detail": { ... } } |
| Existing JS | Uses apiRequest(endpoint, options) wrapper in dashboard.html |
| School ID | schl_001 (test data) |
| Coach Context | Current sport via currentSport variable, coach email from localStorage |
Shared Constants
Status Enums
| Constant | Values | Used By |
|---|---|---|
AthleteStatus |
out, modified, cleared |
Backend enum + frontend labels/badges |
CasePhase |
initial_assessment, active_rehab, return_to_play, full_clearance |
Backend enum + frontend progress bar |
MessageStatus |
sent, delivered, read |
Backend + frontend message bubbles |
Feature Flags
| Flag | Default | Description |
|---|---|---|
ENABLE_MESSAGING |
true |
Controls Messages tab visibility |
ENABLE_ANALYTICS |
true |
Controls Status tab charts |
Endpoints
Status Tab
GET /api/v1/dashboard/coach/status?school_id=X&sport=Football
Response:
{
"sport": "Football",
"total_athletes": 50,
"status_summary": {
"out": 3,
"modified": 2,
"cleared": 45
},
"injury_breakdown": [
{"body_part": "Ankle", "count": 2},
{"body_part": "Knee", "count": 3},
{"body_part": "Shoulder", "count": 1}
],
"recovery_timeline": [
{"athlete_name": "Jake Larson", "status": "out", "phase": "active_rehab", "days_remaining": 14},
{"athlete_name": "Marcus Johnson", "status": "out", "phase": "initial_assessment", "days_remaining": 21},
{"athlete_name": "Tyler Wilson", "status": "modified", "phase": "return_to_play", "days_remaining": 3}
],
"recent_activity": [
{"type": "status_change", "athlete_name": "Jake Larson", "from": "modified", "to": "out", "timestamp": "2026-05-06T00:00:00Z"},
{"type": "new_case", "athlete_name": "Tyler Wilson", "body_part": "Knee", "timestamp": "2026-05-05T12:00:00Z"}
]
}
GET /api/v1/dashboard/coach/athlete/{athlete_id}/timeline
Response:
{
"athlete_name": "Jake Larson",
"cases": [
{
"id": "case_001",
"body_part": "Ankle - Right",
"opened": "2026-04-15T00:00:00Z",
"status": "active",
"milestones": [
{"name": "Initial Assessment", "completed": true, "date": "2026-04-15"},
{"name": "Active Rehab", "completed": false, "date": null},
{"name": "Return to Play", "completed": false, "date": null},
{"name": "Full Clearance", "completed": false, "date": null}
]
}
]
}
Messages Tab
GET /api/v1/messages/coach?school_id=X&sport=Football
Response:
{
"conversations": [
{
"athlete_name": "Jake Larson",
"athlete_id": "ath_001",
"last_message": "Any update on the ankle?",
"last_message_time": "2026-05-05T20:00:00Z",
"unread_count": 2,
"at_name": "Sarah (AT)"
}
]
}
GET /api/v1/messages/coach/{athlete_id}?before=X
Response:
{
"athlete_name": "Jake Larson",
"messages": [
{"id": "msg_001", "sender": "coach", "text": "Any update on the ankle?", "timestamp": "2026-05-05T20:00:00Z", "status": "read"},
{"id": "msg_002", "sender": "at", "text": "He's progressing well. Should be back next week.", "timestamp": "2026-05-05T20:15:00Z", "status": "read"}
]
}
POST /api/v1/messages/coach/{athlete_id}
Request:
{
"text": "Thanks for the update!"
}
Response: 201 Created with the message object
Data Models
StatusData
| Field | Type | Required | Notes |
|---|---|---|---|
| sport | string | yes | Current sport name |
| total_athletes | int | yes | Total roster count for sport |
| status_summary | object | yes | Out/Modified/Cleared counts |
| injury_breakdown | array | yes | Body part → count |
| recovery_timeline | array | yes | Active cases with ETA |
| recent_activity | array | yes | Last 10 events |
Message
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string | yes | UUID |
| sender | string | yes | "coach" or "at" |
| text | string | yes | Message body |
| timestamp | string | yes | ISO-8601 |
| status | string | yes | sent/delivered/read |
UI Components
Status Tab
| Component | Purpose | Data Source |
|---|---|---|
| StatusPills | Three-stat cards (Out/Mod/Cleared) | GET /status |
| InjuryBreakdown | Horizontal bar chart of body parts | GET /status |
| RecoveryTracker | Active cases with progress bars | GET /status |
| ActivityFeed | Chronological feed of recent changes | GET /status |
Messages Tab
| Component | Purpose | Data Source |
|---|---|---|
| ConversationList | List of athlete message threads | GET /messages/coach |
| ChatView | Message thread with athlete's AT | GET /messages/coach/{id}, POST |
| MessageBubble | Single message (sent vs received styling) | Embedded in ChatView |
States to Handle
Every component must handle:
- Loading: Skeleton/spinner while fetching
- Empty: Friendly "no data yet" message with icon
- Error: Actionable error message with retry button
- Populated: Real data display
File Structure
Backend (/home/hoffmann_admin/.openclaw/shared/build-20260506/backend/)
rtsport_coach_api.py ← New endpoints for status + messages
rtsport_messages.py ← Message model + storage (in-memory SQLite)
seed_coach_data.py ← Test coach data (if needed)
Write to Socrates' existing codebase at /home/hoffmann_admin/.openclaw/workspace-socrates/hoffdesk-api/
Frontend (/home/hoffmann_admin/.openclaw/shared/build-20260506/frontend/)
status-tab.html ← Status tab HTML/CSS (inline in dashboard or as partial)
messages-tab.html ← Messages tab HTML/CSS (inline in dashboard or as partial)
dashboard.js ← JS for tab switching, data fetching, chat interactions
The existing dashboard is at /home/hoffmann_admin/.openclaw/shared/build-20260501/frontend/templates/coach/dashboard.html — Daedalus should read that file and integrate the new tabs into it.
Success Criteria
- Status tab loads and shows real data — Coach sees Out/Modified/Cleared breakdown, injury chart, recovery tracker, activity feed
- Messages tab loads conversations — Coach sees list of athlete threads with unread counts
- Coach can open a conversation and send a message — Full chat flow works
- All states handled — Loading, empty, error, populated for both tabs
- Mobile-first, matches existing design system — Same color scheme, typography, spacing
- No regression on Roster tab — Existing dashboard functionality preserved
Integration Notes
- Backend mock data is acceptable for this build — use the existing mock pattern in
rtsport_mock.py - Messages can be in-memory (no persistence needed for demo)
- Status tab charts should be pure CSS — no chart library dependency
- The existing
apiRequest()wrapper handles auth — use it - Coach context:
currentSportvariable already exists, use it to filter API calls
Out of Scope
- Real-time WebSocket/SSE for messages (polling is fine for MVP)
- Push notifications
- File/image attachments in messages
- Multi-school support for AD view