Skip to content

Commit

Permalink
Bootcamp holding (#7256)
Browse files Browse the repository at this point in the history
* Add holding page

* Add more copy

* Add new flow for bootcmap signups

* Link emails

* WIP

* WIP

* Green

* Add flow

* Change url

* Add production guard
  • Loading branch information
iHiD authored Jan 8, 2025
1 parent 8cce1c8 commit 463c0c3
Show file tree
Hide file tree
Showing 35 changed files with 280 additions and 343 deletions.
12 changes: 4 additions & 8 deletions app/commands/user/bootstrap.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class User::Bootstrap
include Mandate

initialize_with :user
initialize_with :user, bootcamp_access_code: nil

def call
user.auth_tokens.create!
Expand All @@ -14,14 +14,10 @@ def call

private
def link_bootcamp_user!
ubd = User::BootcampData.find_by(email: user.email)
ubd = User::BootcampData.find_by(access_code: bootcamp_access_code) if bootcamp_access_code.present?
ubd ||= User::BootcampData.find_by(email: user.email)
return unless ubd

ubd.update!(user:)
User::Bootcamp::SubscribeToOnboardingEmails.defer(ubd)

return unless ubd.paid?

user.update!(bootcamp_attendee: true)
User::LinkWithBootcampData.(user, ubd)
end
end
33 changes: 33 additions & 0 deletions app/commands/user/link_with_bootcamp_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class User::LinkWithBootcampData
include Mandate

initialize_with :user, :bootcamp_data

def call
return unless bootcamp_data

reset_current_user!
reset_old_user!
bootcamp_data.update!(user:)
User::Bootcamp::SubscribeToOnboardingEmails.defer(bootcamp_data)

return unless bootcamp_data.paid?

user.update!(bootcamp_attendee: true)
end

memoize

def reset_current_user!
existing_data = User::BootcampData.find_by(user:)
return unless existing_data

existing_data.update!(user: nil)
end

def reset_old_user!
return unless bootcamp_data.user

bootcamp_data.user.update!(bootcamp_attendee: false)
end
end
5 changes: 5 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base
include BodyClassConcern

# around_action :set_log_level
before_action :store_session_variables
before_action :store_user_location!, if: :storable_location?
before_action :authenticate_user!
before_action :ensure_onboarded!
Expand Down Expand Up @@ -78,6 +79,10 @@ def ensure_trainer!
redirect_to training_data_external_path
end

def store_session_variables
session[:bootcamp_access_code] = params[:bootcamp_access_code] if params[:bootcamp_access_code].present?
end

# We want to mark relevant notifications as read, but we don't
# care about doing this before the rest of the action is run, so we
# use a promise to kick it off async. However, we do want it to finish
Expand Down
9 changes: 8 additions & 1 deletion app/controllers/auth/registrations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ class RegistrationsController < Devise::RegistrationsController
before_action :verify_captcha!, only: [:create]

def create
super { |user| User::Bootstrap.(user) if user.persisted? }
super do |user|
if user.persisted?
User::Bootstrap.(
user,
bootcamp_access_code: session[:bootcamp_access_code]
)
end
end
end

def configure_permitted_parameters
Expand Down
19 changes: 19 additions & 0 deletions app/controllers/bootcamp/base_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
class Bootcamp::BaseController < ApplicationController
layout "bootcamp-ui"
before_action :redirect_unless_attendee!

private
def redirect_unless_attendee!
return if current_user&.bootcamp_attendee?
return if current_user&.bootcamp_mentor?

if session[:bootcamp_access_code].present?
return redirect_to new_user_session_path unless user_signed_in?

ubd = User::BootcampData.find_by(access_code: session[:bootcamp_access_code])
return redirect_to bootcamp_path unless ubd

User::LinkWithBootcampData.(current_user, ubd)
return if current_user.bootcamp_attendee?
end

redirect_to bootcamp_path
end

def use_project
@project = Bootcamp::Project.find_by!(slug: params[:project_slug])
Expand Down
27 changes: 26 additions & 1 deletion app/controllers/bootcamp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ class BootcampController < ApplicationController
layout 'bootcamp'

skip_before_action :authenticate_user!
before_action :redirect_if_paid!
before_action :save_utm!
before_action :setup_data!
before_action :setup_pricing!
Expand Down Expand Up @@ -84,8 +85,18 @@ def stripe_session_status
if session.status == 'complete'
@bootcamp_data.update!(
paid_at: Time.current,
checkout_session_id: session.id
checkout_session_id: session.id,
access_code: SecureRandom.hex(8)
)
if current_user
current_user.update!(bootcamp_attendee: true)
else
user = User.find_by(email: @bootcamp_data.email)
if user
@bootcamp_data.update(user:)
user.update!(bootcamp_attendee: true)
end
end
end

render json: {
Expand All @@ -110,6 +121,10 @@ def setup_data!
@country_code_2 = lookup_country_code_from_ip
session[:country_code_2] = @country_code_2
end

return unless @bootcamp_data.user_id.nil? && cookies.signed[:_exercism_user_id].present?

@bootcamp_data.update(user_id: cookies.signed[:_exercism_user_id])
end

def retrieve_user_bootcamp_data_from_user
Expand Down Expand Up @@ -153,6 +168,10 @@ def create_bootcamp_data!

@bootcamp_data = User::BootcampData.create!(ppp_country: @country_code_2, utm: session[:utm])
session[:bootcamp_data_id] = @bootcamp_data.id

return unless cookies.signed[:_exercism_user_id].present? && @bootcamp_data.user_id.nil?

@bootcamp_data.update(user_id: cookies.signed[:_exercism_user_id])
end

def setup_pricing!
Expand Down Expand Up @@ -190,4 +209,10 @@ def save_utm!
session[:utm][:medium] = params[:utm_medium] if params[:utm_medium].present?
session[:utm][:campaign] = params[:utm_campaign] if params[:utm_campaign].present?
end

def redirect_if_paid!
return unless current_user&.bootcamp_attendee?

redirect_to bootcamp_dashboard_url
end
end
2 changes: 1 addition & 1 deletion app/css/bootcamp/components/exercise-widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

.tag {
@apply border-gray-300;
background-image: url("icons/available.svg");
background-image: url("icons/bootcamp-available.svg");
}
}
&.completed {
Expand Down
2 changes: 1 addition & 1 deletion app/css/bootcamp/components/project-widget.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

.tag {
@apply border-gray-300;
background-image: url("icons/available.svg");
background-image: url("icons/bootcamp-available.svg");
}
}
&.completed {
Expand Down
4 changes: 2 additions & 2 deletions app/css/bootcamp/pages/concept.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
body.controller-concepts.action-show {
body.namespace-bootcamp.controller-concepts.action-show {
@apply bg-grad-basic;
}
#page-concept {
#page-bootcamp-concept {
header {
@apply pb-16;
.details {
Expand Down
28 changes: 23 additions & 5 deletions app/css/bootcamp/pages/dashboard.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
#page-dashboard {
body.namespace-bootcamp.controller-dashboard.action-index {
@apply bg-grad-basic;
}

#page-bootcamp-dashboard {
@apply pt-24;
h1 {
@apply text-[36px] leading-140;
@apply font-bold text-textColor1;
@apply mb-4;
}
h2 {
@apply text-22 leading-150;
@apply font-semibold text-textColor1;
@apply mt-20 mb-4;
}
p.large {
@apply text-20 leading-150;
@apply font-semibold;
@apply mb-4;
@apply mb-8;
}
p {
@apply text-16 leading-150;
p,
ul {
@apply text-18 leading-150;
@apply mb-8;
}
ul {
@apply list-disc pl-20;
}

.exercise {
@apply py-12 px-16 rounded-8 border-1 block;
Expand Down
2 changes: 1 addition & 1 deletion app/css/bootcamp/pages/level.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#page-level {
#page-bootcamp-level {
header {
@apply bg-grad-basic;
@apply pb-16;
Expand Down
2 changes: 1 addition & 1 deletion app/css/bootcamp/pages/project.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
body.controller-projects.action-show {
@apply bg-grad-basic;
}
#page-project {
#page-bootcamp-project {
header {
.project-bar {
@apply flex items-center gap-20 mb-20;
Expand Down
4 changes: 2 additions & 2 deletions app/css/bootcamp/pages/projects.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
body.controller-projects.action-index {
body.namespace-bootcamp.controller-projects.action-index {
@apply bg-grad-basic;
}
#page-projects {
#page-bootcamp-projects {
header {
.title-bar {
@apply pb-20;
Expand Down
1 change: 0 additions & 1 deletion app/css/packs/bootcamp-ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
@import "../bootcamp/components/project-widget";
@import "../bootcamp/components/exercise-widget";
@import "../bootcamp/components/concept-widget";
@import "../bootcamp/components/modal";

@import "../bootcamp/pages/dashboard";
@import "../bootcamp/pages/projects";
Expand Down
20 changes: 20 additions & 0 deletions app/css/packs/bootcamp.css
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,26 @@ section#confirmation {
opacity: 0.2;
animation: spin 3s linear infinite;
}
button,
.button {
@apply w-100;
border-radius: 8px;
font-size: 18px;
height: 56px;
padding-left: 24px;
padding-right: 24px;
align-items: center;
display: flex;
flex-shrink: 0;
font-weight: 600;
justify-content: center;
transition: background-color 0.1s ease-in;
white-space: nowrap;
background-color: var(--purple);
border-color: rgb(19 11 67);
border-width: 1px;
color: rgb(255 255 255);
}
}

@keyframes color-change {
Expand Down
8 changes: 0 additions & 8 deletions app/css/pages/bootcamp/admin/base.css

This file was deleted.

76 changes: 0 additions & 76 deletions app/css/pages/bootcamp/admin/exercises.css

This file was deleted.

Loading

0 comments on commit 463c0c3

Please sign in to comment.