Skip to content

Commit

Permalink
Merge pull request #2986 from DMPRoadmap/madmp-project-details
Browse files Browse the repository at this point in the history
New maDMP additions for the project details page
  • Loading branch information
raycarrick-ed authored Aug 4, 2021
2 parents 75feacd + f3c70f7 commit 1e9749d
Show file tree
Hide file tree
Showing 38 changed files with 749 additions and 452 deletions.
6 changes: 5 additions & 1 deletion app/controllers/plans_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ def show
Template.where(family_id: @plan.template.customization_of).first
end

@research_domains = ResearchDomain.all.order(:label)

respond_to :html
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
Expand Down Expand Up @@ -260,7 +262,7 @@ def update
# TODO: For some reason the `fields_for` isn't adding the
# appropriate namespace, so org_id represents our funder
funder = org_from_params(params_in: attrs, allow_create: true)
@plan.funder_id = funder.id if funder.present?
@plan.funder_id = funder&.id
process_grant(grant_params: plan_params[:grant])
attrs.delete(:grant)
attrs = remove_org_selection_params(params_in: attrs)
Expand Down Expand Up @@ -467,6 +469,8 @@ def plan_params
params.require(:plan)
.permit(:template_id, :title, :visibility, :description, :identifier,
:start_date, :end_date, :org_id, :org_name, :org_crosswalk,
:ethical_issues, :ethical_issues_description, :ethical_issues_report,
:research_domain_id, :funding_status,
grant: %i[name value],
org: %i[id org_id org_name org_sources org_crosswalk],
funder: %i[id org_id org_name org_sources org_crosswalk])
Expand Down
233 changes: 130 additions & 103 deletions app/javascript/src/plans/editDetails.js
Original file line number Diff line number Diff line change
@@ -1,127 +1,154 @@
import { initAutocomplete, scrubOrgSelectionParamsOnSubmit } from '../utils/autoComplete';
import { Tinymce } from '../utils/tinymce.js.erb';
import toggleConditionalFields from '../utils/conditionalFields';
import getConstant from '../utils/constants';

$(() => {
const grantIdField = $('.grant-id-typeahead');
const grantIdHidden = $('input#plan_grant_value');

Tinymce.init();
$('#is_test').click((e) => {
$('#plan_visibility').val($(e.target).is(':checked') ? 'is_test' : 'privately_visible');
});

// Toggle the disabled flags
const toggleCheckboxes = (selections) => {
$('#priority-guidance-orgs, #other-guidance-orgs').find('input[type="checkbox"]').each((i, el) => {
const checkbox = $(el);
if (selections.length >= getConstant('MAX_NUMBER_GUIDANCE_SELECTIONS')) {
if (checkbox.is(':checked')) {
checkbox.removeAttr('disabled');
} else {
checkbox.prop('disabled', true);
}
} else {
checkbox.prop('disabled', false);
}
});
};

// Keep the modal window's guidance selections in line with selections on the main page
const syncGuidance = (ctx) => {
const currentList = $(ctx);
const otherList = (currentList.attr('id') === 'priority-guidance-orgs' ? $('#other-guidance-orgs') : $('#priority-guidance-orgs'));
const selections = currentList.find('input[type="checkbox"]:checked').map((i, el) => $(el).val()).get();
otherList.find('input[type="checkbox"]').each((i, el) => {
const checkbox = $(el);
// Toggle the checked flag to match the current guidance list
if (selections.indexOf(checkbox.val()) >= 0) {
checkbox.prop('checked', true);
} else {
checkbox.prop('checked', false);
}
const form = $('form.edit_plan');

if (form.length > 0) {
Tinymce.init({ selector: '#plan_description' });
Tinymce.init({ selector: '#plan_ethical_issues_description' });

$('#is_test').click((e) => {
$('#plan_visibility').val($(e.target).is(':checked') ? 'is_test' : 'privately_visible');
});
toggleCheckboxes(selections);
};

const grantNumberInfo = (grantId) => `Grant number: ${grantId}`;
const ethicalIssues = $('#plan_ethical_issues');
const funderId = $('#plan_org_id');

if (ethicalIssues.length > 0) {
// If the user checks the ethical_issues field then display the other ethics fields
ethicalIssues.on('change', () => {
toggleConditionalFields(ethicalIssues, ethicalIssues.prop('checked'));
}).change();

const setInitialGrantProjectName = () => {
const grantId = grantIdHidden.val();
const researchProjects = window.researchProjects;
const researchProject = researchProjects.find((datum) => datum.grant_id === grantId);
if (researchProject) {
grantIdField.val(researchProject.description);
toggleConditionalFields(ethicalIssues, ethicalIssues.prop('checked'));
}
};

const setUpTypeahead = () => {
if ($('.edit_plan').length) {
// TODO: Convert this over so that it just loads in the controller?
// Follow this pattern:
// if ($('#org-details-org-controls').length > 0) {
// initAutocomplete('#org-details-org-controls .autocomplete');
// }

$.get('/research_projects.json', (data) => {
window.researchProjects = data;
const descriptionData = $.map((dataIn, datum) => datum.description);
grantIdField.typeahead({ source: descriptionData });
}).then(() => { setInitialGrantProjectName(); });

grantIdField.on('change', () => {
const current = grantIdField.typeahead('getActive');
if (current) {
// match or partial match found
const currentResearchProject = window.researchProjects.find((datum) => {
const fixString = (string) => String(string).toLowerCase();
return fixString(datum.description) === fixString(current);
});
if (currentResearchProject) {
const grantId = currentResearchProject.grant_id;
$('#grant_number_info').html(grantNumberInfo(grantId));
if (grantId.length > 0) {
grantIdHidden.val(grantId);
} else {
grantIdHidden.val(grantIdField.val());
}
if (funderId.length > 0) {
// If the plan has a funder defined then display the other funder fields
funderId.on('change', () => {
toggleConditionalFields(funderId, (funderId.val() !== '{"name":""}' && funderId.val() !== ''));
}).change();

toggleConditionalFields(funderId, (funderId.val() !== '{"name":""}' && funderId.val() !== ''));
}

// Toggle the disabled flags
const toggleCheckboxes = (selections) => {
$('#priority-guidance-orgs, #other-guidance-orgs').find('input[type="checkbox"]').each((i, el) => {
const checkbox = $(el);
if (selections.length >= getConstant('MAX_NUMBER_GUIDANCE_SELECTIONS')) {
if (checkbox.is(':checked')) {
checkbox.removeAttr('disabled');
} else {
checkbox.prop('disabled', true);
}
} else {
$('#grant_number_info').html(grantNumberInfo(''));
grantIdHidden.val(grantIdField.val());
checkbox.prop('disabled', false);
}
});
}
};

$('#other-guidance-orgs').find('input[type="checkbox"]').click((e) => {
const checkbox = $(e.target);
// Since this is the modal window, copy any selections over to the priority list
if (checkbox.is(':checked')) {
const priorityList = $('#priority-guidance-orgs');
if (priorityList.find(`input[value="${checkbox.val()}"]`).length <= 0) {
const li = checkbox.closest('li');
// If its a subgroup copy the whole group otherwise just copy the line
if (li.children('.sublist').length > 0) {
priorityList.append(li.closest('ul').parent().clone());
};

// Keep the modal window's guidance selections in line with selections on the main page
const syncGuidance = (ctx) => {
const currentList = $(ctx);
const otherList = (currentList.attr('id') === 'priority-guidance-orgs' ? $('#other-guidance-orgs') : $('#priority-guidance-orgs'));
const selections = currentList.find('input[type="checkbox"]:checked').map((i, el) => $(el).val()).get();
otherList.find('input[type="checkbox"]').each((i, el) => {
const checkbox = $(el);
// Toggle the checked flag to match the current guidance list
if (selections.indexOf(checkbox.val()) >= 0) {
checkbox.prop('checked', true);
} else {
priorityList.append(li.clone());
checkbox.prop('checked', false);
}
});
toggleCheckboxes(selections);
};

const grantNumberInfo = (grantId) => `Grant number: ${grantId}`;

const setInitialGrantProjectName = () => {
const grantId = grantIdHidden.val();
const researchProjects = window.researchProjects;
const researchProject = researchProjects.find((datum) => datum.grant_id === grantId);
if (researchProject) {
grantIdField.val(researchProject.description);
}
};

const setUpTypeahead = () => {
if ($('.edit_plan').length) {
// TODO: Convert this over so that it just loads in the controller?
// Follow this pattern:
// if ($('#org-details-org-controls').length > 0) {
// initAutocomplete('#org-details-org-controls .autocomplete');
// }

$.get('/research_projects.json', (data) => {
window.researchProjects = data;
const descriptionData = $.map((dataIn, datum) => datum.description);
grantIdField.typeahead({ source: descriptionData });
}).then(() => { setInitialGrantProjectName(); });

grantIdField.on('change', () => {
const current = grantIdField.typeahead('getActive');
if (current) {
// match or partial match found
const currentResearchProject = window.researchProjects.find((datum) => {
const fixString = (string) => String(string).toLowerCase();
return fixString(datum.description) === fixString(current);
});
if (currentResearchProject) {
const grantId = currentResearchProject.grant_id;
$('#grant_number_info').html(grantNumberInfo(grantId));
if (grantId.length > 0) {
grantIdHidden.val(grantId);
} else {
grantIdHidden.val(grantIdField.val());
}
}
} else {
$('#grant_number_info').html(grantNumberInfo(''));
grantIdHidden.val(grantIdField.val());
}
});
}
};

$('#other-guidance-orgs').find('input[type="checkbox"]').click((e) => {
const checkbox = $(e.target);
// Since this is the modal window, copy any selections over to the priority list
if (checkbox.is(':checked')) {
const priorityList = $('#priority-guidance-orgs');
if (priorityList.find(`input[value="${checkbox.val()}"]`).length <= 0) {
const li = checkbox.closest('li');
// If its a subgroup copy the whole group otherwise just copy the line
if (li.children('.sublist').length > 0) {
priorityList.append(li.closest('ul').parent().clone());
} else {
priorityList.append(li.clone());
}
}
}
}
syncGuidance(checkbox.closest('ul[id]'));
});
syncGuidance(checkbox.closest('ul[id]'));
});

$('#priority-guidance-orgs').find('input[type="checkbox"]').click((e) => {
syncGuidance($(e.target).closest('ul[id]'));
});
$('#priority-guidance-orgs').find('input[type="checkbox"]').click((e) => {
syncGuidance($(e.target).closest('ul[id]'));
});

initAutocomplete('#funder-org-controls .autocomplete');
// Scrub out the large arrays of data used for the Org Selector JS so that they
// are not a part of the form submissiomn
scrubOrgSelectionParamsOnSubmit('form.edit_plan');
initAutocomplete('#funder-org-controls .autocomplete');
// Scrub out the large arrays of data used for the Org Selector JS so that they
// are not a part of the form submissiomn
scrubOrgSelectionParamsOnSubmit('form.edit_plan');

toggleCheckboxes($('#priority-guidance-orgs input[type="checkbox"]:checked').map((i, el) => $(el).val()).get());
toggleCheckboxes($('#priority-guidance-orgs input[type="checkbox"]:checked').map((i, el) => $(el).val()).get());

setUpTypeahead();
setUpTypeahead();
}
});
43 changes: 43 additions & 0 deletions app/javascript/src/utils/conditionalFields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Logic to hide/dsiplay a set of fields based on whether or not a related checkbox is clicked
//
// Expecting the checkbox and the corresponding fields to be wrapped in
// a <conditional></conditional> element
//
// For example see: app/views/plans/_edit_details.html.erb
// app/javascript/src/plans/editDetails.js
//
import { Tinymce } from './tinymce.js.erb';

// Expecting `context` to be the field that triggers the hide/show of the corresponding fields
export default function toggleConditionalFields(context, showThem) {
const container = $(context).closest('conditional');

if (container.length > 0) {
if (showThem === true) {
container.find('.toggleable-field').show();

// Resize any TinyMCE editors
container.find('.toggleable-field').find('.tinymce').each((_idx, el) => {
const tinymceEditor = Tinymce.findEditorById($(el).attr('id'));
if (tinymceEditor) {
$(tinymceEditor.iframeElement).height(tinymceEditor.settings.autoresize_min_height);
}
});
} else {
// Clear the contents of any textarea select boxes or input fields
container.find('.toggleable-field').find('input, textarea, select').val('').change();

// TODO: clear check boxes and radio buttons as needed

// Clear the contents of any TinyMCE editors
container.find('.toggleable-field').find('.tinymce').each((_idx, el) => {
const tinymceEditor = Tinymce.findEditorById($(el).attr('id'));
if (tinymceEditor) {
tinymceEditor.setContent('');
}
});

container.find('.toggleable-field').hide();
}
}
}
2 changes: 1 addition & 1 deletion app/models/concerns/identifiable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def self.from_identifiers(array:)
# gets the identifier for the scheme
def identifier_for_scheme(scheme:)
scheme = IdentifierScheme.by_name(scheme.downcase).first if scheme.is_a?(String)
identifiers.select { |id| id.identifier_scheme == scheme }.first
identifiers.select { |id| id.identifier_scheme == scheme }.last
end

# Combines the existing identifiers with the new ones
Expand Down
Loading

0 comments on commit 1e9749d

Please sign in to comment.