diff --git a/Makefile b/Makefile index 5f33742acf2..8c2c016cb60 100755 --- a/Makefile +++ b/Makefile @@ -77,6 +77,9 @@ mri_violations: issue_tracker: target=issue_tracker npm run compile +candidate_list: + target=candidate_list npm run compile + candidate_parameters: target=candidate_parameters npm run compile diff --git a/modules/candidate_list/jsx/candidateListIndex.js b/modules/candidate_list/jsx/candidateListIndex.js index c11de28810e..d141c0dab62 100644 --- a/modules/candidate_list/jsx/candidateListIndex.js +++ b/modules/candidate_list/jsx/candidateListIndex.js @@ -6,6 +6,7 @@ import Loader from 'Loader'; import FilterableDataTable from 'FilterableDataTable'; import Modal from 'Modal'; +import fetchDataStream from 'jslib/fetchDataStream'; import OpenProfileForm from './openProfileForm'; /** @@ -25,10 +26,11 @@ class CandidateListIndex extends Component { super(props); this.state = { - data: {}, + data: [], error: false, isLoaded: false, hideFilter: true, + fieldOptions: {}, show: {profileForm: false}, }; @@ -63,8 +65,18 @@ class CandidateListIndex extends Component { * Called by React when the component has been rendered on the page. */ componentDidMount() { - this.fetchData() - .then(() => this.setState({isLoaded: true})); + fetch('options', + {credentials: 'same-origin'}).then( + (resp) => resp.json() + ).then( + (json) => { + this.setState({ + fieldOptions: json, + }); + } + ); + + this.fetchData(); const searchParams = new URLSearchParams(location.search); if (searchParams.has('hide')) { @@ -80,23 +92,15 @@ class CandidateListIndex extends Component { * @return {object} */ fetchData() { - return fetch(this.props.dataURL, {credentials: 'same-origin'}) - .then((resp) => resp.json()) - .then((data) => { - // Convert concatenated string of cohort and visit labels to array - data.Data = data.Data.map((row) => { - // Visit label - row[2] = (row[2]) ? row[2].split(',') : null; - // Cohort - row[4] = (row[4]) ? row[4].split(',') : null; - return row; - }); - this.setState({data}); - }) - .catch((error) => { - this.setState({error: true}); - console.error(error); - }); + fetchDataStream(this.props.dataURL, + (row) => this.state.data.push(row), + (end) => { + this.setState({data: this.state.data}); + }, + () => { + this.setState({isLoaded: true}); + }, + ); } /** @@ -149,8 +153,7 @@ class CandidateListIndex extends Component { } if (column === 'Cohort') { - // If user has multiple cohorts, join array into string - let result = (cell) ? {cell.join(', ')} : ; + let result = (cell) ? {cell} : ; return result; } @@ -178,7 +181,8 @@ class CandidateListIndex extends Component { * XXX: Currently, the order of these fields MUST match the order of the * queried columns in _setupVariables() in candidate_list.class.inc */ - const options = this.state.data.fieldOptions; + // const options = this.state.data.fieldOptions; + const options = this.state.fieldOptions; const fields = [ { label: 'PSCID', @@ -371,7 +375,7 @@ class CandidateListIndex extends Component { {profileForm} { document.getElementById('lorisworkspace') ).render( + * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 + * @link https://www.github.com/aces/Loris/ + */ +class Options extends \LORIS\Http\Endpoint +{ + /** + * Overloading this method to allow access to site users (their own site only) + * and users w/ multisite privs + * + * @param \User $user The user whose access is being checked + * + * @return bool true if user has access, false otherwise + */ + function _hasAccess(\User $user) : bool + { + return ( + $user->hasPermission('access_all_profiles') + || ($user->hasStudySite() && $user->hasPermission('data_entry')) + + ); + } + + /** + * Return the list of field options required to be serialized to JSON + * in order to render the frontend. + * + * @return array + */ + function getFieldOptions() : array + { + $this->loris->getModule('candidate_parameters')->registerAutoloader(); + + // create user object + $factory = \NDB_Factory::singleton(); + $user = $factory->user(); + $config = $factory->config(); + + // get the list of visit labels + $visit_label_options = \Utility::getVisitList(); + + // get the list of sites available for the user + if ($user->hasPermission('access_all_profiles')) { + $list_of_sites = \Utility::getSiteList(); + } else { + $list_of_sites = $user->getStudySites(); + } + $site_options = []; + foreach (array_values($list_of_sites) as $name) { + $site_options[$name] = $name; + } + + // get the list of projects + $list_of_projects = \Utility::getProjectList(); + $project_options = []; + foreach (array_values($list_of_projects) as $name) { + $project_options[$name] = $name; + } + + // get the list of cohorts + $list_of_cohorts = \Utility::getCohortList(); + $cohort_options = []; + foreach (array_values($list_of_cohorts) as $name) { + $cohort_options[$name] = $name; + } + + // get the list participant status options + $list_of_participant_status + = \Candidate::getParticipantStatusOptions(); + $participant_status_options = []; + foreach (array_values($list_of_participant_status) as $name) { + $participant_status_options[$name] = $name; + } + + return [ + 'visitlabel' => $visit_label_options, + 'site' => $site_options, + 'project' => $project_options, + 'cohort' => $cohort_options, + 'participantstatus' => $participant_status_options, + 'useedc' => $config->getSetting("useEDC"), + 'Sex' => \Utility::getSexList(), + ]; + } + + /** + * An Endpoint acts as middleware which will calculate ETag if applicable. + * + * @param ServerRequestInterface $request The incoming PSR7 request + * + * @return ResponseInterface The outgoing PSR7 response + */ + public function handle( + ServerRequestInterface $request, + ): ResponseInterface { + return new \LORIS\Http\Response\JSON\OK($this->getFieldOptions()); + } +} +