Table of Contents
13 — Published sleep plans
Technical reference. For task-based help, see the corresponding user guide.
Documentation status: complete for the current implementation
Reviewed: 2026-07-21
Primary users: linked family members and their assigned coach
Surfaces: mobile web app and platform API
Purpose
Published sleep plans present the coach's current guidance to a family in a branded, mobile-friendly format. A plan can combine ordered sections, paragraphs, bullet points, images and an optional PDF attachment. Draft and archived versions stay in the coach authoring workflow and are never returned by the family-facing list.
User outcomes
| User | Outcome |
|---|---|
| Family member | Read the coach's published guidance, view supporting images and open the attached PDF. |
| Assigned coach | Review exactly what the family can see from the coach view-as route. |
| Coach author | Keep draft/history work private until an explicit publish; see Feature 21. |
Entry points
| Surface | Route | Function |
|---|---|---|
| Family mobile | /family/sleep-plans | List and read published plans from the current coach relationship. |
| Coach mobile | /coach/families/:familyId/sleep-plans | Read the family-visible published plan collection in coach context. |
| Coach portal | Session prep → Sleep plan | Author the current plan and manage history; covered in Feature 21. |
Published plan model
A family-visible plan carries coach and organization identity, practice branding, title, version, publication timestamps, ordered sections, and optional PDF metadata.
Each plan has 1–30 sections. A section has a title and up to 80 ordered blocks:
| Block | Presentation | Contract |
|---|---|---|
| Paragraph | Normal body copy. | Non-empty text, up to 4,000 characters. |
| Bullet | One list item. Adjacent bullet blocks form the visible list. | Non-empty text, up to 4,000 characters. |
| Image | Signed private image with optional caption and alternative text. | Stored asset identifier, file metadata, optional alt text and caption. |
The plan title and each section title are limited to 180 characters. Image alt text and captions are limited to 500 characters. The family renderer preserves the coach-defined section and block order.
Functional flow
1. Load family-visible plans
- The family route asks the platform for the current relationship's sleep plans.
- The platform returns only records whose status is
published. - The coach read route applies the same published-only filter, so coach view-as matches the family experience rather than exposing drafts.
- Cards display the organization/coach attribution, branded colours and plan title before rendering the ordered content.
- The contract supports a collection. Normal authoring maintains one current non-archived plan, but the family screen safely renders every published record returned by the API.
2. Render rich sections
- Paragraphs and bullets are rendered as readable structured copy, not as raw editor markup.
- An image block requests a signed download URL from the
sleep-plan-imagesbucket with a 900-second lifetime. - Alternative text falls back in this order: explicit alt text, caption, file name.
- A loading placeholder is shown while the URL is being signed and the image is loading.
- If signing or image loading fails, the screen shows an image-unavailable state without hiding the rest of the plan.
3. Open the PDF attachment
- If
pdfAssetIdand file metadata are present, the plan displays an attachment action. - Tapping it requests a fresh 900-second signed URL from
sleep-plan-pdfs. - The WebView asks the native wrapper or host browser to open that URL externally.
- If the URL cannot be signed or the host declines to open it, the user sees Could not open the sleep plan PDF and can retry.
Signed URLs are intentionally temporary. Reopening an image screen or tapping the PDF again requests a fresh URL; the durable plan stores only the private asset identifier and metadata.
4. Publication changes
- A coach draft is invisible to the family.
- Publishing makes the saved plan available on the next load or realtime refresh path.
- Saving a previously published current plan as draft removes it from the family-facing published list until it is published again.
- Archiving removes that current plan from family visibility and creates a new coach draft.
- Restoring history creates/replaces a coach draft; it does not silently republish the archived content.
Loading, empty and failure states
| State | Expected behavior |
|---|---|
| Initial load | Show a plan-loading state. |
| No published plan | Explain that published sleep plans from the coach will appear here. |
| Collection failure | Show the API error and a retry action. |
| Image URL/loading failure | Keep the surrounding plan readable and show an image placeholder. |
| PDF open failure | Keep the plan open, show corrective feedback and permit another attempt. |
| Signed URL expired | A new image mount or PDF action obtains a fresh signed URL. |
| Relationship no longer eligible | Do not return artifacts from a former or inaccessible coach assignment. |
Platform API contract
| Method and path | Role in this feature | Authentication |
|---|---|---|
GET /families/me/sleep-plans | Return the signed-in family's published plan collection. | Family identity and current active assignment |
GET /coach/families/:familyId/sleep-plans | Return the same published-only collection for coach review. | Authorized assigned coach with record access |
POST /files/signed-download-url | Create a short-lived URL for an authorized image or PDF asset. | Authenticated actor with read access to the family artifact |
GET /coach/families/:familyId/sleep-plan | Return/create the coach's current draft plus history. | Coach authoring route; documented in Feature 21 |
Authorization and privacy rules
- Family routes derive the family identity from the authenticated session; a family cannot request another family's plan collection.
- Only
publishedplans cross the family-facing boundary. Draft and archived records remain coach-only. - Coach reads require the matching active assignment, active organization membership, eligible organization and billing, and support record access.
- Private files remain in dedicated Supabase Storage buckets. The API authorizes the request before issuing a time-limited download URL.
- A signed URL is a temporary bearer link and must not be copied into durable page content, logs or analytics.
- Practice branding shown on the card comes from trusted organization metadata stored with/resolved for the artifact.
Acceptance checklist
- Family and coach view-as routes render only published plans.
- Section and paragraph/bullet/image order matches the stored plan.
- Image signing uses the correct private bucket and a short expiry, with useful alt-text fallback.
- A signed PDF opens through the host bridge and reports failure without losing the page.
- Empty, API-error, image-error and PDF-error states are recoverable.
- Saving as draft, archiving or restoring history never exposes un-published edits to the family.
- An unrelated or ineligible coach cannot request plan content or signed file URLs.
Source evidence
Mobile web
mobile-web/src/routes/FamilySleepPlansScreen.tsxmobile-web/src/webview/native-messages.tsmobile-web/src/api/client.ts
Platform and contracts
platform/apps/api/src/routes/family-routes.tsplatform/apps/api/src/routes/coach-routes.tsplatform/apps/api/src/services/family-profile-artifact-service.tsplatform/apps/api/src/services/prisma-file-url-service.tsplatform/packages/shared/src/family-profile-artifacts.ts
