P247 iOS: Plan Tab Implementation Brief
P247 iOS: Plan Tab Implementation Brief
Date: 25 March 2026
For: iOS Development Team
From: Myles Bruggeling, Founder
Status: Backend in progress. iOS work can begin on UI scaffolding now.
Production API: https://app.p247.io
Auth: x-api-key header on all requests
What This Is
The “Events” tab becomes the “Plan” tab. It’s the athlete’s single action screen for the day: completed workouts, coach-suggested sessions, scheduled events, and recovery alerts in one chronological feed.
The coach can now generate complementary workout suggestions (e.g. “you did push this morning, here’s a pull session for this afternoon”). Those suggestions surface as actionable cards in the Plan tab alongside existing events.
Mockup: See p247/mockups/plan-tab.html (render at 393px width) or plan-tab.png.
Tab Bar Change
| Before | After |
|---|---|
| ⚡ Today | 💬 Coach | 🔔 Events | 👤 Profile | ⚡ Today | 💬 Coach | 📋 Plan | 👤 Profile |
- Rename “Events” to “Plan”
- Change icon from bell to clipboard (SF Symbol:
list.clipboardorchecklist) - Active state: system blue (
#007AFF)
Plan Tab Structure
The Plan tab is a vertical scrolling feed, grouped into sections with a “now” marker separating past from future.
Sections (top to bottom)
- Completed (items before “now”)
- Now marker (blue dot + horizontal line + “Now” label)
- Up Next (actionable items from now onward)
Card Types
Each item in the feed is a card. Four types exist:
| Type | Badge | Left border | Source |
|---|---|---|---|
| Completed workout | ✓ Done (green) | #34C759 |
Apple Health sync or Strava |
| Coach suggestion | ✨ Coach (purple) | #7C4DFF |
GET /plan/today |
| Scheduled event | 📅 Event (blue) | #2196F3 |
GET /events/ (existing) |
| Recovery alert | ⚠ Recovery (orange) | #FF9800 |
GET /plan/today |
API Integration
New endpoint: GET /plan/today
Returns the athlete’s plan items for today (coach suggestions + recovery alerts + completed workouts).
{
"date": "2026-03-25",
"items": [
{
"id": "plan_1",
"type": "completed_workout",
"title": "Hybrid S&C Class",
"subtitle": "CrossFit Dural · 52 min",
"time": "06:00",
"status": "completed",
"stats": {
"calories": 487,
"avg_hr": 142,
"load": 38
}
},
{
"id": "plan_2",
"type": "recovery_alert",
"title": "Sleep Crisis: 4.8h",
"subtitle": "Non-negotiable early bedtime tonight. Target 9:30pm.",
"priority": "high",
"status": "active"
},
{
"id": "plan_3",
"type": "coach_suggestion",
"title": "PM Session: Pull + Mobility",
"subtitle": "Complements this morning's push-dominant class. Low impact on knees.",
"time": "~16:30",
"estimated_duration_min": 35,
"status": "pending",
"coach_note": "Based on your 6am workout image. Avoids front squats and loaded lunges (patella rehab).",
"exercises": [
{
"name": "Barbell Rows",
"sets": "4",
"reps": "8-10",
"rest": "90s",
"muscle_group": "Back"
},
{
"name": "Pull-ups",
"sets": "3",
"reps": "AMRAP",
"rest": "90s",
"muscle_group": "Lats"
},
{
"name": "Face Pulls",
"sets": "3",
"reps": "15",
"rest": "60s",
"muscle_group": "Rear Delt"
},
{
"name": "Banded Hip Mobility Flow",
"sets": "2",
"reps": "5 min",
"rest": "continuous",
"muscle_group": "Mobility"
},
{
"name": "Dead Hangs",
"sets": "3",
"reps": "30-45s",
"rest": null,
"muscle_group": "Grip"
}
]
}
]
}
Existing endpoint: GET /events/
Returns scheduled events (races, games, appointments). These are merged client-side into the Plan feed by time.
New endpoint: POST /plan/{item_id}/start
When the athlete taps “Start Workout” on a coach suggestion card. Marks it as in-progress.
New endpoint: POST /plan/{item_id}/complete
When the athlete finishes the workout (or taps “Done”). Accepts optional completion data.
{
"completed_exercises": [
{"name": "Barbell Rows", "completed": true},
{"name": "Pull-ups", "completed": true},
{"name": "Face Pulls", "completed": true},
{"name": "Banded Hip Mobility Flow", "completed": false},
{"name": "Dead Hangs", "completed": true}
],
"notes": "Felt good, skipped mobility as running late"
}
New endpoint: DELETE /plan/{item_id}
Dismiss/remove a plan item (e.g. athlete decides not to do the suggested session).
Card Layouts (SwiftUI)
Completed Workout Card
┌─────────────────────────────────────┐
│ ✓ Done 6:00 AM │ <- green badge, grey time
│ ✅ Hybrid S&C Class │ <- title with check emoji
│ CrossFit Dural · 52 min │ <- grey subtitle
│ │
│ Calories Avg HR Load │
│ 487 kcal 142 bpm +38 │ <- stat row
└─────────────────────────────────────┘
- Left border: 3px green (
#34C759) - Stats row: 3 columns, label above value
Coach Suggestion Card
┌─────────────────────────────────────┐
│ ✨ Coach ~4:30 PM · 35 min │ <- purple badge
│ 🏋️ PM Session: Pull + Mobility │ <- title
│ Complements this morning's push... │ <- subtitle
│ │
│ 💡 Based on your 6am workout image. │ <- coach note (purple bg)
│ Avoids front squats (patella rehab) │
│ │
│ ┌──────────────────────────────────┐ │
│ │ □ Barbell Rows │ │
│ │ 4 × 8-10 · 90s rest Back │ │
│ ├──────────────────────────────────┤ │
│ │ □ Pull-ups │ │
│ │ 3 × AMRAP · 90s rest Lats │ │
│ ├──────────────────────────────────┤ │
│ │ □ Face Pulls │ │
│ │ 3 × 15 · 60s rest Rear Delt│ │
│ ├──────────────────────────────────┤ │
│ │ □ Banded Hip Mobility Flow │ │
│ │ 2 × 5 min · continuous Mob. │ │
│ ├──────────────────────────────────┤ │
│ │ □ Dead Hangs │ │
│ │ 3 × 30-45s Grip │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ ▶ Start Workout │ │ <- purple button
│ └──────────────────────────────────┘ │
└─────────────────────────────────────┘
- Left border: 3px purple (
#7C4DFF) - Background: subtle gradient white to light purple (
#F8F5FF) - Exercise list: white cards within the card, 1px purple-tinted border
- Checkboxes: tappable, track completion per exercise
- Muscle group tags: small purple pill badges, right-aligned
- Coach note: light purple background with 💡 icon
- “Start Workout” button: solid purple, full width
Recovery Alert Card
┌─────────────────────────────────────┐
│ ⚠ Recovery Priority │ <- orange badge
│ 😴 Sleep Crisis: 4.8h │ <- orange title
│ Non-negotiable early bedtime tonight.│
│ Target 9:30pm. │
└─────────────────────────────────────┘
- Left border: 3px orange (
#FF9800) - Background: subtle gradient white to light orange (
#FFF8F0)
Event Card
┌─────────────────────────────────────┐
│ 📅 Event 3:00 PM │ <- blue badge
│ ⚽ Dominic's Game │
│ Marian College vs St Pauls │
└─────────────────────────────────────┘
- Left border: 3px blue (
#2196F3) - Standard white background
Now Marker
Between the Completed section and Up Next section:
─────────── ● Now ───────────
- Blue dot (
#007AFF), 8px diameter - Hairline rules extending left and right
- “Now” label in system blue, 12px semibold
Merging Logic (Client-Side)
The app calls two endpoints and merges results:
GET /plan/today(coach suggestions, recovery alerts, completed workouts)GET /events/(scheduled events, filtered to today)
Sort order:
- Completed items first (sorted by time, most recent on top)
- Now marker
- Active items sorted by: recovery alerts (priority), then by time, then coach suggestions without a specific time
Refresh: Pull-to-refresh reloads both endpoints. Background refresh every 15 minutes.
Coach Integration
When the coach generates a workout suggestion via chat, it simultaneously creates a plan item via an internal call. The athlete sees:
- In the Coach tab: the coach’s conversational response (“I’ve put together a PM session for you…”)
- In the Plan tab: the structured workout card with exercises and a Start button
The coach message should include a line like: “I’ve added this to your Plan. Tap the Plan tab when you’re ready to start.”
This means the POST /agent/messages endpoint, when it detects a workout suggestion in the coach’s response, auto-creates a plan item. This is handled server-side; the iOS app doesn’t need to parse coach messages.
Empty State
When no plan items exist for today:
📋
No plan for today yet.
Ask your coach for a workout suggestion,
or add an event.
[Ask Coach] [Add Event]
“Ask Coach” deep-links to the Coach tab. “Add Event” opens the event creation flow.
Exercise Checkbox Behaviour
- Tap checkbox: toggles complete state (visual only until workout is finished)
- When all exercises checked OR athlete taps “Finish Workout”: calls
POST /plan/{item_id}/complete - Completion data (which exercises were done) is sent to the backend
- Completed coach suggestion cards move to the “Completed” section and show stats if available
Accessibility
- All cards: VoiceOver labels describing card type, title, and key info
- Checkboxes: accessible toggle with exercise name as label
- Now marker: accessible label “Current time separator”
- Start Workout button: minimum 44pt tap target
Design Tokens
| Token | Value | Usage |
|---|---|---|
cardCornerRadius |
16pt | All cards |
innerCardRadius |
10pt | Exercise items within coach card |
cardPadding |
16pt | Card internal padding |
cardMargin |
12pt | Between cards |
sectionLabelSize |
13pt semibold | “Completed”, “Up Next” labels |
badgeSize |
10pt bold | Card type badges |
titleSize |
17pt semibold | Card titles |
subtitleSize |
14pt regular | Card subtitles |
borderWidth |
3pt | Left accent border |
Implementation Order
- Tab rename + icon (5 min, ship immediately)
- Card components (CompletedWorkoutCard, CoachSuggestionCard, RecoveryAlertCard, EventCard)
- Plan feed view with sections and now marker
- API integration (
GET /plan/today+ merge withGET /events/) - Exercise checkboxes + start/complete flow
- Empty state
- Pull-to-refresh + background refresh
This brief covers the iOS side. Backend implementation is happening in parallel. Endpoint schemas above are the contract.