Files
gh-christianlouis-docuelevate/migrations/versions/017_add_onboarding_fields.py
T
copilot-swe-agent[bot] 99df0816b0 feat(onboarding): add multi-step user onboarding wizard
Add a 5-step onboarding flow for new users:
- Migration 017: adds onboarding_completed, onboarding_completed_at,
  contact_email, preferred_destination to user_profiles
- app/api/onboarding.py: REST endpoints (status, profile, plan, storage,
  complete) with session-based auth using sub/preferred_username/email/id
  priority chain
- app/views/onboarding.py: GET /onboarding view with configured-destination
  detection helper for all 8 supported storage providers
- frontend/templates/onboarding.html: Alpine.js wizard with progress
  indicator, tier cards (server-rendered), storage destination cards,
  accessible markup (WCAG AA), and all fetch() API calls
- app/auth.py: redirect first-time OAuth users (onboarding_completed=False)
  to /onboarding after login
- 16 unit tests covering all endpoints, auth enforcement, and edge cases

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-07 11:04:53 +00:00

46 lines
1.4 KiB
Python

"""Add onboarding fields to user_profiles
Revision ID: 017_add_onboarding_fields
Revises: 016_add_userprofile_billing
Create Date: 2026-03-08
"""
from typing import Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "017_add_onboarding_fields"
down_revision: Union[str, None] = "016_add_userprofile_billing"
depends_on: Union[str, None] = None
def upgrade() -> None:
"""Add onboarding_completed, onboarding_completed_at, contact_email, preferred_destination to user_profiles."""
op.add_column(
"user_profiles",
sa.Column("onboarding_completed", sa.Boolean(), nullable=False, server_default="0"),
)
op.add_column(
"user_profiles",
sa.Column("onboarding_completed_at", sa.DateTime(timezone=True), nullable=True),
)
op.add_column(
"user_profiles",
sa.Column("contact_email", sa.String(255), nullable=True),
)
op.add_column(
"user_profiles",
sa.Column("preferred_destination", sa.String(50), nullable=True),
)
def downgrade() -> None:
"""Remove onboarding columns from user_profiles."""
op.drop_column("user_profiles", "preferred_destination")
op.drop_column("user_profiles", "contact_email")
op.drop_column("user_profiles", "onboarding_completed_at")
op.drop_column("user_profiles", "onboarding_completed")