diff --git a/app/assets/stylesheets/styles.scss b/app/assets/stylesheets/styles.scss index 9305262aa..e2c9b4a45 100644 --- a/app/assets/stylesheets/styles.scss +++ b/app/assets/stylesheets/styles.scss @@ -1,5 +1,6 @@ $skills-blue: #3b7bbe; $skills-text-gray: #8e8e8e; +$skills-gray: #f5f5f5; @import "bootstrap"; @@ -42,4 +43,24 @@ pzsh-topbar { .text-gray { color: $skills-text-gray; +} + +.person-profile-header { + background: $skills-gray; + height: 3.25rem; + padding: 12px 20px 12px 20px; + font-size: 18px; +} + +.bg-skills-blue { + background-color: $skills-blue; +} + +.bg-skills-blue:hover { + background-color: #3268a1; +} + +.fixed-table { + table-layout: fixed; + width: 100%; } \ No newline at end of file diff --git a/app/controllers/concerns/param_converters.rb b/app/controllers/concerns/param_converters.rb new file mode 100644 index 000000000..8325fcbb3 --- /dev/null +++ b/app/controllers/concerns/param_converters.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module ParamConverters + + private + + def true?(value) + %w[1 yes true].include?(value.to_s.downcase) + end + + def false?(value) + %w[0 no false].include?(value.to_s.downcase) + end + + def nil_param?(value) + value == 'null' ? nil : value + end +end diff --git a/app/controllers/crud_controller.rb b/app/controllers/crud_controller.rb index 6ce7f425a..e2bdcd269 100644 --- a/app/controllers/crud_controller.rb +++ b/app/controllers/crud_controller.rb @@ -244,6 +244,15 @@ def error_messages # rubocop:enable Rails/OutputSafety end + def get_asset_path(filename) + manifest_file = Rails.application.assets_manifest.assets[filename] + if manifest_file + File.join(Rails.application.assets_manifest.directory, manifest_file) + else + Rails.application.assets&.[](filename)&.filename + end + end + # Class methods for CrudActions. class << self # Convenience callback to apply a callback on both form actions diff --git a/app/controllers/people/picture_controller.rb b/app/controllers/people/picture_controller.rb new file mode 100644 index 000000000..d9d5521bb --- /dev/null +++ b/app/controllers/people/picture_controller.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module People + class PictureController < CrudController + + self.permitted_attrs = %i[picture] + + def update + person.update!(picture: params[:picture]) + render json: { data: { picture_path: picture_person_path(params[:id]) } } + end + + def show + picture_url = person.picture.file.nil? ? default_avatar_path : person.picture.url + send_file(picture_url, disposition: 'inline') + end + + private + + def model_class + Person + end + + def person + @person ||= Person.find(params[:id]) + end + + def default_avatar_path + get_asset_path 'default_avatar.png' + end + end +end diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb index 4e4430c0d..5e6a6d8c9 100644 --- a/app/controllers/people_controller.rb +++ b/app/controllers/people_controller.rb @@ -2,16 +2,20 @@ class PeopleController < CrudController include ExportController + include ParamConverters - # self.permitted_attrs = %i[birthdate location - # marital_status updated_by name nationality nationality2 title - # competence_notes company_id email department_id shortname] + self.permitted_attrs = [:birthdate, :location, :marital_status, :updated_by, :name, :nationality, + :nationality2, :title, :competence_notes, :company_id, :email, + :department_id, :shortname, :picture, :picture_cache, + { person_roles_attributes: [:role_id, :person_role_level_id, + :percent, :id, :_destroy] }] - # self.nested_models = %i[advanced_trainings activities projects - # educations language_skills person_roles - # people_skills categories] - - # self.permitted_relationships = %i[person_roles people_skills] + def update + if false?(params['has_nationality2']['checked']) + params['person']['nationality2'] = nil + end + super + end def show if format_odt? diff --git a/app/helpers/person_helper.rb b/app/helpers/person_helper.rb new file mode 100644 index 000000000..33613661c --- /dev/null +++ b/app/helpers/person_helper.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module PersonHelper + def nationality_string(nationality, nationality2) + if nationality2.blank? + ISO3166::Country[nationality].translations[I18n.locale] + else + "#{ISO3166::Country[nationality].translations[I18n.locale]}, + #{ISO3166::Country[nationality2].translations[I18n.locale]}" + end + end + + def person_role_string(person_role) + "#{person_role.role.name} #{person_role.person_role_level&.level} + #{person_role.percent.nil? ? '' : "#{person_role.percent.to_i}%"}" + end + + def marital_status_translation_map + Person.marital_statuses.keys.map do |marital_status| + [marital_status, t("marital_statuses.#{marital_status}")] + end + end + + def country_alpha2_translation_map + ISO3166::Country.all.sort.map do |country| + [country.alpha2, country.translations[I18n.locale]] + end + end + + # If the path of the avatar includes tmp, the picture is cached + # and we can load it directly without the picture controller + def avatar_cached?(picture) + picture&.file&.file&.include? 'tmp' + end +end diff --git a/app/helpers/role_form_helper.rb b/app/helpers/role_form_helper.rb new file mode 100644 index 000000000..1b06d48b2 --- /dev/null +++ b/app/helpers/role_form_helper.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module RoleFormHelper + # This method creates a link with `data-id` `data-fields` attributes. These attributes are used + # to create new instances of the nested fields through Javascript. + def link_to_add_role(name, form, association) + # Takes an object and creates a new instance of its associated model + new_role = form.object.send(association).klass.new + # Saves the unique ID of the object into a variable. + # This is needed to ensure the key of the associated array is unique. This makes parsing the + # content in the `data-fields` attribute easier through Javascript. + id = new_role.object_id + # child_index` is used to ensure the key of the associated array is unique, and that it matched + # the value in the `data-id` attribute. + fields = form.fields_for(association, new_role, child_index: id) do |builder| + render("#{association.to_s.singularize}_fields", person_role: builder) + end + + + # This renders a simple link, but passes information into `data` attributes. + # This info can be named anything we want, but in this case + # we chose `data-id:` and `data-fields:`. + # We use `gsub("\n", "")` to remove anywhite space from the rendered partial. + # The `id:` value needs to match the value used in `child_index: id`. + link_to(name, '#', + { class: 'add_fields', 'data-action' => 'person-roles#addField', + data: { id: id, fields: fields.gsub("\n", '') } }) + end +end diff --git a/app/javascript/controllers/image_upload_controller.js b/app/javascript/controllers/image_upload_controller.js new file mode 100644 index 000000000..4a29899dd --- /dev/null +++ b/app/javascript/controllers/image_upload_controller.js @@ -0,0 +1,9 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + changeImage() { + const avatarUploader = document.getElementById("avatar-uploader"); + const avatar = document.getElementById("avatar"); + avatar.src = URL.createObjectURL(avatarUploader.files[0]) + } +} \ No newline at end of file diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index d2fdc8dca..2046c521f 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -4,8 +4,17 @@ import { application } from "./application" -import DropdownLinksController from "./dropdown_controller" -application.register("dropdown", DropdownLinksController) +import DropdownController from "./dropdown_controller" +application.register("dropdown", DropdownController) + +import ImageUploadController from "./image_upload_controller" +application.register("image-upload", ImageUploadController) + +import NationalityTwoController from "./nationality_two_controller" +application.register("nationality-two", NationalityTwoController) + +import PersonRolesController from "./person_roles_controller" +application.register("person-roles", PersonRolesController) import RemoteModalController from "./remote_modal_controller" application.register("remote-modal", RemoteModalController) diff --git a/app/javascript/controllers/nationality_two_controller.js b/app/javascript/controllers/nationality_two_controller.js new file mode 100644 index 000000000..1626e5d70 --- /dev/null +++ b/app/javascript/controllers/nationality_two_controller.js @@ -0,0 +1,18 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + setVisibility() { + const natTwoElements = document.getElementsByClassName('nationality-two') + const natTwoCheckbox = document.getElementById('nat-two-checkbox') + for(const element of natTwoElements) { + !natTwoCheckbox.checked ? element.classList.add('d-none') : element.classList.remove('d-none') + } + } + + connect() { + this.setVisibility(); + } + nationalityTwoVisible() { + this.setVisibility() + } +} \ No newline at end of file diff --git a/app/javascript/controllers/person_roles_controller.js b/app/javascript/controllers/person_roles_controller.js new file mode 100644 index 000000000..15a7f57cf --- /dev/null +++ b/app/javascript/controllers/person_roles_controller.js @@ -0,0 +1,47 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + for(let nested_field of document.getElementsByClassName("nested-fields")) { + if(nested_field.querySelector('input[type="hidden"]').value === "true") { + nested_field.style.display = "none"; + } + } + } + addField(e) { + let link = e.target + // Stop the function from executing if a link or event were not passed into the function. + if (!link || !e) return; + // Prevent the browser from following the URL. + e.preventDefault(); + // Save a unique timestamp to ensure the key of the associated array is unique. + let time = new Date().getTime(); + // Save the data id attribute into a variable. This corresponds to `new_object.object_id`. + let linkId = link.dataset.id; + // Create a new regular expression needed to find any instance of the `new_object.object_id` used in the fields data attribute if there's a value in `linkId`. + let regexp = linkId ? new RegExp(linkId, "g") : null; + // Replace all instances of the `new_object.object_id` with `time`, and save markup into a variable if there's a value in `regexp`. + let newFields = regexp ? link.dataset.fields.replace(regexp, time) : null; + // Add the new markup to the form if there are fields to add. + newFields ? link.insertAdjacentHTML("beforebegin", newFields) : null; + } + + removeField(e) { + let link = e.target + // Stop the function from executing if a link or event were not passed into the function. + if (!link || !e) return; + // Prevent the browser from following the URL. + e.preventDefault(); + // Find the parent wrapper for the set of nested fields. + let fieldParent = link.closest(".nested-fields"); + // If there is a parent wrapper, find the hidden delete field. + let deleteField = fieldParent + ? fieldParent.querySelector('input[type="hidden"]') + : null; + // If there is a delete field, update the value to `1` and hide the corresponding nested fields. + if (deleteField) { + deleteField.value = 1; + fieldParent.style.display = "none"; + } + } +} \ No newline at end of file diff --git a/app/models/person.rb b/app/models/person.rb index 85d09d036..a99e11bb4 100644 --- a/app/models/person.rb +++ b/app/models/person.rb @@ -39,6 +39,7 @@ class Person < ApplicationRecord has_many :expertise_topics, through: :expertise_topic_skill_values has_many :language_skills, dependent: :delete_all has_many :person_roles, dependent: :destroy + accepts_nested_attributes_for :person_roles, allow_destroy: true has_many :people_skills, dependent: :destroy has_many :skills, through: :people_skills has_many :roles, through: :person_roles diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index dde0ec35e..606a8267b 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -10,7 +10,7 @@ = javascript_include_tag "application", "data-turbo-track": "reload", type: "module" = favicon_link_tag "favicon.png" %body.d-flex.justify-content-center - %pzsh-container.content + %div.content %div.position-sticky.top-0.z-3 %div.d-flex.justify-content-between.bg-white %div.d-flex diff --git a/app/views/people/_form.html.haml b/app/views/people/_form.html.haml new file mode 100644 index 000000000..e3ede064e --- /dev/null +++ b/app/views/people/_form.html.haml @@ -0,0 +1,70 @@ += form_with model: @person do |form| + %div.d-flex.flex-xl-row.flex-column + %div.col-xl-3.col-12 + - if avatar_cached?(@person.picture) + %img.rounded-circle#avatar{src: "#{@person.picture}?#{Time.now.to_f}", width: '141', height: '141'} + - else + %img.rounded-circle#avatar{src: "#{@person.id}/picture?#{Time.now.to_f}", width: '141', height: '141'} + %br + %label.btn.btn-link{for: "avatar-uploader"} Bild ändern + %div.visually-hidden{"data-controller"=>"image-upload"}= form.file_field :picture, { accept: "image/", "data-action" => "image-upload#changeImage", id: "avatar-uploader" } + = form.hidden_field :picture_cache + %div.pe-5.col-xl-3.col-12 + %table + %tbody + %th.fw-normal Name + %tr + %td= form.text_field :name, class: "mw-100 form-control" + %th.fw-normal Email + %tr + %td= form.text_field :email, class: "mw-100, form-control" + %th.fw-normal Abschluss + %tr + %td= form.text_field :title, class: "mw-100, form-control" + %th.fw-normal Funktionen + %div + = form.fields_for :person_roles do |person_role| + %tr + %td= render "person_role_fields", person_role: person_role + %tr + %td{"data-controller"=>"person-roles"} + = link_to_add_role "Neue Funktion", form, :person_roles + %th.fw-normal Organisationseinheit + %tr + %td= form.collection_select :department_id, Department.order(:name), :id, :name, {}, class: "form-select mw-100" + %th.fw-normal Firma + %tr + %td= form.collection_select :company_id, Company.order(:name), :id, :name, {}, class: "form-select mw-100" + %th.fw-normal Wohnort (Stadt) + %tr + %td= form.text_field :location, class: "form-control mw-100" + + %div.pe-5.col-xl-3.col-12 + %table + %tbody + %th.fw-normal Geburtsdatum + %tr + %td= form.date_field :birthdate, class: "form-control mw-100" + %th.fw-normal Doppelbürger + %tr + %td{"data-controller"=>"nationality-two"}= check_box :has_nationality2, "checked", {"checked" => @person.nationality2?, "data-action" => "nationality-two#nationalityTwoVisible", "id" => "nat-two-checkbox"} + %th.fw-normal Erste Nationalität + %tr + %td= form.collection_select :nationality, country_alpha2_translation_map, :first, :last, {}, class: "form-select mw-100" + %th.fw-normal.nationality-two Zweite Nationalität + %tr.nationality-two + %td= form.collection_select :nationality2, country_alpha2_translation_map, :first, :last, {}, class: "form-select mw-100" + %th.fw-normal Zivilstand + %tr + %td= form.collection_select :marital_status, marital_status_translation_map, :first, :last, { selected: @person.marital_status }, class: "form-select mw-100" + %th.fw-normal Kürzel + %tr + %td= form.text_field :shortname, class: "mw-100 form-control" + %div.col-xl-3.col-12.mw-100 + %div.fw-normal Sprachen + %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}" + %div.mt-3 + = form.submit :Speichern, { class: "btn btn-primary me-3 bg-skills-blue", id: "save-button" } + = link_to "Abbrechen", person_path, { id: "cancel-button" } \ No newline at end of file diff --git a/app/views/people/_person_role_fields.html.haml b/app/views/people/_person_role_fields.html.haml new file mode 100644 index 000000000..9b4b24cca --- /dev/null +++ b/app/views/people/_person_role_fields.html.haml @@ -0,0 +1,10 @@ +%div.border.border-dark-subtle.rounded.p-1.fw-light.nested-fields + = person_role.hidden_field :_destroy + Rolle + = person_role.collection_select :role_id, Role.order(:name), :id, :name, {}, class: "form-select w-100 role-select" + %div + Stufe + %div.d-flex.fw-light + = person_role.collection_select :person_role_level_id, PersonRoleLevel.order(:level), :id, :level, {}, class: "form-select w-50 me-1 role-level-select" + = person_role.number_field :percent, in: 0..200, step: 1, class: "form-control w-50 person-role-percent" + %div{"data-controller"=>"person-roles"}= link_to "Remove", "#", { class: "remove_fields", 'data-action' => 'person-roles#removeField' } \ No newline at end of file diff --git a/app/views/people/edit.html.haml b/app/views/people/edit.html.haml new file mode 100644 index 000000000..b8c5f218c --- /dev/null +++ b/app/views/people/edit.html.haml @@ -0,0 +1,2 @@ +%turbo-frame{id: "#{dom_id @person}"} + = render "form", person: @person \ No newline at end of file diff --git a/app/views/people/edit.turbo_stream.erb b/app/views/people/edit.turbo_stream.erb new file mode 100644 index 000000000..64cee993c --- /dev/null +++ b/app/views/people/edit.turbo_stream.erb @@ -0,0 +1,12 @@ +<%= turbo_stream.update "#{dom_id @person}" do %> + <%= render "form", person: @person %> + <% if @person.errors.any? %> +
+ +
+ <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/people/show.html.haml b/app/views/people/show.html.haml index ee4a424f8..bc9089d84 100644 --- a/app/views/people/show.html.haml +++ b/app/views/people/show.html.haml @@ -1,11 +1,61 @@ -=render partial:"people/search", :locals => {person: @person} - %div - %h1 - = @person.name - 's Profile - %div - %h5 - This Person is from #{ @person.nationality } -= link_to "Person bearbeiten", edit_person_path -= link_to "Show all", people_path \ No newline at end of file + =render partial:"people/search", :locals => {person: @person} + %div.person-profile-header.mw-100.border-bottom + Personalien + %div.mt-4 + %turbo-frame{id: "#{dom_id @person}"} + %div.d-flex.flex-xl-row.flex-column + %div.col-xl-3.col-12 + %img.rounded-circle{src: "/people/#{@person.id}/picture?#{Time.now.to_f}", width: '141', height: '141'} + %div.mt-3= link_to "Bearbeiten", edit_person_path, id: "edit-button" + %div.pe-5.col-xl-3.col-12 + %table.fixed-table + %tbody + %th.fw-normal.text-gray Name + %tr + %td.text-break.pb-1= @person.name + %th.fw-normal.text-gray Email + %tr + %td.text-break.pb-1= @person.email + %th.fw-normal.text-gray Abschluss + %tr + %td.text-break.pb-1= @person.title + %th.fw-normal.text-gray Funktion + - unless @person.person_roles.empty? + - @person.person_roles.each do |person_role| + %tr + %td.pb-1= person_role_string(person_role) + - else + %tr + %td - + %th.fw-normal.text-gray Organisationseinheit + %tr + %td.pb-1= @person.department.nil? ? '-' : @person.department.name + %th.fw-normal.text-gray Firma + %tr + %td= @person.company.name + %div.pe-5.col-xl-3.col-12 + %table.fixed-table + %tbody + %th.fw-normal.text-gray Geburtsdatum + %tr + %td.pb-1= @person.birthdate.to_date.strftime('%d.%m.%Y') + %th.fw-normal.text-gray Nationalität + %tr + %td.text-break.pb-1= nationality_string(@person.nationality, @person.nationality2) + %th.fw-normal.text-gray Wohnort (Stadt) + %tr + %td.text-break.pb-1= @person.location + %th.fw-normal.text-gray Zivilstand + %tr + %td.pb-1= t("marital_statuses.#{@person.marital_status}") + %th.fw-normal.text-gray Kürzel + %tr + %td= @person.shortname.blank? ? '-' : @person.shortname + + %div.col-xl-3.col-12 + %div.fw-normal.text-gray Sprachen + %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} \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index d638d4fbb..808f185c3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -47,5 +47,7 @@ def keycloak_disabled? ENV['KEYCLOAK_DISABLED'].present? end + config.assets.enabled = true + config.assets.paths << Rails.root.join("uploads") end end diff --git a/config/locales/de.yml b/config/locales/de.yml index 232b55be0..dd9767842 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -8,3 +8,9 @@ de: default_set: Default-Set radar: Radar modify: Ändern + marital_statuses: + single: ledig + married: verheiratet + widowed: verwitwet + registered_partnership: eingetragene Partnerschaft + divorced: geschieden diff --git a/config/locales/en.yml b/config/locales/en.yml index 8a9c0d5b7..9e353989a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -8,3 +8,9 @@ en: default_set: Defaultset radar: Radar modify: Modify + marital_statuses: + single: single + married: married + widowed: widowed + registered_partnership: registered partnership + divorced: divorced \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 896fac8c8..120f4ffb7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,13 +21,23 @@ get 'readiness', to: 'status#readiness' end + resources :people do + member do + put 'picture', to: 'people/picture#update' + get 'picture', to: 'people/picture#show' + end + end + resources :skills + # Outdated api routes namespace :api do resources :people do collection do get 'search', to: 'people/search#index' end + end + resources :people do member do put 'picture', to: 'people/picture#update' get 'picture', to: 'people/picture#show' diff --git a/spec/controllers/api/people/picture_controller_spec.rb b/spec/controllers/api/people/picture_controller_spec.rb index 924c2a4b4..b23054202 100644 --- a/spec/controllers/api/people/picture_controller_spec.rb +++ b/spec/controllers/api/people/picture_controller_spec.rb @@ -20,7 +20,7 @@ expect(response.status).to eq(200) expect(bob['picture']).to eq('picture.png') - expect(path).to eq('/api/people/902541635/picture') + expect(path).to eq("/api/people/#{bob.id}/picture") process :show, method: :get , params: { id: bob.id } diff --git a/spec/controllers/people/picture_controller_spec.rb b/spec/controllers/people/picture_controller_spec.rb new file mode 100644 index 000000000..7a1298f1d --- /dev/null +++ b/spec/controllers/people/picture_controller_spec.rb @@ -0,0 +1,40 @@ +require 'rails_helper' + +describe People::PictureController do + before(:each) do + sign_in auth_users(:user), scope: :auth_user + end + + it 'should show default_avatar' do + get :show, params:{ id: bob.id } + + default_avatar_md5 = '96643270fe346cc83165128dfcfccd4c' + + expect(response.status).to eq(200) + expect(Digest::MD5.hexdigest(response.body)).to eq(default_avatar_md5) + end + + it 'should update picture' do + process :update, method: :put, params: { id: bob.id, picture: fixture_file_upload('picture.png', 'image/png') } + + path = json['data']['picture_path'] + + bob.reload + + expect(response.status).to eq(200) + expect(bob['picture']).to eq('picture.png') + expect(path).to eq("/people/#{bob.id}/picture") + + process :show, method: :get , params: { id: bob.id } + + new_picture_md5 = 'c903aeff2bec840bd7c028631bd56596' + + expect(Digest::MD5.hexdigest(response.body)).to eq(new_picture_md5) + end + + private + + def bob + @bob ||= people(:bob) + end +end diff --git a/spec/controllers/people_controller_spec.rb b/spec/controllers/people_controller_spec.rb new file mode 100644 index 000000000..150dc4a9c --- /dev/null +++ b/spec/controllers/people_controller_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +describe PeopleController do + before(:each) do + sign_in auth_users(:user), scope: :auth_user + end + describe 'Update Person' do + render_views + let(:person) { people(:bob).attributes } + + it 'should save nationality2 as nil if checkbox is unchecked and save it if it is checked' do + expect(person["nationality2"]).not_to be(nil) + put :update, params: {id: person["id"], person: person, has_nationality2: {checked: "0"}} + response.code.should == "302" + response.should redirect_to(person_path(person["id"])) + expect(people(:bob).reload.nationality2).to be(nil) + + edited_person = person + edited_person["nationality2"] = "DE" + put :update, params: {id: person["id"], person: edited_person, has_nationality2: {checked: "1"}} + response.code.should == "302" + response.should redirect_to(person_path(person["id"])) + expect(people(:bob).reload.nationality2).to eql("DE") + end + end +end \ No newline at end of file diff --git a/spec/features/people_controller_spec.rb b/spec/features/people_controller_spec.rb deleted file mode 100644 index 7b7428d62..000000000 --- a/spec/features/people_controller_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'rails_helper' - -describe :people do - describe 'People Search', type: :feature, js: true do - - before(:each) do - user = auth_users(:user) - login_as(user, scope: :auth_user) - end - - let(:list) {Person.all.sort_by(&:name) } - - it 'displays people in alphabetical order in select' do - visit people_path - within 'section[data-controller="dropdown"]' do - dropdown_options = list.pluck(:name).unshift("Bitte wählen") - expect(page).to have_select('person_id', options: dropdown_options, selected: "Bitte wählen") - end - end - - - it 'redirects to the selected person on change' do - bob = people(:bob) - visit people_path - within 'section[data-controller="dropdown"]' do - select bob.name, from: 'person_id' - end - - expect(page).to have_current_path(person_path(bob)) - expect(page).to have_select('person_id', selected: bob.name) - end - - it 'redirect to the first entry ' do - visit people_path - within 'section[data-controller="dropdown"]' do - first('#person_id option:enabled', minimum: 1).select_option - end - expect(page).to have_current_path(person_path(list.first)) - expect(page).to have_select('person_id', selected: list.first.name) - end - end -end diff --git a/spec/features/people_spec.rb b/spec/features/people_spec.rb new file mode 100644 index 000000000..23292e025 --- /dev/null +++ b/spec/features/people_spec.rb @@ -0,0 +1,144 @@ +require 'rails_helper' + +describe :people do + describe 'People Search', type: :feature, js: true do + + before(:each) do + sign_in auth_users(:user), scope: :auth_user + end + + let(:list) {Person.all.sort_by(&:name) } + + it 'displays people in alphabetical order in select' do + visit people_path + within 'section[data-controller="dropdown"]' do + dropdown_options = list.pluck(:name).unshift("Bitte wählen") + expect(page).to have_select('person_id', options: dropdown_options, selected: "Bitte wählen") + end + end + + + it 'redirects to the selected person on change' do + bob = people(:bob) + visit people_path + within 'section[data-controller="dropdown"]' do + select bob.name, from: 'person_id' + end + + expect(page).to have_current_path(person_path(bob)) + expect(page).to have_select('person_id', selected: bob.name) + end + + it 'redirect to the first entry ' do + visit people_path + within 'section[data-controller="dropdown"]' do + first('#person_id option:enabled', minimum: 1).select_option + end + expect(page).to have_current_path(person_path(list.first)) + expect(page).to have_select('person_id', selected: list.first.name) + end + end + + def fill_out_person_form + page.attach_file("avatar-uploader", Rails.root + 'app/assets/images/1.png') + fill_in 'person_name', with: 'Hansjakobli' + fill_in 'person_email', with: 'hanswurst@somemail.com' + fill_in 'person_title', with: 'Wurstexperte' + page.first(".add_fields").click + role_select = page.all('.role-select').last + role_level_select = page.all('.role-level-select').last + role_percent_select = page.all('.person-role-percent').last + select 'System-Engineer', from: role_select[:id] + select 'S3', from: role_level_select[:id] + fill_in role_percent_select[:id], with: '80' + + select '/ux', from: 'person_department_id' + select 'Partner', from: 'person_company_id' + fill_in 'person_location', with: 'Las Vegas' + fill_in 'person_birthdate', with: '1979-03-28'.to_date + check 'nat-two-checkbox' + select ISO3166::Country["DE"].translations[I18n.locale], from: 'person_nationality' + select ISO3166::Country["US"].translations[I18n.locale], from: 'person_nationality2' + select I18n.t('marital_statuses.married'), from: 'person_marital_status' + fill_in 'person_shortname', with: 'bb' + end + + describe 'Edit person', type: :feature, js: true do + before(:each) do + sign_in auth_users(:user), scope: :auth_user + end + + it 'should have all edit fields' do + bob = people(:bob) + visit person_path(bob) + page.find('#edit-button').click + expect(page).to have_field('avatar-uploader') + expect(page).to have_field('person_name', with: bob.name) + expect(page).to have_field('person_email', with: bob.email) + expect(page).to have_field('person_title', with: bob.title) + person_roles = bob.person_roles + role_selects = page.all('.role-select') + role_level_selects = page.all('.role-level-select') + role_percent_selects = page.all('.person-role-percent') + expect(role_selects.count).to equal(person_roles.count) + expect(role_level_selects.count).to equal(person_roles.count) + expect(role_percent_selects.count).to equal(person_roles.count) + role_selects.each_with_index do |role_select, i| + expect(role_select.value.to_i).to equal(person_roles[i].role_id) + end + role_level_selects.each_with_index do |role_level_select, i| + person_roles[i].person_role_level_id.nil? ? (expect(role_level_select.value.to_i).to equal(PersonRoleLevel.first.id)) : (expect(role_level_select.value.to_i).to equal(person_roles[i].person_role_level_id)) + end + role_percent_selects.each_with_index do |role_percent_select, i| + expect(role_percent_select.value.to_i).to equal(person_roles[i].percent.to_i) + end + expect(page).to have_select('person_department_id', selected: bob.department.name) + expect(page).to have_select('person_company_id', selected: bob.company.name) + expect(page).to have_field('person_location', with: bob.location) + expect(page).to have_field('person_birthdate', with: bob.birthdate.to_date.strftime) + expect(page).to have_field('nat-two-checkbox', with: bob.nationality2.nil? ? "0" : "1") + expect(page.all('.nationality-two').count).to equal(bob.nationality2.nil? ? 0 : 2) + expect(page).to have_select('person_nationality', selected: ISO3166::Country[bob.nationality].translations[I18n.locale]) + bob.nationality2.nil? ? (expect(page).not_to have_select('person_nationality2')) : (expect(page).to have_select('person_nationality2', selected: ISO3166::Country[bob.nationality2].translations[I18n.locale])) + expect(page).to have_select('person_marital_status', selected: I18n.t("marital_statuses.#{bob.marital_status}")) + expect(page).to have_field('person_shortname', with: bob.shortname) + end + + it 'should edit and save changes' do + bob = people(:bob) + visit person_path(bob) + page.find('#edit-button').click + fill_out_person_form + page.find("#save-button").click + + expect(page).to have_content("Hansjakobli") + + edited_person = Person.find_by(name: 'Hansjakobli') + expect(edited_person.picture.identifier).to eql('1.png') + expect(edited_person.email).to eql('hanswurst@somemail.com') + expect(edited_person.title).to eql('Wurstexperte') + expect(edited_person.person_roles.count).to eql(3) + edited_person_role = edited_person.person_roles.last + expect(edited_person_role.role.name).to eql('System-Engineer') + expect(edited_person_role.person_role_level.level).to eql('S3') + expect(edited_person_role.percent).to eq(80) + expect(edited_person.department.name).to eql('/ux') + expect(edited_person.company.name).to eql('Partner') + expect(edited_person.location).to eql('Las Vegas') + expect(edited_person.birthdate.to_date.strftime('%d.%m.%Y')).to eql('28.03.1979') + expect(edited_person.nationality).to eql('DE') + expect(edited_person.nationality2).to eql('US') + expect(edited_person.marital_status).to eql('married') + expect(edited_person.shortname).to eql('bb') + end + + it 'should edit and cancel without saving' do + person = Person.first + visit person_path(person) + page.find('#edit-button').click + fill_out_person_form + page.find('#cancel-button').click + expect(person.attributes).to eql(Person.first.attributes) + end + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 6dd694bba..9fa6b62df 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -61,6 +61,7 @@ config.include(JsonAssertion, type: :controller) config.include(ControllerHelpers, type: :controller) config.include(Devise::Test::IntegrationHelpers, type: :feature) + config.include(Devise::Test::ControllerHelpers, type: :controller) config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace!