diff --git a/jams-react-client/public/blueprints.json b/jams-react-client/public/blueprints.json new file mode 100644 index 0000000000000000000000000000000000000000..fe51488c7066f6687ef680d6bfaa4f7768ef205c --- /dev/null +++ b/jams-react-client/public/blueprints.json @@ -0,0 +1 @@ +[] diff --git a/jams-react-client/public/groups.json b/jams-react-client/public/groups.json new file mode 100644 index 0000000000000000000000000000000000000000..ac5ed22969b7cc3ddf2c7dead42b20ce1f01c327 --- /dev/null +++ b/jams-react-client/public/groups.json @@ -0,0 +1,8 @@ +[ + { + "name": "Group 1", + "blueprint": "Patients", + "groupMembers": ["lgharib", "aberaud", "cberaud"] + + } +] diff --git a/jams-react-client/src/auth.js b/jams-react-client/src/auth.js index ffaf963905898aac503fa018214aea1ed4de711d..036081463aa6ede28ef89eff226e51e4acb2fd9d 100644 --- a/jams-react-client/src/auth.js +++ b/jams-react-client/src/auth.js @@ -7,15 +7,22 @@ import { api_path_get_server_status, api_path_get_install_lastKnownStep, api_path_get_directories, + api_path_get_subscription_status, + api_path_get_needs_update, + api_path_get_start_update } from "globalUrls"; class Auth { constructor() { - this.authenticated = false; - this.admin = false; - this.installed = false; - this.uri = ''; - this.username = ''; + this.authenticated = false + this.admin = false + this.installed = false + this.uri = '' + this.username = '' + this.localVersion = '' + this.remoteVersion = '' + this.activated = false + this.updateAvailable = false } setJWT(access_token) { @@ -113,11 +120,11 @@ class Auth { isServerInstalled(cb) { axios(configApiCall(api_path_get_server_status, "GET", null, null)).then((response) => { if (response.data['installed'] == 'true') { - this.installed = true; - console.log("Server is insalled"); + this.installed = true + console.log("Server is installed") } else { - this.installed = false; - console.log("Server is not insalled"); + this.installed = false + console.log("Server is not installed") } cb() }).catch((error) => { @@ -125,7 +132,7 @@ class Auth { }); } - checkLastKnowStep(cb) { + checkLastKnownStep(cb) { if(this.installed){ this.authenticated = true console.log("Auth: Server is already installed") @@ -136,12 +143,66 @@ class Auth { this.authenticated = true; cb() }).catch((error) => { - console.log("Error during API request on checkLastKnowStep: " + error); + console.log("Error during API request on checkLastKnownStep: " + error); cb() }) } } + checkForUpdates(cb) { + if (this.installed && this.authenticated) { + axios(configApiCall(api_path_get_subscription_status, 'GET', null, null)).then((response) => { + this.localVersion = response.data['versions']['jams-server.jar']['version']; + this.activated = response.data['activated']; + cb() + }) + } else { + axios(configApiCall(api_path_get_install_lastKnownStep, 'GET', null, null)).then((response) => { + this.uri = response.data['uri'] + this.authenticated = true + cb() + }).catch((error) => { + console.log("Error during API request on checkLastKnownStep: " + error); + cb() + }) + } + } + + getUpdates(cb) { + if (this.installed && this.authenticated) { + axios(configApiCall(api_path_get_needs_update, 'GET', null, null)).then((response) => { + this.remoteVersion = response.data['remoteVersions']['jams-server.jar']['version']; + this.updateAvailable = response.data['updateAvailable']; + cb() + }) + } else { + axios(configApiCall(api_path_get_install_lastKnownStep, 'GET', null, null)).then((response) => { + this.uri = response.data['uri'] + this.authenticated = true + cb() + }).catch((error) => { + console.log("Error during API request on checkLastKnownStep: " + error); + cb() + }) + } + } + + isActivated() { + return this.activated; + } + + isUpdateAvailable() { + return this.updateAvailable; + } + + getLocalVersion() { + return this.localVersion; + } + + getRemoteVersion() { + return this.remoteVersion; + } + hasAdmin() { return this.admin; } diff --git a/jams-react-client/src/components/Sidebar/Sidebar.js b/jams-react-client/src/components/Sidebar/Sidebar.js index e886d2cda6f17977bc128d8b251f44a684609363..5f86acaa912850f98a1822eff7af6d7152f9c162 100755 --- a/jams-react-client/src/components/Sidebar/Sidebar.js +++ b/jams-react-client/src/components/Sidebar/Sidebar.js @@ -17,15 +17,24 @@ import Button from '@material-ui/core/Button'; import AdminNavbarLinks from "components/Navbars/AdminNavbarLinks.js"; import RTLNavbarLinks from "components/Navbars/RTLNavbarLinks.js"; import ExitToAppIcon from '@material-ui/icons/ExitToApp'; +import UpdateIcon from '@material-ui/icons/Update'; import styles from "assets/jss/material-dashboard-react/components/sidebarStyle.js"; +import configApiCall from "api.js"; import auth from "auth"; +import { api_path_get_start_update } from "globalUrls"; +import axios from "axios"; +import Snackbar from "@material-ui/core/Snackbar/Snackbar"; const useStyles = makeStyles(styles); export default function Sidebar(props) { const classes = useStyles(); + const mainPanel = React.createRef(); + const [open, setOpen] = React.useState(false); + const [updating, setUpdating] = React.useState(false); + const [message, setMessage] = React.useState(false) const history = useHistory(); const handleCloseProfile = () => { @@ -36,6 +45,22 @@ export default function Sidebar(props) { e.preventDefault(); history.push(layout+path.substring(0, 2)) }; + + const handleUpdate = () => { + setMessage("Updating JAMS, shutting down shortly...") + axios(configApiCall(api_path_get_start_update, 'POST', null, null)).then(()=>{ + setUpdating(true) + }).catch((error) => { + setMessage("Error while attempting to update JAMS: " + error) + }) + } + + React.useEffect(() => { + + if(auth.isUpdateAvailable()) + setOpen(true) + + }, [mainPanel]); // verifies if routeName is the one active (in browser input) function activeRoute(routeName) { @@ -61,46 +86,78 @@ export default function Sidebar(props) { [" " + classes.whiteFont]: activeRoute(prop.layout + prop.path) }); return ( - <NavLink - to={prop.layout + prop.path} - className={activePro + classes.item} - activeClassName="active" - key={key} - > - <ListItem button className={classes.itemLink + listItemClasses} onClick={e => handleListItemClick(e, prop.layout, prop.path)}> - {typeof prop.icon === "string" ? ( - <Icon - className={classNames(classes.itemIcon, whiteFontClasses, { - [classes.itemIconRTL]: props.rtlActive - })} - > - {prop.icon} - </Icon> - ) : ( - <prop.icon - className={classNames(classes.itemIcon, whiteFontClasses, { - [classes.itemIconRTL]: props.rtlActive + <div className={classes.wrapper}> + {updating &&<Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} + open={open} + message={message} + key={'bottomright'} + > + </Snackbar>} + + + <NavLink + to={prop.layout + prop.path} + className={activePro + classes.item} + activeClassName="active" + key={key} + > + <ListItem button className={classes.itemLink + listItemClasses} onClick={e => handleListItemClick(e, prop.layout, prop.path)}> + {typeof prop.icon === "string" ? ( + <Icon + className={classNames(classes.itemIcon, whiteFontClasses, { + [classes.itemIconRTL]: props.rtlActive + })} + > + {prop.icon} + </Icon> + ) : ( + <prop.icon + className={classNames(classes.itemIcon, whiteFontClasses, { + [classes.itemIconRTL]: props.rtlActive + })} + /> + )} + <ListItemText + primary={props.rtlActive ? prop.rtlName : prop.name} + className={classNames(classes.itemText, whiteFontClasses, { + [classes.itemTextRTL]: props.rtlActive })} + disableTypography={true} /> - )} - <ListItemText - primary={props.rtlActive ? prop.rtlName : prop.name} - className={classNames(classes.itemText, whiteFontClasses, { - [classes.itemTextRTL]: props.rtlActive - })} - disableTypography={true} - /> - </ListItem> - </NavLink> + </ListItem> + </NavLink> + </div> ); })} - <ListItem button className={classes.itemLink} onClick={handleCloseProfile}> + + {open && + <ListItem button className={classes.itemLink} onClick={handleUpdate}> + <Icon + className={classNames(classes.itemIcon, { + [classes.itemIconRTL]: false + })} + > + <UpdateIcon /> + </Icon> + <ListItemText + primary="Update now" + className={classNames(classes.itemText, { + [classes.itemTextRTL]: false + })} + disableTypography={true} + /> + </ListItem> + } + + +<ListItem button className={classes.itemLink} onClick={handleCloseProfile}> <Icon className={classNames(classes.itemIcon, { [classes.itemIconRTL]: false })} > - <ExitToAppIcon /> + <ExitToAppIcon /> </Icon> <ListItemText primary="Logout" diff --git a/jams-react-client/src/index.js b/jams-react-client/src/index.js index b8c9d5b5fbf53ad622ecddd7e2684b6cd2d597f8..805cc2251ec7ef3c7c06bf46b6c1dd14f165cbce 100644 --- a/jams-react-client/src/index.js +++ b/jams-react-client/src/index.js @@ -33,18 +33,18 @@ import "assets/css/material-dashboard-react.css?v=1.9.0"; const hist = createBrowserHistory(); auth.isServerInstalled(() => { auth.checkAdminAccountStatus(() => { - auth.checkLastKnowStep(() => { - ReactDOM.render( - <Router history={hist}> - <Switch> - <ConfiguredRoute path="/signin" component={SignIn} /> - <ProtectedRoute path="/admin" component={Admin} /> - {/*<Route path="/rtl" component={RTL} />*/} - <Redirect from="/" to="/signin" /> - </Switch> - </Router>, - document.getElementById("root") - ); + auth.checkLastKnownStep(() => { + ReactDOM.render( + <Router history={hist}> + <Switch> + <ConfiguredRoute path="/signin" component={SignIn} /> + <ProtectedRoute path="/admin" component={Admin} /> + {/*<Route path="/rtl" component={RTL} />*/} + <Redirect from="/" to="/signin" /> + </Switch> + </Router>, + document.getElementById("root") + );; }); }); }); diff --git a/jams-react-client/src/layouts/Admin.js b/jams-react-client/src/layouts/Admin.js index ecc4883b17126727ae25a8bb8755ec1ecd03a151..132e6c1ad1b50123fceb5bd0e7cd066b4892feac 100644 --- a/jams-react-client/src/layouts/Admin.js +++ b/jams-react-client/src/layouts/Admin.js @@ -9,7 +9,7 @@ import { makeStyles } from "@material-ui/core/styles"; import Navbar from "components/Navbars/Navbar.js"; import Footer from "components/Footer/Footer.js"; import Sidebar from "components/Sidebar/Sidebar.js"; -import FixedPlugin from "components/FixedPlugin/FixedPlugin.js"; +import Snackbar from '@material-ui/core/Snackbar'; import routes from "routes.js"; @@ -18,6 +18,12 @@ import styles from "assets/jss/material-dashboard-react/layouts/adminStyle.js"; import bgImage from "assets/img/sidebar-2.jpg"; import logo from "assets/img/logo-jams.svg"; +import auth from 'auth' +import ReactDOM from "react-dom"; +import {ConfiguredRoute} from "../configured.route"; +import SignIn from "./SignIn"; +import {ProtectedRoute} from "../protected.route"; + let ps; const switchRoutes = ( @@ -55,10 +61,13 @@ export default function Admin({ ...rest }) { const [color, setColor] = React.useState("blue"); const [fixedClasses, setFixedClasses] = React.useState("dropdown"); const [mobileOpen, setMobileOpen] = React.useState(false); + const [open, setOpen] = React.useState(false); + const [message, setMessage] = React.useState(false) const [signedUp, setSignedUp] = React.useState(false); const [signedIn, setSignedIn] = React.useState(false); const [token, setToken] = React.useState(false); + const [severity, setSeverity] = React.useState("success") @@ -89,6 +98,15 @@ export default function Admin({ ...rest }) { // initialize and destroy the PerfectScrollbar plugin React.useEffect(() => { + auth.checkForUpdates(() => { + auth.getUpdates(() => { + if(auth.isUpdateAvailable()) { + setOpen(true) + setMessage("An update is available for JAMS.") + } + }); + }); + if (navigator.platform.indexOf("Win") > -1) { ps = new PerfectScrollbar(mainPanel.current, { suppressScrollX: true, @@ -118,6 +136,7 @@ export default function Admin({ ...rest }) { color={color} {...rest} /> + <div className={classes.mainPanel} ref={mainPanel}> <Navbar routes={routes} diff --git a/jams-react-client/src/layouts/SignIn.js b/jams-react-client/src/layouts/SignIn.js index d9d9447ac27c31ff5fe62a4b4ba5bcf25a7e0a6e..8d1166e725e3cabb21b0b0d4f9f36e04e26a4d32 100644 --- a/jams-react-client/src/layouts/SignIn.js +++ b/jams-react-client/src/layouts/SignIn.js @@ -88,7 +88,7 @@ export default function SignIn(props) { } auth.login(jsonData, () => { if(auth.authenticated && auth.access_token !== ""){ - auth.checkLastKnowStep(() => { + auth.checkLastKnownStep(() => { auth.checkDirectoryType(() => { if(auth.isInstalled){ history.push('/admin/users');