Phase 2 Task 41 — Dashboard redesign. Backend: - Extend GET /users/onboarding-status with email_verified and shop_setup_done. - tried_ai_assistant kept in payload for backward-compat during deploy. Frontend: - New NextStepCard: surfaces the highest-priority incomplete onboarding item with a primary CTA. Priority order: verify email > set up shop > run first FlowPilot session > connect PSA > invite teammate > pick a plan (gated on trial stage warning/urgent/expired). Returns null when all done OR onboarding_dismissed. - New SetupChecklist: unified single list (no SOLO/TEAM bifurcation), drops the stale tried_ai_assistant / Script Builder item, surfaces "Pick a plan" when trial stage is warning or later. - Mounted on QuickStartPage below the hero with a "Show all setup steps" toggle. The whole onboarding section auto-hides when there's nothing left to nudge on, so the dashboard goes back to clean once setup is done. - Removed the orphaned OnboardingChecklist component (was defined but never mounted). - New useOnboardingStatus hook so page + components share one fetch contract. Tests: - Backend: test_onboarding_status_includes_email_verified_and_shop_setup_done. - Frontend (Vitest): 13 new tests across NextStepCard, SetupChecklist, and QuickStartPage covering priority ordering, dismissal, the SOLO/TEAM removal, the toggle reveal, and the trial-stage gate on Pick a plan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
import { apiClient } from './client'
|
|
|
|
export interface OnboardingStatus {
|
|
created_flow: boolean
|
|
ran_session: boolean
|
|
exported_session: boolean
|
|
/** @deprecated Phase 2 — kept for backward-compat. New UI no longer branches on this. */
|
|
tried_ai_assistant: boolean
|
|
invited_teammate: boolean
|
|
connected_psa: boolean
|
|
is_team_user: boolean
|
|
dismissed: boolean
|
|
// Phase 2 (Task 41) — drive the unified next-step card + checklist.
|
|
email_verified: boolean
|
|
shop_setup_done: boolean
|
|
}
|
|
|
|
export async function getOnboardingStatus(): Promise<OnboardingStatus> {
|
|
const response = await apiClient.get('/users/onboarding-status')
|
|
return response.data
|
|
}
|
|
|
|
export async function dismissOnboarding(): Promise<void> {
|
|
await apiClient.post('/users/onboarding-status/dismiss')
|
|
}
|
|
|
|
// --- Welcome wizard (Phase 2) ---------------------------------------------
|
|
|
|
export type WizardStep = 1 | 2 | 3
|
|
export type WizardAction = 'complete' | 'skip'
|
|
export type TeamSizeBucket = '1-2' | '3-5' | '6-10' | '11-25' | '26+'
|
|
export type RoleAtSignup = 'owner' | 'lead_tech' | 'tech' | 'other'
|
|
export type PrimaryPsa = 'connectwise' | 'autotask' | 'halopsa' | 'none'
|
|
|
|
export interface OnboardingStepData {
|
|
// Step 1
|
|
company_name?: string
|
|
team_size_bucket?: TeamSizeBucket
|
|
role_at_signup?: RoleAtSignup
|
|
// Step 2
|
|
primary_psa?: PrimaryPsa
|
|
}
|
|
|
|
export interface OnboardingStepRequest {
|
|
step: WizardStep
|
|
action: WizardAction
|
|
data?: OnboardingStepData
|
|
}
|
|
|
|
export interface OnboardingStepResponse {
|
|
onboarding_step_completed: number | null
|
|
onboarding_dismissed: boolean
|
|
}
|
|
|
|
export const onboardingApi = {
|
|
getStatus: getOnboardingStatus,
|
|
dismiss: dismissOnboarding,
|
|
/** Persist welcome-wizard progress for the current user. */
|
|
async updateStep(payload: OnboardingStepRequest): Promise<OnboardingStepResponse> {
|
|
const response = await apiClient.patch<OnboardingStepResponse>(
|
|
'/users/me/onboarding-step',
|
|
payload,
|
|
)
|
|
return response.data
|
|
},
|
|
/** Skip the rest of the welcome wizard — sets users.onboarding_dismissed=TRUE. */
|
|
async dismissRest(): Promise<OnboardingStepResponse> {
|
|
const response = await apiClient.post<OnboardingStepResponse>(
|
|
'/users/me/onboarding-dismiss-rest',
|
|
)
|
|
return response.data
|
|
},
|
|
}
|