feat(onboarding): reapply onboarding survey and user tours backend#13
Open
marcelogorutuba wants to merge 1 commit intodevelopfrom
Open
feat(onboarding): reapply onboarding survey and user tours backend#13marcelogorutuba wants to merge 1 commit intodevelopfrom
marcelogorutuba wants to merge 1 commit intodevelopfrom
Conversation
This reverts commit 3f2fed3.
Reviewer's GuideReintroduces backend support for onboarding surveys and per-user onboarding tours via new API controllers, models, and database tables, ensuring survey uniqueness per user and tracking tour completion/skipping state. Sequence diagram for saving onboarding setup surveysequenceDiagram
actor User
participant FrontendClient
participant Api_V1_SetupSurveyController
participant User
participant SetupSurveyResponse
participant Database
User->>FrontendClient: Submit setup survey form
FrontendClient->>Api_V1_SetupSurveyController: POST /api/v1/setup_survey
Api_V1_SetupSurveyController->>User: load current_user
Api_V1_SetupSurveyController->>SetupSurveyResponse: find existing by user_id
alt existing response
SetupSurveyResponse-->>Api_V1_SetupSurveyController: return existing record
else no existing response
Api_V1_SetupSurveyController->>User: build_setup_survey_response
User-->>Api_V1_SetupSurveyController: new SetupSurveyResponse
end
Api_V1_SetupSurveyController->>SetupSurveyResponse: assign_attributes(survey_params)
SetupSurveyResponse->>Database: INSERT or UPDATE setup_survey_responses
Database-->>SetupSurveyResponse: success
SetupSurveyResponse-->>Api_V1_SetupSurveyController: save result
Api_V1_SetupSurveyController-->>FrontendClient: 200 OK { completed: true }
FrontendClient-->>User: Indicate survey completion
Sequence diagram for creating or updating user tourssequenceDiagram
actor User
participant FrontendClient
participant Api_V1_UserToursController
participant User
participant UserTour
participant Database
User->>FrontendClient: Complete or skip onboarding tour
FrontendClient->>Api_V1_UserToursController: POST /api/v1/user_tours
Api_V1_UserToursController->>User: load current_user
Api_V1_UserToursController->>UserTour: find_or_initialize_by(user_id, tour_key)
alt tour exists
UserTour-->>Api_V1_UserToursController: existing record
else tour does not exist
UserTour-->>Api_V1_UserToursController: new record
end
Api_V1_UserToursController->>UserTour: set completed_at and status
UserTour->>Database: INSERT or UPDATE user_tours
Database-->>UserTour: success
UserTour-->>Api_V1_UserToursController: save result
Api_V1_UserToursController-->>FrontendClient: 200 OK serialized tour
FrontendClient-->>User: Update tour UI state
Entity relationship diagram for setup survey and user tours tableserDiagram
users {
uuid id PK
string email
}
setup_survey_responses {
uuid id PK
uuid user_id FK
string team_size
string daily_volume
string main_channel
string main_channel_other
string uses_ai
string biggest_pain
string crm_experience
string main_goal
datetime created_at
datetime updated_at
}
user_tours {
uuid id PK
uuid user_id FK
string tour_key
datetime completed_at
string status
datetime created_at
datetime updated_at
}
users ||--o| setup_survey_responses : has_one_setup_survey_response
users ||--o{ user_tours : has_many_user_tours
setup_survey_responses }o--|| users : belongs_to_user
user_tours }o--|| users : belongs_to_user
Class diagram for onboarding survey and user tours backendclassDiagram
class Api_BaseController {
}
class Api_V1_SetupSurveyController {
+show()
+create()
-survey_params()
}
class Api_V1_UserToursController {
+index()
+create()
+destroy()
-tour_params()
-serialize_tour(tour)
}
class SetupSurveyResponse {
+uuid id
+uuid user_id
+string team_size
+string daily_volume
+string main_channel
+string main_channel_other
+string uses_ai
+string biggest_pain
+string crm_experience
+string main_goal
+datetime created_at
+datetime updated_at
+belongs_to user
+validates_uniqueness_of user_id
}
class UserTour {
+STATUSES completed skipped
+uuid id
+uuid user_id
+string tour_key
+datetime completed_at
+string status
+datetime created_at
+datetime updated_at
+belongs_to user
+validates_presence_of tour_key
+validates_uniqueness_of tour_key_scoped_to_user_id
+validates_presence_of completed_at
+validates_inclusion_of_status_in_STATUSES
}
class User {
+uuid id
+has_one setup_survey_response
+has_many user_tours
+setup_survey_completed?()
+build_setup_survey_response()
}
Api_BaseController <|-- Api_V1_SetupSurveyController
Api_BaseController <|-- Api_V1_UserToursController
User "1" -- "1" SetupSurveyResponse : has_one
User "1" -- "*" UserTour : has_many
Api_V1_SetupSurveyController --> User : uses_current_user
Api_V1_SetupSurveyController --> SetupSurveyResponse : manages_survey_response
Api_V1_UserToursController --> User : uses_current_user
Api_V1_UserToursController --> UserTour : manages_user_tours
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
UserToursController#destroy, usingparams[:id]to look up bytour_keyrather than a numeric/UUID id could be confusing—consider either using the primary key or renaming the route/param (e.g.:tour_key) to better reflect the lookup key. - Given
UserTour::STATUSESand thestatusinclusion validation, you might want to use a Railsenumforstatusto centralize allowed values and make querying/filtering by status easier.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `UserToursController#destroy`, using `params[:id]` to look up by `tour_key` rather than a numeric/UUID id could be confusing—consider either using the primary key or renaming the route/param (e.g. `:tour_key`) to better reflect the lookup key.
- Given `UserTour::STATUSES` and the `status` inclusion validation, you might want to use a Rails `enum` for `status` to centralize allowed values and make querying/filtering by status easier.
## Individual Comments
### Comment 1
<location path="app/models/user_tour.rb" line_range="14" />
<code_context>
+# updated_at :datetime not null
+#
+
+class UserTour < ApplicationRecord
+ STATUSES = %w[completed skipped].freeze
+
</code_context>
<issue_to_address>
**nitpick:** Align this model file with the frozen string literal convention used elsewhere.
Other new Ruby files in this change include `# frozen_string_literal: true` at the top, but this model doesn’t. Please add the magic comment here for consistency and to avoid accidental string mutation.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| # updated_at :datetime not null | ||
| # | ||
|
|
||
| class UserTour < ApplicationRecord |
There was a problem hiding this comment.
nitpick: Align this model file with the frozen string literal convention used elsewhere.
Other new Ruby files in this change include # frozen_string_literal: true at the top, but this model doesn’t. Please add the magic comment here for consistency and to avoid accidental string mutation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reapplies the onboarding survey and user tours backend that was reverted in #12.
SetupSurveyController: GET/POST/api/v1/setup_survey— save and check survey completion for authenticated usersUserToursController: CRUD/api/v1/user_tours— track per-user onboarding tour progress (completed/skipped)SetupSurveyResponsemodel with uniqueness validation per userUserTourmodel withcompleted/skippedstatuses and uniquetour_keyper usercreate_user_tours,add_status_to_user_tours,create_setup_survey_responsesTest plan
POST /api/v1/setup_surveysaves survey and returnscompleted: trueGET /api/v1/setup_surveyreturns correctcompletedstatusPOST /api/v1/user_tourscreates/updates a tour entryGET /api/v1/user_toursreturns all tours ordered bycompleted_atDELETE /api/v1/user_tours/:tour_keyresets a tourrails db:migrateruns without errors🤖 Generated with Claude Code
Summary by Sourcery
Reintroduce backend support for onboarding surveys and per-user onboarding tours, including persistence and API endpoints.
New Features:
Enhancements:
Build: