20 KiB
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
- Illustrative empty states across 8 pages with benefit-oriented copy and "Learn more" guide links
- Onboarding starter checklist widget on QuickStartPage (solo and team variants)
- Team branding settings (logo upload, company display name)
- PDF export via WeasyPrint with branded templates
- Supporting data capture during sessions (text snippets + screenshots)
- 7 in-app user guides linked from empty states
- 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.7range (needs to be visible on#101114dark background) - Title:
text-foreground,text-lg(18px),font-semibold(matches existingEmptyState.tsx) - Description:
text-muted-foreground, 13px, max-width ~400px for readability - CTA:
bg-gradient-brandprimary button style - Learn more:
text-muted-foregroundwith→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_dismissedBoolean column on theuserstable (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:
{
"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):
- Create your first flow → navigates to Flow Library
- Run your first session → navigates to Flow Library
- Export a session → navigates to Session History
- Try the AI assistant → navigates to AI Chat
Team admin (5 items):
- Create your first flow → navigates to Flow Library
- Invite a team member → navigates to Team Settings
- Run your first session → navigates to Flow Library
- Connect a PSA integration → navigates to Integrations
- Export a session → navigates to Session History
Visual Design
.glass-cardcontainer withborder-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-foregroundwith 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, nullablelogo_content_type— String (e.g.,image/png), nullablecompany_display_name— String, nullable (falls back toteam.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 theuserstable. 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-geton Ubuntu/Debian) - CI: add to the e2e job's setup step
Export service changes:
- New
generate_pdf()method inexport_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}/exportgainsformat: "pdf"option- Update
SessionExportschema: changeformatfield pattern from^(text|markdown|html|psa)$to^(text|markdown|html|psa|pdf)$ - PDF format returns
Response(content=pdf_bytes, media_type="application/pdf")withContent-Disposition: attachment; filename="session-export-{id}.pdf"header (different return type from the existingPlainTextResponseused by other formats — endpoint must branch on format) - Non-PDF formats continue returning
PlainTextResponseas before
PDF Template Structure
Matches the approved mockup layout:
- Header — Report type label (e.g., "Troubleshooting Report"), flow title, MSP logo or ResolutionFlow logo, company name
- Metadata grid — Engineer, Client, Ticket #, Date, Duration, Outcome (3×2 grid)
- Summary — AI-generated session summary (from existing feature)
- Troubleshooting Path — Visual timeline with cyan step dots, step titles, and decisions at each node. Final resolution step uses green dot.
- Supporting Data — Labeled text snippets (rendered as code blocks) + embedded screenshot images
- 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 @pagerules for margins, header/footer positioning- Page break before Supporting Data section if content runs long
break-inside: avoidon 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
- If team has a logo → use team logo + company display name in header, "Powered by ResolutionFlow" in footer
- If no team logo → use ResolutionFlow logo in header, no "Powered by" footer (it's already the primary brand)
- 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 bysort_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 |
| 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/(avoidsreact-markdowndependency for only 7 short pages). Each guide is a simple functional component using existing typography classes. - Displayed in a
.glass-card-staticcontainer 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: trueafter user creates a tree - Returns
ran_session: trueafter user starts a session - Returns correct
is_team_userflag - 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)
EmptyStatecomponent — renders illustration, title, description, CTA, and learn more link with correct propsEmptyStatewithout 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_dismissedcolumn onuserstable + migration- Team branding columns (
logo_data,logo_content_type,company_display_name) onteamstable + migration - Solo pro branding columns on
userstable + 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
SessionExportschema format pattern to includepdf - Backend tests for all of the above
PR 2: Empty States + Guides
- Upgrade
EmptyState.tsxcomponent - Roll out across 8 pages
- Create 7 markdown guides
- Add
/guides/:slugroute - 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.