diff --git a/app/assets/stylesheets/styles.scss b/app/assets/stylesheets/styles.scss index 84142ea34..f9c597caf 100644 --- a/app/assets/stylesheets/styles.scss +++ b/app/assets/stylesheets/styles.scss @@ -6,14 +6,14 @@ $skills-green-hover: #4e903c; $skills-search-result-blue: #238bca; $skills-dark-blue: #1e5a96; - .modal-dialog { max-width: 60%; } .pointer { cursor: pointer; -}.modal-dialog { +} +.modal-dialog { max-width: 60%; } .content { @@ -46,7 +46,7 @@ $skills-dark-blue: #1e5a96; } pzsh-topbar { - background-color: $skills-blue + background-color: $skills-blue; } .text-gray { @@ -58,14 +58,14 @@ pzsh-topbar { border-bottom: 2px solid $secondary-bg-subtle !important; } -.interest, .level { +.interest, +.level { pointer-events: none !important; } .table-row:hover { background-color: $light; } - .profile-header { background: $skills-gray; height: 3.25rem; @@ -130,7 +130,7 @@ pzsh-topbar { width: 8px; height: 8px; border-radius: 50%; - background-color: #69b978 + background-color: #69b978; } .w-min-content { @@ -203,7 +203,7 @@ pzsh-topbar { color: #ccc; } -.bg-hover-gray:hover{ +.bg-hover-gray:hover { background-color: #e5e5e5; } @@ -212,16 +212,6 @@ pzsh-topbar { height: 82vh; } -.people-skills-actions { - display: flex; - background-color: $skills-gray; - height: min-content; - @extend .border; - @extend .border-light-subtle; - @extend .border-2; - @extend .rounded-1; -} - .filter-form { width: 95%; } @@ -238,7 +228,8 @@ pzsh-topbar { display: block; } -.portfolio-select, .radar-select { +.portfolio-select, +.radar-select { width: 100% !important; padding-right: 0 !important; padding-left: 0 !important; @@ -292,6 +283,7 @@ pzsh-topbar { position: sticky; top: 80px; background-color: white; + z-index: 2; } .skills-selected { @@ -320,12 +312,52 @@ pzsh-topbar { width: fit-content; } -.center-xy{ +.center-xy { align-items: center; justify-content: center; } -.profile-sidebar { +.sidebar-item { + @extend .p-3; + @extend .skills-blue-text; + @extend .bg-skills-gray; + cursor: pointer; +} + +.sidebar { + display: flex; + flex-direction: column; position: sticky; - top: 204px; + height: min-content; + @extend .col-2; + @extend .gap-2; +} + +.sidebar-item-only { + display: flex; + flex-direction: column; + height: min-content; + @extend .sidebar-item; + @extend .border; + @extend .border-light-subtle; + @extend .border-2; + @extend .rounded-1; + @extend .col-2; +} + +.sidebar-extras { + display: flex; + flex-direction: column; + @extend .gap-2; + + a { + @extend .sidebar-item; + } +} + +a > img { + @extend .text-primary; + @extend .me-1; + width: 16px; + height: 16px; } diff --git a/app/javascript/controllers/scroll_controller.js b/app/javascript/controllers/scroll_controller.js index 55bf0cf8c..8533bdfdd 100644 --- a/app/javascript/controllers/scroll_controller.js +++ b/app/javascript/controllers/scroll_controller.js @@ -1,34 +1,36 @@ -import {Controller} from "@hotwired/stimulus" +import { Controller } from "@hotwired/stimulus" export default class extends Controller { - - static targets = ["listItem", "scrollItem"] - currentSelectedIndex = 0; + static targets = ["list", "listItem", "scrollItem", "parent"] + currentSelectedIndex = -1; + offsetY = this.parentTarget.getBoundingClientRect().top + window.scrollY; connect() { + this.listTarget.style.top = `${this.offsetY}px`; document.addEventListener("scroll", () => { - this.scrollEvent(); + this.highlight(); }); + this.highlight() } disconnect() { document.removeEventListener("scroll", () => { - this.scrollEvent(); + this.highlight(); }); } - scrollToElement({params}) { - document.getElementById(params.id).scrollIntoView({ - behavior: "smooth" - }); + scrollToElement({ params }) { + const elem = document.getElementById(params.id); + const y = elem.getBoundingClientRect().top - this.offsetY + window.scrollY; + window.scrollTo({ top: y, behavior: 'smooth' }); } - scrollEvent() { - for(let i = 0; i < this.scrollItemTargets.length; i++) { - if(this.isElementInViewport(this.scrollItemTargets[i])) { - if(this.currentSelectedIndex !== i) { + highlight() { + for (const [i, scrollItem] of this.scrollItemTargets.entries()) { + if (this.isElementInViewport(scrollItem)) { + if (this.currentSelectedIndex !== i) { + this.listItemTargets.forEach(e => e.classList.remove("skills-selected")) this.listItemTargets[i].classList.add("skills-selected"); - this.listItemTargets[this.currentSelectedIndex].classList.remove("skills-selected"); this.currentSelectedIndex = i; } break; @@ -38,10 +40,6 @@ export default class extends Controller { isElementInViewport(el) { let rect = el.getBoundingClientRect(); - return ( - rect.top >= 0 && - rect.left >= 0 && - rect.right <= (window.innerWidth || document.documentElement.clientWidth) - ); + return rect.top >= this.offsetY; } } \ No newline at end of file diff --git a/app/views/application/_tabbar.html.haml b/app/views/application/_tabbar.html.haml index 020964d98..2141ae10f 100644 --- a/app/views/application/_tabbar.html.haml +++ b/app/views/application/_tabbar.html.haml @@ -1,4 +1,4 @@ -%ul.nav.nav-tabs.d-flex.flex-row.mt-2.justify-content-between +%ul.nav.nav-tabs.d-flex.flex-row.mt-2.justify-content-between.mb-3 %div.d-flex.flex-row - tabs.each do |tab| = link_to tab[:path], class: "btn text-primary p-0", "data-action": "click->profile-tab#setCurrentTab" do diff --git a/app/views/layouts/person.html.haml b/app/views/layouts/person.html.haml index aaf4a42e0..bcb93d705 100644 --- a/app/views/layouts/person.html.haml +++ b/app/views/layouts/person.html.haml @@ -4,7 +4,7 @@ =render partial:"people/search", :locals => {person: @person} %div{"data-controller": "profile-tab"} = render "application/tabbar", tabs: person_tabs(@person) do - =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"} + =link_to image_tag("export.svg")+ "Export", export_cv_person_path(@person), class: "btn text-primary", data: { turbo_frame: "remote_modal" } + %turbo-frame#tab-content.d-flex.gap-3{"data-controller": "scroll"} = yield = render template: "layouts/application" \ No newline at end of file diff --git a/app/views/people/_cv.html.haml b/app/views/people/_cv.html.haml index 337f93869..9e7e723aa 100644 --- a/app/views/people/_cv.html.haml +++ b/app/views/people/_cv.html.haml @@ -1,11 +1,9 @@ -%div.d-flex.flex-row.mt-3{"data-controller": "scroll"} - %div.w-20.bg-skills-gray.me-3.border.border-light-subtle.border-2.min-height.profile-sidebar - = render 'scroll_to_menu', - items: %w[personal-data core-competences educations advanced-trainings activities projects], translate: true - %div.w-80#scroll-content{"data-action": "scroll->scroll#scrollEvent"} - = render('profile') - = render('core_competences') - = render('people/person_relations/index', list: @person.educations.list, id: "educations") - = render('people/person_relations/index', list: @person.advanced_trainings.list, id: "advanced-trainings") - = render('people/person_relations/index', list: @person.activities.list, id: "activities") - = render('people/person_relations/index', list: @person.projects.list, id: "projects") += render 'scroll_to_menu', items: %w[personal-data core-competences educations advanced-trainings activities projects], translate: true do + = link_to image_tag("export.svg") + "Export", export_cv_person_path(@person), data: { turbo_frame: "remote_modal" } +%div.w-100{data: { "scroll-target": "parent"}} + = render('profile') + = render('core_competences') + = render('people/person_relations/index', list: @person.educations.list, id: "educations") + = render('people/person_relations/index', list: @person.advanced_trainings.list, id: "advanced-trainings") + = render('people/person_relations/index', list: @person.activities.list, id: "activities") + = render('people/person_relations/index', list: @person.projects.list, id: "projects") \ No newline at end of file diff --git a/app/views/people/_scroll_to_menu.html.haml b/app/views/people/_scroll_to_menu.html.haml index 225042dca..bff14cc1b 100644 --- a/app/views/people/_scroll_to_menu.html.haml +++ b/app/views/people/_scroll_to_menu.html.haml @@ -1,8 +1,13 @@ -- items.each do | item | - %div.ps-3.pt-3.pb-3.skills-blue-text.cursor-pointer{"data-scroll-target": "listItem", "data-action": "click->scroll#scrollToElement", - "data-scroll-id-param": item} - %span - - if translate - = t "profile.#{item}" - - else - = item \ No newline at end of file +%div.sidebar{data: {"scroll-target": "list"}} + %div + - items.each do | item | + %div.sidebar-item.w-100{data: {"scroll-target": "listItem", "action": "click->scroll#scrollToElement", + "scroll-id-param": item.parameterize}} + %span + - if translate + = t "profile.#{item}" + - else + = item + + %div.sidebar-extras + = yield \ No newline at end of file diff --git a/app/views/people/_skills.html.haml b/app/views/people/_skills.html.haml index fc36a04a4..f1da945dd 100644 --- a/app/views/people/_skills.html.haml +++ b/app/views/people/_skills.html.haml @@ -1,6 +1,5 @@ %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" + =link_to image_tag("pencil-square.svg") + "Skills bearbeiten", people_skills_path, class: "btn d-flex align-items-center text-primary" = render "people_skills/form", person: @person \ No newline at end of file diff --git a/app/views/people/competence_notes/_show.html.haml b/app/views/people/competence_notes/_show.html.haml index 6124c5d9d..f4151033d 100644 --- a/app/views/people/competence_notes/_show.html.haml +++ b/app/views/people/competence_notes/_show.html.haml @@ -2,5 +2,5 @@ %div.d-flex %div.persisted-line-breaks= @person.competence_notes %div.d-flex.flex-row - = link_to image_tag("pencil-square.svg", class: "text-primary me-2 ms-0 d-flex align-items-center", width: '16', height: '16')+ "Bearbeiten", + = link_to image_tag("pencil-square.svg")+ "Bearbeiten", competence_notes_person_path(@person), class: "d-flex align-items-center", id: "edit-link" \ No newline at end of file diff --git a/app/views/people/people_skills/edit.html.haml b/app/views/people/people_skills/edit.html.haml index c03c1fa22..fba55fe23 100644 --- a/app/views/people/people_skills/edit.html.haml +++ b/app/views/people/people_skills/edit.html.haml @@ -1,11 +1,11 @@ %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 - = form.submit :Speichern, { class: "btn btn-primary bg-skills-blue w-75", id: "save-button" } - = link_to t("helpers.submit.cancel"), person_people_skills_path(@person), class: "d-flex align-items-center text-decoration-none mt-2 mb-2" - %div.mw-75.border-bottom.people-skills.overflow-scroll + %div.d-flex.flex-row.w-100.gap-3 + %div.sidebar-item-only + = form.submit :Speichern, { class: "btn btn-primary bg-skills-blue", id: "save-button" } + = link_to t("helpers.submit.cancel"), person_people_skills_path(@person), class: "text-center" + %div.border-bottom.people-skills.overflow-scroll %p.d-flex.align-items-center.profile-header Skills ="(#{@person.people_skills.count})" diff --git a/app/views/people/people_skills/index.html.haml b/app/views/people/people_skills/index.html.haml index b09169787..cb92676b5 100644 --- a/app/views/people/people_skills/index.html.haml +++ b/app/views/people/people_skills/index.html.haml @@ -1,43 +1,37 @@ -%div.d-flex.flex-row.w-100.mt-2 - %turbo-frame#people-skill-form.d-flex.flex-row.w-100.gap-2{"data-controller": "scroll"} - %div.w-20.d-flex.flex-column.me-3.mt-3 - %div.profile-sidebar - %div.people-skills-actions.d-flex.flex-column.test-class - = render('people/scroll_to_menu', items: Category.all_parents.pluck(:title), translate: false) - %div.people-skills-actions.edit-skills-buttton.mt-2 - = link_to image_tag("pencil-square.svg", {class: "me-1"})+ t("helpers.submit.edit", model: Skill.model_name.plural.capitalize()), - people_skills_edit_person_path(@person), class: "d-flex align-items-center p-3" - %div.people-skills-actions.add-skills-button.mt-2 - = link_to image_tag("plus-lg.svg", {class: "me-1"}) + t("helpers.submit.add", model: Skill), new_person_people_skill_path(@person), - class: "d-flex align-items-center p-3", data: { turbo_frame: "remote_modal"} - %div.w-80#scroll-content{"data-action": "scroll->scroll#scrollEvent"} - %p.d-flex.align-items-center.profile-header - Skills - ="(#{@person.people_skills.count})" - %div.ms-3 - = form_with(url: person_people_skills_path(@person), method: :get, data: {controller: "filter", turbo_frame: "people-skills"}) do |f| - %span.btn-group - - rating = request.query_parameters[:rating] - = f.radio_button :rating, 1, class: "btn-check", checked: rating == "1", - onClick: "this.form.requestSubmit()", data: {action: "change->filter#submit"} - = f.label :rating, t("people-skills.filter.all"), value: 1, class: "btn btn-outline-primary" +%turbo-frame#people-skill-form.d-flex.gap-3.col-12 + = render('people/scroll_to_menu', items: Category.all_parents.pluck(:title), translate: false) do + = link_to image_tag("pencil-square.svg")+ t("helpers.submit.edit", model: Skill.model_name.plural.capitalize()), + people_skills_edit_person_path(@person) + = link_to image_tag("plus-lg.svg") + t("helpers.submit.add", model: Skill), new_person_people_skill_path(@person), + data: { turbo_frame: "remote_modal"} + %div.w-100{data: { "scroll-target": "parent"}} + %p.d-flex.align-items-center.profile-header + Skills + ="(#{@person.people_skills.count})" + %div.ms-3 + = form_with(url: person_people_skills_path(@person), method: :get, data: {controller: "filter", turbo_frame: "people-skills"}) do |f| + %span.btn-group + - rating = request.query_parameters[:rating] + = f.radio_button :rating, 1, class: "btn-check", checked: rating == "1", + onClick: "this.form.requestSubmit()", data: {action: "change->filter#submit"} + = f.label :rating, t("people-skills.filter.all"), value: 1, class: "btn btn-outline-primary" - = f.radio_button :rating, 0, class: "btn-check", checked: rating == "0", - onClick: "this.form.requestSubmit()", data: {action: "change->filter#submit"} - = f.label :rating, t("people-skills.filter.rated"), value: 0, class: "btn btn-outline-primary" + = f.radio_button :rating, 0, class: "btn-check", checked: rating == "0", + onClick: "this.form.requestSubmit()", data: {action: "change->filter#submit"} + = f.label :rating, t("people-skills.filter.rated"), value: 0, class: "btn btn-outline-primary" - = f.radio_button :rating, -1, class: "btn-check", checked: rating == "-1", - onClick: "this.form.requestSubmit()", data: {action: "change->filter#submit"} - = f.label :rating, t("people-skills.filter.unrated"), value: -1, class: "btn btn-outline-primary" - = turbo_frame_tag 'people-skills' do - - Category.all_parents.each do |category| - %div.profile-header.mw-100.border-bottom.mt-4{id: category.title, "data-scroll-target": "scrollItem"} - = category.title - - ([category] + category.children.to_a).each do |category_child| - %div.white-header.mw-100.border-bottom.text-gray - - @people_skills_of_category = people_skills_of_category(category_child) - =category_child.title + " (#{@people_skills_of_category.count})" - - @people_skills_of_category.each do |person_skill| - %div.mw-100.border-bottom.text-black.ps-5.border - %div.d-flex.flex-row.align-items-center.mt-3.mb-3 - = render("people_skills/person_skill", {person_skill: person_skill}) \ No newline at end of file + = f.radio_button :rating, -1, class: "btn-check", checked: rating == "-1", + onClick: "this.form.requestSubmit()", data: {action: "change->filter#submit"} + = f.label :rating, t("people-skills.filter.unrated"), value: -1, class: "btn btn-outline-primary" + = turbo_frame_tag 'people-skills' do + - Category.all_parents.each do |category| + %div.profile-header.mw-100.border-bottom.mt-4{id: category.title.parameterize, "data-scroll-target": "scrollItem"} + = category.title + - ([category] + category.children.to_a).each do |category_child| + %div.white-header.mw-100.border-bottom.text-gray + - @people_skills_of_category = people_skills_of_category(category_child) + =category_child.title + " (#{@people_skills_of_category.count})" + - @people_skills_of_category.each do |person_skill| + %div.mw-100.border-bottom.text-black.ps-5.border + %div.d-flex.flex-row.align-items-center.mt-3.mb-3 + = render("people_skills/person_skill", {person_skill: person_skill}) \ No newline at end of file diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index dd3064a0d..5ecb23bcd 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -1,2 +1 @@ -%div{"data-profile-tab-target": "cv"} - = render('cv') += render('cv') diff --git a/app/views/people_skills/filter_form/index.html.haml b/app/views/people_skills/filter_form/index.html.haml index 8191d5112..7722713d2 100644 --- a/app/views/people_skills/filter_form/index.html.haml +++ b/app/views/people_skills/filter_form/index.html.haml @@ -10,7 +10,7 @@ = image_tag("x.svg", class: "text-primary pointer", "data-action": "click->people-skills-filter#remove", "data-people-skills-filter-id-param": i, id: "remove-row-#{i+1}") - if filter_params.rows < 4 - = link_to image_tag("plus-lg.svg", class: "text-primary me-1") + "Skill hinzufügen (max. 5)", + = link_to image_tag("plus-lg.svg") + "Skill hinzufügen (max. 5)", filter_form_people_skills_path + "?rows=#{filter_params.rows + 1}&#{filter_params.query_params}", class: "btn d-flex align-items-center text-primary w-75", data: {"turbo-frame": "search-filters"}, id: "add-row-button" \ No newline at end of file diff --git a/app/views/people_skills/index.html.haml b/app/views/people_skills/index.html.haml index 0b86286f8..a7579aa54 100644 --- a/app/views/people_skills/index.html.haml +++ b/app/views/people_skills/index.html.haml @@ -9,7 +9,7 @@ = image_tag("x.svg", class: "text-primary pointer", "data-action": "click->people-skills-filter#remove", "data-people-skills-filter-id-param": i, id: "remove-row-#{i}") - if filter_params.rows_count < 5 - = link_to image_tag("plus-lg.svg", class: "text-primary me-1") + "Skill hinzufügen (max. 5)", + = link_to image_tag("plus-lg.svg") + "Skill hinzufügen (max. 5)", filter_form_people_skills_path + "?rows=#{filter_params.rows_count}&#{filter_params.query_params}", class: "btn d-flex align-items-center text-primary w-75", data: {"turbo-frame": "search-filters"}, id: "add-row-button" %div.profile-header.mw-100.border-bottom.mt-4.fw-normal diff --git a/app/views/skills/_header.html.haml b/app/views/skills/_header.html.haml index e2369c55b..e6f1a1796 100644 --- a/app/views/skills/_header.html.haml +++ b/app/views/skills/_header.html.haml @@ -23,5 +23,5 @@ = "#{t('skills.header.category')}:" = form.collection_select :category, Category.all_parents.order(:title), :id, :title, {include_blank: "Select a category"}, class: "form-select fit-content", onchange: 'this.form.requestSubmit();' =link_to image_tag("download.svg")+ "Export", export_skills_path, class: "btn text-primary d-flex p-0 center-xy" - =link_to image_tag("plus-lg.svg", class: "text-primary")+ "Neuer Skill", + =link_to image_tag("plus-lg.svg")+ "Neuer Skill", new_skill_path, class: "btn text-primary p-0 d-flex center-xy", data: { turbo_frame: "remote_modal" } diff --git a/spec/features/cv_export_spec.rb b/spec/features/cv_export_spec.rb index aa08d36c2..d6febe027 100644 --- a/spec/features/cv_export_spec.rb +++ b/spec/features/cv_export_spec.rb @@ -7,9 +7,15 @@ end describe 'CV-Export', type: :feature, js: true do + it 'should display 2 export buttons' do + visit person_path(people(:bob)) + + expect(page.all('a', text: 'Export').count).to eql(2) + end + it 'should display range after switch was clicked' do visit person_path(people(:bob)) - page.find('a', text: 'Export').click + page.all('a', text: 'Export').first.click expect(page).not_to have_field('levelValue') page.first('#skillsByLevel').click @@ -18,7 +24,7 @@ it 'should display correct label when range increased' do visit person_path(people(:bob)) - page.find('a', text: 'Export').click + page.all('a', text: 'Export').first.click page.first('#skillsByLevel').click page.find('#levelValue').set(5)