Imporve onboarding UI
This commit is contained in:
@@ -124,38 +124,63 @@ class OnboardingService:
|
||||
|
||||
async def get_user_progress(self, user_id: str) -> UserProgress:
|
||||
"""Get current onboarding progress for user"""
|
||||
|
||||
|
||||
# Get user's onboarding data from user preferences or separate table
|
||||
user_progress_data = await self._get_user_onboarding_data(user_id)
|
||||
|
||||
|
||||
# Calculate current status for each step
|
||||
steps = []
|
||||
completed_steps = []
|
||||
|
||||
|
||||
for step_name in ONBOARDING_STEPS:
|
||||
step_data = user_progress_data.get(step_name, {})
|
||||
is_completed = step_data.get("completed", False)
|
||||
|
||||
|
||||
if is_completed:
|
||||
completed_steps.append(step_name)
|
||||
|
||||
|
||||
steps.append(OnboardingStepStatus(
|
||||
step_name=step_name,
|
||||
completed=is_completed,
|
||||
completed_at=step_data.get("completed_at"),
|
||||
data=step_data.get("data", {})
|
||||
))
|
||||
|
||||
|
||||
# Determine current and next step
|
||||
current_step = self._get_current_step(completed_steps)
|
||||
next_step = self._get_next_step(completed_steps)
|
||||
|
||||
|
||||
# Calculate completion percentage
|
||||
completion_percentage = (len(completed_steps) / len(ONBOARDING_STEPS)) * 100
|
||||
|
||||
# Check if fully completed
|
||||
fully_completed = len(completed_steps) == len(ONBOARDING_STEPS)
|
||||
|
||||
|
||||
# Check if fully completed - based on REQUIRED steps only
|
||||
# Define required steps
|
||||
REQUIRED_STEPS = [
|
||||
"user_registered",
|
||||
"setup",
|
||||
"suppliers-setup",
|
||||
"ml-training",
|
||||
"completion"
|
||||
]
|
||||
|
||||
# Get user's subscription tier to determine if bakery-type-selection is required
|
||||
user_registered_data = user_progress_data.get("user_registered", {}).get("data", {})
|
||||
subscription_tier = user_registered_data.get("subscription_tier", "professional")
|
||||
|
||||
# Add bakery-type-selection to required steps for non-enterprise users
|
||||
if subscription_tier != "enterprise":
|
||||
required_steps_for_user = REQUIRED_STEPS + ["bakery-type-selection"]
|
||||
else:
|
||||
required_steps_for_user = REQUIRED_STEPS
|
||||
|
||||
# Check if all required steps are completed
|
||||
required_completed = all(
|
||||
user_progress_data.get(step, {}).get("completed", False)
|
||||
for step in required_steps_for_user
|
||||
)
|
||||
|
||||
fully_completed = required_completed
|
||||
|
||||
return UserProgress(
|
||||
user_id=user_id,
|
||||
steps=steps,
|
||||
@@ -234,27 +259,77 @@ class OnboardingService:
|
||||
|
||||
async def complete_onboarding(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Mark entire onboarding as complete"""
|
||||
|
||||
# Ensure all steps are completed
|
||||
|
||||
# Get user's progress
|
||||
progress = await self.get_user_progress(user_id)
|
||||
|
||||
if not progress.fully_completed:
|
||||
incomplete_steps = [
|
||||
step.step_name for step in progress.steps if not step.completed
|
||||
]
|
||||
user_progress_data = await self._get_user_onboarding_data(user_id)
|
||||
|
||||
# Define REQUIRED steps (excluding optional/conditional steps)
|
||||
# These are the minimum steps needed to complete onboarding
|
||||
REQUIRED_STEPS = [
|
||||
"user_registered",
|
||||
"setup", # bakery-type-selection is conditional for enterprise
|
||||
"suppliers-setup",
|
||||
"ml-training",
|
||||
"completion"
|
||||
]
|
||||
|
||||
# Define CONDITIONAL steps that are only required for certain tiers/flows
|
||||
CONDITIONAL_STEPS = {
|
||||
"child-tenants-setup": "enterprise", # Only for enterprise tier
|
||||
"product-categorization": None, # Optional for all
|
||||
"bakery-type-selection": "non-enterprise", # Only for non-enterprise
|
||||
"upload-sales-data": None, # Optional (manual inventory setup is alternative)
|
||||
"inventory-review": None, # Optional (manual inventory setup is alternative)
|
||||
"initial-stock-entry": None, # Optional
|
||||
"recipes-setup": None, # Optional
|
||||
"quality-setup": None, # Optional
|
||||
"team-setup": None, # Optional
|
||||
}
|
||||
|
||||
# Get user's subscription tier
|
||||
user_registered_data = user_progress_data.get("user_registered", {}).get("data", {})
|
||||
subscription_tier = user_registered_data.get("subscription_tier", "professional")
|
||||
|
||||
# Check if all REQUIRED steps are completed
|
||||
incomplete_required_steps = []
|
||||
for step_name in REQUIRED_STEPS:
|
||||
if not user_progress_data.get(step_name, {}).get("completed", False):
|
||||
# Special case: bakery-type-selection is not required for enterprise
|
||||
if step_name == "bakery-type-selection" and subscription_tier == "enterprise":
|
||||
continue
|
||||
incomplete_required_steps.append(step_name)
|
||||
|
||||
# If there are incomplete required steps, reject completion
|
||||
if incomplete_required_steps:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Cannot complete onboarding: incomplete steps: {incomplete_steps}"
|
||||
detail=f"Cannot complete onboarding: incomplete required steps: {incomplete_required_steps}"
|
||||
)
|
||||
|
||||
|
||||
# Log conditional steps that are incomplete (warning only, not blocking)
|
||||
incomplete_conditional_steps = [
|
||||
step.step_name for step in progress.steps
|
||||
if not step.completed and step.step_name in CONDITIONAL_STEPS
|
||||
]
|
||||
if incomplete_conditional_steps:
|
||||
logger.info(
|
||||
f"User {user_id} completing onboarding with incomplete optional steps: {incomplete_conditional_steps}",
|
||||
extra={"user_id": user_id, "subscription_tier": subscription_tier}
|
||||
)
|
||||
|
||||
# Update user's isOnboardingComplete flag
|
||||
await self.user_service.update_user_field(
|
||||
user_id,
|
||||
"is_onboarding_complete",
|
||||
user_id,
|
||||
"is_onboarding_complete",
|
||||
True
|
||||
)
|
||||
|
||||
return {"success": True, "message": "Onboarding completed successfully"}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Onboarding completed successfully",
|
||||
"optional_steps_skipped": incomplete_conditional_steps
|
||||
}
|
||||
|
||||
def _get_current_step(self, completed_steps: List[str]) -> str:
|
||||
"""Determine current step based on completed steps"""
|
||||
|
||||
Reference in New Issue
Block a user