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

Merging to master for initial release #17

Merged
merged 44 commits into from
Jan 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
33f9b4b
KPMP-521: Initial copy of cassiopeia-web
rlreamy Dec 13, 2018
71fea34
Merge pull request #2 from KPMP/master
rlreamy Dec 13, 2018
0e45693
Added travis.yml
rlreamy Dec 13, 2018
41cc759
Merge branch 'develop' of https://github.com/KPMP/delphinus-web into …
rlreamy Dec 13, 2018
60df4bd
KPMP-528: Adjusted styling and wording, moved under /dpr
rlreamy Dec 13, 2018
8258d2c
KPMP-528: Moved back out of /dpr, apache can handle that for us
rlreamy Dec 13, 2018
9ebb6d0
KPMP-528: Updated the alt text
rlreamy Dec 17, 2018
d8b1d70
KPMP-535: Remove stains
zwright Dec 17, 2018
a604c22
KPMP-541: Changes necessary to make app work under subdirectory
rlreamy Dec 17, 2018
55a9b6e
KPMP-541: removed unused import
rlreamy Dec 17, 2018
dfdbad0
KPMP-541: Fix test
rlreamy Dec 18, 2018
63b490e
Merge pull request #4 from KPMP/KPMP-535_Remove_Stain_Info
rlreamy Dec 18, 2018
8fcb29d
KPMP-534: Removed the summary text and changed the alert text
rlreamy Dec 18, 2018
84ed0e9
KPMP-534: Change the dropdown items, tweak styling
rlreamy Dec 18, 2018
056a9d2
Merge pull request #3 from KPMP/KPMP-528_NewHeader
zwright Dec 18, 2018
5c984f5
Merge pull request #6 from KPMP/KPMP-534_ChangeSummaryPage
zwright Dec 18, 2018
6fc0512
KPMP-541: Fix merge conflicts
rlreamy Dec 18, 2018
f8c3047
KPMP-576: Burger menu closed
zwright Dec 18, 2018
6c6250b
Merge pull request #7 from KPMP/KPMP-576_Burger_Menu_Closed
rlreamy Dec 18, 2018
9cd61c7
KPMP-577: Breakpoints, thumbnails, and zoom
zwright Dec 18, 2018
80f98eb
KPMP-577: Add thumbnails
zwright Dec 18, 2018
f3cd20a
Merge pull request #5 from KPMP/KPMP-541_DemoLandingPage
zwright Dec 18, 2018
0217b99
KPMP-577: Merge conflict
zwright Dec 18, 2018
0ec9ee6
Merge pull request #8 from KPMP/KPMP-577_Migrate_Functionality
rlreamy Dec 18, 2018
844cd18
KPMP-534: Fixed dropdown item labels
rlreamy Dec 18, 2018
863ca63
Merge pull request #9 from KPMP/KPMP-534_FixDropdwonItems
zwright Dec 19, 2018
9e9e12d
KPMP-534: Make warning responsive
rlreamy Dec 19, 2018
2d47d43
Merge pull request #10 from KPMP/KPMP-534_SummaryPage
zwright Dec 19, 2018
95c154b
KPMP-577: Relative path
zwright Dec 19, 2018
e545a8e
Merge pull request #11 from KPMP/KPMP-577_Migrate_2
rlreamy Dec 20, 2018
a407dcd
KPMP-584: Feedback button
zwright Dec 20, 2018
0b6268d
KPMP-591: New logo
zwright Dec 20, 2018
9cfddb7
KPMP-591: Text changes
zwright Dec 20, 2018
a295943
KPMP-589: IE issues
rlreamy Dec 20, 2018
ed52926
KPMP-591: Merge conflicts
zwright Dec 20, 2018
487504e
Merge pull request #12 from KPMP/KPMP-534_SummaryPage
zwright Dec 20, 2018
0857199
KPMP-591: Merge conflicts
zwright Dec 20, 2018
00deb25
Merge pull request #13 from KPMP/KPMP-591_Header_Text_Adjust
rlreamy Dec 20, 2018
3817b44
KPMP-584: Add Google link
zwright Dec 20, 2018
d59a9a1
KPMP-590: Change the quality of the download
rlreamy Dec 20, 2018
d3321de
Merge pull request #14 from KPMP/KPMP-584_Add_Feedback_Button
rlreamy Dec 20, 2018
848131e
Merge pull request #15 from KPMP/KPMP-590_DownloadNotWorking
zwright Dec 20, 2018
36789dc
KPMP 591: New logo
zwright Dec 21, 2018
3483193
Merge pull request #16 from KPMP/KPMP-591_Update_Logo
zwright Dec 21, 2018
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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules/
build/
.project
**/*.css
.idea
*.iml
15 changes: 15 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
language: node_js

node_js:
- 8

script:
- CI=true npm test
- npm run build

notifications:
email:
- rlreamy@umich.edu
- zwright@umich.edu
- cgates@umich.edu
- rossmith@umich.edu
18,274 changes: 18,274 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "delphinus-web",
"version": "0.1.0",
"private": true,
"homepage": "/dpr",
"devDependencies": {
"node-sass-chokidar": "1.3.4",
"npm-run-all": "4.1.5",
"react-scripts": "2.1.1"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "1.2.8",
"@fortawesome/free-solid-svg-icons": "5.5.0",
"@fortawesome/react-fontawesome": "0.1.3",
"antd": "3.11.0",
"axios": "0.18.0",
"bootstrap": "4.1.3",
"es6-shim": "0.35.4",
"history": "4.7.2",
"html-react-parser": "0.4.7",
"lodash": "4.17.11",
"openseadragon": "2.4.0",
"react": "16.6.3",
"react-burger-menu": "2.5.4",
"react-dom": "16.6.3",
"react-ga": "2.5.6",
"react-redux": "5.1.1",
"react-router-dom": "4.3.1",
"reactstrap": "6.5.0",
"redux": "4.0.1",
"redux-thunk": "2.3.0"
},
"scripts": {
"start-js": "react-scripts start",
"start": "npm-run-all -p watch-css start-js",
"build-js": "react-scripts build",
"build": "npm-run-all build-css build-js",
"build-css": "node-sass-chokidar src/ -o src/",
"watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
Binary file added public/.DS_Store
Binary file not shown.
Binary file added public/favicon.ico
Binary file not shown.
Binary file added public/img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/biopsy-process.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/col1-glomerulus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/col2-nephron.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/col3-renal-cortex.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/step-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/step-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/step-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/summary/step-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/thumbnail_stain_he.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/thumbnail_stain_pas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/thumbnail_stain_silver.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/thumbnail_stain_tri.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/tn_KPMP-Ex2_TRI_1of1.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Slide Viewer Concept</title>
<script> Function.prototype.call = function(t) { return this.apply(t, Array.prototype.slice.apply(arguments, [1])); } </script>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
67 changes: 67 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { Component } from 'react';
import NavBar from './components/Nav/NavBar';
import Summary from './components/Summary/Summary';
import Slides from './components/Slides/Slides';
import { Container } from 'reactstrap';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import loadedState from './initialState';
import rootReducer from './reducers';
import { HashRouter, Route } from 'react-router-dom';
import SlidePrintManager from './components/Slides/Menu/SlidePrintManager';
import ReactGA from 'react-ga';
import createHistory from 'history/createBrowserHistory';

const cacheStore = window.sessionStorage.getItem("redux-store");
const initialState = cacheStore ?
JSON.parse(cacheStore) :
loadedState;
const store = applyMiddleware(thunk)(createStore)(rootReducer, initialState, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
const saveState = () => {
window.sessionStorage.setItem("redux-store", JSON.stringify(store.getState()));
};
const GA_TRACKING_ID = 'UA-124331187-3';

ReactGA.initialize(GA_TRACKING_ID);
function logPageView(location, action) {
ReactGA.set({ page: location.pathname + location.search });
ReactGA.pageview(location.pathname + location.search);
}
const history = createHistory();
history.listen((location, action) => {
logPageView(location, action);
});

store.subscribe(function () {
console.log(store.getState())
});

store.subscribe(saveState);

SlidePrintManager.getInstance().setReduxStore(store);

class App extends Component {

componentWillMount() {
logPageView(window.location, "");
}

render() {
return (
<Provider store={store}>
<Container fluid>
<NavBar/>
<HashRouter basename='/dpr'>
<div>
<Route exact path="/" component={Summary}/>
<Route path="/slides" component={Slides}/>
</div>
</HashRouter>
</Container>
</Provider>
);
}
}

export default App;
32 changes: 32 additions & 0 deletions src/actions/Patients/patientActions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import actionNames from '../actionNames';
import axios from 'axios';
import patientSelectSorter from '../../components/Summary/patientSelectSorter';

export const setSelectedPatient = (patient) => {
return {
type: actionNames.SET_SELECTED_PATIENT,
payload: patient
}
}

export const setSelectedSlide = (slide) => {
return {
type: actionNames.SET_SELECTED_SLIDE,
payload: slide
}
}

export const getPatientSlides = (patientId, props) => {
return (dispatch) => {
var config = { headers: {'Content-Type': 'application/json', 'Cache-control': 'no-cache'}}
axios.get('/api/v1/slides/' + patientId, config)
.then(result => {
let slides = patientSelectSorter(result.data);
dispatch(setSelectedPatient({id: patientId, slides: slides, selectedSlide: slides[0]}));
props.history.push("/slides");
})
.catch(err => {
console.log("We were unable to get a list of slides for patient " + patientId);
});
}
}
15 changes: 15 additions & 0 deletions src/actions/Patients/patientActions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { setSelectedPatient } from './patientActions';
import actionNames from '../actionNames';


describe('setSelectedPatient', () => {
it('should pass the argument through to the payload and set the action', () => {
let payload = "I am a payload";
let actionName = actionNames.SET_SELECTED_PATIENT;

let result = setSelectedPatient(payload);

expect(result).toEqual( { payload: payload, type: actionName });
});
});

7 changes: 7 additions & 0 deletions src/actions/actionNames.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const actionNames = {
SET_SELECTED_PATIENT: "SET_SELECTED_PATIENT",
GET_PATIENT_SLIDES: "GET_PATIENT_SLIDES",
SET_SELECTED_SLIDE: "SET_SELECTED_SLIDE"
};

export default actionNames;
8 changes: 8 additions & 0 deletions src/common-values.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
$header-height: 60px;
$menu-margin: 15px;
$menu-button-width: 36px;
$standard-padding: 15px;
$link-blue: #0275d8;
$kpmp-blue: rgba(40, 60, 94, 1);
$thumbnail-height: 70px;
$thumbnail-width: 70px;
25 changes: 25 additions & 0 deletions src/components/Nav/NavBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { Component } from 'react';
import { Row, NavbarBrand, Col, Button } from 'reactstrap';

class NavBar extends Component {
render() {
return (
<Row className="nav-container container-fluid">
<Col xs="3">
<NavbarBrand href={process.env.PUBLIC_URL + "/"}>
<img src="img/logo.png" alt="Digital Pathology Repository" className="logo"/>
</NavbarBrand>
</Col>
<Col xs="6" id="demo-text">Slide Viewer Concept
</Col>
<Col xs="6" id="demo-text-small"></Col>
<Col xs="3">
<div className="float-right" id="feedback-button"><Button color="primary" onClick={() => window.open("https://goo.gl/forms/WkyC7PZM8AIe3NoI3", "_blank")}>Send Feedback</Button></div>
</Col>
</Row>

);
}
}

export default NavBar;
77 changes: 77 additions & 0 deletions src/components/Slides/Menu/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { Component } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCaretLeft, faChevronRight, faChevronLeft, faPrint, faDownload } from '@fortawesome/free-solid-svg-icons';
import { Col, Row } from 'reactstrap';
import ReactGA from 'react-ga';
import SlidePrintManager from './SlidePrintManager';
import { getNextSlide, getPreviousSlide, downloadSlide } from '../slideHelpers.js';

class Header extends Component {

constructor(props) {
super(props);
this.onPrint = this.onPrint.bind(this);
this.handleDownload = this.handleDownload.bind(this);
}

handleNextSlide() {
let nextSlide = getNextSlide(this.props.selectedPatient.slides, this.props.selectedPatient.selectedSlide);
this.props.setSelectedSlide(nextSlide);
this.props.toggleMenu(true);
}

handlePreviousSlide() {
let previousSlide = getPreviousSlide(this.props.selectedPatient.slides, this.props.selectedPatient.selectedSlide);
this.props.setSelectedSlide(previousSlide);
this.props.toggleMenu(true);
}

handleDownload() {
ReactGA.event({
category: 'Slide View',
action: 'Download Slide',
label: this.props.selectedPatient.selectedSlide.slideName
});
let downloadFileName = this.props.selectedPatient.selectedSlide.slideName + ".jpg";
downloadSlide(downloadFileName);
}

onPrint() {
ReactGA.event({
category: 'Slide View',
action: 'Print Slide',
label: this.props.selectedPatient.selectedSlide.slideName
});
SlidePrintManager.getInstance().beforePrint();
setTimeout(window.print, 10);
}

render() {
return(
<div className="menu-slide-list-header">
<Row>
<Col className="menu-title">WHOLE SLIDE IMAGES</Col>
<div className="float-right">
<Col className="menu-control"><FontAwesomeIcon icon={faCaretLeft} className="clickable" onClick={this.props.toggleMenu} size="lg"/></Col>
</div>
</Row>
<Row>
<Col className="float-left" xs="6">
<FontAwesomeIcon icon={faChevronLeft} className="clickable hoverable pad-right" onClick={() => this.handlePreviousSlide()} size="lg"/>
<FontAwesomeIcon icon={faChevronRight} className="clickable hoverable" onClick={() => this.handleNextSlide()} size="lg"/>
</Col>
<Col xs="6">
<div className="float-right">
<FontAwesomeIcon icon={faPrint} onClick={this.onPrint} className="clickable hoverable pad-right" size="lg"/>
<a id="download" //eslint-disable-line
><FontAwesomeIcon icon={faDownload} className="clickable hoverable" onClick={this.handleDownload} size="lg" /></a>
</div>
</Col>
</Row>
</div>
);
}

}

export default Header;
39 changes: 39 additions & 0 deletions src/components/Slides/Menu/Menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { Component } from 'react';
import { slide as BurgerMenu } from 'react-burger-menu';
import SlideListContainer from './SlideListContainer';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBars, faHome } from '@fortawesome/free-solid-svg-icons';
import { Link } from 'react-router-dom';

class Menu extends Component {

constructor(props) {
super(props);
this.state = { isOpen: false };
}

toggleMenu = (newState) => {
let openState = !this.state.isOpen;
if (newState !== undefined || newState !== null) {
openState = newState;
}
this.setState( { isOpen: openState } );
}

render() {
return (
<div id="side-menu">
<BurgerMenu id={ "bm-menu-wrap" } width={"33%"} isOpen={ this.state.isOpen } noOverlay customBurgerIcon={ <FontAwesomeIcon icon={faBars} /> }
customCrossIcon={ false } >
<SlideListContainer toggleMenu={this.toggleMenu}/>
</BurgerMenu>
<Link id="btn-home" to="/">
<FontAwesomeIcon icon={faHome} size="2x"/>
</Link>
</div>
);
}

}

export default Menu;
Loading