Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#187381833] Feature 28: Invitation with Login routing #75

Merged
merged 12 commits into from
Apr 14, 2024
1 change: 1 addition & 0 deletions rails_root/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ group :test do
gem 'rack_session_access'
gem 'rails-controller-testing'
gem 'selenium-webdriver'
gem 'timecop'
gem 'webmock'
end

Expand Down
2 changes: 2 additions & 0 deletions rails_root/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ GEM
yard-sorbet
thor (1.3.1)
thread_safe (0.3.6)
timecop (0.9.8)
timeout (0.4.1)
tty-which (0.5.0)
turbo-rails (2.0.5)
Expand Down Expand Up @@ -537,6 +538,7 @@ DEPENDENCIES
sqlite3 (~> 1.4)
stimulus-rails
tapioca
timecop
turbo-rails
tzinfo-data
web-console
Expand Down
22 changes: 21 additions & 1 deletion rails_root/app/controllers/auth0_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ def callback

if SurveyProfile.find_by(user_id: session[:userinfo]['sub']).nil?
redirect_to new_survey_profile_path
elsif session[:invitation] && claim_invitation
# Nothing to do here, claim_invitation already did the redirect
else

# Redirect to the URL you want after successful auth
redirect_to root_url
end
Expand All @@ -48,4 +49,23 @@ def logout_url

URI::HTTPS.build(host: AUTH0_CONFIG['auth0_domain'], path: '/v2/logout', query: request_params.to_query).to_s
end

def claim_invitation
temporary_invitation_session_var = session[:invitation]

if temporary_invitation_session_var && temporary_invitation_session_var['expiration'] > Time.now
invitation = Invitation.find_by(id: temporary_invitation_session_var['from'])
if invitation
sharecode_from_invitation = invitation.parent_response.share_code
survey_profile = SurveyProfile.find_by(user_id: session[:userinfo]['sub'])
new_response_to_fill = SurveyResponse.create(profile: survey_profile, share_code: sharecode_from_invitation)
invitation.update(claimed_by_id: survey_profile.id, response_id: new_response_to_fill.id)
redirect_to edit_survey_response_path(new_response_to_fill)
return true
end
end

session.delete(:invitation)
false
end
end
18 changes: 17 additions & 1 deletion rails_root/app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ def index
@survey_profile = SurveyProfile.find_by(user_id: session[:userinfo]['sub'])
return if @survey_profile.nil?

@survey_responses = SurveyResponse.where(profile_id: @survey_profile.id)
@survey_responses = fetch_survey_responses
end

private

def fetch_survey_responses
SurveyResponse.where(profile_id: @survey_profile.id).map do |response|
{ response:, invited_by: fetch_invited_by(response) }
end
end

def fetch_invited_by(response)
invitation = Invitation.find_by(response_id: response.id)
parent_response = SurveyResponse.find(invitation.parent_response_id) if invitation
profile = SurveyProfile.find(parent_response.profile_id) if parent_response
name = "#{profile.first_name} #{profile.last_name}" if profile
name || 'N/A'
end
end
48 changes: 36 additions & 12 deletions rails_root/app/controllers/invitations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,50 @@
# The invited user who visited the url will see this page.
class InvitationsController < ApplicationController
def create
@survey_response = SurveyResponse.find_by!(share_code: params[:survey_response_share_code])
@invitation = Invitation.create!(parent_response: @survey_response, created_by_id: @survey_response.profile_id)
flash[:warning] = "Invitation link created: #{invitation_url(@invitation.token)}"
redirect_to survey_response_path(@survey_response)
@parent_survey_response = SurveyResponse.find_by!(id: params[:parent_survey_response_id])
@invitation = Invitation.create!(parent_response: @parent_survey_response, last_sent: Time.now, visited: false)

redirect_to invitation_created_invitation_path(@invitation.token)
end

def show
@invitation = Invitation.find_by(token: params[:token])

if @invitation.nil?
flash[:error] = 'This invitation link has expired.'
redirect_to root_path
elsif @invitation.visited # don't give more info
flash[:error] = 'This invitation link has expired.'
redirect_to root_path
if @invitation.nil? || @invitation.visited
redirect_to not_found_invitations_path
else
@invitation.update(visited: true)
# TODO: (minseo) maybe set the visited timestamp here?

session[:invitation] = { share_code: @invitation.parent_response.share_code, expiration: 15.minute.from_now }
if session[:userinfo].present?
user_id = session[:userinfo]['sub']
user_profile = SurveyProfile.find_by(user_id:)

claim_invitation(user_profile) if user_profile
end

session[:invitation] = { from: @invitation.id, expiration: 15.minute.from_now }
end
end

def not_found
render :not_found
end

def invitation_created
@invitation = Invitation.find_by(token: params[:token])

return unless @invitation.nil?

redirect_to not_found_invitations_path
end

private

def claim_invitation(user_profile)
sharecode_from_invitation = @invitation.parent_response.share_code

@new_response_to_fill = SurveyResponse.create(profile: user_profile, share_code: sharecode_from_invitation)

@invitation.update(claimed_by_id: user_profile.id, response_id: @new_response_to_fill.id)
end
end
90 changes: 55 additions & 35 deletions rails_root/app/controllers/survey_profiles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,10 @@ def edit; end

# POST /survey_profiles or /survey_profiles.json
def create
# If any of the survey_profile_params values are nil, then the form is invalid

if survey_profile_params.values.any? { |value| value.nil? || value.empty? }
respond_to do |format|
format.html do
redirect_to new_survey_profile_url, notice: 'invalid form', status: :unprocessable_entity
end
format.json { render json: { error: 'invalid form' }, status: :unprocessable_entity }
end

# if user_id is not unique, then the form is invalid
elsif SurveyProfile.find_by(user_id: session[:userinfo]['sub'])
respond_to do |format|
format.html do
redirect_to new_survey_profile_url, notice: 'user_id is not unique', status: :unprocessable_entity
end
format.json { render json: { error: 'user_id is not unique' }, status: :unprocessable_entity }
end

if invalid_form? || non_unique_user?
handle_invalid_form
else
@survey_profile = SurveyProfile.new(survey_profile_params)
@survey_profile.user_id = session[:userinfo]['sub']

respond_to do |format|
if @survey_profile.save
format.html do
# default is below
# redirect_to survey_profile_url(@survey_profile), notice: 'Survey profile was successfully created.'
#
# redirect to the home page after creating a new survey profile
redirect_to root_url
end
format.json { render :show, status: :created, location: @survey_profile }

end
end
create_survey_profile
end
end

Expand Down Expand Up @@ -109,6 +77,22 @@ def destroy

private

def create_survey_profile
@survey_profile = SurveyProfile.new(survey_profile_params)
@survey_profile.user_id = session[:userinfo]['sub']

if session[:invitation] && claim_invitation
# claim_invitation method will handle the redirect
else
respond_to do |format|
if @survey_profile.save
format.html { redirect_to root_url }
format.json { render :show, status: :created, location: @survey_profile }
end
end
end
end

# Use callbacks to share common setup or constraints between actions.
def set_survey_profile
@survey_profile = SurveyProfile.find(params[:id])
Expand All @@ -118,4 +102,40 @@ def set_survey_profile
def survey_profile_params
params.require(:survey_profile).permit(:user_id, :first_name, :last_name, :campus_name, :district_name, :role)
end

def invalid_form?
survey_profile_params.values.any? { |value| value.nil? || value.empty? }
end

def non_unique_user?
SurveyProfile.find_by(user_id: session[:userinfo]['sub'])
end

def handle_invalid_form
respond_to do |format|
format.html do
redirect_to new_survey_profile_url, notice: 'invalid form', status: :unprocessable_entity
end
format.json { render json: { error: 'invalid form' }, status: :unprocessable_entity }
end
end

def claim_invitation
temporary_invitation_session_var = session[:invitation]

if temporary_invitation_session_var && temporary_invitation_session_var['expiration'] > Time.now
invitation = Invitation.find_by(id: temporary_invitation_session_var['from'])
if invitation
sharecode_from_invitation = invitation.parent_response.share_code
# survey_profile = SurveyProfile.find_by(user_id: session[:userinfo]['sub'])
new_response_to_fill = SurveyResponse.create(profile: @survey_profile, share_code: sharecode_from_invitation)
invitation.update(claimed_by_id: @survey_profile.id, response_id: new_response_to_fill.id)
redirect_to edit_survey_response_path(new_response_to_fill)
return true
end
end

session.delete(:invitation)
false
end
end
4 changes: 3 additions & 1 deletion rails_root/app/models/invitation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
# once they finish their survey.
class Invitation < ApplicationRecord
belongs_to :response, class_name: 'SurveyResponse', foreign_key: 'response_id', optional: true
belongs_to :created_by, class_name: 'SurveyProfile', foreign_key: 'created_by_id'
belongs_to :claimed_by, class_name: 'SurveyProfile', foreign_key: 'claimed_by_id', optional: true
belongs_to :parent_response, class_name: 'SurveyResponse', foreign_key: 'parent_response_id'

validates_uniqueness_of :token

before_create :generate_token

private
Expand Down
3 changes: 2 additions & 1 deletion rails_root/app/models/survey_profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ class SurveyProfile < ApplicationRecord
foreign_key: :profile_id,
class_name: 'SurveyResponse',
dependent: :destroy
has_many :created_invitations, class_name: 'Invitation', foreign_key: 'created_by_id'

has_many :claimed_invitations, class_name: 'Invitation', foreign_key: 'claimed_by_id'
end
67 changes: 32 additions & 35 deletions rails_root/app/views/home/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
<!-- app/views/home/index.html.erb -->
<main>
<h1 class="visually-hidden">Home</h1>

<% if session[:userinfo].present? %>
<h1><%= session[:userinfo]['name'] %> - Welcome to Our Rails App</h1>
<br>
<h2>Your Profile</h2>
<% if @survey_profile.present? %>
<%= render @survey_profile%>
<%end%>
<h2>Your History Responses</h2>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Created at</th>
<th>Invited by</th>
<th>Your share code</th>
<th>Actions</th>
</tr>
</thead>
<% if !@survey_responses.nil?%>
<% if session[:userinfo].present? %>
<h1><%= session[:userinfo]['name'] %> - Welcome to Our Rails App</h1>
<br>
<h2>Your Profile</h2>
<% if @survey_profile.present? %>
<%= render @survey_profile%>
<%end%>
<h2>Your History Responses</h2>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Created at</th>
<th>Invited by</th>
<th>Your share code</th>
<th>Actions</th>
</tr>
</thead>
<% if !@survey_responses.nil?%>
<tbody>
<% @survey_responses.each do |response| %>
<% @survey_responses.each do |response_hash| %>
<tr>
<td><%= response.created_at%>
<td></td>
<td><%= response.share_code%></td>
<td><%= link_to "Show", response %></td>
<td><%= response_hash[:response].created_at %></td>
<td><%= response_hash[:invited_by] %></td>
<td><%= response_hash[:response].share_code %></td>
<td><%= link_to "Show", survey_response_path(response_hash[:response]) %></td>
</tr>
<% end %>
</tbody>
<%end%>
</table>
</div>
<%= link_to "Take Survey", new_survey_response_path, class: "btn btn-outline-primary"%>
<% else%>
<h1>Welcome to Our Rails App</h1>
<h1>You are not logged in. Please login.</h1>
<% end %>


<% end %>
</table>
</div>
<%= link_to "Take Survey", new_survey_response_path, class: "btn btn-outline-primary"%>
<% else%>
<h1>Welcome to Our Rails App</h1>
<h1>You are not logged in. Please login.</h1>
<% end %>
</main>
3 changes: 3 additions & 0 deletions rails_root/app/views/invitations/invitation_created.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- app/views/invitations/invitation_created.html.erb -->
<h1>Invitation Created</h1>
<p>Your invitation link is: <%= invitation_url(@invitation.token) %></p>
3 changes: 3 additions & 0 deletions rails_root/app/views/invitations/not_found.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- app/views/invitations/not_found.html.erb -->
<h1>Invitation Not Found</h1>
<p>The invitation you're looking for could not be found.</p>
5 changes: 2 additions & 3 deletions rails_root/app/views/invitations/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<h1>You've been invited to take a survey (...or have you?)</h1>
<p>You were invited by <%= @invitation.created_by.first_name %> <%= @invitation.created_by.last_name %>.</p>
<p>You were invited by <%= @invitation.parent_response.profile.first_name %> <%= @invitation.parent_response.profile.last_name %>.</p>
<p>
<%# <%= link_to "Take the Survey", survey_response_path(@invitation.parent_response), class: 'button-class' %>
<% if session[:userinfo] && session[:invitation] %>
<%= link_to "Take the Survey", new_survey_response_path(share_code: session[:invitation][:share_code]), class: 'button-class' %>
<%= button_to "Take the Survey", edit_survey_response_path(@new_response_to_fill), method: :get, class: 'button-class' %>
<% else %>
<%= button_to 'Take the Survey', '/auth/auth0', method: :post, data: { turbo: false }, class: "button-class" %>
<% end %>
Expand Down
2 changes: 1 addition & 1 deletion rails_root/app/views/survey_responses/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
class: "btn btn-outline-primary"
%>
<%= button_to "Create Invitation",
invitations_path(survey_response_share_code: @survey_response.share_code),
invitations_path(parent_survey_response_id: @survey_response.id),
method: :post,
id: "invitation-button",
form_class: "btn btn-outline-success",
Expand Down
Loading
Loading