99df0816b0
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>
46 lines
1.4 KiB
Python
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")
|