[P0] Onboarding & empty state experience #101

Open
opened 2026-04-01 22:50:24 +02:00 by claude · 1 comment
Owner

Parent Epic

#100

Problem

When a new team deploys Cameleer3 and opens the UI with zero agents connected, they see empty tables with no guidance. The first-run experience is where 80% of trial users are lost. Competing tools (Datadog, Grafana Cloud) nail guided setup — Cameleer currently has nothing.

Current State

  • Empty exchanges table with "0 of 0 exchanges"
  • Empty sidebar with no applications
  • Dashboard shows zeroed KPIs
  • Runtime shows "0 agents"
  • No indication of what to do next

Proposed Solution

1. Welcome / Getting Started Screen

When no agents are connected, replace the main content area with a guided onboarding flow:

┌─────────────────────────────────────────────────────┐
│                                                     │
│        🐪  Welcome to Cameleer                      │
│        Observability for Apache Camel               │
│                                                     │
│  ┌─────────────────────────────────────────────┐    │
│  │  Step 1: Connect your first agent           │    │
│  │                                             │    │
│  │  Add the Cameleer3 agent to your Camel      │    │
│  │  application. Choose your framework:        │    │
│  │                                             │    │
│  │  [Spring Boot]  [Quarkus]  [Standalone]     │    │
│  │                                             │    │
│  │  ┌─── Maven dependency ──────────────────┐  │    │
│  │  │ <dependency>                          │  │    │
│  │  │   <groupId>com.cameleer3</groupId>    │  │    │
│  │  │   <artifactId>cameleer3-agent</...>   │  │    │
│  │  │   <version>1.0</version>              │  │    │
│  │  │ </dependency>           [Copy]        │  │    │
│  │  └───────────────────────────────────────┘  │    │
│  │                                             │    │
│  │  ┌─── application.yml ──────────────────┐   │    │
│  │  │ cameleer:                            │   │    │
│  │  │   server-url: https://your-server    │   │    │
│  │  │   bootstrap-token: <token>           │   │    │
│  │  │   app-name: my-app           [Copy]  │   │    │
│  │  └──────────────────────────────────────┘   │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  ┌─────────────────────────────────────────────┐    │
│  │  Step 2: Verify connection                  │    │
│  │                                             │    │
│  │  Once your application starts, it will      │    │
│  │  appear here automatically.                 │    │
│  │                                             │    │
│  │  ● Waiting for first agent...               │    │
│  │                                   [Refresh] │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  ── or ──                                           │
│                                                     │
│  [Launch Demo Mode]                                 │
│  See Cameleer with sample data                      │
│                                                     │
└─────────────────────────────────────────────────────┘

2. Empty States Per Page

Each page should have contextual empty states instead of blank tables:

Exchanges tab (empty):

"No exchanges recorded yet. Connect an agent and trigger some Camel routes to see message flows appear here in real-time."

Dashboard tab (empty):

"Dashboard metrics will appear once exchanges start flowing. Connect an agent to get started." + link to setup.

Runtime tab (empty):

"No agents connected. Follow the setup guide to connect your first Camel application." + link to setup.

3. Bootstrap Token Display

The admin should be able to view/copy the bootstrap token from the Admin area (or it should be shown during initial setup). Currently there's no way to get this token from the UI.

4. Demo Mode (stretch)

A "demo mode" toggle that loads synthetic sample data so prospects can explore the UI before connecting their own applications. This is common in SaaS products and dramatically improves time-to-value perception.

Design Considerations

  • The onboarding should detect agent connection in real-time (use existing SSE) and automatically transition to the normal view
  • Framework-specific instructions (Spring Boot vs Quarkus) should show the right config format
  • The bootstrap token should be easily copyable
  • Consider a dismissible "Setup progress" banner that persists until first exchange is received
  • The empty states should use the design system's EmptyState component

Acceptance Criteria

  • New users see a guided setup flow instead of empty tables
  • Framework-specific agent installation instructions with copy-to-clipboard
  • Bootstrap token is viewable and copyable from the UI
  • Empty states on each tab provide contextual guidance
  • Auto-detects when first agent connects and transitions to normal view
  • "Skip" or "dismiss" option for experienced users

References

  • Datadog's onboarding wizard: multi-step, framework detection, copy-paste snippets
  • Grafana Cloud's "Connect data" flow: guided setup per data source
  • Sentry's onboarding: SDK selection → copy code → verify connection
## Parent Epic #100 ## Problem When a new team deploys Cameleer3 and opens the UI with zero agents connected, they see empty tables with no guidance. The first-run experience is where 80% of trial users are lost. Competing tools (Datadog, Grafana Cloud) nail guided setup — Cameleer currently has nothing. ## Current State - Empty exchanges table with "0 of 0 exchanges" - Empty sidebar with no applications - Dashboard shows zeroed KPIs - Runtime shows "0 agents" - No indication of what to do next ## Proposed Solution ### 1. Welcome / Getting Started Screen When no agents are connected, replace the main content area with a guided onboarding flow: ``` ┌─────────────────────────────────────────────────────┐ │ │ │ 🐪 Welcome to Cameleer │ │ Observability for Apache Camel │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ Step 1: Connect your first agent │ │ │ │ │ │ │ │ Add the Cameleer3 agent to your Camel │ │ │ │ application. Choose your framework: │ │ │ │ │ │ │ │ [Spring Boot] [Quarkus] [Standalone] │ │ │ │ │ │ │ │ ┌─── Maven dependency ──────────────────┐ │ │ │ │ │ <dependency> │ │ │ │ │ │ <groupId>com.cameleer3</groupId> │ │ │ │ │ │ <artifactId>cameleer3-agent</...> │ │ │ │ │ │ <version>1.0</version> │ │ │ │ │ │ </dependency> [Copy] │ │ │ │ │ └───────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─── application.yml ──────────────────┐ │ │ │ │ │ cameleer: │ │ │ │ │ │ server-url: https://your-server │ │ │ │ │ │ bootstrap-token: <token> │ │ │ │ │ │ app-name: my-app [Copy] │ │ │ │ │ └──────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ Step 2: Verify connection │ │ │ │ │ │ │ │ Once your application starts, it will │ │ │ │ appear here automatically. │ │ │ │ │ │ │ │ ● Waiting for first agent... │ │ │ │ [Refresh] │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ ── or ── │ │ │ │ [Launch Demo Mode] │ │ See Cameleer with sample data │ │ │ └─────────────────────────────────────────────────────┘ ``` ### 2. Empty States Per Page Each page should have contextual empty states instead of blank tables: **Exchanges tab (empty):** > "No exchanges recorded yet. Connect an agent and trigger some Camel routes to see message flows appear here in real-time." **Dashboard tab (empty):** > "Dashboard metrics will appear once exchanges start flowing. Connect an agent to get started." + link to setup. **Runtime tab (empty):** > "No agents connected. Follow the setup guide to connect your first Camel application." + link to setup. ### 3. Bootstrap Token Display The admin should be able to view/copy the bootstrap token from the Admin area (or it should be shown during initial setup). Currently there's no way to get this token from the UI. ### 4. Demo Mode (stretch) A "demo mode" toggle that loads synthetic sample data so prospects can explore the UI before connecting their own applications. This is common in SaaS products and dramatically improves time-to-value perception. ## Design Considerations - The onboarding should detect agent connection in real-time (use existing SSE) and automatically transition to the normal view - Framework-specific instructions (Spring Boot vs Quarkus) should show the right config format - The bootstrap token should be easily copyable - Consider a dismissible "Setup progress" banner that persists until first exchange is received - The empty states should use the design system's `EmptyState` component ## Acceptance Criteria - [ ] New users see a guided setup flow instead of empty tables - [ ] Framework-specific agent installation instructions with copy-to-clipboard - [ ] Bootstrap token is viewable and copyable from the UI - [ ] Empty states on each tab provide contextual guidance - [ ] Auto-detects when first agent connects and transitions to normal view - [ ] "Skip" or "dismiss" option for experienced users ## References - Datadog's onboarding wizard: multi-step, framework detection, copy-paste snippets - Grafana Cloud's "Connect data" flow: guided setup per data source - Sentry's onboarding: SDK selection → copy code → verify connection
claude added the featureuxpmf labels 2026-04-01 22:50:24 +02:00
Author
Owner

Design Specification

1. First-Run Detection Logic

The UI determines onboarding state using existing hooks — no new backend endpoint needed for detection.

// ui/src/hooks/useOnboardingState.ts

export type OnboardingPhase = 'welcome' | 'connected' | 'receiving' | 'complete' | 'dismissed';

export function useOnboardingState() {
  const { data: agents, isLoading: agentsLoading } = useAgents();
  const { data: catalog, isLoading: catalogLoading } = useRouteCatalog(...);
  const dismissed = localStorage.getItem('cameleer-onboarding-dismissed') === 'true';

  const hasAgents = (agents ?? []).length > 0;
  const hasExchanges = (catalog ?? []).some(app => app.exchangeCount > 0);

  let phase: OnboardingPhase;
  if (dismissed) phase = 'dismissed';
  else if (!hasAgents && !hasExchanges) phase = 'welcome';
  else if (hasAgents && !hasExchanges) phase = 'connected';
  else if (hasAgents && hasExchanges) phase = 'receiving';
  else phase = 'complete';

  return { phase, hasAgents, hasExchanges, isLoading: agentsLoading || catalogLoading,
    dismiss: () => localStorage.setItem('cameleer-onboarding-dismissed', 'true'),
    reset: () => localStorage.removeItem('cameleer-onboarding-dismissed') };
}

Phase transitions:

welcome (0 agents, 0 exchanges)
  → connected (agent registers)
    → receiving (first exchange arrives)
      → complete (auto-dismiss after 10s)

At any point: user clicks Skip → dismissed

localStorage key: cameleer-onboarding-dismissed = "true" | absent


2. Welcome Screen Layout

Replaces the main content area when phase === 'welcome'. Sidebar, top bar, tabs remain visible but empty.

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│         [cameleer3 logo 48px]                                │
│         Welcome to Cameleer                                  │
│         Observability for Apache Camel                       │
│                                                             │
│  ┌─ STEP 1 of 2 ──────────────────────────────────────────┐ │
│  │                                                        │ │
│  │  Add the agent to your Camel application               │ │
│  │                                                        │ │
│  │  Framework:  [Spring Boot*] [Quarkus] [Standalone]     │ │
│  │  Build tool: [Maven*] [Gradle]                         │ │
│  │                                                        │ │
│  │  1. Add the dependency                                 │ │
│  │  ┌──────────────────────────────────────────────[Copy]┐ │ │
│  │  │ <dependency>                                      │ │ │
│  │  │   <groupId>com.cameleer3</groupId>                │ │ │
│  │  │   <artifactId>cameleer3-spring-boot-starter</..>  │ │ │
│  │  │   <version>1.0.0</version>                        │ │ │
│  │  │ </dependency>                                     │ │ │
│  │  └───────────────────────────────────────────────────┘ │ │
│  │                                                        │ │
│  │  2. Configure the agent                                │ │
│  │  ┌──────────────────────────────────────────────[Copy]┐ │ │
│  │  │ cameleer:                                         │ │ │
│  │  │   server-url: https://your-server:8081            │ │ │
│  │  │   bootstrap-token: ●●●●●●●● [Show] [Copy]        │ │ │
│  │  │   application-name: my-app                        │ │ │
│  │  └───────────────────────────────────────────────────┘ │ │
│  │                                                        │ │
│  │  3. Start your application                             │ │
│  │     Run your Camel app. The agent connects on startup. │ │
│  └────────────────────────────────────────────────────────┘ │
│                                                             │
│  ┌─ STEP 2 of 2 ──────────────────────────────────────────┐ │
│  │  Verify the connection                                 │ │
│  │  [pulsing amber dot] Waiting for first agent...        │ │
│  └────────────────────────────────────────────────────────┘ │
│                                                             │
│  Already know what you're doing?  [Skip setup]              │
└─────────────────────────────────────────────────────────────┘

Dynamic content:

  • server-url auto-populated from window.location.origin (or config.apiBaseUrl in dev)
  • bootstrap-token fetched from new admin endpoint, masked by default
  • Snippets change per framework + build tool selection

Snippet matrix:

Framework Build Tool Dependency Config
Spring Boot Maven cameleer3-spring-boot-starter in XML application.yml
Spring Boot Gradle implementation '...:cameleer3-spring-boot-starter:...' application.yml
Quarkus Maven cameleer3-quarkus in XML application.properties
Quarkus Gradle implementation '...:cameleer3-quarkus:...' application.properties
Standalone Maven cameleer3-agent in XML Java code (CameleerAgent.builder()...)
Standalone Gradle implementation '...:cameleer3-agent:...' Java code

3. Bootstrap Token Management

New backend endpoint:

GET /api/v1/admin/bootstrap-token
Authorization: Bearer <JWT with ADMIN role>

Response 200: { "token": "...", "hasRotationToken": true/false }
Response 403: Non-admin users

Token display in onboarding:

  • Default: masked with bullets, same length as real token
  • [Show] toggles visibility
  • [Copy] copies full config block with real token (even when masked)
  • Non-admin users see placeholder your-bootstrap-token with note: "Ask your administrator for the bootstrap token."

Token display in Admin > App Config (new section at top):

┌─ Bootstrap Token ──────────────────────────────────────┐
│  ●●●●●●●●●●●●  [Show]  [Copy]                         │
│  [ROTATION ACTIVE] Previous token also accepted        │
│  > How to rotate the bootstrap token                   │
└────────────────────────────────────────────────────────┘

4. Per-Page Empty States

All use the design system EmptyState component.

Exchanges (no exchanges):

  • Icon: Lucide Workflow 48px
  • Title: "No exchanges recorded yet"
  • Description: "Connect an agent and trigger some Camel routes to see message flows appear here in real-time."
  • Action: [Set up your first agent] button

Dashboard (no metrics):

  • Icon: Lucide BarChart3 48px
  • Title: "No analytics data yet"
  • Description: "Dashboard metrics will populate automatically once exchanges start flowing through your Camel routes."
  • Action: [Go to setup guide] button

Runtime (no agents):

  • Icon: Lucide Radio 48px
  • Title: "No agents connected"
  • Description: "Follow the setup guide to connect your first Apache Camel application. Agents register automatically when your application starts."
  • Action: [Set up your first agent] button

Sidebar (no apps):

  • Small text below search: "No applications connected yet. Follow the setup guide to get started." (12px, var(--text-muted))

5. Agent Connection Detection

Uses existing polling (no new SSE needed):

  • useAgents() polls every 10s
  • useRouteCatalog() polls every 15s

Step 2 transitions:

Waiting → Connected:

  • "Waiting for first agent..." fades out (200ms)
  • "Agent connected: {name} (1 instance)" fades in
  • Toast: "Agent connected! {displayName} is now reporting."

Connected → Receiving:

  • "Waiting for first exchange..." fades out
  • "You're all set!" success state fades in
  • Toast: "First exchange received! Your observability pipeline is live."
  • After 5s, "View your data" button pulses once

Receiving → Complete (auto after 10s):

  • Onboarding content fades out (300ms)
  • Normal page content fades in (300ms)

6. Setup Progress Banner

Thin banner between ContentTabs and <main> in LayoutShell:

┌──────────────────────────────────────────────────────────────────┐
│ [●] Getting started: (1) Add dependency > (2) Configure > (3) Start  [x]│
└──────────────────────────────────────────────────────────────────┘

Three banner states:

  • welcome: Steps 1-3 shown, step 1 highlighted in amber
  • connected: Steps 1-3 checked green, step 4 "Send first exchange" highlighted
  • receiving: "Setup complete! Your observability pipeline is live." — auto-dismisses after 10s

Collapse animation: max-height: 44px → 0 with 200ms ease-out.


7. Dismissal & Return

Dismissal methods:

  1. "Skip setup" link on welcome screen
  2. Banner dismiss (x) button
  3. Auto-dismiss 10s after receiving phase
  4. Not shown on admin pages

Re-access: "Setup Guide" item added to user dropdown menu in TopBar. Calls reset() (clears localStorage) and navigates to /exchanges. Always visible — useful for onboarding additional applications.


8. Component Mapping

DS Component Usage
Card Wrapper for each step
CodeBlock Dependency + config snippets (copyable prop)
Button Skip, Set up, View data, Copy
ButtonGroup Framework selector, build tool selector
Badge "ROTATION ACTIVE" on token
EmptyState Per-page empty states
StatusDot Connection status indicators
MonoText Token display
useToast Connection/exchange notifications

CSS variables only — no hardcoded colors.


9. File Structure

New files:

ui/src/hooks/useOnboardingState.ts
ui/src/pages/Onboarding/OnboardingWelcome.tsx
ui/src/pages/Onboarding/OnboardingWelcome.module.css
ui/src/pages/Onboarding/OnboardingBanner.tsx
ui/src/pages/Onboarding/OnboardingBanner.module.css
ui/src/pages/Onboarding/snippets.ts
ui/src/api/queries/bootstrap-token.ts
server: BootstrapTokenController.java (GET /api/v1/admin/bootstrap-token)

Modified files:

ui/src/components/LayoutShell.tsx (add OnboardingBanner)
ui/src/pages/Dashboard/Dashboard.tsx (add empty state)
ui/src/pages/DashboardTab/DashboardL1.tsx (add empty state)
ui/src/pages/RuntimeTab/AgentHealth.tsx (add empty state)
ui/src/pages/Exchanges/ExchangesPage.tsx (render OnboardingWelcome)

10. Edge Cases

Case Behavior
Non-admin user Token placeholder shown with "Ask your admin" note
Multiple tabs Independent polling, both transition, idempotent localStorage
Agent connects then dies Stays in connected (counts all agents regardless of status)
Server restart clears registry If dismissed → stays dismissed. If not → regresses to welcome until agents re-register
Long bootstrap token Show first 4 + last 4 chars masked, copy always copies full value
Dev mode (UI :5173, API :8081) getServerUrl() uses config.apiBaseUrl stripped of /api/v1 suffix
OIDC viewer Cannot fetch token, sees placeholder — correct behavior
Re-onboarding 2nd app "Setup Guide" menu re-shows flow with existing agent context
## Design Specification ### 1. First-Run Detection Logic The UI determines onboarding state using existing hooks — no new backend endpoint needed for detection. ```typescript // ui/src/hooks/useOnboardingState.ts export type OnboardingPhase = 'welcome' | 'connected' | 'receiving' | 'complete' | 'dismissed'; export function useOnboardingState() { const { data: agents, isLoading: agentsLoading } = useAgents(); const { data: catalog, isLoading: catalogLoading } = useRouteCatalog(...); const dismissed = localStorage.getItem('cameleer-onboarding-dismissed') === 'true'; const hasAgents = (agents ?? []).length > 0; const hasExchanges = (catalog ?? []).some(app => app.exchangeCount > 0); let phase: OnboardingPhase; if (dismissed) phase = 'dismissed'; else if (!hasAgents && !hasExchanges) phase = 'welcome'; else if (hasAgents && !hasExchanges) phase = 'connected'; else if (hasAgents && hasExchanges) phase = 'receiving'; else phase = 'complete'; return { phase, hasAgents, hasExchanges, isLoading: agentsLoading || catalogLoading, dismiss: () => localStorage.setItem('cameleer-onboarding-dismissed', 'true'), reset: () => localStorage.removeItem('cameleer-onboarding-dismissed') }; } ``` **Phase transitions:** ``` welcome (0 agents, 0 exchanges) → connected (agent registers) → receiving (first exchange arrives) → complete (auto-dismiss after 10s) At any point: user clicks Skip → dismissed ``` localStorage key: `cameleer-onboarding-dismissed = "true" | absent` --- ### 2. Welcome Screen Layout Replaces the main content area when `phase === 'welcome'`. Sidebar, top bar, tabs remain visible but empty. ``` ┌─────────────────────────────────────────────────────────────┐ │ │ │ [cameleer3 logo 48px] │ │ Welcome to Cameleer │ │ Observability for Apache Camel │ │ │ │ ┌─ STEP 1 of 2 ──────────────────────────────────────────┐ │ │ │ │ │ │ │ Add the agent to your Camel application │ │ │ │ │ │ │ │ Framework: [Spring Boot*] [Quarkus] [Standalone] │ │ │ │ Build tool: [Maven*] [Gradle] │ │ │ │ │ │ │ │ 1. Add the dependency │ │ │ │ ┌──────────────────────────────────────────────[Copy]┐ │ │ │ │ │ <dependency> │ │ │ │ │ │ <groupId>com.cameleer3</groupId> │ │ │ │ │ │ <artifactId>cameleer3-spring-boot-starter</..> │ │ │ │ │ │ <version>1.0.0</version> │ │ │ │ │ │ </dependency> │ │ │ │ │ └───────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 2. Configure the agent │ │ │ │ ┌──────────────────────────────────────────────[Copy]┐ │ │ │ │ │ cameleer: │ │ │ │ │ │ server-url: https://your-server:8081 │ │ │ │ │ │ bootstrap-token: ●●●●●●●● [Show] [Copy] │ │ │ │ │ │ application-name: my-app │ │ │ │ │ └───────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 3. Start your application │ │ │ │ Run your Camel app. The agent connects on startup. │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ ┌─ STEP 2 of 2 ──────────────────────────────────────────┐ │ │ │ Verify the connection │ │ │ │ [pulsing amber dot] Waiting for first agent... │ │ │ └────────────────────────────────────────────────────────┘ │ │ │ │ Already know what you're doing? [Skip setup] │ └─────────────────────────────────────────────────────────────┘ ``` **Dynamic content:** - `server-url` auto-populated from `window.location.origin` (or `config.apiBaseUrl` in dev) - `bootstrap-token` fetched from new admin endpoint, masked by default - Snippets change per framework + build tool selection **Snippet matrix:** | Framework | Build Tool | Dependency | Config | |-----------|-----------|------------|--------| | Spring Boot | Maven | `cameleer3-spring-boot-starter` in XML | `application.yml` | | Spring Boot | Gradle | `implementation '...:cameleer3-spring-boot-starter:...'` | `application.yml` | | Quarkus | Maven | `cameleer3-quarkus` in XML | `application.properties` | | Quarkus | Gradle | `implementation '...:cameleer3-quarkus:...'` | `application.properties` | | Standalone | Maven | `cameleer3-agent` in XML | Java code (`CameleerAgent.builder()...`) | | Standalone | Gradle | `implementation '...:cameleer3-agent:...'` | Java code | --- ### 3. Bootstrap Token Management **New backend endpoint:** ``` GET /api/v1/admin/bootstrap-token Authorization: Bearer <JWT with ADMIN role> ``` Response 200: `{ "token": "...", "hasRotationToken": true/false }` Response 403: Non-admin users **Token display in onboarding:** - Default: masked with bullets, same length as real token - [Show] toggles visibility - [Copy] copies full config block with real token (even when masked) - Non-admin users see placeholder `your-bootstrap-token` with note: "Ask your administrator for the bootstrap token." **Token display in Admin > App Config (new section at top):** ``` ┌─ Bootstrap Token ──────────────────────────────────────┐ │ ●●●●●●●●●●●● [Show] [Copy] │ │ [ROTATION ACTIVE] Previous token also accepted │ │ > How to rotate the bootstrap token │ └────────────────────────────────────────────────────────┘ ``` --- ### 4. Per-Page Empty States All use the design system `EmptyState` component. **Exchanges (no exchanges):** - Icon: Lucide `Workflow` 48px - Title: "No exchanges recorded yet" - Description: "Connect an agent and trigger some Camel routes to see message flows appear here in real-time." - Action: [Set up your first agent] button **Dashboard (no metrics):** - Icon: Lucide `BarChart3` 48px - Title: "No analytics data yet" - Description: "Dashboard metrics will populate automatically once exchanges start flowing through your Camel routes." - Action: [Go to setup guide] button **Runtime (no agents):** - Icon: Lucide `Radio` 48px - Title: "No agents connected" - Description: "Follow the setup guide to connect your first Apache Camel application. Agents register automatically when your application starts." - Action: [Set up your first agent] button **Sidebar (no apps):** - Small text below search: "No applications connected yet. Follow the setup guide to get started." (12px, `var(--text-muted)`) --- ### 5. Agent Connection Detection Uses existing polling (no new SSE needed): - `useAgents()` polls every 10s - `useRouteCatalog()` polls every 15s **Step 2 transitions:** Waiting → Connected: - "Waiting for first agent..." fades out (200ms) - "Agent connected: {name} (1 instance)" fades in - Toast: "Agent connected! {displayName} is now reporting." Connected → Receiving: - "Waiting for first exchange..." fades out - "You're all set!" success state fades in - Toast: "First exchange received! Your observability pipeline is live." - After 5s, "View your data" button pulses once Receiving → Complete (auto after 10s): - Onboarding content fades out (300ms) - Normal page content fades in (300ms) --- ### 6. Setup Progress Banner Thin banner between ContentTabs and `<main>` in LayoutShell: ``` ┌──────────────────────────────────────────────────────────────────┐ │ [●] Getting started: (1) Add dependency > (2) Configure > (3) Start [x]│ └──────────────────────────────────────────────────────────────────┘ ``` Three banner states: - **welcome:** Steps 1-3 shown, step 1 highlighted in amber - **connected:** Steps 1-3 checked green, step 4 "Send first exchange" highlighted - **receiving:** "Setup complete! Your observability pipeline is live." — auto-dismisses after 10s Collapse animation: `max-height: 44px → 0` with 200ms ease-out. --- ### 7. Dismissal & Return **Dismissal methods:** 1. "Skip setup" link on welcome screen 2. Banner dismiss (x) button 3. Auto-dismiss 10s after `receiving` phase 4. Not shown on admin pages **Re-access:** "Setup Guide" item added to user dropdown menu in TopBar. Calls `reset()` (clears localStorage) and navigates to `/exchanges`. Always visible — useful for onboarding additional applications. --- ### 8. Component Mapping | DS Component | Usage | |-------------|-------| | `Card` | Wrapper for each step | | `CodeBlock` | Dependency + config snippets (`copyable` prop) | | `Button` | Skip, Set up, View data, Copy | | `ButtonGroup` | Framework selector, build tool selector | | `Badge` | "ROTATION ACTIVE" on token | | `EmptyState` | Per-page empty states | | `StatusDot` | Connection status indicators | | `MonoText` | Token display | | `useToast` | Connection/exchange notifications | CSS variables only — no hardcoded colors. --- ### 9. File Structure **New files:** ``` ui/src/hooks/useOnboardingState.ts ui/src/pages/Onboarding/OnboardingWelcome.tsx ui/src/pages/Onboarding/OnboardingWelcome.module.css ui/src/pages/Onboarding/OnboardingBanner.tsx ui/src/pages/Onboarding/OnboardingBanner.module.css ui/src/pages/Onboarding/snippets.ts ui/src/api/queries/bootstrap-token.ts server: BootstrapTokenController.java (GET /api/v1/admin/bootstrap-token) ``` **Modified files:** ``` ui/src/components/LayoutShell.tsx (add OnboardingBanner) ui/src/pages/Dashboard/Dashboard.tsx (add empty state) ui/src/pages/DashboardTab/DashboardL1.tsx (add empty state) ui/src/pages/RuntimeTab/AgentHealth.tsx (add empty state) ui/src/pages/Exchanges/ExchangesPage.tsx (render OnboardingWelcome) ``` --- ### 10. Edge Cases | Case | Behavior | |------|----------| | Non-admin user | Token placeholder shown with "Ask your admin" note | | Multiple tabs | Independent polling, both transition, idempotent localStorage | | Agent connects then dies | Stays in `connected` (counts all agents regardless of status) | | Server restart clears registry | If dismissed → stays dismissed. If not → regresses to welcome until agents re-register | | Long bootstrap token | Show first 4 + last 4 chars masked, copy always copies full value | | Dev mode (UI :5173, API :8081) | `getServerUrl()` uses `config.apiBaseUrl` stripped of `/api/v1` suffix | | OIDC viewer | Cannot fetch token, sees placeholder — correct behavior | | Re-onboarding 2nd app | "Setup Guide" menu re-shows flow with existing agent context |
Sign in to join this conversation.