Skip to content
Permalink
Browse files
Add expected compensation developer (#108)
* Add and install money-rails gem

* Add monetized compensation fields to developers

* Add monetize fields to developer

* Add compensation input fields for developer

* Show salary and hourly rate if present.

* Update money config with proper defaults

These settings are the new defaults, stops the deprecation warnings.

* Remove money gem and use integers for amount

* Rebased with main and add viewcomponents

* Add tests for the amount and compensation components

* Add min - max compensation fields.

Renamed expected_ to preferred_ as well

* add $ in form instead of USD

* rename component to preferred and update min /max

* Add suffix for hourly (hr) or annually (annual)

* Fix linting errors

* Fix linting errors part 2

* Rename test to proper component name

* making sure tests are passing

* Use 1 migration for the compensation fields

* refactor compensation view_components

* Update form layout with ranges side by side

* Move migration after latest from main

* Max rate/salary must be higher than min

* Copy edits

* Allow nil to avoid nil-to-float comparison

Co-authored-by: Joe Masilotti <joe@masilotti.com>
  • Loading branch information
peterberkenbosch and joemasilotti committed Nov 28, 2021
1 parent 8e2d06b commit 8600ca1
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 2 deletions.
@@ -348,4 +348,4 @@ RUBY VERSION
ruby 3.0.2p107

BUNDLED WITH
2.2.27
2.2.31
@@ -0,0 +1,3 @@
<dd class="flex mt-1 text-sm text-gray-900">
<%= amounts.join(" - ") %> /<%= suffix -%>
</dd>
@@ -0,0 +1,18 @@
class CompensationAmountComponent < ApplicationComponent
attr_reader :amount_range, :suffix

def initialize(amount_range:, suffix:)
@amount_range = amount_range
@suffix = suffix
end

def amounts
amount_range.map do |amount|
helpers.number_to_currency(helpers.number_to_human(amount, format: "%n%u", units: {thousand: "K"}), precision: 0)
end
end

def render?
amount_range.present?
end
end
@@ -0,0 +1,7 @@
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">
Preferred compensation
</dt>
<%= render CompensationAmountComponent.new(amount_range: developer.preferred_hourly_rate_range, suffix: "hr") %>
<%= render CompensationAmountComponent.new(amount_range: developer.preferred_salary_range, suffix: "year") %>
</div>
@@ -0,0 +1,11 @@
class PreferredCompensationComponent < ApplicationComponent
attr_reader :developer

def initialize(developer)
@developer = developer
end

def render?
developer.preferred_hourly_rate_range.present? || developer.preferred_salary_range.present?
end
end
@@ -61,6 +61,10 @@ def developer_params
:avatar,
:cover_image,
:search_status,
:preferred_min_hourly_rate,
:preferred_max_hourly_rate,
:preferred_min_salary,
:preferred_max_salary,
role_type_attributes: [
:part_time_contract,
:full_time_contract,
@@ -21,9 +21,19 @@ class Developer < ApplicationRecord
max_file_size: 2.megabytes
validates :cover_image, content_type: ["image/png", "image/jpg", "image/jpeg", "image/gif"],
max_file_size: 10.megabytes
validates :preferred_max_hourly_rate, allow_nil: true, numericality: {greater_than_or_equal_to: :preferred_min_hourly_rate}, if: -> { preferred_min_hourly_rate.present? }
validates :preferred_max_salary, allow_nil: true, numericality: {greater_than_or_equal_to: :preferred_min_salary}, if: -> { preferred_min_salary.present? }

scope :available, -> { where("available_on <= ?", Date.today) }
scope :most_recently_added, -> { order(created_at: :desc) }

after_initialize :build_role_type, if: -> { role_type.blank? }

def preferred_salary_range
[preferred_min_salary, preferred_max_salary].compact
end

def preferred_hourly_rate_range
[preferred_min_hourly_rate, preferred_max_hourly_rate].compact
end
end
@@ -6,6 +6,7 @@

<%= render SearchStatusComponent.new(developer) %>
<%= render RoleTypeComponent.new(developer.role_type) %>
<%= render PreferredCompensationComponent.new(developer) %>

<div class="mt-6 border-t border-gray-200 py-6 space-y-8">
<div>
@@ -162,6 +162,57 @@
</div>
</div>

<div class="bg-white shadow mt-8 px-4 py-5 lg:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
<h3 class="text-lg font-medium leading-6 text-gray-900">Preferred compensation</h3>
<p class="mt-1 text-sm text-gray-500">What is your preferred annual salary and/or hourly rate?</p>
</div>

<div class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<%= form.label :preferred_min_salary, "Salary (min)", class: "block text-sm font-medium text-gray-700" %>
<div class="mt-1 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
$
</span>
<%= form.number_field :preferred_min_salary, class: "flex-1 focus:ring-gray-500 focus:border-gray-500 block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300" %>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<%= form.label :preferred_max_salary, "Salary (max)", class: "block text-sm font-medium text-gray-700" %>
<div class="mt-1 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
$
</span>
<%= form.number_field :preferred_max_salary, class: "flex-1 focus:ring-gray-500 focus:border-gray-500 block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300" %>
</div>
</div>

<div class="col-span-6 sm:col-span-3">
<%= form.label :preferred_min_hourly_rate, "Hourly rate (min)", class: "block text-sm font-medium text-gray-700" %>
<div class="mt-1 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
$
</span>
<%= form.number_field :preferred_min_hourly_rate, class: "flex-1 focus:ring-gray-500 focus:border-gray-500 block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300" %>
</div>
</div>
<div class="col-span-6 sm:col-span-3">
<%= form.label :preferred_max_hourly_rate, "Hourly rate (max)", class: "block text-sm font-medium text-gray-700" %>
<div class="mt-1 flex rounded-md shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
$
</span>
<%= form.number_field :preferred_max_hourly_rate, class: "flex-1 focus:ring-gray-500 focus:border-gray-500 block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300" %>
</div>
</div>
</div>
</div>
</div>
</div>

<div class="bg-white shadow mt-8 px-4 py-5 lg:rounded-lg sm:p-6">
<div class="md:grid md:grid-cols-3 md:gap-6">
<div class="md:col-span-1">
@@ -0,0 +1,8 @@
class AddPreferredCompensationFieldsToDevelopers < ActiveRecord::Migration[7.0]
def change
add_column :developers, :preferred_min_hourly_rate, :integer
add_column :developers, :preferred_max_hourly_rate, :integer
add_column :developers, :preferred_min_salary, :integer
add_column :developers, :preferred_max_salary, :integer
end
end

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

@@ -0,0 +1,18 @@
require "test_helper"

class CompensationAmountComponentTest < ViewComponent::TestCase
test "doesn't render without amounts" do
render_inline CompensationAmountComponent.new(amount_range: [], suffix: nil)
assert_no_select "*"
end

test "renders with amount range in USD" do
render_inline CompensationAmountComponent.new(amount_range: [150, 250], suffix: "hr")
assert_text "$150 - $250 /hr"
end

test "renders with only 1 value" do
render_inline CompensationAmountComponent.new(amount_range: [250], suffix: "hr")
assert_text "$250 /hr"
end
end
@@ -0,0 +1,24 @@
require "test_helper"

class PreferredCompensationComponentTest < ViewComponent::TestCase
setup do
@developer = developers(:available)
end

test "doesn't render without an expected compensation" do
render_inline(PreferredCompensationComponent.new(@developer))
assert_no_select "*"
end

test "renders hourly rate if provided" do
@developer.preferred_min_hourly_rate = 150
render_inline(PreferredCompensationComponent.new(@developer))
assert_text "$150"
end

test "renders yearly salary if provided" do
@developer.preferred_min_salary = 250000
render_inline(PreferredCompensationComponent.new(@developer))
assert_text "$250K"
end
end
@@ -79,4 +79,22 @@ class DeveloperTest < ActiveSupport::TestCase
assert_includes developers, developers(:available)
refute_includes developers, developers(:unavailable)
end

test "max rate must be higher than min" do
developer = developers(:available)

developer.preferred_min_hourly_rate = 100
developer.preferred_max_hourly_rate = 50

refute developer.valid?
end

test "max salary must be higher than min" do
developer = developers(:available)

developer.preferred_min_salary = 100_000
developer.preferred_max_salary = 50_000

refute developer.valid?
end
end

0 comments on commit 8600ca1

Please sign in to comment.