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

Setup basic route/view/controller for mentee application states #447

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<li class="border-2 flex flex-col p-4 mb-8 gap-2 shadow-lg">
<%= link_to link_text, mentee_application, class: 'font-bold text-lg text-secondary underline hover:no-underline' %>
<%= link_to link_text,
user_mentee_application_mentee_application_state_path(user_mentee_application_id: mentee_application.id, status_name: mentee_application.current_state),
class: 'font-bold text-lg text-secondary underline hover:no-underline' %>
<p>Status: <%= mentee_application.status.humanize %></p>
<p>Submitted: <%= mentee_application.created_at.to_fs(:slash_format) %></p>
</li>
Original file line number Diff line number Diff line change
@@ -1,42 +1,16 @@
class UserMenteeApplications::MenteeApplicationStatesController < ApplicationController
before_action :load_user_mentee_application
before_action -> { authorize :user_only, :application_reviewer? }
skip_before_action :only_authorize_agent

def new; end

# rubocop:disable Metrics/AbcSize
def create
respond_to do |format|
MenteeApplicationTransitionService.call(
application: @user_mentee_application,
reviewer: current_user,
action: application_state_params[:reviewer_action].to_sym,
note: application_state_params[:note]
)
@user_mentee_application.reload
format.html { redirect_to user_mentee_applications_path }
format.turbo_stream do
flash.now[:notice] = "Application updated to #{@user_mentee_application.current_state.status.humanize.downcase}"
end
rescue MenteeApplicationTransitionService::InvalidTransitionError => e
format.html do
redirect_to new_user_mentee_applications_mentee_application_state_path(@user_mentee_application),
alert: e.message
end
format.turbo_stream { flash.now[:alert] = e.message }
end
def show
@state = @user_mentee_application.mentee_application_states.find_by!(status: params[:status_name])
@previous_state = @state.previous_state
@next_state = @state.next_state
end
# rubocop:enable Metrics/AbcSize

private

def load_user_mentee_application
@user_mentee_application = UserMenteeApplication.find(params[:user_mentee_application_id])
end

def application_state_params
params
.permit(:reviewer_action, :note)
.merge(status_changed_id: current_user.id)
@user_mentee_application = current_user.mentee_applications.find(params[:user_mentee_application_id])
end
end
18 changes: 17 additions & 1 deletion app/models/mentee_application_state.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MenteeApplicationState < ApplicationRecord

enum status: {
application_received: 0,
coding_challenge_sent: 1,
coding_challenge: 1,
coding_challenge_received: 2,
coding_challenge_approved: 3,
phone_screen_scheduled: 4,
Expand All @@ -41,4 +41,20 @@ class MenteeApplicationState < ApplicationRecord
def valid_transitions
MenteeApplicationTransitionService.valid_transitions(status:)
end

def future_state
MenteeApplicationTransitionService.future_state(status:)
end

def next_state
user_mentee_application.mentee_application_states.where('created_at > ?', created_at).order(:created_at).first
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These methods could arguably go on the user_mentee_application, but it'd have to accept a parameter for the state. This is one thing I'm not too sure about.

Copy link
Collaborator

Choose a reason for hiding this comment

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

these are fine for now

end

def previous_state
user_mentee_application.mentee_application_states.where('created_at < ?', created_at).order(:created_at).last
end

def to_param
status
end
end
11 changes: 8 additions & 3 deletions app/services/mentee_application_transition_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ class InvalidStatusError < StandardError; end
STATUS_TRANSITION_MAPPING = {
application_received: {
valid_transitions: %i[promote reject withdrawn],
promote_transition: :coding_challenge_sent
promote_transition: :coding_challenge
},
coding_challenge_sent: {
coding_challenge: {
valid_transitions: %i[promote withdrawn],
promote_transition: :coding_challenge_received
},
Expand Down Expand Up @@ -53,7 +53,7 @@ def call(application:, reviewer:, action:, note: nil)
application.mentee_application_states.create!(status: transition_status, reviewer:, note:)
# handle side effects
case transition_status
when :coding_challenge_sent then code_challenge_sent_side_effects(application)
when :coding_challenge then code_challenge_sent_side_effects(application)
when :coding_challenge_approved then coding_challenge_approved_side_effects(application)
when :accepted then accepted_side_effects(application)
when :rejected then rejected_side_effects(application)
Expand All @@ -69,6 +69,11 @@ def valid_transitions(status:)
STATUS_TRANSITION_MAPPING[status][:valid_transitions]
end

def future_state(status:)
status = status.to_sym
STATUS_TRANSITION_MAPPING[status][:promote_transition]
end

private

def code_challenge_sent_side_effects(application)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h3>Accepted!</h3>

<p>Congratulations, your application to join the Agency of Learning has been accepted. We are finishing up interviews with others. After this process is complete, we will contact you with an invite to join our discord.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<h3 class='text-xl mt-4'>What is our Mission?</h3>
<p>At AOL, we bridge the gap between learning and doing. We team up junior developers with seasoned mentors to tackle real-world projects, ensuring that every member not only gains invaluable experience but becomes an integral part of a community that champions each other's growth and success in the tech industry.</p>

<h3 class='text-xl mt-4'>Who would be a good fit?</h3>
<ol class='list-decimal list-inside'>
<li><span class='font-bold'>Aspiring Developers</span>: Individuals who are passionate about coding and technology, looking for real-world experience. They could be recent graduates, self-taught programmers, or career switchers aiming to break into the tech industry.</li>
<li><span class='font-bold'>Continuous Learners</span>: Those who have a genuine curiosity and drive to continuously upskill, learn from others, and share their knowledge.</li>
<li><span class='font-bold'>Team Players</span>: Given the collaborative nature of projects, individuals who communicate well, are open to feedback, and can work effectively in a team would thrive in AOL.</li>
<li><span class='font-bold'>Solution Seekers</span>: Members who are proactive, ready to tackle challenges head-on, and are always looking for innovative solutions.</li>
<li><span class='font-bold'>Community-Focused Individuals</span>: Those who believe in giving back. This includes experienced developers who've landed jobs and are keen on mentoring, guiding, and sharing their experiences with newcomers.</li>
<li><span class='font-bold'>Adaptable and Resilient</span>: The tech world is ever-evolving, and projects can be unpredictable. Members who are adaptable to new technologies, methodologies, and can handle setbacks with a positive attitude would be ideal.</li>
<li><span class='font-bold'>Ethical and Respectful</span>: Given the diverse nature of most tech communities. it's crucial for members to uphold a standard of respect, understanding, and integrity, ensuring a safe and inclusive environment for all.</li>
</ol>
<p>In essence, AOL would be perfect for those who are keen on growing both technically and personally, while valuing the strength of a supportive community.</p>

<h3 class='text-xl mt-4'>What happens if you don't get accepted?</h3>
<p>If you are not accepted, we will try to give you feedback over email. Our goal is your success. It's common for people to re-apply later and be accepted.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h3>Coding Challenge</h3>

<p>You have been sent an email with a coding challenge. This should take you no more than 2-3 hours to complete.</p>

<!-- TODO: fill in more content here -->
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do we want more copy here?

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h3>Coding Challenge Approved</h3>

<!-- TODO: do we need this step anymore? It's not outlined in the notion -->
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This step is not in the notion outline for this feature. Do we want it?

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h3>Coding Challenge Received</h3>

<p>You have submitted your solution to our coding challenge. We will contact you after our team has reviewed your work.</p>

<!-- TODO: fill more content here -->
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Does this need any more copy?

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h3>Phone Screen Completed</h3>

<!-- This is another status I'm not sure we need? -->
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Don't think we need this?

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h3>Phone Screen</h3>

<p>We would like to schedule a zoom call with you. This call is a quick interview with one of our senior members. It will last about 30 minutes. We will have read your application and reviewed your code. The ideal call is an informal conversation about your goals and your coding journey. There will also be an opportunity for you to ask us questions as well.</p>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Maybe this open with something about how we reviewed their code challenge and liked it?


<p>Visit this calendly link to book a time: <%= link_to "Schedule Link", "https://savvycal.com/davepaola/f70795af", target: "_blank", rel: "nofollow", class: "link link-primary" %></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h3>Rejected</h3>

<p>
Thank you for submitting your application to join us at the <strong>Agency of Learning</strong>!
</p>

<p>
Unfortunately you have not been accepted into the upcoming batch. This was a tough decision. We can't make any promises about being admitted on a future application, but we hope that you <strong>reapply for a future batch</strong>.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<h3>Withdrawn</h3>

<p>You have withdrawn your application to the Agency.</p>

<!-- TODO the notion references links? What might those be? -->
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Notion references different links that we could give the user here. What did people have in mind for this?

<p>We encourage you to reapply in the future.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div class="text-center">
<h1 class="text-xl font-bold"><%= current_user.full_name %></h1>
<h2 class="text-lg">Application Status</h2>
</div>

<div class="text-center">
<% if @previous_state.present? %>
<p>The previous state is <%= link_to @previous_state.status.humanize, user_mentee_application_mentee_application_state_path(user_mentee_application_id: @user_mentee_application.id, status_name: @previous_state) %></p>
<% end %>

<p>Current State: <%= @state.status.humanize %></p>

<% if @next_state.present? %>
<p>The next state is <%= link_to @next_state.status.humanize, user_mentee_application_mentee_application_state_path(user_mentee_application_id: @user_mentee_application.id, status_name: @next_state) %></p>
<% else %>
<p>The next state is <%= @state.future_state.to_s.humanize %></p>
<% end %>
</div>

<div class="p-2 max-w-prose mx-auto">
<%= render @state.status %>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ en:
<p> - send an email to the applicant <strong>with a link to the code challenge</strong> </p>
<br />
<p>If you choose to promote, please include why you think the applicant is a good fit for AOL in the note.</p>"
coding_challenge_sent:
coding_challenge:
status_description: "We sent out the code challenge and are awaiting the applicant's response (the typeform submission)."
acceptance_criteria: "In the note, please include the link for the applicant's response (i.e. typeform url) to the code challenge."
coding_challenge_received:
Expand Down
6 changes: 5 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@
end
end

resources :user_mentee_applications, only: %i[index show new create edit update]
resources :user_mentee_applications, only: %i[index show new create edit update] do
scope module: :user_mentee_applications do
resources :mentee_application_states, only: %i[show], param: :status_name
end
end

scope controller: :static do
get :faq
Expand Down
1 change: 1 addition & 0 deletions config/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
"./app/javascript/**/*.js",
"./app/views/**/*.{erb,haml,html,slim}",
"./app/components/**/*.{rb,erb,html}",
"./config/locales/**/*.yml"
],
theme: {
extend: {
Expand Down
4 changes: 2 additions & 2 deletions spec/factories/mentee_application_states.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
trait :application_received do
status { :application_received }
end
trait :coding_challenge_sent do
status { :coding_challenge_sent }
trait :coding_challenge do
status { :coding_challenge }
end
trait :coding_challenge_received do
status { :coding_challenge_received }
Expand Down
4 changes: 2 additions & 2 deletions spec/factories/user_mentee_applications.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@
end
end

trait :coding_challenge_sent do
trait :coding_challenge do
after(:create) do |user_mentee_application|
create(:mentee_application_state, :coding_challenge_sent, user_mentee_application:)
create(:mentee_application_state, :coding_challenge, user_mentee_application:)
end
end

Expand Down
80 changes: 80 additions & 0 deletions spec/models/mentee_application_state_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# == Schema Information
#
# Table name: mentee_application_states
#
# id :bigint not null, primary key
# note :text
# status :integer default("application_received"), not null
# created_at :datetime not null
# updated_at :datetime not null
# status_changed_id :bigint
# user_mentee_application_id :bigint not null
#
# Indexes
#
# index_mentee_application_states_on_status_changed_id (status_changed_id)
# index_mentee_application_states_on_user_mentee_application_id (user_mentee_application_id)
#
# Foreign Keys
#
# fk_rails_... (status_changed_id => users.id)
# fk_rails_... (user_mentee_application_id => user_mentee_applications.id)
require 'rails_helper'

RSpec.describe MenteeApplicationState do
let(:user) { create(:user) }
let(:mentee_application) { create(:user_mentee_application) }
let(:mentee_application_state) { create(:mentee_application_state, user_mentee_application: mentee_application) }

context 'when status is application received' do
describe 'future_state' do
it 'returns the future state as coding challenge sent' do
expect(mentee_application_state.future_state).to eq(:coding_challenge)
end
end

describe 'next_state' do
it 'returns the next state as nil' do
expect(mentee_application_state.next_state).to be_nil
end
end

describe 'previous_state' do
it 'returns the current state' do
expect(mentee_application_state.previous_state.status.to_sym).to eq(:application_received)
end
end
end

context 'when status is coding challenge sent' do
let(:mentee_application_state) do
create(:mentee_application_state, :coding_challenge, user_mentee_application: mentee_application)
end

describe 'future_state' do
it 'returns the future state as coding_challenge_received' do
expect(mentee_application_state.future_state).to eq(:coding_challenge_received)
end
end

describe 'next_state' do
it 'returns the next state as coding_challenge_completed' do
expect(mentee_application_state.next_state).to be_nil
end

context 'when the user is on application_received state' do
let(:previous_mentee_application_state) { mentee_application_state.previous_state }

it 'returns the next state as coding_challenge' do
expect(previous_mentee_application_state.next_state.status.to_sym).to eq(:coding_challenge)
end
end
end

describe 'previous_state' do
it 'returns the previous state as application_received' do
expect(mentee_application_state.previous_state.status.to_sym).to eq(:application_received)
end
end
end
end
2 changes: 1 addition & 1 deletion spec/models/user_mentee_application_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
end

context "when the status is 'in_review" do
let(:status) { :coding_challenge_sent }
let(:status) { :coding_challenge }

it 'is available' do
expect(mentee_application.action_available?).to be true
Expand Down
Loading