Skip to content

feat(onboarding): reapply onboarding survey and user tours backend#13

Open
marcelogorutuba wants to merge 1 commit intodevelopfrom
feat/onboarding-survey-v2
Open

feat(onboarding): reapply onboarding survey and user tours backend#13
marcelogorutuba wants to merge 1 commit intodevelopfrom
feat/onboarding-survey-v2

Conversation

@marcelogorutuba
Copy link
Copy Markdown
Contributor

@marcelogorutuba marcelogorutuba commented May 5, 2026

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 users
  • UserToursController: CRUD /api/v1/user_tours — track per-user onboarding tour progress (completed/skipped)
  • SetupSurveyResponse model with uniqueness validation per user
  • UserTour model with completed/skipped statuses and unique tour_key per user
  • 3 migrations: create_user_tours, add_status_to_user_tours, create_setup_survey_responses

Test plan

  • POST /api/v1/setup_survey saves survey and returns completed: true
  • GET /api/v1/setup_survey returns correct completed status
  • POST /api/v1/user_tours creates/updates a tour entry
  • GET /api/v1/user_tours returns all tours ordered by completed_at
  • DELETE /api/v1/user_tours/:tour_key resets a tour
  • rails db:migrate runs 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:

  • Add API endpoints for creating, listing, and resetting per-user onboarding tours.
  • Add an authenticated API to save and query a user’s onboarding setup survey completion state.
  • Introduce SetupSurveyResponse and UserTour models to persist onboarding survey responses and tour progress per user.

Enhancements:

  • Enforce uniqueness and validation constraints for survey responses per user and tour keys per user.
  • Order returned user tours by completion timestamp to provide consistent progress listing.

Build:

  • Add database migrations for setup survey responses and user tours, including status tracking for tour completion or skipping.

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 5, 2026

Reviewer's Guide

Reintroduces 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 survey

sequenceDiagram
  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
Loading

Sequence diagram for creating or updating user tours

sequenceDiagram
  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
Loading

Entity relationship diagram for setup survey and user tours tables

erDiagram
  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
Loading

Class diagram for onboarding survey and user tours backend

classDiagram
  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
Loading

File-Level Changes

Change Details Files
Add CRUD API for tracking per-user onboarding tour progress
  • Introduce Api::V1::UserToursController with index/create/destroy actions scoped to current_user
  • Serialize tour responses with id, tour_key, completed_at, and status fields
  • Validate and default tour status based on a constrained status list
app/controllers/api/v1/user_tours_controller.rb
app/models/user_tour.rb
Add API for saving and checking onboarding setup survey completion for authenticated users
  • Introduce Api::V1::SetupSurveyController with show/create actions to read/write survey state
  • Reuse or build associated SetupSurveyResponse for current_user and persist permitted survey attributes
  • Return a simple completed flag in responses to indicate survey completion
app/controllers/api/v1/setup_survey_controller.rb
app/models/setup_survey_response.rb
Create database schema for setup survey responses and user tours, including status tracking
  • Create setup_survey_responses table with a unique user reference and survey metadata columns
  • Create user_tours table keyed by user and tour_key with completed_at timestamps and a unique composite index
  • Add status column to user_tours with non-null default of 'completed' to support completed/skipped states
db/migrate/20260409000001_create_setup_survey_responses.rb
db/migrate/20260407000001_create_user_tours.rb
db/migrate/20260407000002_add_status_to_user_tours.rb

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread app/models/user_tour.rb
# updated_at :datetime not null
#

class UserTour < ApplicationRecord
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant