Skip to content

Commit

Permalink
Merge pull request #29 from KPMP/develop
Browse files Browse the repository at this point in the history
Release v1.3
  • Loading branch information
rlreamy authored Nov 30, 2021
2 parents e823c44 + 30e24c4 commit 897ee04
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 45 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
# libra-web
The web-layer of the KPMP curation tools.

## How to work with this locally
Libra-web lives atop the DLU ecosystem and uses orion-data as its service layer.
In production, DLU and DMD are served up with the same Apache, but we have not yet confgured our local instance to handle this since it would mean that one of the applications would need to run at a different path, which makes compiling for local and production different.

Instead, the easiest way to get the DMD running locally is to modify your .env in heavens-docker/orion to set the ENV_REACT_APPDIR to point at your checked out libra-web instead of orion-web

## Hosted at:
dev-datamanager.kpmp.org
datamanager.kpmp.org
26 changes: 17 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
{
"name": "libra-web",
"version": "1.2.0",
"version": "1.3.0",
"private": true,
"dependencies": {
"axios": "0.19.0",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.16",
"axios": "^0.24.0",
"babel-eslint": "10.1.0",
"babel-polyfill": "6.26.0",
"bootstrap-css-only": "4.3.1",
"history": "4.10.1",
"kpmp-custom-react-scripts": "1.1.2",
"moment": "2.24.0",
"bootstrap-css-only": "4.4.1",
"history": "5.1.0",
"moment": "2.29.1",
"node-sass-chokidar": "1.5.0",
"npm-run-all": "4.1.5",
"prop-types": "15.7.2",
"react": "16.9.0",
"react-csv": "^2.0.3",
"react-dom": "16.9.0",
"react-ga": "2.6.0",
"react-redux": "7.1.1",
"react-redux": "7.2.6",
"react-router-dom": "5.0.1",
"react-scripts": "4.0.3",
"react-table": "6.10.3",
"reactstrap": "8.0.1",
"redux": "4.0.4",
"redux-thunk": "2.3.0"
"redux": "4.1.2",
"redux-thunk": "2.4.0"
},
"devDependencies": {
"acorn": "^8.6.0",
"node-sass-chokidar": "^1.5.0",
"npm-run-all": "4.1.5"
},
Expand Down
7 changes: 6 additions & 1 deletion src/actions/Error/errorActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ export const sendMessageToBackend = (error) => {
return (dispatch) => {
api.post('/api/v1/error', errorMessage)
.then(res=> {
dispatch(handleError(error.response.status));
if (error.response) {
dispatch(handleError(error.response.status));
} else {
dispatch(handleError(error));
}

});
};
}
Expand Down
19 changes: 19 additions & 0 deletions src/actions/Packages/packageActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,23 @@ export const setPackages = (packages) => {
type: actionNames.SET_PACKAGES,
payload: packages
}
}

export const setStateDisplayMap = (stateDisplayMap) => {
return {
type: actionNames.SET_STATE_DISPLAY_MAP,
payload: stateDisplayMap
}
}

export const getStateDisplayMap = () => {
return (dispatch) => {
api.get('/api/v1/state/stateDisplayMap')
.then(res => {
dispatch(setStateDisplayMap(res.data));
})
.catch(err => {
dispatch(sendMessageToBackend(err));
});
};
}
1 change: 1 addition & 0 deletions src/actions/actionNames.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const actionNames = {
GET_PACKAGES: 'GET_PACKAGES',
RESET_STATE: 'RESET_STATE',
SET_PACKAGES: 'SET_PACKAGES',
SET_STATE_DISPLAY_MAP: 'SET_STATE_DISPLAY_MAP',
SET_VALIDATION_RESULT: 'SET_VALIDATION_RESULT'
};

Expand Down
4 changes: 3 additions & 1 deletion src/components/PackageDashboard/PackageDashboardPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class PackageDashboardPage extends Component {
<Row className='mt-3'>
<Col sm={12}>
<PackageTable
packages={this.props.packages} movePackageFiles={this.props.movePackageFiles}
packages={this.props.packages}
movePackageFiles={this.props.movePackageFiles}
stateDisplayMap={this.props.stateDisplayMap}
/>
</Col>
</Row>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { connect } from 'react-redux';
import { getPackages, movePackageFiles } from "../../actions/Packages/packageActions";
import { getPackages, movePackageFiles, getStateDisplayMap } from "../../actions/Packages/packageActions";
import PackageDashboardPage from './PackageDashboardPage';

const mapStateToProps = (state, props) => ({
packages: state.packages
packages: state.packages,
stateDisplayMap: state.stateDisplayMap
});

const mapDispatchToProps = (dispatch, props) => ({
getPackages() {
dispatch(getPackages());
dispatch(getStateDisplayMap());
},
movePackageFiles(packageId) {
dispatch(movePackageFiles(packageId));
Expand Down
76 changes: 54 additions & 22 deletions src/components/PackageDashboard/PackageTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import ReactTable from 'react-table';
import ReactGA from 'react-ga';
import PropTypes from 'prop-types';
import Moment from 'moment';
import stateMap from './stateMap';
import { Button } from 'reactstrap';
import { Row, Col, Button } from 'reactstrap';
import { getStateDisplayText } from './stateDisplayHelper';
import { faDownload } from '@fortawesome/free-solid-svg-icons';
import { CSVLink } from 'react-csv';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

const PACKAGE_ID_LABEL = 'Package ID';
const PACKAGE_TYPE_LABEL = 'Package Type';
Expand Down Expand Up @@ -49,6 +52,7 @@ class PackageTable extends Component {
};
};


getColumns() {
return [
{
Expand Down Expand Up @@ -90,10 +94,7 @@ class PackageTable extends Component {
Header: PACKAGE_STATE_LABEL,
id: PACKAGE_STATE_ID,
accessor: (row) => {
if (row.state && row.state[PACKAGE_STATE_ID]) {
return stateMap.has(row.state[PACKAGE_STATE_ID]) ? stateMap.get(row.state[PACKAGE_STATE_ID]) : row.state[PACKAGE_STATE_ID];
}
return '';
return getStateDisplayText(row.state, this.props.stateDisplayMap);
}
}, {
Header: GLOBUS_LINK_LABEL,
Expand Down Expand Up @@ -171,27 +172,58 @@ class PackageTable extends Component {
});
}

prepareData = (packages) => {
return packages.map((pkg) => {
return {
[PACKAGE_ID_LABEL]: pkg[PACKAGE_INFO_PROPERTY][PACKAGE_ID],
[SUBJECT_ID_LABEL]: pkg[PACKAGE_INFO_PROPERTY][SUBJECT_ID],
[PACKAGE_TYPE_LABEL]: pkg[PACKAGE_INFO_PROPERTY][PACKAGE_TYPE_ID],
[SUBMITTER_LABEL]: pkg[PACKAGE_INFO_PROPERTY].submitter && pkg[PACKAGE_INFO_PROPERTY].submitter[SUBMITTER_ID] ? pkg[PACKAGE_INFO_PROPERTY].submitter[SUBMITTER_ID] : pkg[PACKAGE_INFO_PROPERTY].submitter[SUBMITTER_FIRST_NAME] + ' ' + pkg[PACKAGE_INFO_PROPERTY].submitter[SUBMITTER_LAST_NAME],
[TIS_NAME_LABEL]: pkg[PACKAGE_INFO_PROPERTY][TIS_NAME_ID],
[DATE_SUBMITTED_LABEL]: new Moment(pkg[PACKAGE_INFO_PROPERTY][DATE_SUBMITTED_ID]).local().format(DATE_FORMAT),
[PACKAGE_STATE_LABEL]: getStateDisplayText(pkg.state, this.props.stateDisplayMap)
}
});
}

render() {
return <ReactTable
data={this.props.packages}
ref={this.reactTable}
sorted={this.state.sorted}
filtered={this.state.filtered}
onSortedChange={this.onSortedChange}
onFilteredChange={this.onFilteredChange}
columns={this.state.columns}
defaultPageSize={12}
defaultFilterMethod={this.defaultFilterMethod}
filterable
className='-striped -highlight'
showPageSizeOptions={false}
noDataText={'No packages found'}
/>;
return (
<article>
<Row><Col xs={12} className='mb-2'>
<CSVLink
data={this.prepareData(this.props.packages)}
filename={'dmd-package-info.csv'}
target="_blank"
className="text-body icon-container"
>
<FontAwesomeIcon icon={faDownload} pull='right' />
</CSVLink>
</Col></Row>
<Row><Col xs={12}>
<ReactTable
data={this.props.packages}
ref={this.reactTable}
sorted={this.state.sorted}
filtered={this.state.filtered}
onSortedChange={this.onSortedChange}
onFilteredChange={this.onFilteredChange}
columns={this.state.columns}
defaultPageSize={12}
defaultFilterMethod={this.defaultFilterMethod}
filterable
className='-striped -highlight'
showPageSizeOptions={false}
noDataText={'No packages found'}
/>
</Col></Row>
</article>
);
}
}

PackageTable.propTypes = {
packages: PropTypes.arrayOf(PropTypes.object)
packages: PropTypes.arrayOf(PropTypes.object),
stateDisplayMap: PropTypes.arrayOf(PropTypes.object)
};

export default PackageTable;
15 changes: 15 additions & 0 deletions src/components/PackageDashboard/stateDisplayHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const getStateDisplayText = (state, stateDisplayMap) => {

let stateDisplayText = stateDisplayMap.filter(function(stateDisplayItem) {
if (stateDisplayItem.state === state.state) {
return stateDisplayItem;
}
return undefined;
}, state);

if (stateDisplayText[0]) {
return stateDisplayText[0].apps.dmd.text
} else {
return '';
}
}
17 changes: 17 additions & 0 deletions src/components/PackageDashboard/stateDisplayHelper.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { getStateDisplayText } from './stateDisplayHelper';
import React from 'react';

describe('getStateDisplayText', () => {
it('should return empty string when no match', () => {
expect(getStateDisplayText({},[])).toEqual('');
});

it('should return the correct state text', () => {
let state = { state: 'alive' };
let stateDisplayMap = [
{ state: 'alive', apps: { dmd: { text: 'hey ho' }}},
{ state: 'dead', apps: { dmd: { text: 'oh no' }}}
];
expect(getStateDisplayText(state, stateDisplayMap)).toEqual('hey ho');
})
});
13 changes: 13 additions & 0 deletions src/components/PackageDashboard/stateDisplayMapReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import actionNames from '../../actions/actionNames';

export const stateDisplayMap = (state = {}, action) => {
let newState = {};
let stateDisplayMap = action.payload;
switch (action.type) {
case actionNames.SET_STATE_DISPLAY_MAP:
newState = stateDisplayMap;
return newState;
default:
return state;
};
};
31 changes: 31 additions & 0 deletions src/components/PackageDashboard/stateDisplayMapReducer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import actionNames from '../../actions/actionNames';
import { stateDisplayMap } from './stateDisplayMapReducer';

describe('stateDisplayMap', () => {
it('should return the given state when not SET_STATE_DISPLAY_MAP', () => {
let action = {
type: 'SOME_OTHER_ACTION',
payload: [{'key': 'value'}]
};
let expectedState = [ {'stateKey': 'stateValue'}];
expect(stateDisplayMap(expectedState, action)).toEqual(expectedState);
});

it('should return {} in state if state is undefined and not a covered action', () => {
let action = {
type: 'SOME_OTHER_ACTION',
payload: [{'key': 'value'}]
};
expect(stateDisplayMap(undefined, action)).toEqual({});
});

it('should construct the appropriate state for SET_STATE_DISPLAY_MAP action', () => {
let action = {
type: actionNames.SET_STATE_DISPLAY_MAP,
payload: { 'map': 'a' }
};
let expectedState = { 'map': 'a' };

expect(stateDisplayMap({}, action)).toEqual(expectedState);
});
});
10 changes: 0 additions & 10 deletions src/components/PackageDashboard/stateMap.js

This file was deleted.

2 changes: 2 additions & 0 deletions src/reducers.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { combineReducers } from 'redux';
import { resetStateReducer } from './resetStateReducer';
import { packages } from './components/PackageDashboard/packageReducer';
import { stateDisplayMap } from './components/PackageDashboard/stateDisplayMapReducer';
import { filenameValidation } from './components/Validation/filenameValidationReducer';
import actionNames from './actions/actionNames';
import loadedState from './initialState';

const appReducer = combineReducers({
filenameValidation,
resetStateReducer,
stateDisplayMap,
packages
});

Expand Down

0 comments on commit 897ee04

Please sign in to comment.