Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Instrument manager] Reactify Menu Filters #4142

Merged
merged 9 commits into from
Nov 23, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions modules/instrument_manager/jsx/instrumentManagerIndex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';

import {Tabs, TabPane} from 'Tabs';
import Loader from 'Loader';
import FilterableDataTable from 'FilterableDataTable';

import InstrumentUploadForm from './uploadForm';

class InstrumentManagerIndex extends Component {
constructor(props) {
super(props);

this.state = {
data: {},
error: false,
isLoaded: false,
};

this.fetchData = this.fetchData.bind(this);
this.formatColumn = this.formatColumn.bind(this);
}

componentDidMount() {
this.fetchData()
.then(() => this.setState({isLoaded: true}));
}

/**
* Retrieve data from the provided URL and save it in state
* Additionally add hiddenHeaders to global loris variable
* for easy access by columnFormatter.
*
* @return {object}
*/
fetchData() {
return fetch(this.props.dataURL, {credentials: 'same-origin'})
.then((resp) => resp.json())
.then((data) => this.setState({data}))
.catch((error) => {
this.setState({error: true});
console.error(error);
});
}

/**
* Modify behaviour of specified column cells in the Data Table component
*
* @param {string} column - column name
* @param {string} cell - cell content
* @param {object} row - row content indexed by column
*
* @return {*} a formated table cell for a given column
*/
formatColumn(column, cell, row) {
// Set class to 'bg-danger' if file is hidden.
xlecours marked this conversation as resolved.
Show resolved Hide resolved
return (
<td>{cell}</td>
);
}

render() {
// If error occurs, return a message.
// XXX: Replace this with a UI component for 500 errors.
if (this.state.error) {
return <h3>An error occured while loading the page.</h3>;
}

// Waiting for async data to load
if (!this.state.isLoaded) {
return <Loader/>;
}

const fields = [
{label: 'Instrument', show: true, filter: {
name: 'instrument',
type: 'text',
}},
{label: 'Instrument Type', show: true, filter: {
name: 'instrument_type',
xlecours marked this conversation as resolved.
Show resolved Hide resolved
type: 'select',
options: {
'Instrument Builder': 'Instrument Builder',
'PHP': 'PHP',
'Missing': 'Missing',
},
}},
{label: 'Table Installed', show: true, filter: {
name: 'table_installed',
type: 'select',
options: {
'Exists': 'Exists',
'Missing': 'Missing',
},
}},
{label: 'Table Valid', show: true, filter: {
name: 'table_valid',
type: 'text',
}},
{label: 'Pages Valid', show: true, filter: {
name: 'pages_valid',
type: 'text',
}},
];

const tabs = [
{id: 'browse', label: 'Browse'},
{id: 'upload', label: 'Upload'},
];

const feedback = () => {
if (!this.state.data.caninstall) {
Copy link
Collaborator

@HenriRabalais HenriRabalais Nov 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps canInstall should be passed in a permission object? It just seems to odd to have it as part of 'data'. This may require some restructuring of how we handle permissions on the front end. Is there a way to access this information via this.props.hasPermission?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. Whether or not they can install is a function of the filesystem permissions/setup, not the user's permission.

Copy link
Contributor Author

@xlecours xlecours Nov 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I partly agree with you. Yes, it is odd the it is part of the data but it is not a permission either. This tells if quat user is configured correctly so the table can be created.

Copy link
Collaborator

@HenriRabalais HenriRabalais Nov 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@driusan @xlecours Hmm, okay. Yeah, that makes. I guess as we are making standards for the objects we are passing to the front end (e.g. data, fieldOptions, etc.), we may want to group things like this into a configuration object? Rather than have stray configuration booleans in the data object. Not necessary for this PR, just something to think about.

return (
<div className='alert alert-warning'>
Instrument installation is not possible given the current server
configuration; the LORIS 'quatUser' is not configured properly.
File upload is still possible but instruments will need to be
installed manually
</div>
);
}
};

const uploadTab = () => {
let content = null;
if (this.state.data.writable) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same,this tells if the project/instrument and project/table_sql directory can be written into.

let url = loris.BaseURL.concat('/instrument_manager/?format=json');
content = (
<InstrumentUploadForm action={url}/>
);
} else {
content = (
<div className='alert alert-warning'>
Installation is not possiblegiven the current server configuration.
xlecours marked this conversation as resolved.
Show resolved Hide resolved
Please contact your administrator if you require this functionality
</div>
);
}
return content;
};

return (
<Tabs tabs={tabs} defaultTab="browse" updateURL={true}>
<TabPane TabId={tabs[0].id}>
<FilterableDataTable
name="instrument_manager"
data={this.state.data.Data}
fields={fields}
getFormattedCell={this.formatColumn}
/>
</TabPane>
<TabPane TabId='upload'>
{feedback()}
{uploadTab()}
</TabPane>
</Tabs>
);
}
}

InstrumentManagerIndex.propTypes = {
dataURL: PropTypes.string.isRequired,
hasPermission: PropTypes.func.isRequired,
};

window.addEventListener('load', () => {
ReactDOM.render(
<InstrumentManagerIndex
dataURL={`${loris.BaseURL}/instrument_manager/?format=json`}
hasPermission={loris.userHasPermission}
/>,
document.getElementById('lorisworkspace')
);
});
97 changes: 97 additions & 0 deletions modules/instrument_manager/jsx/uploadForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';

class InstrumentUploadForm extends Component {
constructor(props) {
super(props);

this.state = {
data: {},
selectedFile: null,
};

this.fileSelected = this.fileSelected.bind(this);
this.upload = this.upload.bind(this);
}

fileSelected(element, file) {
this.setState({
selectedFile: file,
});
}

upload() {
const data = new FormData();
data.append('install_file', this.state.selectedFile);

fetch(this.props.action, {
method: 'POST',
credentials: 'same-origin',
body: data,
})
.then((resp) => {
if (resp.status == 201) {
swal({
xlecours marked this conversation as resolved.
Show resolved Hide resolved
title: 'Installation Successful!',
type: 'success',
}, function() {
window.location.assign(loris.BaseURL + '/instrument_manager/');
});
}
return resp.json();
})
.then((data) => {
if (data.message) {
swal({
title: 'Upload Successful!',
type: 'success',
text: data.message,
}, function() {
window.location.assign(loris.BaseURL + '/instrument_manager/');
});
}
if (data.error) {
swal({
title: 'An error occured',
type: 'error',
text: data.error,
});
}
})
.catch((error) => {
this.setState({error: true});
console.error(error);
});
}

render() {
const disabled = () => this.state.selectedFile == null;
xlecours marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className="row">
<div className="col-xs-4">
<div className="panel panel-primary">
<div className="panel-heading">Upload Instrument</div>
<div className="panel-body">
<div className="col-xs-12">
<FileElement
name='install_file'
label='Instrument file'
onUserInput={this.fileSelected}
value={this.state.selectedFile}
/>
<button className="btn btn-default" onClick={this.upload} disabled={disabled()}>Install</button>
</div>
</div>
</div>
</div>
</div>
);
}
}

InstrumentUploadForm.propTypes = {
action: PropTypes.string.isRequired,
};

export default InstrumentUploadForm;
Loading