441 lines
20 KiB
Markdown
441 lines
20 KiB
Markdown
# Empty States, Onboarding & Professional Exports — Design Spec
|
||
|
||
> **Date:** 2026-03-16
|
||
> **Product:** ResolutionFlow
|
||
> **Approach:** Bottom-up (foundation → empty states → onboarding → exports)
|
||
|
||
---
|
||
|
||
## Purpose
|
||
|
||
Make ResolutionFlow feel polished and professional by eliminating dead-end empty pages, guiding new users through setup, and providing client-ready PDF exports that MSPs can hand directly to customers.
|
||
|
||
---
|
||
|
||
## Scope
|
||
|
||
### In Scope
|
||
|
||
1. Illustrative empty states across 8 pages with benefit-oriented copy and "Learn more" guide links
|
||
2. Onboarding starter checklist widget on QuickStartPage (solo and team variants)
|
||
3. Team branding settings (logo upload, company display name)
|
||
4. PDF export via WeasyPrint with branded templates
|
||
5. Supporting data capture during sessions (text snippets + screenshots)
|
||
6. 7 in-app user guides linked from empty states
|
||
7. Tests: backend integration, frontend unit, Playwright e2e
|
||
|
||
### Out of Scope
|
||
|
||
- Bring-your-own-storage (S3/Azure) for supporting data — future feature
|
||
- Full file attachments beyond screenshots
|
||
- Removing "Powered by ResolutionFlow" footer (potential premium tier)
|
||
- Multi-browser Playwright matrix
|
||
|
||
---
|
||
|
||
## 1. Empty States
|
||
|
||
### Component Upgrade
|
||
|
||
Extend the existing `EmptyState.tsx` component to support the illustrative style:
|
||
|
||
- **SVG illustration slot** — optional prop, renders a brand-colored line-art illustration above the title
|
||
- **Benefit-oriented description** — explains what the page does and why it matters, not just "no data"
|
||
- **Primary CTA button** — navigates to the action that populates the page
|
||
- **Secondary "Learn more" link** — navigates to the relevant in-app guide
|
||
|
||
### Pages (8 total)
|
||
|
||
| Page | Title | Description | CTA | Guide Link |
|
||
|------|-------|-------------|-----|------------|
|
||
| Flow Library (no flows) | Build your first troubleshooting flow | Flows guide your team through proven resolution paths, capturing every decision along the way. | Create a Flow | `/guides/creating-flows` |
|
||
| Flow Library (no filter results) | No flows match your filters | Try adjusting your search or filters. | Clear Filters | — |
|
||
| Analytics (My/Team) | Track your troubleshooting performance | Analytics show resolution times, common paths, and team efficiency. Data appears automatically as you complete sessions. | Run Your First Session | `/guides/understanding-analytics` |
|
||
| Session History (empty) | Your session history will appear here | Every troubleshooting session is recorded with decisions, timing, and outcomes — ready for export or review. | Start a Session | `/guides/running-sessions` |
|
||
| Integrations | Connect your PSA for seamless workflows | Link ConnectWise or other PSA tools to pull ticket context into sessions and push documentation back automatically. | Connect Integration | `/guides/psa-setup` |
|
||
| Step Library (empty) | Build a reusable step library | Save common troubleshooting steps once, reuse them across flows. Keeps your team consistent and saves build time. | Browse Steps | `/guides/step-library` |
|
||
| Script Library (empty) | Automate with script templates | Pre-built and custom scripts your team can reference during sessions. PowerShell, bash, and more. | Explore Templates | `/guides/script-templates` |
|
||
| My Shares (empty) | Share session results with your team | Create shareable links to completed sessions for knowledge sharing and client communication. | View Sessions | `/guides/sharing-sessions` |
|
||
|
||
### Illustrations
|
||
|
||
Simple SVG line art using the cyan brand color palette (`#06b6d4` → `#22d3ee`). Each page gets a unique illustration relevant to its content. Lightweight — no complex animations or heavy graphics.
|
||
|
||
### Visual Style
|
||
|
||
- Container: centered content within the page's existing layout
|
||
- Illustration: 60-80px height, `opacity: 0.4` → `0.7` range (needs to be visible on `#101114` dark background)
|
||
- Title: `text-foreground`, `text-lg` (18px), `font-semibold` (matches existing `EmptyState.tsx`)
|
||
- Description: `text-muted-foreground`, 13px, max-width ~400px for readability
|
||
- CTA: `bg-gradient-brand` primary button style
|
||
- Learn more: `text-muted-foreground` with `→` arrow, hover brightens
|
||
|
||
---
|
||
|
||
## 2. Onboarding Starter Checklist
|
||
|
||
### Location
|
||
|
||
Dismissible `.glass-card` widget on QuickStartPage, positioned below the greeting and above the stats/activity sections.
|
||
|
||
### Visibility Rules
|
||
|
||
- Shows for users who haven't dismissed it and haven't completed all items
|
||
- Auto-hides with a brief "You're all set!" state once all items are checked, then disappears
|
||
- Dismissible at any time via "×" button
|
||
- Dismissed/completed state stored in a new `onboarding_dismissed` Boolean column on the `users` table (requires migration). Not using JSON — a simple column is clearer and queryable.
|
||
- Never reappears once dismissed or completed
|
||
|
||
### Completion Tracking
|
||
|
||
No new database table. A single API endpoint queries existing data to determine completion status.
|
||
|
||
**Endpoint:** `GET /api/v1/users/onboarding-status`
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"created_flow": true,
|
||
"ran_session": false,
|
||
"exported_session": false,
|
||
"tried_ai_assistant": false,
|
||
"invited_teammate": false,
|
||
"connected_psa": false,
|
||
"is_team_user": true,
|
||
"dismissed": false
|
||
}
|
||
```
|
||
|
||
**Completion queries:**
|
||
|
||
| Item | Condition |
|
||
|------|-----------|
|
||
| `created_flow` | User owns at least 1 tree |
|
||
| `ran_session` | User has at least 1 session |
|
||
| `exported_session` | User has at least 1 session with `exported=True` |
|
||
| `tried_ai_assistant` | User has at least 1 assistant chat |
|
||
| `invited_teammate` | Team has more than 1 member |
|
||
| `connected_psa` | Team has at least 1 PSA connection |
|
||
|
||
**Dismiss endpoint:** `POST /api/v1/users/onboarding-status/dismiss` — sets `onboarding_dismissed=True` on the user record.
|
||
|
||
### Checklist Variants
|
||
|
||
**Solo pro (4 items):**
|
||
|
||
1. Create your first flow → navigates to Flow Library
|
||
2. Run your first session → navigates to Flow Library
|
||
3. Export a session → navigates to Session History
|
||
4. Try the AI assistant → navigates to AI Chat
|
||
|
||
**Team admin (5 items):**
|
||
|
||
1. Create your first flow → navigates to Flow Library
|
||
2. Invite a team member → navigates to Team Settings
|
||
3. Run your first session → navigates to Flow Library
|
||
4. Connect a PSA integration → navigates to Integrations
|
||
5. Export a session → navigates to Session History
|
||
|
||
### Visual Design
|
||
|
||
- `.glass-card` container with `border-radius: 16px`
|
||
- Cyan progress bar at top showing completion (e.g., "2 of 5 complete")
|
||
- Section label: "Getting Started" in `font-label text-[0.625rem] uppercase tracking-[0.1em]`
|
||
- Each item: checkbox (auto-checked with cyan fill when complete) + label + subtle navigation arrow
|
||
- Completed items: muted text with cyan checkmark
|
||
- Uncompleted items: `text-foreground` with hover highlight, clickable to navigate
|
||
|
||
---
|
||
|
||
## 3. Team Branding & Logo Upload
|
||
|
||
### Location
|
||
|
||
New "Branding" section on the existing Team Settings page. Team admin only. Solo pros get a simpler version on their Account Settings page.
|
||
|
||
### Fields
|
||
|
||
- **Company logo** — image upload (PNG, JPG, or SVG, max 2MB)
|
||
- **Company display name** — text field, falls back to team name if empty
|
||
- **Logo preview** — shows how the logo will appear on exports
|
||
|
||
### Backend
|
||
|
||
**New columns on `teams` table:**
|
||
|
||
- `logo_data` — Text, base64-encoded image data, nullable
|
||
- `logo_content_type` — String (e.g., `image/png`), nullable
|
||
- `company_display_name` — String, nullable (falls back to `team.name`)
|
||
|
||
**Endpoints:**
|
||
|
||
- `PATCH /api/v1/teams/{team_id}/branding` — upload logo (multipart form) + display name. Team admin only.
|
||
- `GET /api/v1/teams/{team_id}/branding` — retrieve logo data + display name. Any team member.
|
||
- `DELETE /api/v1/teams/{team_id}/branding/logo` — remove logo. Team admin only.
|
||
|
||
**Validation:**
|
||
|
||
- File size: max 2MB
|
||
- Content type: `image/png`, `image/jpeg`, `image/svg+xml`
|
||
- Solo pros: branding columns (`logo_data`, `logo_content_type`, `company_display_name`) added directly to the `users` table. Same schema as teams. Solo pros without a team still get branded exports.
|
||
|
||
### Why Base64 in DB
|
||
|
||
Logos are small (< 2MB raw, ~2.67MB base64-encoded) and there's one per team/user. The 2MB validation limit applies to the raw uploaded file size (before base64 encoding). Avoids S3/file storage dependency entirely. Easy to migrate to object storage later when BYOS is implemented for supporting data.
|
||
|
||
---
|
||
|
||
## 4. PDF Export via WeasyPrint
|
||
|
||
### Backend
|
||
|
||
**New dependency:** `weasyprint` in `requirements.txt`
|
||
|
||
**System dependencies (required by WeasyPrint):**
|
||
- `libpango1.0-dev`, `libcairo2-dev`, `libgdk-pixbuf2.0-dev`, `libffi-dev`
|
||
- Must be added to the Railway Dockerfile via `apt-get install`
|
||
- Local dev: install via system package manager (`apt-get` on Ubuntu/Debian)
|
||
- CI: add to the e2e job's setup step
|
||
|
||
**Export service changes:**
|
||
|
||
- New `generate_pdf()` method in `export_service.py`
|
||
- Renders a Jinja2 HTML template with session data + branding, then converts to PDF via WeasyPrint
|
||
- Template location: `backend/app/templates/export_pdf.html`
|
||
|
||
**Existing endpoint change:**
|
||
|
||
- `POST /sessions/{session_id}/export` gains `format: "pdf"` option
|
||
- Update `SessionExport` schema: change `format` field pattern from `^(text|markdown|html|psa)$` to `^(text|markdown|html|psa|pdf)$`
|
||
- PDF format returns `Response(content=pdf_bytes, media_type="application/pdf")` with `Content-Disposition: attachment; filename="session-export-{id}.pdf"` header (different return type from the existing `PlainTextResponse` used by other formats — endpoint must branch on format)
|
||
- Non-PDF formats continue returning `PlainTextResponse` as before
|
||
|
||
### PDF Template Structure
|
||
|
||
Matches the approved mockup layout:
|
||
|
||
1. **Header** — Report type label (e.g., "Troubleshooting Report"), flow title, MSP logo or ResolutionFlow logo, company name
|
||
2. **Metadata grid** — Engineer, Client, Ticket #, Date, Duration, Outcome (3×2 grid)
|
||
3. **Summary** — AI-generated session summary (from existing feature)
|
||
4. **Troubleshooting Path** — Visual timeline with cyan step dots, step titles, and decisions at each node. Final resolution step uses green dot.
|
||
5. **Supporting Data** — Labeled text snippets (rendered as code blocks) + embedded screenshot images
|
||
6. **Footer** — Generation timestamp (left) + "Powered by ResolutionFlow" (right)
|
||
|
||
### CSS/Styling
|
||
|
||
- White background, dark text (print-optimized)
|
||
- Cyan accent color (`#06b6d4`) for section borders, timeline dots, and branding
|
||
- `@page` rules for margins, header/footer positioning
|
||
- Page break before Supporting Data section if content runs long
|
||
- `break-inside: avoid` on individual supporting data items
|
||
- JetBrains Mono for code/command output blocks
|
||
|
||
### Frontend Changes
|
||
|
||
- Add "PDF" to the format selector in `ExportPreviewModal`
|
||
- PDF option triggers a direct file download (no textarea preview — PDFs aren't editable inline). The modal should switch to a "download-only" mode when PDF is selected: hide the textarea, show a download button with loading state. The format selector stays visible for switching between formats.
|
||
- Show a loading spinner while PDF generates server-side
|
||
- Existing formats (markdown, text, HTML, PSA) continue to work as before with the textarea preview
|
||
|
||
### Branding Logic
|
||
|
||
1. If team has a logo → use team logo + company display name in header, "Powered by ResolutionFlow" in footer
|
||
2. If no team logo → use ResolutionFlow logo in header, no "Powered by" footer (it's already the primary brand)
|
||
3. Solo pro with logo → same as team logo behavior
|
||
|
||
---
|
||
|
||
## 5. Supporting Data Capture
|
||
|
||
### Database
|
||
|
||
**New table: `session_supporting_data`**
|
||
|
||
| Column | Type | Notes |
|
||
|--------|------|-------|
|
||
| `id` | UUID | Primary key |
|
||
| `session_id` | UUID | FK to sessions |
|
||
| `label` | String(255) | User-provided label (e.g., "Port Scan Output") |
|
||
| `data_type` | Enum | `text_snippet` or `screenshot` |
|
||
| `content` | Text | Raw text or base64-encoded image |
|
||
| `content_type` | String(50) | Nullable. e.g., `image/png` for screenshots |
|
||
| `sort_order` | Integer | Display ordering |
|
||
| `created_at` | DateTime(timezone=True) | Auto-set |
|
||
| `updated_at` | DateTime(timezone=True) | Auto-set, auto-update |
|
||
|
||
### API Endpoints
|
||
|
||
- `POST /api/v1/sessions/{session_id}/supporting-data` — add an item (label, type, content). Returns created item.
|
||
- `GET /api/v1/sessions/{session_id}/supporting-data` — list all items for a session, ordered by `sort_order`.
|
||
- `PATCH /api/v1/sessions/{session_id}/supporting-data/{id}` — update label or content.
|
||
- `DELETE /api/v1/sessions/{session_id}/supporting-data/{id}` — remove an item.
|
||
|
||
### Validation
|
||
|
||
- Image size: max 2MB per screenshot (keeps DB growth manageable — at 20 items × 2.67MB base64 = ~53MB worst case per session)
|
||
- Text snippet: max 50,000 characters
|
||
- Max 20 items per session
|
||
- Only the session owner or team admins can add/delete
|
||
- Monitor DB size growth in production — if supporting data exceeds expectations, prioritize BYOS migration
|
||
|
||
### Session Runner UI
|
||
|
||
- **"Add Supporting Data" button** — positioned near the existing notes input in both troubleshooting and procedural session runners
|
||
- **Add modal** with two tabs/options:
|
||
- **Text Snippet** — label input + multiline textarea
|
||
- **Screenshot** — label input + drag-and-drop zone / file picker + clipboard paste (`Ctrl+V`) support
|
||
- **Supporting data list** — collapsible section below session notes showing added items:
|
||
- Each item: type icon (code bracket for text, image icon for screenshot) + label + preview (truncated text or thumbnail) + delete button
|
||
- No reordering in v1 — items display in creation order
|
||
|
||
### Export Integration
|
||
|
||
Supporting data is included in all export formats:
|
||
|
||
| Format | Text Snippets | Screenshots |
|
||
|--------|--------------|-------------|
|
||
| Markdown | Labeled fenced code blocks | `` or `[Screenshot: label]` |
|
||
| Plain Text | Labeled indented blocks | `[Screenshot: {label}]` placeholder |
|
||
| HTML | `<pre>` blocks with labels | `<img>` tags with base64 src |
|
||
| PSA | Labeled code blocks (markdown) | `[Screenshot: {label}]` placeholder |
|
||
| PDF | Styled code blocks matching mockup | Embedded images |
|
||
|
||
---
|
||
|
||
## 6. User Guides
|
||
|
||
### Route
|
||
|
||
`/guides/:slug` — new frontend route inside the authenticated app shell.
|
||
|
||
### Implementation
|
||
|
||
- Markdown files stored in `frontend/src/content/guides/`
|
||
- Written as React components in `frontend/src/pages/guides/` (avoids `react-markdown` dependency for only 7 short pages). Each guide is a simple functional component using existing typography classes.
|
||
- Displayed in a `.glass-card-static` container within the standard app shell layout
|
||
- Simple breadcrumb: "Guides → {title}"
|
||
- Images/illustrations stored in `frontend/public/guides/` and referenced via absolute paths
|
||
- Unknown slugs show a "Guide not found" empty state with a link back to the dashboard
|
||
|
||
### Guides (7)
|
||
|
||
| Slug | Title | Content Covers |
|
||
|------|-------|---------------|
|
||
| `creating-flows` | Creating Flows | Manual flow creation, AI-assisted creation, flow types (troubleshooting, procedural, maintenance), basic editor usage |
|
||
| `understanding-analytics` | Understanding Analytics | What each metric means, how data populates over time, team vs personal views |
|
||
| `running-sessions` | Running Sessions | Starting a session, navigating decisions, adding notes, adding supporting data, completing and exporting |
|
||
| `psa-setup` | Connecting Your PSA | ConnectWise setup walkthrough, where to find API credentials, what the integration enables |
|
||
| `step-library` | Using the Step Library | Browsing shared steps, adding steps to flows, creating reusable steps |
|
||
| `script-templates` | Script Templates | Browsing templates, using scripts during sessions, creating custom templates |
|
||
| `sharing-sessions` | Sharing Sessions | Creating share links, public vs account-only access, revoking shares |
|
||
|
||
### Guide Content Style
|
||
|
||
- Concise — each guide should be 300-600 words
|
||
- Task-oriented — "How to do X" structure, not reference documentation
|
||
- Include relevant screenshots/illustrations where helpful
|
||
- End with a CTA that links back to the relevant feature page
|
||
|
||
---
|
||
|
||
## 7. Testing
|
||
|
||
### Backend Integration Tests (pytest)
|
||
|
||
**Onboarding status:**
|
||
- Returns correct booleans for a fresh user (all false)
|
||
- Returns `created_flow: true` after user creates a tree
|
||
- Returns `ran_session: true` after user starts a session
|
||
- Returns correct `is_team_user` flag
|
||
- Dismiss endpoint sets `dismissed: true`
|
||
|
||
**Team branding:**
|
||
- Upload logo — stores base64, returns success
|
||
- Upload oversized file — returns 400
|
||
- Upload invalid content type — returns 400
|
||
- Retrieve branding — returns logo data + display name
|
||
- Delete logo — clears logo data
|
||
- Non-admin cannot update branding — returns 403
|
||
|
||
**Supporting data:**
|
||
- Create text snippet — stores and returns item
|
||
- Create screenshot — stores base64 and returns item
|
||
- List items — returns in sort order
|
||
- Delete item — removes from DB
|
||
- Exceed 20 item limit — returns 400
|
||
- Exceed 2MB screenshot — returns 400
|
||
- Non-owner cannot add to session — returns 403
|
||
|
||
**PDF export:**
|
||
- Generate PDF — returns valid PDF bytes with correct content type
|
||
- PDF includes branding when team has logo
|
||
- PDF uses ResolutionFlow defaults when no team logo
|
||
- PDF includes supporting data items
|
||
- PDF handles session with no supporting data gracefully
|
||
|
||
### Frontend Unit Tests (Vitest)
|
||
|
||
- `EmptyState` component — renders illustration, title, description, CTA, and learn more link with correct props
|
||
- `EmptyState` without optional props — renders without illustration or learn more link
|
||
- Onboarding checklist — renders correct items for solo vs team user
|
||
- Onboarding checklist — completed items show cyan checkmark and muted style
|
||
- Onboarding checklist — dismiss button calls dismiss endpoint
|
||
- Export format selector — includes PDF option
|
||
- Export modal — PDF selection triggers download behavior instead of textarea preview
|
||
|
||
### Playwright E2E Tests
|
||
|
||
- **Empty state flow** — log in as fresh user, navigate to Flow Library, verify empty state renders with CTA and "Learn more" link
|
||
- **Onboarding checklist** — log in as fresh user, verify checklist visible on dashboard, create a flow, verify checklist item updates
|
||
- **PDF export** — complete a session, navigate to session detail, select PDF format, verify download triggers
|
||
- **Guide page** — click "Learn more" from an empty state, verify guide page loads with content
|
||
|
||
These Playwright tests focus on happy paths only — one representative flow per feature area.
|
||
|
||
---
|
||
|
||
## Implementation Order (Bottom-Up)
|
||
|
||
### PR 1: Backend Foundation
|
||
- `onboarding_dismissed` column on `users` table + migration
|
||
- Team branding columns (`logo_data`, `logo_content_type`, `company_display_name`) on `teams` table + migration
|
||
- Solo pro branding columns on `users` table + migration (can combine with onboarding migration)
|
||
- Branding CRUD endpoints
|
||
- Supporting data table + migration
|
||
- Supporting data CRUD endpoints (POST, GET, PATCH, DELETE)
|
||
- Onboarding status endpoint + dismiss endpoint
|
||
- WeasyPrint dependency + system deps in Dockerfile + PDF generation in export service
|
||
- Update `SessionExport` schema format pattern to include `pdf`
|
||
- Backend tests for all of the above
|
||
|
||
### PR 2: Empty States + Guides
|
||
- Upgrade `EmptyState.tsx` component
|
||
- Roll out across 8 pages
|
||
- Create 7 markdown guides
|
||
- Add `/guides/:slug` route
|
||
- Frontend unit tests for EmptyState
|
||
|
||
### PR 3: Onboarding Checklist
|
||
- QuickStartPage checklist widget
|
||
- Solo vs team variant logic
|
||
- Dismiss and auto-complete behavior
|
||
- Frontend unit tests
|
||
- Playwright test for checklist
|
||
|
||
### PR 4: PDF Export + Supporting Data UI
|
||
- Supporting data capture in session runner
|
||
- PDF format option in ExportPreviewModal
|
||
- Team branding section on Team Settings page
|
||
- Playwright tests for export and empty states
|
||
|
||
---
|
||
|
||
## Future Considerations
|
||
|
||
- **BYOS (Bring Your Own Storage):** Allow MSPs to configure their own S3/Azure blob storage for supporting data. Removes our storage burden and addresses data sovereignty.
|
||
- **Premium branding tier:** Option to remove "Powered by ResolutionFlow" footer for higher-tier plans.
|
||
- **Full file attachments:** Extend supporting data to accept arbitrary file types (logs, configs, CSVs) once object storage is in place.
|
||
- **Export templates:** Let teams customize the PDF template layout, colors, and sections included.
|
||
- **Onboarding expansion:** Feature tours, tooltips, and contextual help beyond the starter checklist.
|