Files
resolutionflow/backend/app/schemas/onboarding.py
Michael Chihlas 16f5e4ce05 feat(onboarding): add PATCH /users/me/onboarding-step + dismiss-rest
Persists welcome-wizard Step 1/2/3 progress for self-serve signup Phase 2.
PATCH validates step cannot decrease, ignores `data` on action="skip", and
is idempotent on re-PATCH of the same step. POST /users/me/onboarding-dismiss-rest
backs the wizard's "Skip the rest" button.

Both routes added to _EMAIL_VERIFICATION_ALLOWLIST and _SUBSCRIPTION_GUARD_ALLOWLIST
so the wizard runs before email verification and during the trial. 4 integration
tests cover field writes, skip semantics, decrease guard, and dismiss-rest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 20:04:43 -04:00

52 lines
1.4 KiB
Python

from typing import Literal, Optional
from pydantic import BaseModel, Field
class OnboardingStatus(BaseModel):
created_flow: bool
ran_session: bool
exported_session: bool
tried_ai_assistant: bool
invited_teammate: bool
connected_psa: bool
is_team_user: bool
dismissed: bool
# --- Welcome wizard (Phase 2) ----------------------------------------------
TeamSizeBucket = Literal["1-2", "3-5", "6-10", "11-25", "26+"]
RoleAtSignup = Literal["owner", "lead_tech", "tech", "other"]
PrimaryPsa = Literal["connectwise", "autotask", "halopsa", "none"]
WizardStep = Literal[1, 2, 3]
WizardAction = Literal["complete", "skip"]
class OnboardingStepData(BaseModel):
"""Optional payload carried with `action="complete"` for steps 1 and 2.
Step 1 fields: company_name, team_size_bucket, role_at_signup
Step 2 fields: primary_psa
Step 3 has no data (invitations posted separately).
"""
# Step 1
company_name: Optional[str] = Field(default=None, max_length=255)
team_size_bucket: Optional[TeamSizeBucket] = None
role_at_signup: Optional[RoleAtSignup] = None
# Step 2
primary_psa: Optional[PrimaryPsa] = None
class OnboardingStepRequest(BaseModel):
step: WizardStep
action: WizardAction
data: Optional[OnboardingStepData] = None
class OnboardingStepResponse(BaseModel):
onboarding_step_completed: Optional[int]
onboarding_dismissed: bool