diff --git a/resources/database/data/db/.gitkeep b/resources/database/data/db/.gitkeep old mode 100644 new mode 100755 diff --git a/src/App.js b/src/App.js index 7a0fbe36b267e6836c3e14951b2038ea2daf3189..2f0cb6503663ee440a56def740e42eb44faf20d5 100644 --- a/src/App.js +++ b/src/App.js @@ -13,6 +13,7 @@ import Visualization from "./components/Visualization/Visualization"; import Comparisons from "./components/Comparisons/Comparisons"; import Comparison from "./components/Comparisons/Comparison"; import Profile from "./pages/Profile/Profile"; +import EditProfile from "./components/EditProfile/EditProfile"; import RunBoard from "./components/RunBoard/RunBoard"; import NotFound from "./pages/NotFound"; @@ -28,8 +29,8 @@ function App() { <Route exact path="/login" component={Login} /> <PrivateRoute exact path="/newrun" component={RunBoard} /> <PrivateRoute exact path="/comparison" component={Comparisons} /> - <PrivateRoute exact path="/profile" component={Profile} /> - + <PrivateRoute exact path="/machines" component={Profile} /> + <PrivateRoute exact path="/profile" component={EditProfile} /> <PrivateRoute exact path="/newcomparison" diff --git a/src/backend/routes/api/userController.js b/src/backend/routes/api/userController.js index 6ca00541d33f89e084f52eca44b7663832c71eda..90ccdc0476e4a9c30433b5d771cc3b8b1d75f89f 100644 --- a/src/backend/routes/api/userController.js +++ b/src/backend/routes/api/userController.js @@ -18,6 +18,7 @@ const User = require("../../models/User"); // @desc Register user // @access Public router.post("/register", (req, res) => { + console.log(req.body); // Form validation const { errors, isValid } = validateRegisterInput(req.body); // Check validation @@ -191,23 +192,85 @@ router.get("/:id", function (req, res) { // // } // // ); // }); -router.put("/:id", (req, res, next) => { +router.put("/:id", (req, res) => { + console.log(req.body); + console.log(req.params); + const { errors, isValid } = validateRegisterInput(req.body); + // Check validation + if (!isValid) { + return res.status(400).json(errors); + } + let newPassword = []; const updatedUser = new User({ _id: req.params.id, name: req.body.name, email: req.body.email, machines: req.body.machines, }); - User.updateOne({ _id: req.params.id }, updatedUser) - .then(() => { - res.status(201).json({ - message: "Thing updated successfully!", - }); - }) - .catch((error) => { - res.status(400).json({ - error: error, - }); + User.findOne({ _id: req.params.id }).then((user) => { + // Check if user exists + if (!user) { + return res + .status(404) + .json({ namenotfound: "Account name not found" }); + } + // Check password + bcrypt.compare(req.body.oldpass, user.password).then((isMatch) => { + if (isMatch) { + bcrypt.genSalt(10, (err, salt) => { + bcrypt.hash(req.body.password, salt, (err, hash) => { + if (err) throw err; + newPassword = hash; + console.log(newPassword); + User.findByIdAndUpdate( + req.params.id, + { + $set: { + password: newPassword, + email: req.body.email, + }, + }, + { + safe: true, + useFindAndModify: false, + }, + function (err, model) { + console.log(err); + } + ); + }); + }); + + const payload = { + id: user.id, + name: user.name, + jbPath: user.jbPath, + email: req.body.email, + runs: user.runs, + views: user.runs, + machines: user.machines, + }; + // Sign token + jwt.sign( + payload, + process.env.SECRET, + { + expiresIn: 31556926, // 1 year in seconds + }, + (err, token) => { + res.json({ + success: true, + token: "Bearer " + token, + }); + } + ); + // }); + } else { + return res + .status(400) + .json({ passwordincorrect: "Old Password incorrect" }); + } }); + }); }); module.exports = router; diff --git a/src/components/EditProfile/EditProfile.js b/src/components/EditProfile/EditProfile.js new file mode 100644 index 0000000000000000000000000000000000000000..68ba54286111debea4a65e27231bceba8834f61a --- /dev/null +++ b/src/components/EditProfile/EditProfile.js @@ -0,0 +1,9 @@ +import React from "react"; +import DashLayout from "../Tableau/DashLayout"; +import EditProfileFilling from "../EditProfileFilling/EditProfileFilling"; + +const EditProfile = () => { + return <DashLayout Filling={EditProfileFilling} />; +}; + +export default EditProfile; diff --git a/src/components/EditProfileFilling/EditProfileFilling.js b/src/components/EditProfileFilling/EditProfileFilling.js new file mode 100644 index 0000000000000000000000000000000000000000..cba7de0ea8c53e7dec93f4cd20fca02427f0914f --- /dev/null +++ b/src/components/EditProfileFilling/EditProfileFilling.js @@ -0,0 +1,197 @@ +import React, { useReducer, useEffect } from "react"; +import { useHistory } from "react-router-dom"; +import { Link } from "react-router-dom"; +import { UPDATE_FORM } from "../../actions/types"; +import classnames from "classnames"; +import { formReducer } from "../../reducers/formReducer"; +import { useAuth } from "../../hooks/useAuth"; +import Avatar from "@material-ui/core/Avatar"; +import Button from "@material-ui/core/Button"; +import CssBaseline from "@material-ui/core/CssBaseline"; +import TextField from "@material-ui/core/TextField"; +import FormControlLabel from "@material-ui/core/FormControlLabel"; +import Checkbox from "@material-ui/core/Checkbox"; +import Grid from "@material-ui/core/Grid"; +import Box from "@material-ui/core/Box"; +import LockOutlinedIcon from "@material-ui/icons/LockOutlined"; +import Typography from "@material-ui/core/Typography"; +import { makeStyles } from "@material-ui/core/styles"; +import Container from "@material-ui/core/Container"; + +function Copyright() { + return ( + <Typography variant="body2" color="textSecondary" align="center"> + {"Copyright © "} + <Link color="inherit" to="https://material-ui.com/"> + Your Website + </Link>{" "} + {new Date().getFullYear()} + {"."} + </Typography> + ); +} +const useStyles = makeStyles((theme) => ({ + paper: { + marginTop: theme.spacing(8), + display: "flex", + flexDirection: "column", + alignItems: "center", + }, + avatar: { + margin: theme.spacing(1), + backgroundColor: theme.palette.secondary.main, + }, + form: { + width: "100%", // Fix IE 11 issue. + marginTop: theme.spacing(3), + }, + submit: { + margin: theme.spacing(3, 0, 2), + }, +})); + +const EditProfileFilling = () => { + const classes = useStyles(); + + const auth = useAuth(); + const initialState = { + name: auth.user.user.name, + email: auth.user.user.email, + oldpass: "", + password: "", + password2: "", + errors: {}, + }; + console.log(auth.user.user.name); + const [state, dispatch] = useReducer(formReducer, initialState); + const history = useHistory(); + + const onChange = (e) => { + e.persist(); + dispatch({ + type: UPDATE_FORM, + payload: { ...state, [e.target.id]: e.target.value }, + }); + }; + const onSubmit = (e) => { + e.preventDefault(); + console.log("updating info"); + auth.handleEditProfile(state, dispatch, history); + }; + const { errors } = state; + + return ( + <Container component="main" maxWidth="md"> + <CssBaseline /> + <div className={classes.paper}> + <Avatar className={classes.avatar}> + <LockOutlinedIcon /> + </Avatar> + + <Typography component="h1" variant="h5"> + Update {auth.user.user.name} Account Information + </Typography> + + <form className={classes.form} noValidate onSubmit={onSubmit}> + <Grid container spacing={2}> + <Grid item xs={12}> + <TextField + onChange={onChange} + value={state.email} + error={errors.email === ""} + helperText={errors.email === "" ? "Empty!" : errors.email} + className={classnames("", { + invalid: errors.email, + })} + id="email" + type="email" + variant="outlined" + required + fullWidth + label="Email Address" + name="email" + autoComplete="email" + /> + </Grid> + <Grid item xs={12} sm={12}> + <TextField + onChange={onChange} + value={state.oldpass} + error={errors.passwordincorrect === ""} + helperText={ + errors.passwordincorrect === "" + ? "Empty!" + : errors.passwordincorrect + } + className={classnames("", { + invalid: errors.passwordincorrect, + })} + id="oldpass" + type="password" + variant="outlined" + required + fullWidth + name="oldpass" + label="Old Password" + type="password" + autoComplete="current-password" + /> + </Grid> + <Grid item xs={12} sm={6}> + <TextField + onChange={onChange} + value={state.password} + error={errors.password === ""} + helperText={errors.password === "" ? "Empty!" : errors.password} + className={classnames("", { + invalid: errors.password, + })} + id="password" + type="password" + variant="outlined" + required + fullWidth + name="password" + label="New Password" + autoComplete="current-password" + /> + </Grid> + <Grid item xs={12} sm={6}> + <TextField + onChange={onChange} + value={state.password2} + error={errors.password2 === ""} + helperText={ + errors.password2 === "" ? "Empty!" : errors.password2 + } + className={classnames("", { + invalid: errors.password2, + })} + id="password2" + type="password" + variant="outlined" + required + fullWidth + name="password2" + label="Confirm New Password" + type="password" + autoComplete="current-password" + /> + </Grid> + </Grid> + <Button + type="submit" + fullWidth + variant="contained" + color="primary" + className={classes.submit} + > + Update{" "} + </Button> + </form> + </div> + </Container> + ); +}; + +export default EditProfileFilling; diff --git a/src/components/Login/Login.js b/src/components/Login/Login.js index e62a80b6380b04eabc2742a01f8f6eb1efd63489..5c8e9446b1ec9b9c9b34f11fb8feecaac39b3d7d 100644 --- a/src/components/Login/Login.js +++ b/src/components/Login/Login.js @@ -85,7 +85,7 @@ const Login = () => { const { errors } = state; return ( - <Container component="main" maxWidth="xs"> + <Container component="main" maxWidth="md"> <CssBaseline /> <div className={classes.paper}> <Avatar className={classes.avatar}> diff --git a/src/components/Navbar/Navbar.js b/src/components/Navbar/Navbar.js deleted file mode 100644 index e8ebecb6ce0e8f2d6b0798f856a1bf5b9c5cf77f..0000000000000000000000000000000000000000 --- a/src/components/Navbar/Navbar.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; -import { Link } from "react-router-dom"; -import { useAuth } from "../../hooks/useAuth"; -const Navbar = () => { - const auth = useAuth(); - return ( - <div> - <nav - className={`${ - auth.user.isAuthenticated - ? "#00acc1 cyan darken-1" - : "#00897b teal darken-1" - }`} - > - <div className="nav-wrapper container"> - <Link to="/" className="brand-logo"> - BiSSProP - </Link> - <ul id="nav-mobile" className="right hide-on-med-and-down"> - <li> - <Link to="/docs">Docs</Link> - </li> - <li> - <Link to="/examples">Examples</Link> - </li> - {auth.user.isAuthenticated ? ( - <> - <li> - <Link to="/newrun">Runs</Link> - </li> - <li> - <Link onClick={auth.signout}>Logout</Link> - </li> - </> - ) : ( - <li> - <Link to="/login">Login</Link> - </li> - )} - </ul> - </div> - </nav> - </div> - ); -}; - -export default Navbar; diff --git a/src/components/Register/Register.js b/src/components/Register/Register.js index 8ab15dfaba86bb25d276b1ea563a072598a3931f..0dff51797a376b827c5d1f6c54b39a1bf572ba11 100644 --- a/src/components/Register/Register.js +++ b/src/components/Register/Register.js @@ -80,7 +80,7 @@ const Register = () => { const { errors } = state; return ( - <Container component="main" maxWidth="xs"> + <Container component="main" maxWidth="md"> <CssBaseline /> <div className={classes.paper}> <Avatar className={classes.avatar}> diff --git a/src/components/Table/Table.js b/src/components/Table/Table.js index cce1d8baa9ed03e31b78929b1dbc2725533507b5..19646a97a1ea75b9f72d8758293f2026ee2e2c73 100644 --- a/src/components/Table/Table.js +++ b/src/components/Table/Table.js @@ -706,17 +706,18 @@ export default function InteractiveList() { : true } key={sample._id} - onClick={() => { - if (row.remote) { - // handleRemoteFiles(row, sample); - downloadFiles(row, sample, tracks); - } else { - console.log(sample); - const path = `${row.outdir}/results/${sample.samplePath}/${sample.samplePath}-multiqc_report.html`; - console.log(path); - createBrowserWindow(path); - } - }} + onClick={ + row.remote + ? () => + // handleRemoteFiles(row, sample); + downloadFiles(row, sample, tracks) + : () => { + console.log(sample); + const path = `${row.outdir}/results/${sample.samplePath}/${sample.samplePath}-multiqc_report.html`; + console.log(path); + createBrowserWindow(path); + } + } > <ListItemAvatar> <Avatar> diff --git a/src/components/Tableau/DashLayout.js b/src/components/Tableau/DashLayout.js index b8fe628a03d05cfbc158396f1dfbfa87f499a5b2..b415e9cd2f66c12d8437e71df2b437c39f97b95f 100644 --- a/src/components/Tableau/DashLayout.js +++ b/src/components/Tableau/DashLayout.js @@ -19,7 +19,12 @@ import SettingsIcon from "@material-ui/icons/Settings"; import LibraryBooksIcon from "@material-ui/icons/LibraryBooks"; import { useConfig } from "../../hooks/useConfig"; import Alert from "@material-ui/lab/Alert"; - +import Menu from "@material-ui/core/Menu"; +import MenuItem from "@material-ui/core/MenuItem"; +import Fade from "@material-ui/core/Fade"; +import ListItemIcon from "@material-ui/core/ListItemIcon"; +import AccountCircleIcon from "@material-ui/icons/AccountCircle"; +import EditIcon from "@material-ui/icons/Edit"; const drawerWidth = 240; const useStyles = makeStyles((theme) => ({ @@ -131,7 +136,16 @@ const DashLayout = ({ Filling }) => { const handleDrawerClose = () => { setOpenDrawer(false); }; + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; return ( <div className={classes.root}> <CssBaseline /> @@ -159,24 +173,12 @@ const DashLayout = ({ Filling }) => { noWrap className={classes.title} > - {auth.user.user.name} Dashboard + Dashboard </Typography> {auth.user.isAuthenticated ? ( <> - <IconButton component={Link} to="/profile" color="inherit"> - <Typography - component="h1" - variant="h6" - color="inherit" - noWrap - className={classes.title} - > - Manage Remote Machines - </Typography>{" "} - <SettingsIcon /> - </IconButton> - <IconButton component={Link} to="/profile" color="inherit"> + {/* <IconButton component={Link} to="/docs" color="inherit"> <Typography component="h1" variant="h6" @@ -188,8 +190,9 @@ const DashLayout = ({ Filling }) => { </Typography> <LibraryBooksIcon /> - </IconButton> - <IconButton onClick={auth.signout} color="inherit"> + </IconButton> */} + + <IconButton onClick={handleClick} color="inherit"> <Typography component="h1" variant="h6" @@ -197,10 +200,41 @@ const DashLayout = ({ Filling }) => { noWrap className={classes.title} > - Logout + Account </Typography> - <ExitToAppIcon /> + <AccountCircleIcon /> </IconButton> + + <Menu + id="fade-menu" + anchorEl={anchorEl} + keepMounted + open={open} + onClose={handleClose} + TransitionComponent={Fade} + > + <MenuItem component={Link} to="/profile"> + <ListItemIcon> + <EditIcon /> + </ListItemIcon> + <Typography variant="inherit">Edit Profile </Typography> + </MenuItem> + <MenuItem component={Link} to="/machines"> + <ListItemIcon> + <SettingsIcon /> + </ListItemIcon> + <Typography variant="inherit"> + Manage Remote Machines + </Typography> + </MenuItem> + + <MenuItem onClick={auth.signout}> + <ListItemIcon> + <ExitToAppIcon /> + </ListItemIcon> + <Typography variant="inherit">Logout </Typography> + </MenuItem> + </Menu> </> ) : ( "" diff --git a/src/components/Visualization/VisualizationFill.js b/src/components/Visualization/VisualizationFill.js index a2f50d07bf89f8fc3e5011c8fde90f1c2780cc3a..edbd1a477161f57dbb723fa5d2a6c8b0673218df 100644 --- a/src/components/Visualization/VisualizationFill.js +++ b/src/components/Visualization/VisualizationFill.js @@ -188,16 +188,13 @@ export default function VisualizationFill() { return false; } }; - const downloadFiles = (row, sample, tracks) => { + const downloadFiles = (row, tracks) => { let sftp = new Client(); console.log(tracks); - console.log(row, sample); console.log("download files"); console.log(homedir); - console.log(row.machine); - console.log(sample); if (!fs.existsSync(bisepsTemp)) { fs.mkdirSync(bisepsTemp); @@ -600,7 +597,7 @@ export default function VisualizationFill() { edge="end" disabled={sampleExist} aria-label="files" - onClick={() => downloadFiles(row, sample, tracks)} + onClick={() => downloadFiles(row, tracks)} > <GetAppIcon style={{ @@ -661,6 +658,7 @@ export default function VisualizationFill() { path.basename(bed) ); console.log(checkedComp); + const tracks = [bed, bedtbi]; return ( <ListItem key={`${comparison._id}-${idx}-${context}`} @@ -735,9 +733,7 @@ export default function VisualizationFill() { <IconButton edge="end" aria-label="files" - onClick={() => - downloadFiles(row, sample, tracks) - } + onClick={() => downloadFiles(row, tracks)} > <GetAppIcon /> </IconButton> diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js index 9981ceed3da2c3f2ab46a223ab33e3d896e74c50..6f0d9760e6f6d63e13f34f2e938cdb80f75f70a6 100644 --- a/src/hooks/useAuth.js +++ b/src/hooks/useAuth.js @@ -107,6 +107,7 @@ const useProvideAuth = () => { req.end(); }; const handleEditProfile = async (userData, dispatch, history) => { + console.log(userData); const options = { method: "PUT", path: `http://localhost/api/users/${user.user.id}`, @@ -128,14 +129,9 @@ const useProvideAuth = () => { res.on("end", function () { const body = Buffer.concat(chunks).toString(); const jsbody = JSON.parse(body); - if (!jsbody.token) { - dispatch({ - type: GET_ERRORS, - payload: jsbody, - }); - console.log(jsbody); - history.push("/alignment"); - } else { + console.log(jsbody); + + if ("success" in jsbody) { // Save to localStorage // Set token to localStorage const { token } = jsbody; @@ -148,7 +144,13 @@ const useProvideAuth = () => { type: SET_CURRENT_USER, payload: decoded, }); + console.log("successful put request"); history.push("/alignment"); + } else { + dispatch({ + type: GET_ERRORS, + payload: jsbody, + }); } }); });