feat(tutorial): implement comprehensive tutorial system with step guidance
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m9s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m9s
- Added tutorial controller to manage tutorial state and progress. - Introduced HelpButton component for contextual help throughout the application. - Created various tutorial steps for onboarding and feature guidance. - Integrated tutorial prompts in multiple components (ChildrenListView, LoginButton, ScheduleModal, etc.) to enhance user experience. - Implemented logic to show tutorials based on user actions and state. - Added functionality to dismiss and skip tutorial sessions. - Established a mechanism to hydrate tutorial state from user profile.
This commit is contained in:
@@ -49,6 +49,8 @@ def get_profile():
|
||||
'image_id': user.image_id,
|
||||
'email_digest_enabled': user.email_digest_enabled,
|
||||
'push_notifications_enabled': user.push_notifications_enabled,
|
||||
'tutorial_enabled': user.tutorial_enabled,
|
||||
'tutorial_progress': user.tutorial_progress or {},
|
||||
}), 200
|
||||
|
||||
@user_api.route('/user/profile', methods=['PUT'])
|
||||
@@ -109,6 +111,37 @@ def update_profile():
|
||||
|
||||
return jsonify({'message': 'Profile updated'}), 200
|
||||
|
||||
@user_api.route('/user/tutorial-progress', methods=['PATCH'])
|
||||
def update_tutorial_progress():
|
||||
user_id = get_validated_user_id()
|
||||
if not user_id:
|
||||
return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401
|
||||
user = get_current_user()
|
||||
if not user:
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
data = request.get_json() or {}
|
||||
|
||||
if data.get('reset') is True:
|
||||
user.tutorial_progress = {}
|
||||
elif 'enabled' in data:
|
||||
user.tutorial_enabled = bool(data.get('enabled'))
|
||||
elif 'step_id' in data:
|
||||
step_id = str(data.get('step_id') or '').strip()
|
||||
if not step_id:
|
||||
return jsonify({'error': 'Missing step_id'}), 400
|
||||
progress = dict(user.tutorial_progress or {})
|
||||
progress[step_id] = bool(data.get('seen', True))
|
||||
user.tutorial_progress = progress
|
||||
else:
|
||||
return jsonify({'error': 'No-op'}), 400
|
||||
|
||||
users_db.update(user.to_dict(), UserQuery.email == user.email)
|
||||
send_event_for_current_user(Event(EventType.PROFILE_UPDATED.value, ProfileUpdated(user.id)))
|
||||
return jsonify({
|
||||
'tutorial_enabled': user.tutorial_enabled,
|
||||
'tutorial_progress': user.tutorial_progress,
|
||||
}), 200
|
||||
|
||||
@user_api.route('/user/image', methods=['PUT'])
|
||||
def update_image():
|
||||
user_id = get_validated_user_id()
|
||||
|
||||
@@ -25,6 +25,8 @@ class User(BaseModel):
|
||||
timezone: str | None = None
|
||||
email_digest_enabled: bool = True
|
||||
push_notifications_enabled: bool = True
|
||||
tutorial_enabled: bool = True
|
||||
tutorial_progress: dict = field(default_factory=dict)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
@@ -51,6 +53,8 @@ class User(BaseModel):
|
||||
timezone=d.get('timezone'),
|
||||
email_digest_enabled=d.get('email_digest_enabled', True),
|
||||
push_notifications_enabled=d.get('push_notifications_enabled', True),
|
||||
tutorial_enabled=d.get('tutorial_enabled', True),
|
||||
tutorial_progress=d.get('tutorial_progress', {}) or {},
|
||||
id=d.get('id'),
|
||||
created_at=d.get('created_at'),
|
||||
updated_at=d.get('updated_at')
|
||||
@@ -82,5 +86,7 @@ class User(BaseModel):
|
||||
'timezone': self.timezone,
|
||||
'email_digest_enabled': self.email_digest_enabled,
|
||||
'push_notifications_enabled': self.push_notifications_enabled,
|
||||
'tutorial_enabled': self.tutorial_enabled,
|
||||
'tutorial_progress': self.tutorial_progress,
|
||||
})
|
||||
return base
|
||||
|
||||
Reference in New Issue
Block a user