diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2928e419e49..869a6d4576a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,8 @@ requesting a new account and will be displayed in the User Accounts module (PR #
### Modules
#### Issue Tracker
- Readability of comments and history was improved. (PR #6138)
+#### Candidate Parameters
+- Consents may now be grouped in UI of consent tab (PR #6042, PR #6044)
### Clean Up
- *Add item here*
### Notes For Existing Projects
diff --git a/modules/candidate_parameters/ajax/getData.php b/modules/candidate_parameters/ajax/getData.php
index 22c6bdbf9ef..42a6642f2dd 100644
--- a/modules/candidate_parameters/ajax/getData.php
+++ b/modules/candidate_parameters/ajax/getData.php
@@ -423,6 +423,9 @@ function getConsentStatusFields()
$date = [];
$withdrawalDate = [];
+ // Get consent groups
+ $consentGroups = \Utility::getConsentGroups();
+
// Get list of all consent types
$consentDetails = \Utility::getConsentList();
// Get list of consents for candidate
@@ -431,6 +434,10 @@ function getConsentStatusFields()
foreach ($consentDetails as $consentID=>$consent) {
$consentName = $consent['Name'];
$consentList[$consentName] = $consent['Label'];
+ $groupID = $consent['ConsentGroupID'];
+
+ // Append consent as a child to its group
+ $consentGroups[$groupID]['Children'][] = $consentName;
if (isset($candidateConsent[$consentID])) {
$candidateConsentID = $candidateConsent[$consentID];
@@ -453,6 +460,7 @@ function getConsentStatusFields()
'withdrawals' => $withdrawalDate,
'consents' => $consentList,
'history' => $history,
+ 'consentGroups' => $consentGroups,
];
return $result;
@@ -469,7 +477,7 @@ function getConsentStatusFields()
*/
function getConsentStatusHistory($pscid)
{
- $db = \Database::singleton();
+ $db = (\NDB_Factory::singleton())->database();
$historyData = $db->pselect(
"SELECT EntryDate, DateGiven, DateWithdrawn, PSCID,
@@ -481,23 +489,16 @@ function getConsentStatusHistory($pscid)
);
$formattedHistory = [];
- foreach ($historyData as $key => $entry) {
- $consentName = $entry['ConsentName'];
- $consentLabel = $entry['ConsentLabel'];
-
- $history = [
- 'data_entry_date' => $entry['EntryDate'],
- 'entry_staff' => $entry['EntryStaff'],
- $consentName => $entry['Status'],
- $consentName . '_date' => $entry['DateGiven'],
- $consentName . '_withdrawal' => $entry['DateWithdrawn'],
- ];
- $consentHistory = [
- $key => $history,
- 'label' => $consentLabel,
- 'consentType' => $consentName,
+ foreach ($historyData as $entry) {
+ $formattedHistory[] = [
+ 'data_entry_date' => $entry['EntryDate'],
+ 'entry_staff' => $entry['EntryStaff'],
+ 'consentStatus' => $entry['Status'],
+ 'date' => $entry['DateGiven'],
+ 'withdrawal' => $entry['DateWithdrawn'],
+ 'label' => $entry['ConsentLabel'],
+ 'consentType' => $entry['ConsentName'],
];
- $formattedHistory[$key] = $consentHistory;
}
return $formattedHistory;
}
diff --git a/modules/candidate_parameters/jsx/ConsentStatus.js b/modules/candidate_parameters/jsx/ConsentStatus.js
index 5dee37e8e40..6493739bcac 100644
--- a/modules/candidate_parameters/jsx/ConsentStatus.js
+++ b/modules/candidate_parameters/jsx/ConsentStatus.js
@@ -1,6 +1,8 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import swal from 'sweetalert2';
+
+import {VerticalTabs, TabPane} from 'Tabs';
import Loader from 'Loader';
/**
@@ -20,7 +22,8 @@ class ConsentStatus extends Component {
formData: {},
error: false,
isLoaded: false,
- loadedData: 0,
+ submitDisabled: false,
+ showHistory: false,
};
/**
@@ -29,6 +32,9 @@ class ConsentStatus extends Component {
this.fetchData = this.fetchData.bind(this);
this.setFormData = this.setFormData.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
+ this.showHistory = this.showHistory.bind(this);
+ this.renderFormattedHistory = this.renderFormattedHistory.bind(this);
+ this.renderConsent = this.renderConsent.bind(this);
}
componentDidMount() {
@@ -173,6 +179,10 @@ class ConsentStatus extends Component {
}
formData.append('tab', this.props.tabName);
formData.append('candID', this.state.Data.candID);
+
+ // Disable submit button to prevent form resubmission
+ this.setState({submitDisabled: true});
+
$.ajax({
type: 'POST',
url: this.props.action,
@@ -181,221 +191,252 @@ class ConsentStatus extends Component {
contentType: false,
processData: false,
success: (data) => {
- swal.fire('Success!', 'Update successful.', 'success');
+ swal.fire('Success!', 'Update successful.', 'success')
+ .then((result) => {
+ if (result.value) {
+ this.setState({submitDisabled: false});
+ this.fetchData();
+ }
+ }
+ );
this.fetchData();
},
error: (error) => {
console.error(error);
+ // Enable submit button for form resubmission
+ this.setState({submitDisabled: false});
let errorMessage = error.responseText || 'Update failed.';
swal.fire('Error!', errorMessage, 'error');
},
});
}
+ showHistory() {
+ this.setState({showHistory: !this.state.showHistory});
+ }
+
+ renderFormattedHistory() {
+ const historyBtnLabel = this.state.showHistory ?
+ 'Hide Consent History' : 'Show Consent History';
+
+ const formattedHistory = this.state.Data.history.map((info, key) => {
+ const label = info.label;
+ const dataEntry = info.data_entry_date;
+ const user = info.entry_staff;
+ const consentStatus = info.consentStatus;
+ const consentDate = info.date;
+ const withdrawal = info.withdrawal;
+ const dateHistory = consentDate ? (
+
+ , Date of Consent to {consentDate}
+
+ ) : null;
+ const withdrawalHistory = withdrawal ? (
+
+ , Date of Consent Withdrawal to {withdrawal}
+
+ ) : null;
+
+ return (
+
+
+
+
+ {dataEntry} - {user}
+ updated for {label} :
+ Status to {' '}
+ {this.state.consentOptions[consentStatus]}
+ {dateHistory}
+ {withdrawalHistory}
+
+
+ );
+ });
+
+ return (
+
+
+ {historyBtnLabel}
+
+
+ {formattedHistory}
+
+
+ );
+ }
+
+ renderConsent(consentName) {
+ // Allow editing if user has permission
+ let disabled = loris.userHasPermission('candidate_parameter_edit')
+ ? false : true;
+
+ // Set up props for front-end validation
+ const oldConsent = this.state.Data.consentStatuses[consentName];
+ const newConsent = this.state.formData[consentName];
+ const withdrawalDate = this.state.Data.withdrawals[consentName];
+ // Define defaults
+ let emptyOption = true;
+ let dateRequired = false;
+ let withdrawalRequired = false;
+ // Let date of withdrawal field be disabled until it is needed
+ let withdrawalDisabled = true;
+
+ // If answer to consent is 'yes', require date of consent
+ if (newConsent === 'yes') {
+ dateRequired = true;
+ }
+ // If answer to consent is 'no', require date of consent
+ if (newConsent === 'no') {
+ dateRequired = true;
+ // If answer was previously 'yes' and consent is now being withdrawn, enable and require withdrawal date
+ // If consent was previously withdrawn and stays withdrawn, enable and require withdrawal date
+ if (oldConsent === 'yes' ||
+ (oldConsent === 'no' && withdrawalDate)
+ ) {
+ withdrawalDisabled = false;
+ withdrawalRequired = true;
+ }
+ }
+ // Disallow clearing a valid consent status by removing empty option
+ if (oldConsent === 'no' || oldConsent === 'yes') {
+ emptyOption = false;
+ }
+
+ // Set up elements
+ const label = this.state.Data.consents[consentName];
+ const statusLabel = 'Response';
+ const consentDate = consentName + '_date';
+ const consentDate2 = consentName + '_date2';
+ const consentDateLabel = 'Date of Response';
+ const consentDateConfirmationLabel = 'Confirmation Date of Response';
+ const consentWithdrawal = consentName + '_withdrawal';
+ const consentWithdrawal2 = consentName + '_withdrawal2';
+ const consentWithdrawalLabel = 'Date of Withdrawal of Consent';
+ const consentWithdrawalConfirmationLabel =
+ 'Confirmation Date of Withdrawal of Consent';
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+
render() {
// If error occurs, return a message.
// XXX: Replace this with a UI component for 500 errors.
if (this.state.error) {
- return An error occured while loading the page. ;
+ return An error occurred while loading the page. ;
}
if (!this.state.isLoaded) {
return ;
}
- let disabled = true;
- let updateButton = null;
- if (loris.userHasPermission('candidate_parameter_edit')) {
- disabled = false;
- updateButton = ;
- }
- const emptyOption = [];
- const dateRequired = [];
- const withdrawalRequired = [];
- const withdrawalDisabled = [];
- let i = 0;
- for (let consent in this.state.Data.consents) {
- if (this.state.Data.consents.hasOwnProperty(consent)) {
- const oldConsent = this.state.Data.consentStatuses[consent];
- const newConsent = this.state.formData[consent];
- const withdrawalDate = this.state.Data.withdrawals[consent];
- // Set defaults
- emptyOption[i] = true;
- dateRequired[i] = false;
- withdrawalRequired[i] = false;
- // Let date of withdrawal field be disabled until it is needed
- withdrawalDisabled[i] = true;
- // If answer to consent is 'yes', require date of consent
- if (newConsent === 'yes') {
- dateRequired[i] = true;
- }
- // If answer to consent is 'no', require date of consent
- if (newConsent === 'no') {
- dateRequired[i] = true;
- // If answer was previously 'yes' and consent is now being withdrawn, enable and require withdrawal date
- // If consent was previously withdrawn and stays withdrawn, enable and require withdrawal date
- if (oldConsent === 'yes'
- || (oldConsent === 'no' && withdrawalDate)) {
- withdrawalDisabled[i] = false;
- withdrawalRequired[i] = true;
- }
- }
- // Disallow clearing a valid consent status by removing empty option
- if (oldConsent === 'no' || oldConsent === 'yes') {
- emptyOption[i] = false;
- }
- i++;
- }
- }
+ // Allow editing if user has permission
+ let updateButton = loris.userHasPermission('candidate_parameter_edit') ?
+ ( ) : null;
- let consents = [];
- i = 0;
- for (let consentStatus in this.state.Data.consents) {
- if (this.state.Data.consents.hasOwnProperty(consentStatus)) {
- let consentLabel = this.state.Data.consents[consentStatus];
- let statusLabel = 'Response';
- let consentDate = consentStatus + '_date';
- let consentDate2 = consentStatus + '_date2';
- let consentDateLabel = 'Date of ' + statusLabel;
- let consentDateConfirmationLabel = 'Confirmation Date of '
- + statusLabel;
- let consentWithdrawal = consentStatus + '_withdrawal';
- let consentWithdrawal2 = consentStatus + '_withdrawal2';
- let consentWithdrawalLabel = 'Date of Withdrawal of Consent';
- let consentWithdrawalConfirmationLabel =
- 'Confirmation Date of Withdrawal of Consent';
-
- const consent = (
-
-
-
-
-
-
-
-
-
- );
- consents.push(consent);
- i++;
- }
- }
-
- let formattedHistory = [];
- const history = this.state.Data.history;
- for (let consentKey in history) {
- if (history.hasOwnProperty(consentKey)) {
- let consentLabel = history[consentKey].label;
- let consentType = history[consentKey].consentType;
- for (let field in history[consentKey]) {
- if (history[consentKey].hasOwnProperty(field)) {
- let line = '';
- let historyConsent = history[consentKey][field];
- for (let field2 in historyConsent) {
- if (historyConsent.hasOwnProperty(field2)) {
- let current = historyConsent[field2];
- if (current !== null) {
- switch (field2) {
- case 'data_entry_date':
- line += '[';
- line += current;
- line += '] ';
- break;
- case 'entry_staff':
- line += current;
- line += ' ';
- break;
- case consentType:
- line += consentLabel + ' Status: ';
- line += current;
- line += ' ';
- break;
- case consentType + '_date':
- line += 'Date of Consent: ';
- line += current;
- line += ' ';
- break;
- case consentType + '_withdrawal':
- line += 'Date of '
- + 'Consent Withdrawal: ';
- line += current;
- line += ' ';
- break;
- default:
- }
- }
+ // Set up vertical tabs to group consent by consent groups
+ const tabList = [];
+ const tabPanes = Object.keys(this.state.Data.consentGroups).map(
+ (consentID) => {
+ if (this.state.Data.consentGroups[consentID].Children) {
+ tabList.push({
+ id: consentID,
+ label: this.state.Data.consentGroups[consentID].Label,
+ });
+ return (
+
+
+
+
+ {this.state.Data.consentGroups[consentID].Children
+ .map((consentName) => {
+ return this.renderConsent(consentName);
}
- }
- formattedHistory.push({line}
);
- }
- }
+ )}
+ {updateButton}
+
+
+ );
}
- }
-
+ }
+ );
return (
-
-
-
- {consents}
- {updateButton}
- {formattedHistory}
-
+ {tabPanes}
+
+ {this.renderFormattedHistory()}
);
}
diff --git a/php/libraries/Utility.class.inc b/php/libraries/Utility.class.inc
index 9015b727e42..ab649b833dc 100644
--- a/php/libraries/Utility.class.inc
+++ b/php/libraries/Utility.class.inc
@@ -97,6 +97,25 @@ class Utility
return $age;
}
+ /**
+ * Returns list of consent groups in the database
+ *
+ * @return array An associative array of consent grouping,
+ * with their names and labels. The keys of
+ * the arrays are the IDs of the groups.
+ */
+ static function getConsentGroups(): array
+ {
+ $factory = NDB_Factory::singleton();
+ $DB = $factory->database();
+
+ $query = "SELECT ConsentGroupID, Name, Label FROM consent_group";
+ $key = "ConsentGroupID";
+
+ $result = $DB->pselectWithIndexKey($query, [], $key);
+
+ return $result;
+ }
/**
* Returns list of consents in the database
*