diff --git a/app/views/answers/_new_edit.html.erb b/app/views/answers/_new_edit.html.erb index caccfc1..64914b0 100644 --- a/app/views/answers/_new_edit.html.erb +++ b/app/views/answers/_new_edit.html.erb @@ -2,11 +2,52 @@ + +

+ <%= raw question.text %> +

+<% q_format = question.question_format %> +<% if q_format.rda_metadata? %> + <% answer_hash = answer.answer_hash %> +
+
Your Selected Standards:
+
+ OR Search:
+
+ +
+
Please select a subject
+
Please select a sub-subject
+
+
+ + <%=_("Browse Standards") %> + + +
+

+ <%=_("Please wait, Standards are loading")%> +

+
+
+ Standard not listed? Add your own. + +
+
+
+<% end %> + <%= form_for answer, url: {controller: :answers, action: :create_or_update}, html: {method: :post, 'data-autosave': question.id, class: 'form-answer' } do |f| %> <% if !readonly %> <%= f.hidden_field :plan_id %> <%= f.hidden_field :question_id %> <%= f.hidden_field :lock_version %> + <% if q_format.rda_metadata? %> + <%= hidden_field_tag :standards, answer_hash['standards'].to_json %> + <% end %> <% end %>
> <% if question.option_based? %> @@ -30,4 +71,4 @@ <% end %> -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/questions/_add_question.html.erb b/app/views/questions/_add_question.html.erb index 72045fa..5a9c3f1 100644 --- a/app/views/questions/_add_question.html.erb +++ b/app/views/questions/_add_question.html.erb @@ -31,7 +31,7 @@ :id, :title, QuestionFormat.id_for(QuestionFormat.formattypes[:textarea])), - {}, + {}, class: "form-control", 'data-toggle': 'tooltip', 'data-html': true, @@ -44,13 +44,13 @@
<%= render "/question_options/option_fields", f: f, q: @new_question %> -
- -
+
<%= f.label(:default_value, _('Default answer'), class: "control-label") %> @@ -90,4 +90,3 @@
<% end %> - diff --git a/app/views/questions/_edit_question.html.erb b/app/views/questions/_edit_question.html.erb index 7d354f9..4d970dc 100644 --- a/app/views/questions/_edit_question.html.erb +++ b/app/views/questions/_edit_question.html.erb @@ -30,7 +30,7 @@ :id, :title, question.question_format_id), - {}, + {}, class: "form-control", 'data-toggle': 'tooltip', 'data-html': true, @@ -42,13 +42,14 @@
<%= render "/question_options/option_fields", f: f, q: question %> -
- -
+ <% comment_disp = question.question_format.option_based? || question.question_format.rda_metadata? %> +
+ +
<%= f.label(:default_value, _('Default answer'), class: "control-label") %> diff --git a/config/branding_example.yml b/config/branding_example.yml index dfd82d3..454bd9d 100644 --- a/config/branding_example.yml +++ b/config/branding_example.yml @@ -48,7 +48,7 @@ admin_privileges: true added_as_coowner: true feedback_requested: true - feedback_provided: true + feedback_provided: true owners_and_coowners: visibility_changed: true admins: diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index 9671418..f0c99af 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -8,6 +8,7 @@ // Page specific JS import './views/answers/edit'; +import './views/answers/rda_metadata'; import './views/annotations/add'; import './views/annotations/edit'; import './views/contacts/new'; diff --git a/lib/assets/javascripts/views/answers/rda_metadata.js b/lib/assets/javascripts/views/answers/rda_metadata.js new file mode 100644 index 0000000..e5701d2 --- /dev/null +++ b/lib/assets/javascripts/views/answers/rda_metadata.js @@ -0,0 +1,397 @@ +$(() => { + // url for the api we will be querying + let url = ''; + + // key/value lookup for standards + const descriptions = {}; + // cleaned up structure of the API results + const minTree = {}; + // keeps track of how many waiting api-requests still need to run + let noWaiting = 0; + + + // prune the min_tree where there are no standards + // opporates on the principal that no two subjects have the same name + function removeUnused(name) { + const num = Object.keys(minTree).find(x => minTree[x].name === name); + // if not top level standard + if (num === undefined) { + // for each top level standard + Object.keys(minTree).forEach((knum) => { + const child = Object.keys(minTree[knum].children).find(x => + minTree[knum].children[x].name === name); + if (num !== undefined) { + delete minTree[num].children[child]; + $(`.rda_metadata .sub-subject select option[value="${name}"]`).remove(); + } + }); + } else { + delete minTree[num]; + // remove min_tree[num] from top-level dropdowns + $(`.rda_metadata .subject select option[value="${name}"]`).remove(); + } + } + + + function getDescription(id) { + $.ajax({ + url: url + id.slice(4), + type: 'GET', + crossDomain: true, + dataType: 'json', + }).done((results) => { + descriptions[id] = {}; + descriptions[id].title = results.title; + descriptions[id].description = results.description; + noWaiting -= 1; + }); + } + + + // init descriptions lookup table based on passed ids + function initDescriptions(ids) { + ids.forEach((id) => { + if (!(id in descriptions)) { + noWaiting += 1; + getDescription(id); + } + }); + } + + // takes in a subset of the min_tree which has name and standards properties + // initializes the standards property to the result of an AJAX POST + function getStandards(name, num, child) { + // slice -4 from url to remove '/api/' + noWaiting += 1; + $.ajax({ + url: `${url.slice(0, -4)}query/schemes`, + type: 'POST', + crossDomain: true, + data: `keyword=${name}`, + dataType: 'json', + }).done((result) => { + if (child === undefined) { + minTree[num].standards = result.ids; + } else { + minTree[num].children[child].standards = result.ids; + } + if (result.ids.length < 1) { + removeUnused(name); + } + noWaiting -= 1; + initDescriptions(result.ids); + }); + } + + // clean up the data initially returned from the API + function cleanTree(apiTree) { + // iterate over api_tree + Object.keys(apiTree).forEach((num) => { + minTree[num] = {}; + minTree[num].name = apiTree[num].name; + minTree[num].children = []; + if (apiTree[num].children !== undefined) { + Object.keys(apiTree[num].children).forEach((child) => { + minTree[num].children[child] = {}; + minTree[num].children[child].name = apiTree[num].children[child].name; + minTree[num].children[child].standards = []; + getStandards(minTree[num].children[child].name, num, child); + }); + } + // init a standards on top level + minTree[num].standards = []; + getStandards(minTree[num].name, num, undefined); + }); + } + + + // create object for typeahead + function initTypeahead() { + const data = []; + const simpdat = []; + Object.keys(descriptions).forEach((id) => { + data.push({ value: descriptions[id].title, id }); + simpdat.push(descriptions[id].title); + }); + $('.standards-typeahead').typeahead({ source: simpdat }); + } + + function initStandards() { + // for each metadata question, init selected standards according to html + $('.rda_metadata').each(() => { + // list of selected standards + const selectedStandards = $(this).find('.selected_standards .list'); + // form listing of standards + const formStandards = $(this).next('form').find('#standards'); + // need to pull in the value from frm_stds + console.log(formStandards.val()); + const standardsArray = JSON.parse(formStandards.val()); + // init the data value + formStandards.data('standard', standardsArray); + Object.keys(standardsArray).forEach((key) => { + // add the standard to list + if (key === standardsArray[key]) { + selectedStandards.append(`
  • ${key}${descriptions[key].title}
  • `); + } + }); + }); + } + + function waitAndUpdate() { + if (noWaiting > 0) { + // if we are waiting on api responces, call this function in 1 seccond + setTimeout(waitAndUpdate, 1000); + } else { + // update all the dropdowns/ standards explore box (calling on subject + // will suffice since it will necisarily update sub-subject) + $('.rda_metadata .subject selec').change(); + initStandards(); + initTypeahead(); + } + } + + // given a subject name, returns the portion of the min_tree applicable + function getSubject(subjectText) { + const num = Object.keys(minTree).find(x => minTree[x].name === subjectText); + return minTree[num]; + } + + // given a subsubject name and an array of children, data, return the + // applicable child + function getSubSubject(subsubjectText, data) { + const child = Object.keys(data).find(x => data[x].name === subsubjectText); + return data[child]; + } + + function updateSaveStatus(group) { + // update save/autosave status + group.next('form').find('fieldset input').change(); + } + + // change sub-subjects and standards based on selected subject + $(this).on('change', '.rda_metadata .subject select', () => { + console.log('changed subject'); + const group = $(this).closest('.rda_metadata'); + const subSubject = group.find('.sub-subject select'); + const subjectText = $(this).find(':selected').text(); + // find subject in min_tree + const subject = getSubject(subjectText); + // check to see if this object has no children(and thus it's own standards) + if (subject.children.length === 0) { + // hide sub-subject since there's no data for it + subSubject.closest('div').hide(); + // update the standards display selector + $('.rda_metadata .sub-subject selec').change(); + } else { + // show the sub-subject incase it was previously hidden + subSubject.closest('div').show(); + // update the sub-subject display selector + subSubject.find('option').remove(); + subject.children.forEach((child) => { + $('