Skip to content

Commit

Permalink
Feature/608 profile skills (#648)
Browse files Browse the repository at this point in the history
* implement tabs to switch view

* implement template for view and restructure view to include people search

* implement working form which updates people_skills

* style people skill rows

* add logic to change label of level

* use radio buttons to define interest

* implement star rating for interest

* hide radio buttons and include rating of interest in request

* modify styles and adjust controller method

* refactor logic to work with turbo

* restore old behaviour of form

* implement show view of people_skills

* implement form actions in aside container

* restructure form to be able to update multiple people_skills at the same time

* rebuild form with fields_for method

* make save possible by including nested values in permitted params

* fix label of skill level

* delete unnecessary ressources

* use person as model instead of person_skill

* make redirect possible

* make style adjustments

* delete unnecessary partial

* adjust styling and remove dublicate of people search

* add count of skills to category sections

* implement feature tests

* implement before action to set person

* add label for unweighted skills

* make rubocop happy

* move searchbar to the top decrease space between profile tabs and insert new icon for cv-export

* make people-skill actions sticky

* add scrollable behaviour
  • Loading branch information
MarcEgliP authored Apr 9, 2024
1 parent be93be7 commit 43721aa
Show file tree
Hide file tree
Showing 20 changed files with 373 additions and 11 deletions.
1 change: 1 addition & 0 deletions app/assets/images/export.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 76 additions & 1 deletion app/assets/stylesheets/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,19 @@ pzsh-topbar {
font-size: 18px;
}

.white-header {
padding: 12px 20px 12px 20px;
font-size: 18px;
}

.bg-skills-blue {
background-color: $skills-blue;
}

.bg-skills-gray {
background-color: $skills-gray;
}

.bg-skills-blue:hover {
background-color: #3268a1;
}
Expand All @@ -100,6 +109,72 @@ pzsh-topbar {
background-color: #69b978
}

.bg-hover-gray:hover{

.rate {
float: left;
height: 30px;
width: max-content;

label {
display: flex;
align-items: center;
height: 30px;
width: 25px;
}
}
.rate:not(:checked) > input {
display: none;
}
.rate:not(:checked) > label {
float: right;
overflow:hidden;
white-space:nowrap;
cursor:pointer;
font-size:30px;
color:#ccc;
}
.rate:not(:checked) > label:before {
content: '';
}
.rate > input:checked ~ label {
color: #ffc700;
}
.rate:not(:checked) > label:hover,
.rate:not(:checked) > label:hover ~ label {
color: #deb217;
}
.rate > input:checked + label:hover,
.rate > input:checked + label:hover ~ label,
.rate > input:checked ~ label:hover,
.rate > input:checked ~ label:hover ~ label,
.rate > label:hover ~ input:checked ~ label {
color: #c59b08;
}

.rate-show:not(:checked) > label {
cursor: default;
}

.rate-show > input:checked + label:hover,
.rate-show > input:checked + label:hover ~ label,
.rate-show > input:checked ~ label:hover,
.rate-show > input:checked ~ label:hover ~ label,
.rate-show > label:hover ~ input:checked ~ label {
color: #ffc700;
}
.rate-show:not(:checked) > label:hover,
.rate-show:not(:checked) > label:hover ~ label {
color: #ccc;
}.bg-hover-gray:hover{
background-color: #e5e5e5;
}

.people-skills {
min-width: 80%;
height: 82vh;
}

.people-skills-actions {
min-width: 20%;
height: min-content;
}
28 changes: 28 additions & 0 deletions app/controllers/people/people_skills_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

class People::PeopleSkillsController < CrudController
include ParamConverters
self.permitted_attrs = [{ people_skills_attributes: [:id, :certificate, :level,
:interest, :core_competence, :_destroy] }]
helper_method :people_skills_of_category
before_action :set_person

def self.model_class
Person
end

def show_path
people_skills_person_path(@person)
end

private

def people_skills_of_category(category)
@person.people_skills.where(skill_id: category.skills.pluck(:id))
end

def set_person
@person = Person.find(params[:id])
end

end
6 changes: 6 additions & 0 deletions app/javascript/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ application.register("date-picker", DatePickerController)
import DropdownLinksController from "./dropdown_controller"
application.register("dropdown", DropdownLinksController)

import ProfileTabController from "./profile_tab_controller"
application.register("profile-tab", ProfileTabController)

import SkillsLevelController from "./skill_level_controller"
application.register("skills-level", SkillsLevelController)

16 changes: 16 additions & 0 deletions app/javascript/controllers/profile_tab_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Controller} from "@hotwired/stimulus"

export default class extends Controller {
static targets = ['tab']

connect() {
this.element.addEventListener("click", this.switchTab.bind(this));
}

switchTab(event) {
if(event.target.className === 'nav-link') {
this.tabTargets.forEach((element) => element.classList.remove('active'))
event.target.classList.add('active');
}
}
}
10 changes: 10 additions & 0 deletions app/javascript/controllers/skill_level_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {Controller} from "@hotwired/stimulus"

export default class extends Controller {
static targets = ['switch', 'label']
levels = {"1": "Trainee", "2": "Junior", "3": "Professional", "4": "Senior", "5": "Expert"}

toggleLevel() {
this.labelTarget.textContent = this.levels[this.switchTarget.value]
}
}
2 changes: 1 addition & 1 deletion app/models/expertise_topic_skill_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ExpertiseTopicSkillValue < ApplicationRecord
belongs_to :expertise_topic
belongs_to :person, touch: true

enum skill_level: { trainee: 0, junior: 1, professional: 2, senior: 3, expert: 4 }
enum skill_level: { unweighted: -1, trainee: 0, junior: 1, professional: 2, senior: 3, expert: 4 }

validates :skill_level, presence: true, inclusion: { in: skill_levels.keys }
validates :last_use, length: { is: 4 }, allow_nil: true
Expand Down
1 change: 1 addition & 0 deletions app/models/person.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Person < ApplicationRecord
has_many :person_roles, dependent: :destroy
accepts_nested_attributes_for :person_roles, allow_destroy: true
has_many :people_skills, dependent: :destroy
accepts_nested_attributes_for :people_skills
has_many :skills, through: :people_skills
has_many :roles, through: :person_roles

Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
%div.d-flex.justify-content-center
%ul.navbar
%li
%a.nav-link.cursor-pointer.ps-2.pe-2= "Profil"
%a.nav-link.cursor-pointer.ps-2.pe-2{href: people_path} Profil
%li
%a.nav-link.cursor-pointer.ps-2.pe-2= "Skill Suche"
%li
Expand Down
2 changes: 1 addition & 1 deletion app/views/people/_advanced_trainings.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%div.border.border-secondary-subtle.border-1.d-flex.flex-column
%div.border.border-secondary-subtle.border-1.d-flex.flex-column.mt-4
%div.profile-header.mw-100.border-bottom
= "#{t "activerecord.models.advanced_training"} (#{@person.advanced_trainings.count})"
%div.d-flex.flex-column.ms-5.mt-3.mb-3
Expand Down
3 changes: 3 additions & 0 deletions app/views/people/_cv.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
= render('profile')
= render('core_competences')
= render('advanced_trainings')
4 changes: 1 addition & 3 deletions app/views/people/_profile.html.haml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
%div
=render partial:"people/search", :locals => {person: @person}
%div.profile-header.mw-100.border-bottom
Personalien
%div.mt-4
Expand Down Expand Up @@ -58,5 +57,4 @@
%div.border.border-dark-subtle.mt-1.p-2.rounded
- @person.language_skills.each do |language|
%div.mb-1= "#{language.language}: #{language.level} - #{language.certificate}"
= link_to "Show all", people_path, {"data-turbo": false}
= link_to image_tag("plus-lg.svg", class: "text-primary")+ "Export", export_cv_person_path(@person), class: "btn text-primary", data: { turbo_frame: "remote_modal" }
= link_to "Show all", people_path, {"data-turbo"=>false}
6 changes: 6 additions & 0 deletions app/views/people/_skills.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
%div.profile-header.mw-100.border-bottom
%p.d-flex.align-items-center
Skills
=link_to image_tag("pencil-square.svg", class: "text-primary me-1") + "Skills bearbeiten",
people_skills_path, class: "btn d-flex align-items-center text-primary w-75"
= render "people_skills/form", person: @person
52 changes: 52 additions & 0 deletions app/views/people/people_skills/edit.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
%turbo-frame{id: "people-skill-form"}
%div.w-100
= form_with(model: @person, url: people_skills_person_path(@person)) do |form|
%div.d-flex.flex-row.w-100
%div.w-25.bg-skills-gray.d-flex.flex-column.align-items-center.people-skills-actions.pt-3.pb-3.me-2.mt-2.border.border-light-subtle.border-2
%div.d-flex.flex-row
= image_tag("pencil-square.svg", {class: "me-1"})
%span.fw-medium Bearbeiten
= link_to "Abbrechen", people_skills_person_path(@person), class: "d-flex align-items-center text-decoration-none mt-2 mb-2"
= form.submit :Speichern, { class: "btn btn-primary bg-skills-blue w-75", id: "save-button" }
%div.mw-75.border-bottom.people-skills.overflow-scroll
%p.d-flex.align-items-center.profile-header
Skills
="(#{@person.people_skills.count})"
- Category.all_parents.each do |category|
%div.profile-header.mw-100.border-bottom.mt-4
= category.title
- category.children.each do |category_child|
%div.white-header.mw-100.border-bottom.text-gray
=category_child.title + " (#{people_skills_of_category(category_child).count})"
= form.fields_for :people_skills, form.object.people_skills.where(skill: category_child.skills) do |ff|
%div.mw-100.border-bottom.text-black.ps-5.border
%div.d-flex.flex-row.align-items-center.mt-3.mb-3
%div.col-2
= ff.object.skill.title
%div.col-2
%div.d-flex.flex-column{data: {controller: "skills-level"}}
%label.form-label.text-gray{"data-skills-level-target": "label"}= t ExpertiseTopicSkillValue.skill_levels.key(ff.object.level - 1)
= ff.range_field :level, min: 1, max: 5, class: "form-range w-75",
"data-action": "change->skills-level#toggleLevel", "data-skills-level-target": "switch"
%div.col-2
%div.d-flex.flex-column
%label.form-label.text-gray= t "people-skills.interest"
.rate
= ff.radio_button :interest, 5, checked: ff.object.interest == 5, id: "star5#{ff.object.id}"
%label{for: "star5#{ff.object.id}", title: "text", id: "star-label5#{ff.object.id}", class: "star5"} 5 stars
= ff.radio_button :interest, 4, checked: ff.object.interest == 4, id: "star4#{ff.object.id}"
%label{for: "star4#{ff.object.id}", title: "text", id: "star-label4#{ff.object.id}", class: "star4"} 4 stars
= ff.radio_button :interest, 3, checked: ff.object.interest == 3, id: "star3#{ff.object.id}"
%label{for: "star3#{ff.object.id}", title: "text", id: "star-label3#{ff.object.id}", class: "star3"} 3 stars
= ff.radio_button :interest, 2, checked: ff.object.interest == 2, id: "star2#{ff.object.id}"
%label{for: "star2#{ff.object.id}", title: "text", id: "star-label2#{ff.object.id}", class: "star2"} 2 stars
= ff.radio_button :interest, 1, checked: ff.object.interest == 1, id: "star1#{ff.object.id}"
%label{for: "star1#{ff.object.id}", title: "text", id: "star-label1#{ff.object.id}", class: "star1"} 1 star
%div.col-2
%div.d-flex.flex-row.form-check
= ff.check_box :certificate, class: "form-check-input me-2"
%label.form-label.text-gray= t "people-skills.certificate"
%div.col-2
%div.d-flex.flex-row.form-check
= ff.check_box :core_competence, class: "form-check-input me-2"
%label.form-label.text-gray= t "people-skills.core-competence"
53 changes: 53 additions & 0 deletions app/views/people/people_skills/index.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
%turbo-frame{id: "tab-content"}
%div.d-flex.flex-row.w-100.mt-2
%turbo-frame.d-flex.flex-row.w-100{id: "people-skill-form"}
%div.bg-skills-gray.d-flex.justify-content-center.people-skills-actions.me-2.mt-2.border.border-light-subtle.border-2
= link_to image_tag("pencil-square.svg", {class: "me-1"})+ "Skills bearbeiten",
people_skills_edit_person_path(@person), class: "d-flex align-items-center p-3"
%div.border-bottom.people-skills.overflow-scroll
%p.d-flex.align-items-center.profile-header
Skills
="(#{@person.people_skills.count})"
- Category.all_parents.each do |category|
%div.profile-header.mw-100.border-bottom.mt-4
= category.title
- category.children.each do |category_child|
%div.white-header.mw-100.border-bottom.text-gray
=category_child.title + " (#{people_skills_of_category(category_child).count})"
- people_skills_of_category(category_child).each do |person_skill|
= form_with(model: @person) do
%div.mw-100.border-bottom.text-black.ps-5.border
%div.d-flex.flex-row.align-items-center.mt-3.mb-3
%div.col-2
= person_skill.skill.title
%div.col-2
%div.d-flex.flex-column
%label.form-label.text-gray= t ExpertiseTopicSkillValue.skill_levels.key(person_skill.level - 1)
%input{value: person_skill.level, class: "form-range w-75", type: "range", min: 1, max: 5, disabled: true}
%div.col-2
%div.d-flex.flex-column
%label.form-label.text-gray= t "people-skills.interest"
.rate.rate-show
%input{name: "people_skill[interest]", type: "radio", value: 5, id: "star5#{person_skill.id}",
checked: person_skill.interest == 5, disabled: true}/
%label{for: "star5#{person_skill.id}", title: "text", id: "star-label5#{person_skill.id}"} 5 stars
%input{name: "people_skill[interest]", type: "radio", value: 4, id: "star4#{person_skill.id}",
checked: person_skill.interest == 4, disabled: true}/
%label{for: "star4#{person_skill.id}", title: "text", id: "star-label4#{person_skill.id}"} 4 stars
%input{name: "people_skill[interest]", type: "radio", value: 3, id: "star3#{person_skill.id}",
checked: person_skill.interest == 3, disabled: true}/
%label{for: "star3#{person_skill.id}", title: "text", id: "star-label3#{person_skill.id}"} 3 stars
%input{name: "people_skill[interest]", type: "radio", value: 2, id: "star2#{person_skill.id}",
checked: person_skill.interest == 2, disabled: true}/
%label{for: "star2#{person_skill.id}", title: "text", id: "star-label2#{person_skill.id}"} 2 stars
%input{name: "people_skill[interest]", type: "radio", value: 1, id: "star1#{person_skill.id}",
checked: person_skill.interest == 1, disabled: true}/
%label{for: "star1#{person_skill.id}", title: "text", id: "star-label1#{person_skill.id}"} 1 star
%div.col-2
%div.d-flex.flex-row.form-check
%input.check_box{type: "checkbox", class: "form-check-input me-2", checked: person_skill.certificate, disabled: true}
%label.form-label.text-gray= t "people-skills.certificate"
%div.col-2
%div.d-flex.flex-row.form-check
%input.check_box{type: "checkbox", class: "form-check-input me-2", checked: person_skill.core_competence, disabled: true}
%label.form-label.text-gray= t "people-skills.core-competence"
23 changes: 19 additions & 4 deletions app/views/people/show.html.haml
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
= render('profile')
= render('core_competences')
= render('advanced_trainings')

%div.mt-2
=render partial:"people/search", :locals => {person: @person}
%div{"data-controller": "profile-tab"}
%ul.nav.nav-tabs.d-flex.flex-row.mt-2
%div.w-50.d-flex.flex-row
=link_to person_path(@person), class: "btn d-flex align-items-center justify-content-center text-primary p-0",
data: {turbo_frame: "tab-content"} do
%div.nav-link.active{ "data-profile-tab-target": "tab"}
CV
=link_to people_skills_person_path(@person), class: "btn d-flex align-items-center justify-content-center text-primary p-0",
data: {turbo_frame: "tab-content"} do
%div.nav-link{ "data-profile-tab-target": "tab"}
Skills
%div.w-50.d-flex.justify-content-end
%div.d-flex.justify-content-end
=link_to image_tag("export.svg", class: "text-primary")+ "Export", export_cv_person_path(@person), class: "btn text-primary", data: { turbo_frame: "remote_modal" }
%turbo-frame{id: "tab-content"}
%div{"data-profile-tab-target": "cv"}
= render('cv')
1 change: 1 addition & 0 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ de:
divorced: geschieden
people-skills:
level:
unweighted: Nicht bewertet
trainee: Trainee
junior: Junior
professional: Professional
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ en:
divorced: divorced
people-skills:
level:
unweighted: Unweighted
trainee: Trainee
junior: Junior
professional: Professional
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
get 'export', to: 'people#export'
get 'competence-notes', to: 'people/competence_notes#edit'
post 'competence-notes', to: 'people/competence_notes#update'

get 'people-skills', to: 'people/people_skills#index'
get 'people-skills-edit', to: 'people/people_skills#edit'
patch 'people-skills', to: 'people/people_skills#update'
end

end
Expand Down
Loading

0 comments on commit 43721aa

Please sign in to comment.