From 1c0f89b7323c4d5b823d641c4a4bb930ef098733 Mon Sep 17 00:00:00 2001 From: Bilel Kihal <61744974+Bilelkihal@users.noreply.github.com> Date: Thu, 24 Aug 2023 09:05:35 +0200 Subject: [PATCH] Feature: Implement the new upload ontology form (#292) * use inline svg in file input loader component * add hint to text input component * add error state to input field component * add helper text to input field component * add notes to input field component to explain how to use it * combine text area input component with input field component * groupe inputs in one folder in the lookbook * fix style issues in input field component * refactor input field component code to remove repetition * remove the duplicated alert component preview * make the input field component generic using a the content slot * implement form date component * implement form text area component * implement form text input component * duplicate the select component to be in the "form" namespace * fix select input component inversed open_to_add_values argument usage * move and rename file input loader component to be in the form module * update select component to use InputFieldComponent to have a label * remove unsed text in NestedFormInputComponentPreview * Fix bug of select list view of ontology (#289) * Add Chosen.js functionality to fix bug of select views of ontolgy * Add Chosen.js functionality for list views ontologies * fix bug of update ontology where there is no view * Refactor code * Delete ponse = @ontology.update * fix nested form component inputs focus color to use primary variable * add upload ontology from style file * extract from upload ontology from the location partial * update upload ontology form design * make the upload ontology form button style apply also submit button * add id argument to chips components to permit having multiple chips with same name * fix label not showing for the from select component * put the correct input names in the new ontology form * replace the finish div in the new ontology form with a submit tag * use for the administrated_by input with the form select component * implement the backend create action for the new upload ontology form * fix text area component not showing the value given as argument * add the default values for the inputs in the new ontology form * extract submission_from_params method from the save_submission concern * handle new ontology upload errors in the create action * add default empty submission in the new action * add type parametre to text_input_component so we can specify type as email, number .. * make nested form component start by default by one form initialized * add URI field to the upload ontology process * add search input component * add ontology search input component re-using search-input component * re-use ontology search input component in the topnav * re-use the ontology-search-input in the home * update search-input component to make it more generic * rename view components "form" namespace to "input" * create the display namespace for the data display components * create the layout namespace for view component that structure content * use the table component in the ontology mappings tab * add table component (style, preview and code) * make dropdown content. with no default padding * update concept details component to use the Table and dropdown component * add concept details component preview * update collection, concept, label_xl and scheme views with the new concept details component * migrate notes table to use Table component * install tom select * update select component with tom select * use not centred layout for the select input previews * remove the select id in the select input component template * remove is_selected_value variable in the select input component * fix select component id * add pill button component * add loader component small option and previews * update ontology subscribe component design and add preview * move rounded button preview to the button namespace * extract modal helpers into a helper file * use the new modal helpers instead of writing the view component or frame * add option to modal to show in connect alone * add modal component style * add modal component preview * move the modal component preview into the layout namespace * Update the design of the switch component * add switch input component preview * update search input component to handle custom link turbo targets * update search input component to have form-control style by default * remove no used home search controller replaced with search input * fix. ontology search input component ajax_url argument to add ?acronym= * fix home page search input style * migrate search input style from home.scss to search_input.scss * add margin top and bottom for the nested form component * update nested form component to have an empty state to send to the back * handle contact nested form empty state * migrate the fair score js code to stimulus to make it work with turbo * remove the old fair_score.js code * fix home page responsiveness issues * fix footer responsiveness * fix nav bar responsiveness and add admin to nav items * fix home annotator/recommender section title * fix home responsiveness for small screens * add link text component * use link text component to implement popup link component * use link text component to implement external link component * use link text component to implement internal link component * use internal and external links component in the label link component * add previews to link text components * change internal link icon in link text component tmp tmp tmp tmp tmp tmp tmp tmp tmp tmp * refactor select input component template to use the rails helper * use stimulus values in select input component controller * Add email, password, url components to lookbook * use url component in upload ontology process * fix location subform selection via text on upload ontology * change date of creation by modification date field, and add comment of changes field when upload new version of an ontology * fix select input component to use the name * use the correct name for the visibility and allowed to view inputs * make ontology edit and new form use multi part * fix URI input name and value to replace ontology by submission * add disable state to input field component * make acronym field disabled when uploading a new version of an ontology * migrate the ontology picker single partial to select component * Add the checked property to the chips component * update date component with flatpicker * add disabled state to input field component * update submission helpers with the new components * refactor update ontology code to remove undesired ifs * add text field and text area components to lookbook * add select component to lookbook * add date field to the lookbook * add "inline_svg" gem to use svg in files * make the components preview container include JS cod if needed * add language field component * add link field component * add date_time field component * add license field component * add text area field component * add summary section component preview * add dropdown component * add field container component to show a label with it's value vertically * add nested form input component preview * add a pop-up with list of ontologies in group and category admin table * Revert "add a pop-up with list of ontologies in group and category admin table" This reverts commit c4b391a9d3e5272703051922bcb9495ac35297c8. * add a select to update ontologies in the pop-up edit of group in admin table * add a select to update ontologies in the pop-up edit of category in admin table * Refactor code * refactor code and fix bug * remove old undesired text area input from lookbook * add Upload file input component to lookbook * combine all input components in one globale component * remove style params from input field component * recover text area field component (after deleting it by mistake in a previous commit * add hint to text input component * add error state to input field component * add helper text to input field component * add notes to input field component to explain how to use it * combine text area input component with input field component * groupe inputs in one folder in the lookbook * make the input field component generic using a the content slot * implement form date component * implement form text area component * implement form text input component * duplicate the select component to be in the "form" namespace * move and rename file input loader component to be in the form module * update select component to use InputFieldComponent to have a label * remove unsed text in NestedFormInputComponentPreview * fix nested form component inputs focus color to use primary variable * add upload ontology from style file * extract from upload ontology from the location partial * update upload ontology form design * make the upload ontology form button style apply also submit button * add id argument to chips components to permit having multiple chips with same name * fix label not showing for the from select component * put the correct input names in the new ontology form * replace the finish div in the new ontology form with a submit tag * use for the administrated_by input with the form select component * implement the backend create action for the new upload ontology form * fix text area component not showing the value given as argument * add the default values for the inputs in the new ontology form * extract submission_from_params method from the save_submission concern * handle new ontology upload errors in the create action * add default empty submission in the new action * add type parametre to text_input_component so we can specify type as email, number .. * make nested form component start by default by one form initialized * add URI field to the upload ontology process * rename view components "form" namespace to "input" * create the display namespace for the data display components * create the layout namespace for view component that structure content * add modal component preview * move the modal component preview into the layout namespace * change internal link icon in link text component tmp tmp tmp tmp tmp tmp tmp tmp tmp tmp * add text field and text area components to lookbook * add date field to the lookbook * fix label not showing for the from select component * fix text area component not showing the value given as argument * add type parametre to text_input_component so we can specify type as email, number .. * refactor select input component template to use the rails helper * use stimulus values in select input component controller * Add email, password, url components to lookbook * use url component in upload ontology process * fix location subform selection via text on upload ontology * fix select input component to use the name * use the correct name for the visibility and allowed to view inputs * make ontology edit and new form use multi part * fix URI input name and value to replace ontology by submission * migrate the ontology picker single partial to select component * Add the checked property to the chips component * Merge remote-tracking branch 'origin/feature/update-upload-ontology' into feature/update-upload-ontology * change date of creation by modification date field, and add comment of changes field when upload new version of an ontology * add disable state to input field component * update submission helpers with the new components * refactor update ontology code to remove undesired ifs * add id parameter to input field component * replace finish button in upload ontology by the button component * Add size parameter to button component * Add color parameter to button component * Add state parameter to the button component * make finish button animated in upload ontology process * add progress pages component * put again the type argument for button component * fix progress pages item not center if test is long * migrate upload ontology form to use progress pages component * make progress pages container take full width by default * update login page button component usage to have an ID * put again the removed JS code for visibility and is a view * fix chips style padding removed from merging with the browse branch * update date input in the ontology form to use the metadata helper * fix chips style in upload ontology form * remove old new submission form from the bottom of the new ontology page * Fix select multiple items, delete icon style in select component * fix upload ontology style to have less margin bottom * extract ontology updater and saver concern form ontologies controller code * update submission create action tu update also the ontology * use AlertComponent to display ontology form error messages * create input helpers to use inputs component more easily and use them in ontology form * remove no more used style code in upload ontology style file * fix ontology form date and contact inputs not showing error message * set update ontology to true in the create action of submission controller * fix alignement of chips and button components --------- Co-authored-by: Syphax Bouazzouni Co-authored-by: SirineMhedhbi <31127782+SirineMhedhbi@users.noreply.github.com> Co-authored-by: Sirine Mhedhbi --- app/assets/images/arrow-right.svg | 4 +- app/assets/images/json.svg | 2 +- app/assets/images/white-check.svg | 3 + .../stylesheets/application.css.scss.erb | 2 +- app/assets/stylesheets/components/chips.scss | 9 +- .../stylesheets/components/input_field.scss | 16 + .../components/primary_button.scss | 101 ++++++ .../components/regular_button.scss | 2 + app/assets/stylesheets/upload_ontology.scss | 105 ++++++ .../primary_button_component.html.haml | 23 ++ .../buttons/regular_button_component.rb | 2 +- .../chips_component/chips_component.html.haml | 2 +- app/components/input/date_component.rb | 7 +- app/components/input/input_field_component.rb | 5 +- .../input_field_component.html.haml | 2 +- app/components/input/text_input_component.rb | 8 +- .../nested_form_inputs_component.html.haml | 2 +- app/components/select_input_component.rb | 4 +- app/controllers/concerns/ontology_updater.rb | 92 +++++ .../concerns/submission_updater.rb | 14 +- app/controllers/ontologies_controller.rb | 49 +-- app/controllers/submissions_controller.rb | 27 +- app/helpers/application_helper.rb | 15 +- app/helpers/inputs_helper.rb | 45 +++ app/helpers/ontologies_helper.rb | 18 +- app/helpers/submissions_helper.rb | 33 +- .../controllers/tom_select_controller.js | 16 + app/views/layouts/_topnav.html.haml | 2 +- app/views/layouts/component_preview.html.erb | 1 + app/views/login/index.html.haml | 2 +- app/views/ontologies/_form.html.haml | 330 +++++++----------- .../_submission_location_form.html.haml | 49 +++ app/views/ontologies/edit.html.haml | 2 +- app/views/ontologies/new.html.haml | 4 +- .../shared/_ontology_picker_single.html.erb | 2 +- app/views/submissions/new.html.haml | 11 +- 36 files changed, 679 insertions(+), 332 deletions(-) create mode 100644 app/assets/images/white-check.svg create mode 100644 app/assets/stylesheets/components/primary_button.scss create mode 100644 app/assets/stylesheets/upload_ontology.scss create mode 100644 app/components/buttons/primary_button_component/primary_button_component.html.haml create mode 100644 app/controllers/concerns/ontology_updater.rb create mode 100644 app/helpers/inputs_helper.rb create mode 100644 app/javascript/controllers/tom_select_controller.js create mode 100644 app/views/ontologies/_submission_location_form.html.haml diff --git a/app/assets/images/arrow-right.svg b/app/assets/images/arrow-right.svg index 21ce5092c..07eb88ef1 100644 --- a/app/assets/images/arrow-right.svg +++ b/app/assets/images/arrow-right.svg @@ -1,3 +1,3 @@ - - + + \ No newline at end of file diff --git a/app/assets/images/json.svg b/app/assets/images/json.svg index 7af4b2fb2..e7f7e8898 100644 --- a/app/assets/images/json.svg +++ b/app/assets/images/json.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/app/assets/images/white-check.svg b/app/assets/images/white-check.svg new file mode 100644 index 000000000..6b657da4f --- /dev/null +++ b/app/assets/images/white-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/assets/stylesheets/application.css.scss.erb b/app/assets/stylesheets/application.css.scss.erb index a3e69e6ab..e509a4f5d 100755 --- a/app/assets/stylesheets/application.css.scss.erb +++ b/app/assets/stylesheets/application.css.scss.erb @@ -50,7 +50,7 @@ @import "components/index"; @import "account"; @import "agents"; - +@import "upload_ontology"; @import "nav_bar"; @import "ontology_details_header"; @import "ontology_viewer"; diff --git a/app/assets/stylesheets/components/chips.scss b/app/assets/stylesheets/components/chips.scss index d0539b6a0..9815dbdf1 100644 --- a/app/assets/stylesheets/components/chips.scss +++ b/app/assets/stylesheets/components/chips.scss @@ -1,3 +1,7 @@ +.chips-container > div{ + margin-right: 10px; +} + .chips-container svg path{ fill: var(--primary-color); } @@ -7,6 +11,7 @@ display: none; } + .chips-container div label{ cursor: pointer; } @@ -20,9 +25,9 @@ background:white; border: 0.5px solid #BDBDBD; color: #a7a7a7; - padding: 3px; + padding: 8px; border-radius: 5px; - font-size: 12px; + font-size: 13px; user-select: none; } diff --git a/app/assets/stylesheets/components/input_field.scss b/app/assets/stylesheets/components/input_field.scss index 9a5ad99c9..f11b84188 100644 --- a/app/assets/stylesheets/components/input_field.scss +++ b/app/assets/stylesheets/components/input_field.scss @@ -66,4 +66,20 @@ right: calc(0.75rem + 5px); top: 50%; width: 0; +} + +.ts-wrapper.multi .ts-control>div{ + border-radius: 5px; + font-size: 11px; + padding: 2px 0 4px 6px; + color: #888888; +} + +.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{ + border-left: none; + color: #888888 !important; + margin-left: 0; +} +.ts-wrapper.plugin-remove_button .item .remove:hover{ + background: unset; } \ No newline at end of file diff --git a/app/assets/stylesheets/components/primary_button.scss b/app/assets/stylesheets/components/primary_button.scss new file mode 100644 index 000000000..5e534fecd --- /dev/null +++ b/app/assets/stylesheets/components/primary_button.scss @@ -0,0 +1,101 @@ +.button-container{ + width: 100%; +} +.primary-button { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + font-size: 16px; + color: white; + height: 60px; + background-color: var(--primary-color); + border: none; + border-radius: 9px; + transition: background-color ease 0.3s; +} +.primary-button:hover { + background-color: var(--hover-color); + cursor: pointer; +} + + +.animation-container{ + width: 100%; + height: 60px; + font-size: 16px; + background-color: var(--hover-color); + border: none; + border-radius: 9px; + justify-content: center; + align-items: center; + display: none; + +} +.lds-ellipsis { + display: inline-block; + position: relative; + margin-top: 50px; + width: 80px; + height: 80px; + transform: scale(0.7); +} + +.lds-ellipsis div { + position: absolute; + width: 13px; + height: 13px; + border-radius: 50%; + background: white; + animation-timing-function: cubic-bezier(0, 1, 1, 0); +} + +.lds-ellipsis div:nth-child(1) { + left: 8px; + animation: lds-ellipsis1 0.6s infinite; +} + +.lds-ellipsis div:nth-child(2) { + left: 8px; + animation: lds-ellipsis2 0.6s infinite; +} + +.lds-ellipsis div:nth-child(3) { + left: 32px; + animation: lds-ellipsis2 0.6s infinite; +} + +.lds-ellipsis div:nth-child(4) { + left: 56px; + animation: lds-ellipsis3 0.6s infinite; +} + +@keyframes lds-ellipsis1 { + 0% { + transform: scale(0); + } + + 100% { + transform: scale(1); + } +} + +@keyframes lds-ellipsis3 { + 0% { + transform: scale(1); + } + + 100% { + transform: scale(0); + } +} + +@keyframes lds-ellipsis2 { + 0% { + transform: translate(0, 0); + } + + 100% { + transform: translate(24px, 0); + } +} diff --git a/app/assets/stylesheets/components/regular_button.scss b/app/assets/stylesheets/components/regular_button.scss index 954165297..762b285b2 100644 --- a/app/assets/stylesheets/components/regular_button.scss +++ b/app/assets/stylesheets/components/regular_button.scss @@ -105,10 +105,12 @@ .left-button-icon{ margin-right: 10px; + margin-bottom: 3px; } .right-button-icon { margin-left: 10px; + margin-bottom: 3px; } .secondary-button-icon path { diff --git a/app/assets/stylesheets/upload_ontology.scss b/app/assets/stylesheets/upload_ontology.scss new file mode 100644 index 000000000..d5026556c --- /dev/null +++ b/app/assets/stylesheets/upload_ontology.scss @@ -0,0 +1,105 @@ +.upload-ontology-container { + display: flex; + justify-content: center; + padding: 40px 0; +} + +.upload-ontology-card { + width: 589px; + border-radius: 14px; + box-shadow: rgba(0, 0, 0, 0.05) 0px 20px 50px; + padding: 20px 40px; + +} + +.upload-ontology-center { + display: flex; + justify-content: center; + flex-direction: column; +} + +.Upload-ontology-title { + font-size: 18px; + display: flex; + font-weight: bold; + flex-direction: column; + align-items: center; +} + +.Upload-ontology-title hr { + border: 1px solid var(--primary-color); + width: 93px; + margin: 0; +} + +.upload-ontology-progress { + display: flex; + align-items: center; + margin-top: 20px; + margin-bottom: 50px; +} + + +.upload-ontology-chips-container{ + display: flex; + flex-wrap: wrap; +} + + +.hide { + display: none; +} + +.show { + display: block; +} + +.upload-ontology-desc { + font-size: 12px; + color: #777777; + margin-bottom: 23px; +} + +.upload-ontology-desc a { + text-decoration: none; + color: var(--primary-color); +} +.upload-ontology-desc a svg{ + transform: scale(1.2); +} + + +.upload-ontology-contact .add-another-contact div { + font-size: 11px; + color: #DADADA; + margin-left: 10px; + +} + +.upload-ontology-field-container .location-choice{ + display: flex; + align-items: center; + margin-bottom: 3px; +} + +.upload-ontology-field-container .location-choice .title{ + font-size: 13px; + color: black; + margin-left: 13px; + margin-bottom: 0; + cursor: pointer; +} + +.upload-ontology-field-container > div{ + font-size: 12px; + color: #666666; +} + + +.upload-ontology-input-field-container{ + margin-bottom: 10px; +} + +.upload-ontology-input-field-container .switch-filter p{ + font-size: 12px !important; +} diff --git a/app/components/buttons/primary_button_component/primary_button_component.html.haml b/app/components/buttons/primary_button_component/primary_button_component.html.haml new file mode 100644 index 000000000..154eb2abe --- /dev/null +++ b/app/components/buttons/primary_button_component/primary_button_component.html.haml @@ -0,0 +1,23 @@ +- if @type == "submit" + .button-container{onclick: "displayAnimation()", id: "primary-button"} + %input.primary-button{:name => @name, :type => "submit", :value => @value, oncklick: @onclick}/ +- else + .button-container + .primary-button{:name => @name, onclick: "displayAnimation()", id: "primary-button", oncklick: @onclick} + = @value + +.animation-container#loading-animation + .lds-ellipsis + %div + %div + %div + %div + +:javascript + const button = document.getElementById("primary-button") + const loading = document.getElementById("loading-animation") + function displayAnimation(){ + console.log("working") + button.style.display = 'none'; + loading.style.display = 'flex'; + } \ No newline at end of file diff --git a/app/components/buttons/regular_button_component.rb b/app/components/buttons/regular_button_component.rb index c40a9f67b..cb75ef32a 100644 --- a/app/components/buttons/regular_button_component.rb +++ b/app/components/buttons/regular_button_component.rb @@ -16,7 +16,7 @@ def initialize(id: , value:, variant: "primary", color: "normal", href: "", size def button_label hide_icon_left = icon_left == nil ? "hide" : " " hide_icon_right = icon_right == nil ? "hide" : " " - content_tag(:span, icon_left, class: "#{@variant}-button-icon left-button-icon #{hide_icon_left}") + @value + content_tag(:span, icon_right, class: "#{@variant}-button-icon right-button-icon #{hide_icon_right}") + content_tag(:span, icon_left, class: "#{@variant}-button-icon left-button-icon #{hide_icon_left}") + content_tag(:div, @value) + content_tag(:span, icon_right, class: "#{@variant}-button-icon right-button-icon #{hide_icon_right}") end def button_elem diff --git a/app/components/chips_component/chips_component.html.haml b/app/components/chips_component/chips_component.html.haml index 48a0b88a4..3191dc1d3 100644 --- a/app/components/chips_component/chips_component.html.haml +++ b/app/components/chips_component/chips_component.html.haml @@ -5,6 +5,6 @@ %span %svg.chips-check-icon{:fill => "none", :height => "8", :viewbox => "0 0 10 8", :width => "10", :xmlns => "http://www.w3.org/2000/svg"} %path{:d => "M9.76764 0.232287C9.45824 -0.0775267 8.95582 -0.0773313 8.646 0.232287L3.59787 5.28062L1.35419 3.03696C1.04438 2.72714 0.542174 2.72714 0.23236 3.03696C-0.0774534 3.34677 -0.0774534 3.84897 0.23236 4.15879L3.03684 6.96326C3.19165 7.11807 3.39464 7.19567 3.59765 7.19567C3.80067 7.19567 4.00386 7.11827 4.15867 6.96326L9.76764 1.3541C10.0775 1.0445 10.0775 0.542081 9.76764 0.232287Z"} - %div.ml-1 + %div = @label = count diff --git a/app/components/input/date_component.rb b/app/components/input/date_component.rb index fe85fa892..2549e3670 100644 --- a/app/components/input/date_component.rb +++ b/app/components/input/date_component.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true class Input::DateComponent < Input::InputFieldComponent - def initialize(label: '', name:, value: Date.today, placeholder: '', error_message: '', helper_text: '') - super(label: label, name: name, value: value, placeholder: placeholder, error_message: error_message, helper_text: helper_text) + def initialize(label: '', name:, value: Date.today, placeholder: '', error_message: '', helper_text: '', id: nil) + data_flat_picker = { controller: "flatpickr", flatpickr_date_format: "Y-m-d", flatpickr_alt_input: "true", flatpickr_alt_format: "F j, Y" } + super(label: label, name: name, value: value, placeholder: placeholder, error_message: error_message, helper_text: helper_text, data: data_flat_picker, id: id) end def call - render Input::InputFieldComponent.new(label: @label, name: @name, value: @value, placeholder: @placeholder, error_message: @error_message, helper_text: @helper_text, type: 'date') + render Input::InputFieldComponent.new(label: @label, name: @name, value: @value, placeholder: @placeholder, error_message: @error_message, helper_text: @helper_text, data: @data, type: 'date', id: @id) end end diff --git a/app/components/input/input_field_component.rb b/app/components/input/input_field_component.rb index 01ed85ef6..d026ca0e5 100644 --- a/app/components/input/input_field_component.rb +++ b/app/components/input/input_field_component.rb @@ -1,5 +1,5 @@ class Input::InputFieldComponent < ViewComponent::Base - def initialize(label: "" , name:, value: 'Syphax', type: 'text', placeholder: "", error_message: "", helper_text: "") + def initialize(label: "" , name:, value: 'Syphax', type: 'text', placeholder: "", error_message: "", helper_text: "", disabled: false, data: nil, id: '') @label = label @name = name @placeholder = placeholder @@ -7,6 +7,9 @@ def initialize(label: "" , name:, value: 'Syphax', type: 'text', placeholder: "" @helper_text = helper_text @value = value @type = type + @disabled = disabled + @id = id + @data = data end diff --git a/app/components/input/input_field_component/input_field_component.html.haml b/app/components/input/input_field_component/input_field_component.html.haml index 0bd9f9a10..9500ba74d 100644 --- a/app/components/input/input_field_component/input_field_component.html.haml +++ b/app/components/input/input_field_component/input_field_component.html.haml @@ -6,7 +6,7 @@ - if content = content - else - %input.input-field-component.text-input{name: @name, type: @type, placeholder: @placeholder, style: error_style, value: @value} + %input.input-field-component.text-input{class: @disabled ? '' : 'not-disabled', name: @name, type: @type, placeholder: @placeholder, style: error_style, id: @id, data: @data, value: @value, disabled: @disabled} diff --git a/app/components/input/text_input_component.rb b/app/components/input/text_input_component.rb index 7a38b3de0..d5cca42eb 100644 --- a/app/components/input/text_input_component.rb +++ b/app/components/input/text_input_component.rb @@ -1,11 +1,13 @@ # frozen_string_literal: true class Input::TextInputComponent < Input::InputFieldComponent - def initialize(label: '', name:, value: nil, placeholder: '', error_message: '', helper_text: '') - super(label: label, name: name, value: value, placeholder: placeholder, error_message: error_message, helper_text: helper_text) + def initialize(label: '', name:, value: nil, placeholder: '', error_message: '', helper_text: '', disabled: false, id: '') + super(label: label, name: name, value: value, placeholder: placeholder, error_message: error_message, helper_text: helper_text, disabled: disabled, id: id) end def call - render Input::InputFieldComponent.new(label: @label, name: @name, value: @value, placeholder: @placeholder, error_message: @error_message, helper_text: @helper_text, type: @type) + render Input::InputFieldComponent.new(label: @label, name: @name, value: @value, placeholder: @placeholder, + error_message: @error_message, helper_text: @helper_text, + type: @type, disabled: @disabled) end end diff --git a/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml b/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml index 78a23641d..730ec86f4 100644 --- a/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml +++ b/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml @@ -31,4 +31,4 @@ %div.add-another-object{data: {action:"click->nested-form#add"}} = inline_svg 'icons/plus.svg' %div - Add another #{@object_name} \ No newline at end of file + Add another #{@object_name} diff --git a/app/components/select_input_component.rb b/app/components/select_input_component.rb index 935087fe5..99a481bb5 100644 --- a/app/components/select_input_component.rb +++ b/app/components/select_input_component.rb @@ -13,7 +13,7 @@ def initialize(id:, name:, values:, selected:, multiple: false, open_to_add_valu end def call - select_input_tag(@id, @values, @selected, multiple: @multiple, open_to_add_values: @open_to_add_values) + select_input_tag(@name, @values, @selected, multiple: @multiple, open_to_add_values: @open_to_add_values) end private @@ -32,7 +32,7 @@ def select_input_tag(id, values, selected, options = {}) 'select-input-open-add-value': open_to_add_values } } - #binding.pry + select_tag(id, options_for_select(values, selected), select_html_options) end end diff --git a/app/controllers/concerns/ontology_updater.rb b/app/controllers/concerns/ontology_updater.rb new file mode 100644 index 000000000..c45020ce9 --- /dev/null +++ b/app/controllers/concerns/ontology_updater.rb @@ -0,0 +1,92 @@ +module OntologyUpdater + extend ActiveSupport::Concern + include SubmissionUpdater + def ontology_from_params + ontology = LinkedData::Client::Models::Ontology.new(values: ontology_params) + ontology.viewOf = nil unless ontology.isView + ontology + end + + def save_ontology + + @ontology = save_new_ontology + + if response_error?(@ontology) + show_new_errors(@ontology) + return + end + + + @submission = save_new_submission(params[:submission], @ontology) + + if response_error?(@submission) + @ontology.delete + show_new_errors(@submission) + else + redirect_to "/ontologies/success/#{@ontology.acronym}" + end + end + + def add_ontology_submission(acronym) + @ontology = update_existent_ontology(acronym) + + if @ontology.nil? || response_error?(@ontology) + show_new_errors(@ontology) + return + end + + @submission = @ontology.explore.latest_submission({ display: 'all' }) + submission_params = submission_params(params[:submission]) + submission_params = submission_params(ActionController::Parameters.new(@submission.to_hash.delete_if { |k, v| v.nil? || v.respond_to?(:empty?) && v.empty? })).merge(submission_params) if @submission + submission_params.delete 'submissionId' + @submission = save_new_submission(ActionController::Parameters.new(submission_params), @ontology) + + if response_error?(@submission) + show_new_errors(@submission) + else + redirect_to "/ontologies/success/#{@ontology.acronym}" + end + end + + def save_new_ontology + ontology = ontology_from_params + ontology.save + end + + def update_existent_ontology(acronym) + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(acronym).first + return nil if @ontology.nil? + + @ontology.update_from_params(ontology_params) + end + + def save_new_submission(submission_hash, ontology) + new_submission_params = submission_hash + new_submission_params[:ontology] = ontology.acronym + save_submission(new_submission_params) + end + + def ontology_params + p = params.require(:ontology).permit(:name, :acronym, { administeredBy: [] }, :viewingRestriction, { acl: [] }, + { hasDomain: [] }, :viewOf,:isView, :subscribe_notifications, { group: [] }) + + p[:administeredBy].reject!(&:blank?) if p[:administeredBy] + # p[:acl].reject!(&:blank?) + p[:hasDomain].reject!(&:blank?) if p[:hasDomain] + p[:group].reject!(&:blank?) if p[:group] + p.to_h + end + + def show_new_errors(object) + # TODO optimize + @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true, display_links: false, display_context: false) + @categories = LinkedData::Client::Models::Category.all + @groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false) + @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] } + @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase } + @errors = response_errors(object) + @ontology = ontology_from_params + @submission = submission_from_params(params[:submission]) + render 'ontologies/new' + end +end diff --git a/app/controllers/concerns/submission_updater.rb b/app/controllers/concerns/submission_updater.rb index 43746c38e..d8c63ba72 100644 --- a/app/controllers/concerns/submission_updater.rb +++ b/app/controllers/concerns/submission_updater.rb @@ -1,11 +1,15 @@ module SubmissionUpdater extend ActiveSupport::Concern - def save_submission(new_submission_hash) + def submission_from_params(new_submission_hash) convert_values_to_types(new_submission_hash) + LinkedData::Client::Models::OntologySubmission.new(values: submission_params(new_submission_hash)) + end + def save_submission(new_submission_hash) + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(new_submission_hash[:ontology]).first - @submission = LinkedData::Client::Models::OntologySubmission.new(values: submission_params(new_submission_hash)) + @submission = submission_from_params(new_submission_hash) update_ontology_summary_only @submission.save(cache_refresh_all: false) @@ -64,12 +68,12 @@ def update_ontology_summary_only(is_remote = @submission.isRemote) def convert_values_to_types(new_submission_hash) unless new_submission_hash[:contact].nil? - new_submission_hash[:contact] = new_submission_hash[:contact].values + new_submission_hash[:contact] = new_submission_hash[:contact].values unless new_submission_hash[:contact].is_a?(Array) new_submission_hash[:contact].delete_if { |c| c[:name].empty? || c[:email].empty? } end # Convert metadata that needs to be integer to int - @metadata.map do |hash| + submission_metadata.map do |hash| if hash["enforce"].include?("integer") if !new_submission_hash[hash["attribute"]].nil? && !new_submission_hash[hash["attribute"]].eql?("") new_submission_hash[hash["attribute"].to_s.to_sym] = Integer(new_submission_hash[hash["attribute"].to_s.to_sym]) @@ -111,7 +115,7 @@ def submission_params(params) :publication ] - @metadata.each do |m| + submission_metadata.each do |m| m_attr = m["attribute"].to_sym diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index f7b9ae2fb..a50f1797c 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -7,6 +7,7 @@ class OntologiesController < ApplicationController include SchemesHelper, ConceptsHelper include CollectionsHelper include MappingStatistics + include OntologyUpdater require 'multi_json' require 'cgi' @@ -195,26 +196,9 @@ def properties end def create - if params[:commit].eql? 'Cancel' - redirect_to ontologies_path and return - end - @ontology = LinkedData::Client::Models::Ontology.new(values: ontology_params) - @ontology_saved = @ontology.save - if response_error?(@ontology_saved) - @categories = LinkedData::Client::Models::Category.all - @groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false) - @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] } - @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase } - @errors = response_errors(@ontology_saved) - render 'new' - else - if @ontology_saved.summaryOnly - redirect_to "/ontologies/success/#{@ontology.acronym}" - else - redirect_to new_ontology_submission_path(@ontology.acronym) - end - end + # redirect_to ontologies_path and return if params[:commit].eql? 'Cancel' + save_ontology end def edit @@ -239,7 +223,8 @@ def mappings def new @ontology = LinkedData::Client::Models::Ontology.new - @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true,display_links: false, display_context: false) + @submission = LinkedData::Client::Models::OntologySubmission.new + @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true, display_links: false, display_context: false) @categories = LinkedData::Client::Models::Category.all @groups = LinkedData::Client::Models::Group.all @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]} @@ -454,9 +439,9 @@ def widgets render partial: 'ontologies/sections/widgets', layout: 'ontology_viewer' end end - + def show_licenses - + @metadata = submission_metadata @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id]).first @licenses= ["hasLicense","morePermissions","copyrightHolder"] @@ -464,28 +449,14 @@ def show_licenses render partial: 'ontologies/sections/licenses' end def ajax_ontologies - - + + render json: LinkedData::Client::Models::Ontology.all(include_views: true, display: 'acronym,name', display_links: false, display_context: false) end - private - - - - def ontology_params - p = params.require(:ontology).permit(:name, :acronym, { administeredBy:[] }, :viewingRestriction, { acl:[] }, - { hasDomain:[] }, :isView, :viewOf, :subscribe_notifications, {group:[]}) - - p[:administeredBy].reject!(&:blank?) - p[:acl].reject!(&:blank?) - p[:hasDomain].reject!(&:blank?) - p[:group].reject!(&:blank?) - p.to_h - end - + private def get_views(ontology) views = ontology.explore.views || [] views.select!{ |view| view.access?(session[:user]) } diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 2bea30bd3..66572cd8e 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -1,5 +1,5 @@ class SubmissionsController < ApplicationController - include SubmissionsHelper, SubmissionUpdater + include SubmissionsHelper, SubmissionUpdater, OntologyUpdater layout :determine_layout before_action :authorize_and_redirect, :only => [:edit, :update, :create, :new] before_action :submission_metadata, only: [:create, :edit, :new, :update, :index] @@ -28,28 +28,17 @@ def new @submission = @ontology.explore.latest_submission @submission ||= LinkedData::Client::Models::OntologySubmission.new @submission.id = nil + @categories = LinkedData::Client::Models::Category.all + @groups = LinkedData::Client::Models::Group.all + @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]} + @user_select_list.sort! {|a,b| a[1].downcase <=> b[1].downcase} + @is_update_ontology = true end # Called when form to "Add submission" is submitted def create - # Make the contacts an array - _, submission_params = params[:submission].each.first - @required_only = !params['required-only'].nil? - @filters_disabled = true - @submission_saved = save_submission(submission_params) - if response_error?(@submission_saved) - @errors = response_errors(@submission_saved) # see application_controller::response_errors - if @errors && @errors[:uploadFilePath] - @errors = ["Please specify the location of your ontology"] - elsif @errors && @errors[:contact] - @errors = ["Please enter a contact"] - end - - reset_agent_attributes - render 'new', status: 422 - else - redirect_to "/ontologies/success/#{@ontology.acronym}" - end + @is_update_ontology = true + add_ontology_submission(params[:ontology][:acronym] || params[:id]) end # Called when form to "Edit submission" is submitted diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3bdc2334b..396f68883 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -367,14 +367,14 @@ def get_groups_data def metadata_for_select get_metadata return @metadata_for_select - end + end def get_metadata @metadata_for_select = [] submission_metadata.each do |data| @metadata_for_select << data["attribute"] end - end + end def ontologies_to_acronyms(ontologyIDs) @@ -426,7 +426,7 @@ def add_proposal_button(parent_id, parent_type) class: "add_proposal btn btn-primary", data: { show_modal_title_value: "Add a new proposal"} end end - + def subscribe_button(ontology_id) ontology_acronym = ontology_id.split('/').last @@ -611,10 +611,10 @@ def skos? submission = @submission || @submission_latest submission&.hasOntologyLanguage === 'SKOS' end - + def current_page?(path) request.path.eql?(path) - end + end def request_lang lang = params[:language] || params[:lang] @@ -646,12 +646,9 @@ def bp_config_json config[:ncbo_slice] = @subdomain_filter[:acronym] if (@subdomain_filter[:active] && !@subdomain_filter[:acronym].empty?) config.to_json end - - def portal_name $SITE - end - + end def navitems items = [["/ontologies", "Browse"],["/mappings", "Mappings"],["/recommender", "Recommender"],["/annotator", "Annotator"], ["/landscape", "Landscape"]] end diff --git a/app/helpers/inputs_helper.rb b/app/helpers/inputs_helper.rb new file mode 100644 index 000000000..ff0f14294 --- /dev/null +++ b/app/helpers/inputs_helper.rb @@ -0,0 +1,45 @@ +module InputsHelper + + def text_input(label: nil, name: , value:, disabled: false) + render Input::TextInputComponent.new(label: input_label(label, name) , name: name, value: value, error_message: input_error_message(name), disabled: disabled) + end + + def select_input(label: nil, name: , values:, selected: nil, multiple: false) + render Input::SelectComponent.new(label: input_label(label, name), name: name, value: values, selected: selected, multiple: multiple) + end + + def check_input(id:, name: , label: '', value:, checked: false) + render ChipsComponent.new(name: name, id: id, label: label, value: value, checked: checked) + end + + def switch_input(id: , name:, label: ,checked: false) + render SwitchInputComponent.new(id: id, name: name, label: label, checked: checked) + end + + def url_input(label: nil, name: , value:) + render Input::UrlComponent.new(label: input_label(label, name), name: name, value: value, error_message: input_error_message(name) ) + end + + def text_area_input(label: nil, name: , value:) + render Input::TextAreaComponent.new(label: input_label(label, name), name: name, value: value, error_message: input_error_message(name)) + end + + def date_input(label: nil, name:, value:) + render Input::DateComponent.new(label: input_label(label, name) ,name: name, value: value || Date.today, error_message: input_error_message(name)) + end + + private + + def method_name(name) + match = /.*\[(.*?)\]/.match(name) + match.nil? ? name : match[1] + end + + def input_label(label, name) + label || method_name(name).humanize + end + + def input_error_message(name) + attribute_error(method_name(name)) + end +end \ No newline at end of file diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index b421e5ae4..cf35e422d 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -534,10 +534,26 @@ def ontology_edit_button def submission_json_button render RoundedButtonComponent.new(link: "#{(@submission_latest || @ontology).id}?display=all", target: '_blank', size: 'medium') end + + def attribute_error(attr) + return '' unless @errors && @errors[attr.to_sym] + errors = @errors[attr.to_sym] + + errors.values.join(', ') + end + + def error_message + if !@errors[:error].nil? && @errors[:error].is_a?(String) + @errors[:error] + else + "Errors in fields #{@errors.keys.join(', ')}" + end + + end private def submission_languages(submission = @submission) - submission.naturalLanguage.map { |natural_language| natural_language["iso639"] && natural_language.split('/').last }.compact + submission&.naturalLanguage.map { |natural_language| natural_language["iso639"] && natural_language.split('/').last }.compact end end diff --git a/app/helpers/submissions_helper.rb b/app/helpers/submissions_helper.rb index cc338b994..ca55ce95b 100644 --- a/app/helpers/submissions_helper.rb +++ b/app/helpers/submissions_helper.rb @@ -338,12 +338,7 @@ def generate_agent_input(attr, type: 'person') def generate_date_input(attr) field_id = [:submission, attr["attribute"].to_s, @ontology.acronym].join('_') date_value = @submission.send(attr["attribute"]).presence - data_flat_picker = { controller: "flatpickr", flatpickr_date_format: "Y-m-d", flatpickr_alt_input: "true", flatpickr_alt_format: "F j, Y" } - content_tag(:div, class: 'input-group') do - [ - date_field(object_name, attr["attribute"].to_s.to_sym, value: date_value, id: field_id, data: data_flat_picker, class: "not-disabled") - ].join.html_safe - end + render Input::DateComponent.new(label: (attr["label"] || attr["attribute"]).to_s ,name: object_name, value: date_value || Date.today, id: field_id) end def generate_textarea_input(attr) @@ -355,10 +350,13 @@ def generate_select_input(attr, name, select_values, metadata_values, multiple: render SelectInputComponent.new(id: id, name: name, values: select_values , selected: metadata_values , multiple: multiple) end - def generate_list_field_input(attr, name, values, field_func) + def generate_list_field_input(attr, name, label, values, &block) render NestedFormInputsComponent.new do |c| - c.template do - method(field_func).call("#{name}[NEW_RECORD]", '', :id => attr["attribute"].to_s + "_" + @ontology.acronym, class: "metadataInput form-control my-1") + c.header do + content_tag(:div, label) + end + c.template do + block.call('', "#{name}[NEW_RECORD]", attr["attribute"].to_s + "_" + @ontology.acronym) end c.empty_state do @@ -367,20 +365,25 @@ def generate_list_field_input(attr, name, values, field_func) values.each_with_index do |metadata_val, i| c.row do - method(field_func).call("#{name}[#{i}]", metadata_val, :id => "submission_#{attr["attribute"].to_s}" + "_" + @ontology.acronym, class: "metadataInput my-1 form-control") + block.call(metadata_val, "#{name}[#{i}]" ,"submission_#{attr["attribute"].to_s}" + "_" + @ontology.acronym) end end end end - def generate_url_input(attr, name, values) - generate_list_field_input(attr, name, values, :url_field_tag) + def generate_url_input(attr, name, values, label:"") + generate_list_field_input(attr, name, label, values) do |value, row_name, id| + render Input::UrlComponent.new(label: "", name: row_name, value: value, id: id) + end end - def generate_list_text_input(attr, name, values) - generate_list_field_input(attr, name, values, :text_field_tag) + def generate_list_text_input(attr, name, values, label:"") + generate_list_field_input(attr, name, label, values) do |value, row_name, id| + render Input::TextInputComponent.new(label: "", name: row_name, value: value, id: id) + end end + def generate_boolean_input(attr, name) value = attribute_values(attr) value = value.to_s unless value.nil? @@ -412,7 +415,7 @@ def generate_attribute_input(attr_label, options = {}) input_html = ''.html_safe # Get the attribute hash corresponding to the given attribute - attr = @metadata.select { |attr_hash| attr_hash["attribute"].to_s.eql?(attr_label) }.first + attr = submission_metadata.select { |attr_hash| attr_hash["attribute"].to_s.eql?(attr_label) }.first object_name, name = attribute_input_name(attr["attribute"]) diff --git a/app/javascript/controllers/tom_select_controller.js b/app/javascript/controllers/tom_select_controller.js new file mode 100644 index 000000000..4f157b2aa --- /dev/null +++ b/app/javascript/controllers/tom_select_controller.js @@ -0,0 +1,16 @@ +import { Controller } from "@hotwired/stimulus" +import TomSelect from "tom-select" +// Connects to data-controller="tom-select" +export default class extends Controller { + + + + connect() { + + new TomSelect(this.element, { + + + //plugins: ['remove_button'] + }); + } +} diff --git a/app/views/layouts/_topnav.html.haml b/app/views/layouts/_topnav.html.haml index 2fcbcf94f..94c144037 100644 --- a/app/views/layouts/_topnav.html.haml +++ b/app/views/layouts/_topnav.html.haml @@ -44,7 +44,7 @@ = link_to("Logout", logout_path) = select_tag('language', options_for_select([['EN','en'],['FR','fr']]), id: 'language-select', class: 'nav-language', - data: { controller: "platform-language", action: "change->platform-language#handleLangChanged" }) + data: { controller: "platform-language", action: "change->platform-language#handleLangChanged" }) = render DropdownButtonComponent.new do |d| - d.header do diff --git a/app/views/layouts/component_preview.html.erb b/app/views/layouts/component_preview.html.erb index 5369cd053..429f63010 100644 --- a/app/views/layouts/component_preview.html.erb +++ b/app/views/layouts/component_preview.html.erb @@ -55,5 +55,6 @@ <%= javascript_include_tag "application" %> + \ No newline at end of file diff --git a/app/views/login/index.html.haml b/app/views/login/index.html.haml index de0a663a4..41ce080fe 100644 --- a/app/views/login/index.html.haml +++ b/app/views/login/index.html.haml @@ -15,7 +15,7 @@ %a.login-forgot-password{:href => "/lost_pass"} %p Forgot password? .login-button-container - = render Buttons::RegularButtonComponent.new(value: "Login", name: "commit", type: "submit") + = render Buttons::RegularButtonComponent.new(id: 'login-button', value: "Login", type:'submit') %p.dont-have-account Don't have an account? %a.text-decoration-none{:href => new_user_path} Register diff --git a/app/views/ontologies/_form.html.haml b/app/views/ontologies/_form.html.haml index fe945e9f6..012a3056a 100644 --- a/app/views/ontologies/_form.html.haml +++ b/app/views/ontologies/_form.html.haml @@ -1,220 +1,130 @@ -- button_text ||= "Create ontology" -- title_text ||= "Submit New Ontology" +.upload-ontology-container + %div{style: 'width: 589px'} + - unless @errors.nil? + = render Display::AlertComponent.new(message: error_message, type: 'danger', closable: false) + .upload-ontology-card + .upload-ontology-center + .Upload-ontology-title + %div + = @is_update_ontology ? "Upload new update" : "Upload ontology" + %hr + .upload-ontology-progress + = render Layout::ProgressPagesComponent.new(pages_title: ['Details', 'General metadata', 'Dates contacts']) do |c| + - c.page do + .upload-ontology-input-field-container + = text_input(name: 'ontology[name]', value: @ontology.name) + .upload-ontology-input-field-container + = text_input(name: 'ontology[acronym]', value: @ontology.acronym, disabled: @is_update_ontology) + = hidden_field_tag 'ontology[acronym]', @ontology.acronym if @is_update_ontology + + .upload-ontology-input-field-container#visibilityContainer + = select_input(label: "Visibility", name: "ontology[viewingRestriction]", values: ["public","private"], selected: @ontology.viewingRestriction ) + .upload-ontology-input-field-container#visibility-group{style: 'display: none'} + = select_input(label: "Add or remove accounts that are allowed to view classes in this ontology using the account name", name: "ontology[acl]", values: @user_select_list, selected: @ontology.acl, multiple: true) + + .upload-ontology-input-field-container + = select_input(label: "Administrator", name: "ontology[administeredBy]", values: @user_select_list, selected: @ontology.administeredBy || session[:user].id, multiple: true) + .upload-ontology-input-field-container + = render Input::InputFieldComponent.new(name: '', label:'Categories') do + %div.upload-ontology-chips-container + - @categories.each do |category| + = check_input(name: "ontology[hasDomain][]", id: category[:acronym] , label: category[:acronym], value: category[:id], checked: @ontology.hasDomain&.any?{|x| x.eql?(category[:id])}) + .upload-ontology-field-container + = render Input::InputFieldComponent.new(name: '', label:'Groups') do + %div.upload-ontology-chips-container + - @groups.each do |group| + = check_input(name: "ontology[group][]", id: group[:acronym] , label: group[:acronym], value: group[:id], checked: @ontology.group&.any?{|x| x.eql?(group[:id])}) + + .upload-ontology-input-field-container.mt-2 + %span.d-flex + = switch_input(id: 'ontology_isView', name: 'ontology[isView]', label: 'Is a view of another ontology?', checked: @ontology.view?) + %div#ontology_viewOf{style: "display: #{ !@ontology.view? ? 'none' : 'block'}"} + = render partial: "shared/ontology_picker_single", locals: {placeholder: "", field_name: "viewOf", selected: @ontology.viewOf} + + - c.page do + .upload-ontology-desc + %div + To understand the ontologies metadata: + %a{:href => "#seethewiki"} + see the Wiki + %svg{:fill => "none", :height => "8", :viewbox => "0 0 8 8", :width => "8", :xmlns => "http://www.w3.org/2000/svg"} + %path{:d => "M5.77776 8H1.33333C0.977156 8 0.642334 7.8613 0.390512 7.60946C0.138689 7.35762 0 7.02278 0 6.66666V2.22222C0 1.86607 0.138704 1.53124 0.390527 1.27942C0.64235 1.0276 0.977172 0.888894 1.33334 0.888894H3.11111C3.35659 0.888894 3.55556 1.08787 3.55556 1.33334C3.55556 1.57881 3.35659 1.77779 3.11111 1.77779H1.33333C1.2146 1.77779 1.10301 1.82402 1.01907 1.90795C0.935144 1.99188 0.888894 2.1035 0.888894 2.22222V6.66666C0.888894 6.78538 0.935129 6.89698 1.01907 6.98094C1.10301 7.06486 1.2146 7.11111 1.33333 7.11111H5.77775C5.89647 7.11111 6.00807 7.06487 6.09202 6.98091C6.17595 6.89698 6.22218 6.78537 6.22218 6.66664V4.88889C6.22218 4.64341 6.42117 4.44445 6.66664 4.44445C6.91212 4.44445 7.11111 4.64343 7.11111 4.88889V6.66666C7.11111 7.02281 6.9724 7.35762 6.72056 7.60947C6.46872 7.8613 6.13389 8 5.77776 8ZM3.11111 5.33332C2.99736 5.33332 2.88362 5.28994 2.79685 5.20315C2.62329 5.02959 2.62329 4.74816 2.79685 4.5746L6.48254 0.888894H4.88889C4.64341 0.888894 4.44445 0.68992 4.44445 0.444447C4.44445 0.198974 4.64341 0 4.88889 0H7.55555C7.61702 0 7.67556 0.0124825 7.72882 0.0350409C7.77851 0.0560624 7.82518 0.0865233 7.86602 0.126439L7.86605 0.12647C7.86634 0.126765 7.86664 0.127045 7.86692 0.12734C7.86699 0.127417 7.8671 0.127511 7.86718 0.127588C7.8674 0.127805 7.86765 0.128038 7.86786 0.128271C7.86802 0.128427 7.86816 0.128566 7.86831 0.128721C7.86848 0.128892 7.86867 0.129079 7.86881 0.129218C7.86912 0.129529 7.86946 0.129855 7.86977 0.130181C7.87008 0.130491 7.87042 0.130833 7.87074 0.131143C7.87091 0.131299 7.87109 0.131501 7.87122 0.13164C7.87139 0.131796 7.87151 0.131935 7.87167 0.132091C7.87191 0.132323 7.87213 0.132541 7.87235 0.132789C7.87243 0.132851 7.87254 0.13296 7.8726 0.133038C7.87289 0.133333 7.87319 0.133628 7.87347 0.133923L7.8735 0.133954C7.9134 0.174817 7.94388 0.221486 7.96488 0.271167C7.98744 0.32442 7.99994 0.382951 7.99994 0.444431V3.1111C7.99994 3.35657 7.80095 3.55555 7.55548 3.55555C7.31 3.55555 7.11104 3.35657 7.11104 3.1111V1.51744L3.4253 5.20317C3.33859 5.28995 3.22485 5.33332 3.11111 5.33332Z", :fill => "#31B404"} + .upload-ontology-input-field-container + = url_input(label: 'URL', name: "submission[URI]", value: @submission.URI) + .upload-ontology-input-field-container + = text_area_input(name: "submission[description]", value: @submission.description) + + - if @is_update_ontology + .upload-ontology-input-field-container + = generate_list_text_input("notes", "submission[notes]", Array(@submission.notes), label: "Change notes") + .upload-ontology-field-container + = select_input(label: "Format", name: "submission[hasOntologyLanguage]", values: ["OBO", "OWL", "SKOS", "UMLS"], selected: @submission.hasOntologyLanguage) + .upload-ontology-desc.hide + %div + SKOS vocabularies submitted to BioPortal must contain a minimum of one concept scheme and top concept assertion. Please + refer to the NCBO wiki for a more + %a{:href => "#seethewiki"} + detailed explanation + %svg{:fill => "none", :height => "8", :viewbox => "0 0 8 8", :width => "8", :xmlns => "http://www.w3.org/2000/svg"} + %path{:d => "M5.77776 8H1.33333C0.977156 8 0.642334 7.8613 0.390512 7.60946C0.138689 7.35762 0 7.02278 0 6.66666V2.22222C0 1.86607 0.138704 1.53124 0.390527 1.27942C0.64235 1.0276 0.977172 0.888894 1.33334 0.888894H3.11111C3.35659 0.888894 3.55556 1.08787 3.55556 1.33334C3.55556 1.57881 3.35659 1.77779 3.11111 1.77779H1.33333C1.2146 1.77779 1.10301 1.82402 1.01907 1.90795C0.935144 1.99188 0.888894 2.1035 0.888894 2.22222V6.66666C0.888894 6.78538 0.935129 6.89698 1.01907 6.98094C1.10301 7.06486 1.2146 7.11111 1.33333 7.11111H5.77775C5.89647 7.11111 6.00807 7.06487 6.09202 6.98091C6.17595 6.89698 6.22218 6.78537 6.22218 6.66664V4.88889C6.22218 4.64341 6.42117 4.44445 6.66664 4.44445C6.91212 4.44445 7.11111 4.64343 7.11111 4.88889V6.66666C7.11111 7.02281 6.9724 7.35762 6.72056 7.60947C6.46872 7.8613 6.13389 8 5.77776 8ZM3.11111 5.33332C2.99736 5.33332 2.88362 5.28994 2.79685 5.20315C2.62329 5.02959 2.62329 4.74816 2.79685 4.5746L6.48254 0.888894H4.88889C4.64341 0.888894 4.44445 0.68992 4.44445 0.444447C4.44445 0.198974 4.64341 0 4.88889 0H7.55555C7.61702 0 7.67556 0.0124825 7.72882 0.0350409C7.77851 0.0560624 7.82518 0.0865233 7.86602 0.126439L7.86605 0.12647C7.86634 0.126765 7.86664 0.127045 7.86692 0.12734C7.86699 0.127417 7.8671 0.127511 7.86718 0.127588C7.8674 0.127805 7.86765 0.128038 7.86786 0.128271C7.86802 0.128427 7.86816 0.128566 7.86831 0.128721C7.86848 0.128892 7.86867 0.129079 7.86881 0.129218C7.86912 0.129529 7.86946 0.129855 7.86977 0.130181C7.87008 0.130491 7.87042 0.130833 7.87074 0.131143C7.87091 0.131299 7.87109 0.131501 7.87122 0.13164C7.87139 0.131796 7.87151 0.131935 7.87167 0.132091C7.87191 0.132323 7.87213 0.132541 7.87235 0.132789C7.87243 0.132851 7.87254 0.13296 7.8726 0.133038C7.87289 0.133333 7.87319 0.133628 7.87347 0.133923L7.8735 0.133954C7.9134 0.174817 7.94388 0.221486 7.96488 0.271167C7.98744 0.32442 7.99994 0.382951 7.99994 0.444431V3.1111C7.99994 3.35657 7.80095 3.55555 7.55548 3.55555C7.31 3.55555 7.11104 3.35657 7.11104 3.1111V1.51744L3.4253 5.20317C3.33859 5.28995 3.22485 5.33332 3.11111 5.33332Z", :fill => "#31B404"} + with examples. + .upload-ontology-field-container.mt-3 + = select_input(name: "submission[status]", values: ["alpha", "beta", "production", "retired"], selected: @submission.status) + .upload-ontology-field-container + .mt-3.mb-2 Location + = render partial: 'ontologies/submission_location_form' + + - c.page do + .upload-ontology-input-field-container + - if @is_update_ontology + = date_input(label: 'Modification date (dd/mm/yy)', name: 'submission[modificationDate]', value: @submission.modificationDate) + - else + = date_input(label: 'Date of original creation (dd/mm/yy)', name: 'submission[released]', value: @submission.released) + .upload-ontology-contact + = render Input::InputFieldComponent.new(name:'', error_message: attribute_error(:contact)) do + = render NestedFormInputsComponent.new(object_name: "Contact") do |c| + - c.header do + - content_tag(:div, 'Contact name', class: 'w-50') + content_tag(:div, 'Contact email', class: 'w-50') + - c.template do + = content_tag(:div, class: "d-flex my-1") do + .w-50.mr-2 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][NEW_RECORD][name]") + .w-50 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][NEW_RECORD][email]") + - Array(@submission.contact).each_with_index do |contact, i| + - c.row do + = content_tag(:div, class: "d-flex my-1") do + .w-50.mr-2 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][#{i}][name]", value: contact["name"]) + .w-50 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][#{i}][email]", value: contact["email"]) :javascript - function hideAllRestrictions() { - jQuery(".viewing_restriction_disabled").attr("disabled", true); - jQuery("div.viewing_restriction_types").addClass("hidden"); - } - - function showRestrictionPrivate() { - jQuery("#ontology_acl").removeAttr("disabled"); - jQuery("#viewingRestrictionsPrivate").removeClass("hidden"); - } - - function showRestrictionLicensed() { - jQuery("#ontology_licenseInformation").removeAttr("disabled"); - jQuery("#viewingRestrictionsLicensed").removeClass("hidden"); - } - jQuery(document).data().bp.acronyms = #{raw LinkedData::Client::Models::Ontology.all.map {|o| o.acronym}.to_json}; + function showPrivateAclSelect() { + const visibilityGroupDiv = document.getElementById('visibility-group'); + // Get the selected value from the select element + const selectElement = document.getElementById('select_ontology[viewingRestriction]'); + const selectedValue = selectElement.value; - jQuery(document).ready(function(){ - // Wire up options for restriction how an ontology is viewed - jQuery("#ontology_viewingRestriction").change(function(){ - var select = jQuery(this); - if (select.val() == "private") { - hideAllRestrictions() - showRestrictionPrivate(); - } else if (select.val() == "licensed") { - hideAllRestrictions(); - showRestrictionLicensed(); - } else if (select.val() == "public") { - hideAllRestrictions(); - } - }); - // Make sure you can see the account select if the select list has private selected - if (jQuery("#ontology_viewingRestriction").val() == "private") { - showRestrictionPrivate(); - } else if (jQuery("#ontology_viewingRestriction").val() == "licensed") { - showRestrictionLicensed(); + // Check if the selected value is "private" + if (selectedValue === 'private') { + // If it's "private", show the visibility-group div + visibilityGroupDiv.style.display = 'block'; + } else { + // If it's not "private", hide the visibility-group div + visibilityGroupDiv.style.display = 'none'; } + } + const parentDiv = document.getElementById('visibilityContainer'); + parentDiv.addEventListener('change', () => { showPrivateAclSelect() }); + - jQuery("#ontology_isView").live("click", function(){ - console.log(jQuery("#ontology_isView").is(":checked")) + jQuery("#ontology_isView").live("click", function(){ if (jQuery("#ontology_isView").is(":checked")) { - jQuery("#ontology_viewOf").removeAttr('disabled').trigger("liszt:updated"); + jQuery("#ontology_viewOf").removeAttr('disabled').show(); } else { - jQuery("#ontology_viewOf").attr('disabled', true).trigger("liszt:updated"); + jQuery("#ontology_viewOf").attr('disabled', true).hide(); } - }); - - // Wire up chosen selectors - jQuery("#ontology_administeredBy").chosen(); - jQuery("#ontology_acl").chosen(); - jQuery("#ontology_hasDomain").chosen(); - jQuery("#ontology_group").chosen(); - - // Make acronym upcase as you type - jQuery("#ontology_acronym").on('input', function(e) { - var input = $(this); - var start = input[0].selectionStart; - $(this).val(function (_, val) { - return val.toUpperCase(); - }); - input[0].selectionStart = input[0].selectionEnd = start; - }); - - // Check acronym as you type - jQuery("#ontology_acronym").on('input', function(e) { - var $this = $(this); - var errors = []; - var errorHTML = ""; - - if ($this.val().match("^[^a-z^A-Z]{1}")) { - errors.push("Acronym must start with a letter"); - } - - if ($this.val().match("[^-_0-9a-zA-Z]")) { - errors.push("Acronym must only contain the folowing characters: -, _, letters, and numbers"); - } - - if ($this.val().match(".{17,}")) { - errors.push("Acronym must be sixteen characters or less"); - } - - if (jQuery(document).data().bp.acronyms.indexOf($this.val()) !== -1) { - errors.push("Acronym already in use"); - } - - if (errors.length > 0) { - errorHTML = "
  • " + errors.join("
  • ") + "
  • "; - } - - jQuery("#acronym_errors").html(errorHTML); - }); - - jQuery("#ontologyForm").validate({ - errorClass: "ontologyFormError", - errorElement: "div", - rules: { - "ontology[name]": "required", - "ontology[acronym]": "required", - }, - messages: { - "ontology[name]": "Please enter a name", - "ontology[acronym]": "Please enter an acronym", - }, - }); - }); - -:css - div.ontologyFormError { - color: red; - padding-top: 3px; - } - -- unless @errors.nil? - .enable-lists{:style => "color:red;"} - %strong Errors On Form - %ul - - if @errors[:error].instance_of? OpenStruct - - errors = @errors[:error].to_h - - errors.delete :links - - errors.delete :context - - errors.to_h.each do |errors_field, error| - - next if error.nil? - - %li - - if error.instance_of? OpenStruct - - error_hash = error.to_h - - error_hash.delete :links - - error_hash.delete :context - - error_hash.each do |error_type, e| - = "#{error_type} : #{e}" - - else - = errors_field - - else - -# A generic fallback - = @errors.to_json - -%div.p-5 - %div - %h1 - #{title_text} - - %div.p-5.card - %small.asterik.mb-2.text-right - * fields are required - %div.form-row - %div.form-group.col-md-6 - = f.label :name, "Name" - %span.asterik * - = f.text_field :name, value: @ontology.name, class:"form-control" - %div.form-group.col-md-3 - = f.label :acronym, "Acronym" - %span.asterik * - - acronym_enabled = @ontology.acronym.nil? || ! @errors.nil? - = f.text_field(:acronym, value: @ontology.acronym, :disabled => ! acronym_enabled, data: { acronyms: acronyms(@ontologies) }, class:"form-control") - %ul#acronym_errors.enable-lists{style: "color: red; padding: 3px;"} - %div.form-group.col-md-3 - %div - - viewing_help = "Public ontologies; will be accessible to everyone via UI and API. Download can be desactivated on demand.
    Private ontologies; are only accessible via UI and API to logged users listed explicitly." - = f.label :viewingRestriction do - = render partial: "shared/ui-component/label_with_help", locals:{help_text: viewing_help, id:"viewing_tooltip" ,label: "Viewing Restriction"} - - view_restiction_options = [["Public", "public"], ["Private", "private"]] - - selected = @ontology.private? ? "private" : "" - - selected = @ontology.licensed? ? "licensed" : selected - - display_private = @ontology.private? ? "" : "hidden" - - display_licensed = @ontology.licensed? ? "" : "hidden" - = f.select :viewingRestriction, view_restiction_options, { :selected => selected }, class:"form-control" - - %div.form-row - #viewingRestrictionsPrivate.form-group.col.viewing_restriction_types{class: display_private} - = f.label :acl do - Add or remove accounts that are allowed to view classes in this ontology using the account name - = f.select(:acl, @user_select_list, {include_blank: true, selected: @ontology.acl}, {multiple: true, :"data-placeholder" => "Select users who have access", class:"form-control"}) - %div.form-row - %div#viewingRestrictionsLicensed.form-group.col.viewing_restriction_types{class: display_licensed} - = f.label :licenseInformation do - %b License Text: - The text below explains what licensing information you want to collect before allowing access. We will display this text and record the user's response when the user attempts to access your ontology. - - disabled = @ontology.licensed? ? {} : {:disabled => "true"} - = f.text_area :licenseInformation, { :rows => 5, :class => "viewing_restriction_disabled form-control", :style => "width: 90%;" }.merge(disabled) - - %div.form-row.form-group - %div.col-md-1 - = f.label :administeredBy, "Administrators", class:" col-form-label" - %span.asterik * - %div.col-md-11 - = f.select(:administeredBy, @user_select_list ,{selected: @ontology.administeredBy || session[:user].id}, {multiple: true, :"data-placeholder" => "Select administrators", class:"form-control"}) - %div.form-group.row - %div.col-md-1 - = f.label :hasDomain, "Categories", class:"col-form-label" - %div.col-md-11 - - cat_select = @categories.sort{|a,b| a.name <=> b.name}.map{|c| [c.name, c.id]} - = f.hidden_field(:hasDomain, {value: "", id: "ontology_hasDomain_empty_select_hack", name: "ontology[hasDomain][]"}) - = f.select(:hasDomain, cat_select, {selected: @ontology.hasDomain}, {multiple: true, :"data-placeholder" => "Select category (domain)", class:"form-control"}) - %div.form-group.row - %div.col-md-1 - = f.label :group, "Groups", class:"col-form-label" - %div.col-md-11 - - group_select = @groups.sort{|a,b| a.name <=> b.name}.map{|c| [c.name, c.id]} - = f.hidden_field(:group, {value: "", id: "ontology_group_empty_select_hack", name: "ontology[group][]"}) - = f.select(:group, group_select, {selected: @ontology.group}, {multiple: true, :"data-placeholder" => "Select group", class:"form-control"}) - %div.form-row - %div.from-group.col-md-6 - = f.label :isView, "This ontology is a view of:" - = f.check_box :isView, checked: @ontology.view? - %div#viewOf_picker.row.form-group - - single_picker_locals = {:picker_id => "ontology_viewOf", placeholder: "Select an ontology to create a view on", field_name: "viewOf", disabled: !@ontology.view?, selected: @ontology.viewOf} - = render :partial => "shared/ontology_picker_single", :locals => single_picker_locals - %div.form-row - %div.from-group.col-md-6 - = f.label :subscribe_notifications, "Subscribe to email notifications for new notes" - = f.check_box :subscribe_notifications - - %div.d-flex.justify-content-center - = submit_tag "Cancel", formnovalidate: "formnovalidate", class: "btn btn-secondary mx-1 col-2" - = submit_tag button_text, class: "btn btn-primary mx-1 col-2" + }); \ No newline at end of file diff --git a/app/views/ontologies/_submission_location_form.html.haml b/app/views/ontologies/_submission_location_form.html.haml new file mode 100644 index 000000000..4fbdc2b24 --- /dev/null +++ b/app/views/ontologies/_submission_location_form.html.haml @@ -0,0 +1,49 @@ +.location-choice + %input{type: "radio", name: "submission[isRemote]", value: "3", id: "metadata_only", onchange: "displayMetadataOnlyForm()"} + %label.title{for: "metadata_only"} + Metadata only (No file) +.upload-ontology-desc.mb-2 + Allow users to view and search your ontology metadata, but not its classes and properties. +#metadata-only-form.d-none +.location-choice + %input{type: "radio", name: "submission[isRemote]", value: "1", id: "load_from_url", onchange: "displayUrlForm()"} + %label.title{for: "load_from_url"} + Load from URL +.upload-ontology-desc.mb-1 + New versions loaded on a nightly basis. +#url-form.d-none + = render Input::UrlComponent.new(label: "", name: "submission[pullLocation]") +.location-choice.mb-3.mt-3 + %input{type: "radio", name: "submission[isRemote]", value: "0", id: "upload_local_file", checked: true, onchange: "displayLocalFileForm()"} + %label.title{for: "upload_local_file"} + Upload local file +#local-file-form + = render Input::FileInputComponent.new(name: "submission[filePath]") + + +:javascript + const MetadataOnlyForm = document.getElementById("metadata-only-form"); + const UrlForm = document.getElementById("url-form"); + const LocalFileForm = document.getElementById("local-file-form"); + + const displayForm = (formElement) => { + [MetadataOnlyForm, UrlForm, LocalFileForm].forEach((form) => { + if (form === formElement) { + form.classList.remove("d-none"); + } else { + form.classList.add("d-none"); + } + }); + }; + + const displayMetadataOnlyForm = () => { + displayForm(MetadataOnlyForm); + }; + + const displayUrlForm = () => { + displayForm(UrlForm); + }; + + const displayLocalFileForm = () => { + displayForm(LocalFileForm); + }; diff --git a/app/views/ontologies/edit.html.haml b/app/views/ontologies/edit.html.haml index edaa0d654..ef1fd0f78 100644 --- a/app/views/ontologies/edit.html.haml +++ b/app/views/ontologies/edit.html.haml @@ -1,5 +1,5 @@ - @title = "Edit Ontology Information" %div{:style => "margin:10px;"} - = form_for :ontology, url: ontology_path(@ontology.acronym), html: {method: :put, id: "ontologyForm"} do |f| + = form_for :ontology, url: ontology_path(@ontology.acronym), html: {method: :put, id: "ontologyForm", multipart: true} do |f| = render partial: "form", locals: {f: f, button_text: "Save ontology", title_text: "Edit Ontology Information"} diff --git a/app/views/ontologies/new.html.haml b/app/views/ontologies/new.html.haml index a8bdab8e0..29a66b45e 100644 --- a/app/views/ontologies/new.html.haml +++ b/app/views/ontologies/new.html.haml @@ -1,5 +1,5 @@ - @title = "Submit New Ontology" %div{:style => "margin:10px;"} - = form_for :ontology, url: {action: "create"}, html: {id: "ontologyForm"} do |f| - = render partial: "form", locals: {f: f} + = form_for :ontology, url: {action: "create"}, html: {id: "ontologyForm", multipart: true} do |f| + = render partial: "ontologies/form", locals: {f: f} diff --git a/app/views/shared/_ontology_picker_single.html.erb b/app/views/shared/_ontology_picker_single.html.erb index 53e2b4f9d..1883ce904 100644 --- a/app/views/shared/_ontology_picker_single.html.erb +++ b/app/views/shared/_ontology_picker_single.html.erb @@ -25,6 +25,6 @@
    - <%= select object_name, field_name, @onts_for_select, { :include_blank => true, :selected => selected }, :id => picker_id, :class => "ontology_picker_single form-control", "data-placeholder".to_sym => placeholder, disabled: disabled %> + <%= render Input::SelectComponent.new(label: placeholder, name: "#{object_name}[#{field_name}]", value: @onts_for_select, selected: selected) %>
    diff --git a/app/views/submissions/new.html.haml b/app/views/submissions/new.html.haml index d6413ca0a..82e77242e 100644 --- a/app/views/submissions/new.html.haml +++ b/app/views/submissions/new.html.haml @@ -1,9 +1,2 @@ -- @title = "Add new ontology submission" - -%div.py-4.py-md-5 - %h3.text-center.mb-4 - Add new submission - - if !(@submission.ontology.nil? || (@submission.ontology.is_a? String)) - %small.text-muted for #{@submission.ontology.acronym} - = form_for :submission, url: {action: "create"}, html: {id: "ontology_submission_form", multipart: true, 'data-turbo': true} do |f| - = render partial: "form", locals: {f: f, button_text: "Add submission"} += form_for :submission, url: {action: "create"}, html: {id: "ontology_submission_form", multipart: true} do |f| + = render partial: "ontologies/form", locals: {f: f, button_text: "Add submission"} \ No newline at end of file