--- phase: 03-operator-experience plan: 02 type: execute wave: 2 depends_on: ["03-01"] files_modified: - packages/portal/app/api/slack/callback/route.ts - packages/portal/app/(dashboard)/onboarding/page.tsx - packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx - packages/portal/app/(dashboard)/onboarding/steps/configure-agent.tsx - packages/portal/app/(dashboard)/onboarding/steps/test-message.tsx - packages/portal/app/(dashboard)/settings/api-keys/page.tsx - packages/portal/lib/queries.ts - packages/portal/components/onboarding-stepper.tsx autonomous: false requirements: - PRTA-03 - PRTA-04 - LLM-03 must_haves: truths: - "Operator can click Add to Slack and complete OAuth flow to connect their workspace" - "Operator can paste WhatsApp credentials and have them validated and stored" - "After connecting a channel, operator can send a test message that verifies end-to-end connectivity" - "Agent goes live automatically after test message succeeds" - "Operator completes the full onboarding sequence (connect -> configure -> test) in a guided wizard" - "Operator can add, view, and delete BYO LLM API keys from a settings page" artifacts: - path: "packages/portal/app/api/slack/callback/route.ts" provides: "Next.js Route Handler for Slack OAuth redirect" - path: "packages/portal/app/(dashboard)/onboarding/page.tsx" provides: "Onboarding wizard with 3-step stepper" - path: "packages/portal/app/(dashboard)/settings/api-keys/page.tsx" provides: "BYO API key management page" key_links: - from: "packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx" to: "/api/portal/channels/slack/install" via: "fetch to get OAuth URL, then window.location redirect" pattern: "channels/slack/install" - from: "packages/portal/app/api/slack/callback/route.ts" to: "/api/portal/channels/slack/callback" via: "proxy the OAuth callback to FastAPI backend" pattern: "channels/slack/callback" - from: "packages/portal/app/(dashboard)/onboarding/steps/test-message.tsx" to: "/api/portal/channels/{tenant_id}/test" via: "POST to send test message" pattern: "channels.*test" - from: "packages/portal/app/(dashboard)/settings/api-keys/page.tsx" to: "/api/portal/tenants/{tenant_id}/llm-keys" via: "GET/POST/DELETE for BYO key CRUD (endpoints created in Plan 01 Task 3)" pattern: "tenants.*llm-keys" --- Channel connection wizard (Slack OAuth + WhatsApp manual setup), onboarding flow with 3-step stepper, and BYO API key management page. Purpose: Operators can connect their messaging channels and onboard their AI employee through a guided wizard, plus manage their own LLM API keys -- all from the portal UI. Output: Onboarding wizard (connect channel -> configure agent -> test message), Slack OAuth callback handler, WhatsApp manual connect form, BYO key settings page. @/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md @/home/adelorenzo/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-operator-experience/03-CONTEXT.md @.planning/phases/03-operator-experience/03-RESEARCH.md @.planning/phases/03-operator-experience/03-01-SUMMARY.md From packages/shared/shared/api/channels.py (created in Plan 01): ```python # GET /api/portal/channels/slack/install?tenant_id={id} -> { "authorize_url": "https://slack.com/oauth/v2/authorize?..." } # GET /api/portal/channels/slack/callback?code={code}&state={state} -> { "success": true, "workspace_name": "..." } # POST /api/portal/channels/whatsapp/connect -> { "success": true } (body: { tenant_id, phone_number_id, waba_id, system_user_token }) # POST /api/portal/channels/{tenant_id}/test -> { "success": true, "message": "Test message sent" } (body: { channel_type }) ``` From packages/shared/shared/api/llm_keys.py (created in Plan 01 Task 3): ```python # GET /api/portal/tenants/{tenant_id}/llm-keys -> [{ id, provider, label, key_hint, created_at }] # POST /api/portal/tenants/{tenant_id}/llm-keys -> { id, provider, label, key_hint, created_at } (body: { provider, label, api_key }) # DELETE /api/portal/tenants/{tenant_id}/llm-keys/{key_id} -> 204 ``` From packages/shared/shared/api/billing.py (created in Plan 01): ```python # POST /api/portal/billing/checkout -> { "checkout_url": "https://checkout.stripe.com/..." } # POST /api/portal/billing/portal -> { "portal_url": "https://billing.stripe.com/..." } ``` From packages/shared/shared/crypto.py (created in Plan 01): ```python class KeyEncryptionService: def encrypt(self, plaintext: str) -> str: ... def decrypt(self, ciphertext: str) -> str: ... def rotate(self, ciphertext: str) -> str: ... ``` From packages/portal/lib/api.ts: ```typescript // API client for FastAPI backend — use this for all portal API calls ``` From packages/portal/lib/queries.ts: ```typescript // TanStack Query hooks — add new hooks for channels, billing, usage here ``` Established patterns: - shadcn/ui components - react-hook-form + zod v4 + standardSchemaResolver - TanStack Query for data fetching - App Router with (dashboard) route group - proxy.ts for auth protection (Next.js 16 pattern) Task 1: Slack OAuth callback route, onboarding wizard with 3-step stepper, and channel connection forms packages/portal/app/api/slack/callback/route.ts, packages/portal/app/(dashboard)/onboarding/page.tsx, packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx, packages/portal/app/(dashboard)/onboarding/steps/configure-agent.tsx, packages/portal/app/(dashboard)/onboarding/steps/test-message.tsx, packages/portal/components/onboarding-stepper.tsx, packages/portal/lib/queries.ts 1. Create `packages/portal/app/api/slack/callback/route.ts`: - Next.js Route Handler (GET) that receives the OAuth redirect from Slack - Extract `code` and `state` from searchParams - Forward to FastAPI backend: GET /api/portal/channels/slack/callback?code={code}&state={state} - On success: redirect to /onboarding?step=2&channel=slack&connected=true - On error: redirect to /onboarding?step=1&error=slack_auth_failed 2. Create `packages/portal/components/onboarding-stepper.tsx`: - 3-step stepper component using shadcn/ui: "Connect Channel" -> "Configure Agent" -> "Test Message" - Visual progress indicator (numbered steps with active/complete/pending states) - Step state managed via URL searchParams (step=1|2|3) for shareable/refreshable URLs - Each step renders the corresponding step component 3. Create `packages/portal/app/(dashboard)/onboarding/page.tsx`: - Reads `step` from searchParams (default 1) - Reads `tenant_id` from session or query param - Renders OnboardingStepper with the appropriate step component - Guards: if no tenant selected, redirect to tenant selection 4. Create `packages/portal/app/(dashboard)/onboarding/steps/connect-channel.tsx`: - Two cards: "Add to Slack" and "Connect WhatsApp" - **Slack card:** Button that fetches /api/portal/channels/slack/install?tenant_id={id}, then does window.location.href = authorize_url (external redirect to Slack) - **WhatsApp card:** Expandable form with 3 fields (Phone Number ID, WhatsApp Business Account ID, System User Token) + step-by-step instructions text - On submit: POST /api/portal/channels/whatsapp/connect - Validation with zod + react-hook-form - On successful connection (either channel): advance to step 2 automatically - If returning from Slack OAuth (connected=true in searchParams): show success toast and advance 5. Create `packages/portal/app/(dashboard)/onboarding/steps/configure-agent.tsx`: - If tenant already has an agent: show existing agent summary with "Edit" link to Agent Designer - If no agent: redirect to Agent Designer page with return URL back to onboarding?step=3 - Minimal step — the Agent Designer (from Phase 1) handles all agent configuration - "Next" button enabled only when at least one active agent exists for the tenant 6. Create `packages/portal/app/(dashboard)/onboarding/steps/test-message.tsx`: - Shows connected channel(s) with a "Send Test Message" button per channel - On click: POST /api/portal/channels/{tenant_id}/test with {channel_type} - Shows loading state while test runs, then success/failure result - On success: show "Your AI employee is live!" celebration message - Agent goes live automatically (is_active already true by default) — NO separate "Go Live" button per user decision - Per user decision: test message step is REQUIRED, not skippable 7. Add TanStack Query hooks to `packages/portal/lib/queries.ts`: - useSlackInstallUrl(tenantId) — GET /channels/slack/install - useConnectWhatsApp() — mutation POST /channels/whatsapp/connect - useSendTestMessage() — mutation POST /channels/{tenantId}/test - useChannelConnections(tenantId) — GET to list existing connections cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20 - Slack OAuth callback route handler proxies to FastAPI and redirects appropriately - Onboarding page renders 3-step stepper with progress indicator - Connect Channel step shows Slack OAuth button and WhatsApp manual form - Configure Agent step links to existing Agent Designer - Test Message step sends test and shows result - Agent goes live automatically after successful test (no Go Live button) - All TanStack Query hooks defined - Portal builds without errors Task 2: BYO API key management settings page packages/portal/app/(dashboard)/settings/api-keys/page.tsx, packages/portal/lib/queries.ts 1. Create `packages/portal/app/(dashboard)/settings/api-keys/page.tsx`: - Tenant-level settings page (per user decision — simpler than per-agent for v1) - List existing BYO keys: show provider name, label, key_hint (last 4 chars), created date (NOT the key itself — never display decrypted keys) - "Add API Key" button opens a form: - Provider: select dropdown (OpenAI, Anthropic, Custom) - Label: text input (human-readable name, e.g., "Production OpenAI key") - API Key: password input (masked by default) - Submit: POST to /api/portal/tenants/{tenant_id}/llm-keys (backend endpoint created in Plan 01 Task 3) - Delete button per key with confirmation dialog - Use shadcn/ui Card, Table, Dialog, Button, Input, Select components - react-hook-form + zod for validation (provider required, label 3-100 chars, key not empty) 2. Add TanStack Query hooks to `packages/portal/lib/queries.ts`: - useLlmKeys(tenantId) — GET /api/portal/tenants/{tenant_id}/llm-keys - useAddLlmKey() — mutation POST /api/portal/tenants/{tenant_id}/llm-keys - useDeleteLlmKey() — mutation DELETE /api/portal/tenants/{tenant_id}/llm-keys/{keyId} 3. Add navigation link to settings/api-keys in the dashboard layout sidebar (if sidebar exists) or in the tenant detail page. cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20 - BYO API key settings page renders with list of existing keys (redacted — shows key_hint only) - Add key form validates and submits to backend - Delete key with confirmation dialog works - Portal builds without errors Task 3: Verify onboarding wizard and BYO key management n/a Human verifies the onboarding wizard and BYO key management UI: 1. Start the portal: `cd packages/portal && npm run dev` 2. Navigate to /onboarding — verify 3-step stepper displays 3. Step 1: Verify "Add to Slack" button and WhatsApp form are present 4. Step 2: Verify it links to Agent Designer or shows existing agent 5. Step 3: Verify "Send Test Message" button is present 6. Navigate to /settings/api-keys — verify key list and add form render 7. Try adding a BYO key with the form — verify it submits without JS errors 8. Confirm there is NO separate "Go Live" button — agent goes live after test Human visual inspection of onboarding flow and settings page Operator confirms onboarding wizard works through all 3 steps and BYO key page renders correctly - Portal builds successfully: `cd packages/portal && npx next build` - Onboarding wizard navigable through all 3 steps - BYO API key page renders and accepts input - No separate "Go Live" button exists (per user decision) - Test message step is required (not skippable) - Operator can initiate Slack OAuth from the portal - Operator can paste WhatsApp credentials via guided form - Onboarding wizard completes in 3 steps: connect -> configure -> test - Agent goes live automatically after successful test message - Operator can manage BYO API keys from settings page (backed by Plan 01 Task 3 endpoints) - Portal builds without errors After completion, create `.planning/phases/03-operator-experience/03-02-SUMMARY.md`