diff --git a/asreview/webapp/public/index.html b/asreview/webapp/public/index.html index 02adf2fac..7aef04546 100644 --- a/asreview/webapp/public/index.html +++ b/asreview/webapp/public/index.html @@ -1,4 +1,4 @@ - + diff --git a/asreview/webapp/src/App.js b/asreview/webapp/src/App.js index be3d27c44..94eb7f37c 100644 --- a/asreview/webapp/src/App.js +++ b/asreview/webapp/src/App.js @@ -1,10 +1,10 @@ import React from "react"; import { QueryClient, QueryClientProvider } from "react-query"; -import { useSelector, useDispatch } from 'react-redux'; +import { useSelector, useDispatch } from "react-redux"; import { Routes, Route } from "react-router-dom"; import "typeface-roboto"; import { Box, CssBaseline, createTheme, useMediaQuery } from "@mui/material"; -import CircularProgress from '@mui/material/CircularProgress'; +import CircularProgress from "@mui/material/CircularProgress"; import { ThemeProvider, StyledEngineProvider } from "@mui/material/styles"; import "./App.css"; @@ -22,7 +22,7 @@ import { SettingsDialog, SignIn, SignInOAuthCallback, - SignUpForm + SignUpForm, } from "./Components"; import { HomePage } from "./HomeComponents"; import { ProjectPage } from "./ProjectComponents"; @@ -37,7 +37,7 @@ import { useToggle } from "./hooks/useToggle"; // Ensure that on localhost we use 'localhost' instead of '127.0.0.1' const currentDomain = window.location.href; if (currentDomain.includes("127.0.0.1")) { - let newDomain = currentDomain.replace("127.0.0.1", "localhost") + let newDomain = currentDomain.replace("127.0.0.1", "localhost"); window.location.replace(newDomain); } @@ -45,11 +45,13 @@ const queryClient = new QueryClient(); const App = (props) => { // state related stuff for booting the app - const [appReady, setAppReadyState] = React.useState(false) + const [appReady, setAppReadyState] = React.useState(false); const dispatch = useDispatch(); - const authentication = useSelector(state => state.authentication); - const allowAccountCreation = useSelector(state => state.allow_account_creation); - const emailVerification = useSelector(state => state.email_verification); + const authentication = useSelector((state) => state.authentication); + const allowAccountCreation = useSelector( + (state) => state.allow_account_creation, + ); + const emailVerification = useSelector((state) => state.email_verification); // Dialog state const [onSettings, toggleSettings] = useToggle(); @@ -75,74 +77,71 @@ const App = (props) => { // Navigation drawer state const [onNavDrawer, toggleNavDrawer] = useToggle(mobileScreen ? false : true); - // This effect does a boot request to gather information + // This effect does a boot request to gather information // from the backend React.useEffect(() => { BaseAPI.boot({}) - .then(response => { - dispatch(setBootData(response)); - // set oauth services if there are any - if (response?.oauth) { - dispatch(setOAuthServices(response.oauth)); - } - }) - .catch(err => { console.log(err); }); - }, [dispatch]) - - // This effect makes sure we handle routing at the + .then((response) => { + dispatch(setBootData(response)); + // set oauth services if there are any + if (response?.oauth) { + dispatch(setOAuthServices(response.oauth)); + } + }) + .catch((err) => { + console.log(err); + }); + }, [dispatch]); + + // This effect makes sure we handle routing at the // moment we know for sure if there is, or isn't authentication. React.useEffect(() => { if ( - authentication !== undefined && - allowAccountCreation !== undefined && - emailVerification !== undefined - ) { - setAppReadyState(true); + authentication !== undefined && + allowAccountCreation !== undefined && + emailVerification !== undefined + ) { + setAppReadyState(true); } else { - setAppReadyState(false); + setAppReadyState(false); } - }, [authentication, allowAccountCreation, emailVerification]) - + }, [authentication, allowAccountCreation, emailVerification]); const render_sign_routes = () => { return ( <> - { allowAccountCreation && + {allowAccountCreation && ( } /> - } + )} } + path="/signin" + element={} /> } + path="/oauth_callback" + element={} /> } /> - } - /> + } /> } /> - { - emailVerification && + {emailVerification && ( } /> - } + )} ); - } + }; const render_routes = () => { return ( @@ -191,7 +190,7 @@ const App = (props) => { /> - ) + ); }; return ( @@ -201,27 +200,26 @@ const App = (props) => {
- { (appReady === false) && + {appReady === false && ( - + - } - { (appReady === true) && (authentication === false) && - {render_routes()} } + )} + {appReady === true && authentication === false && ( + {render_routes()} + )} - { (appReady === true) && (authentication === true) && + {appReady === true && authentication === true && ( - { render_sign_routes() } - }> - { render_routes() } - + {render_sign_routes()} + }>{render_routes()} - } + )}
{/* Dialogs */} @@ -239,7 +237,6 @@ const App = (props) => { toggleUndoEnabled={toggleUndoEnabled} /> - diff --git a/asreview/webapp/src/Components/AppBarWithinDialog.js b/asreview/webapp/src/Components/AppBarWithinDialog.js index 88e1ad48a..9dec5063a 100644 --- a/asreview/webapp/src/Components/AppBarWithinDialog.js +++ b/asreview/webapp/src/Components/AppBarWithinDialog.js @@ -134,7 +134,7 @@ const AppBarWithinDialog = React.forwardRef( startIconIsClose, title, }, - ref + ref, ) => { return ( @@ -241,7 +241,7 @@ const AppBarWithinDialog = React.forwardRef( ); - } + }, ); AppBarWithinDialog.propTypes = { diff --git a/asreview/webapp/src/Components/ConfirmAccount.js b/asreview/webapp/src/Components/ConfirmAccount.js index 484899672..3aace3043 100644 --- a/asreview/webapp/src/Components/ConfirmAccount.js +++ b/asreview/webapp/src/Components/ConfirmAccount.js @@ -8,33 +8,32 @@ const ConfirmAccount = () => { const [searchParams] = useSearchParams(); const [errorMessage, setErrorMessage] = React.useState(false); - // This effect does a boot request to gather information + // This effect does a boot request to gather information // from the backend React.useEffect(() => { - let userId = searchParams.get('user_id') - let token = searchParams.get('token'); + let userId = searchParams.get("user_id"); + let token = searchParams.get("token"); console.log(userId, token); AuthAPI.confirmAccount({ userId: userId, - token: token + token: token, }) - .then(response => { - navigate('/signin') - }) - .catch(err => { - // I'd like to have a flash! - console.log(err); - setErrorMessage('Could not confirm account: ' + err.message); - }); - }, [navigate, searchParams]) + .then((response) => { + navigate("/signin"); + }) + .catch((err) => { + // I'd like to have a flash! + console.log(err); + setErrorMessage("Could not confirm account: " + err.message); + }); + }, [navigate, searchParams]); return (
{Boolean(errorMessage) && }
- ) - + ); }; -export default ConfirmAccount; \ No newline at end of file +export default ConfirmAccount; diff --git a/asreview/webapp/src/Components/DrawerItem.js b/asreview/webapp/src/Components/DrawerItem.js index f80a53414..f19a23ce8 100644 --- a/asreview/webapp/src/Components/DrawerItem.js +++ b/asreview/webapp/src/Components/DrawerItem.js @@ -20,7 +20,7 @@ import { Download, Edit, History, - PeopleAlt + PeopleAlt, } from "@mui/icons-material"; const PREFIX = "DrawerItem"; diff --git a/asreview/webapp/src/Components/DrawerItemContainer.js b/asreview/webapp/src/Components/DrawerItemContainer.js index d5a28c7ac..41ac1697e 100644 --- a/asreview/webapp/src/Components/DrawerItemContainer.js +++ b/asreview/webapp/src/Components/DrawerItemContainer.js @@ -24,7 +24,12 @@ import { Diversity3, Help, Payment, Settings } from "@mui/icons-material"; import { DrawerItem, ElasGame } from "../Components"; import { ProjectAPI } from "../api/index.js"; -import { communityURL, donateURL, projectModes, projectStatuses } from "../globals.js"; +import { + communityURL, + donateURL, + projectModes, + projectStatuses, +} from "../globals.js"; import Finished from "../images/ElasHoldingSIGNS_Finished.svg"; import InReview from "../images/ElasHoldingSIGNS_InReview.svg"; import SetUp from "../images/ElasHoldingSIGNS_SetUp.svg"; @@ -91,8 +96,8 @@ const StyledList = styled(List)(({ theme }) => ({ const DrawerItemContainer = (props) => { const { project_id } = useParams(); - const authentication = useSelector(state => state.authentication); - const allowTeams = useSelector(state => state.allow_teams); + const authentication = useSelector((state) => state.authentication); + const allowTeams = useSelector((state) => state.allow_teams); const queryClient = useQueryClient(); const isFetchingInfo = useIsFetching("fetchInfo"); @@ -102,7 +107,7 @@ const DrawerItemContainer = (props) => { const fetchProjectInfo = React.useCallback(async () => { const data = await queryClient.fetchQuery( ["fetchInfo", { project_id }], - ProjectAPI.fetchInfo + ProjectAPI.fetchInfo, ); setProjectInfo(data); }, [project_id, queryClient]); @@ -154,11 +159,14 @@ const DrawerItemContainer = (props) => { path: "history", label: "History", }, - ...(authentication && allowTeams ? - [{ - path: "team", - label: "Team", - }] : []), + ...(authentication && allowTeams + ? [ + { + path: "team", + label: "Team", + }, + ] + : []), { path: "export", label: "Export", diff --git a/asreview/webapp/src/Components/ElasGame.js b/asreview/webapp/src/Components/ElasGame.js index 546dcf6cf..b90c993a1 100644 --- a/asreview/webapp/src/Components/ElasGame.js +++ b/asreview/webapp/src/Components/ElasGame.js @@ -71,13 +71,13 @@ const ElasGame = (props) => { if (paperSelected?.length < 2) { setPaperSelected((paperSelected) => paperSelected?.concat(image)); setPaperSelectedIds((paperSelectedIds) => - paperSelectedIds?.concat(index) + paperSelectedIds?.concat(index), ); if (paperSelected?.length === 1) { if (paperSelected[0] === image) { setOpenCards((openCards) => - openCards?.concat([paperSelected[0], image]) + openCards?.concat([paperSelected[0], image]), ); } setTimeout(() => { diff --git a/asreview/webapp/src/Components/ForgotPassword.js b/asreview/webapp/src/Components/ForgotPassword.js index faefb6afc..23420387b 100644 --- a/asreview/webapp/src/Components/ForgotPassword.js +++ b/asreview/webapp/src/Components/ForgotPassword.js @@ -1,5 +1,5 @@ import * as React from "react"; -import { useSelector } from 'react-redux'; +import { useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import { useMutation, useQueryClient } from "react-query"; import { @@ -62,10 +62,9 @@ const Root = styled("div")(({ theme }) => ({ }, })); - const ForgotPassword = () => { - const emailConfig = useSelector(state => state.email_config) || false; - const [email, setEmail] = React.useState(''); + const emailConfig = useSelector((state) => state.email_config) || false; + const [email, setEmail] = React.useState(""); const [successMessage, setSuccessMessage] = React.useState(false); const queryClient = useQueryClient(); const navigate = useNavigate(); @@ -78,28 +77,28 @@ const ForgotPassword = () => { queryClient.resetQueries("refresh"); }, onSuccess: (data) => { - setEmail(''); - setSuccessMessage(data.message) + setEmail(""); + setSuccessMessage(data.message); }, onError: (data) => { - console.error('Forgot password error', data); - } - } + console.error("Forgot password error", data); + }, + }, ); const handleSubmit = (event) => { event.preventDefault(); reset(); mutate({ email }); - } + }; const handleEmailChange = (event) => { setEmail(event.target.value); }; const handleSignin = (event) => { - navigate('/signin') - } + navigate("/signin"); + }; return ( @@ -115,15 +114,18 @@ const ForgotPassword = () => { alt="ASReview LAB" /> Forgot your password? - { emailConfig && + {emailConfig && (

- Enter your email address, click on the submit button and an email will be sent to you. - Check your spam or bulk folder if you don't get an email. + Enter your email address, click on the submit button and + an email will be sent to you. Check your spam or bulk + folder if you don't get an email.

- } - { !emailConfig &&

Contact your ASReview-app administrator

} + )} + {!emailConfig && ( +

Contact your ASReview-app administrator

+ )} - { emailConfig && + {emailConfig && ( <> { > Submit - - } + )} @@ -162,4 +167,4 @@ const ForgotPassword = () => { ); }; -export default ForgotPassword; \ No newline at end of file +export default ForgotPassword; diff --git a/asreview/webapp/src/Components/Header.js b/asreview/webapp/src/Components/Header.js index 523335750..26fdd2ada 100644 --- a/asreview/webapp/src/Components/Header.js +++ b/asreview/webapp/src/Components/Header.js @@ -46,7 +46,7 @@ const mapStateToProps = (state) => { const Header = (props) => { const navigate = useNavigate(); - const authentication = useSelector(state => state.authentication); + const authentication = useSelector((state) => state.authentication); return ( @@ -73,7 +73,9 @@ const Header = (props) => { /> - { authentication === true && } + {authentication === true && ( + + )} diff --git a/asreview/webapp/src/Components/HelpDialog.js b/asreview/webapp/src/Components/HelpDialog.js index 2128581cc..e3e8a5e8f 100644 --- a/asreview/webapp/src/Components/HelpDialog.js +++ b/asreview/webapp/src/Components/HelpDialog.js @@ -107,7 +107,7 @@ const HelpDialog = (props) => { { enabled: props.onHelpDialog, refetchOnWindowFocus: false, - } + }, ); React.useEffect(() => { diff --git a/asreview/webapp/src/Components/PersistSignIn.js b/asreview/webapp/src/Components/PersistSignIn.js index fb7ffe82a..bbcf08f5d 100644 --- a/asreview/webapp/src/Components/PersistSignIn.js +++ b/asreview/webapp/src/Components/PersistSignIn.js @@ -6,11 +6,10 @@ import useAuth from "../hooks/useAuth"; import { AuthAPI } from "../api"; const PersistSignIn = () => { - const location = useLocation(); const { auth, setAuth } = useAuth(); const [isLoading, setIsLoading] = React.useState( - !auth?.logged_in ? true : false + !auth?.logged_in ? true : false, ); const { isError } = useQuery("refresh", AuthAPI.refresh, { diff --git a/asreview/webapp/src/Components/ProfilePopper.js b/asreview/webapp/src/Components/ProfilePopper.js index 2d8fe3095..2923b6a2a 100644 --- a/asreview/webapp/src/Components/ProfilePopper.js +++ b/asreview/webapp/src/Components/ProfilePopper.js @@ -19,7 +19,7 @@ import { Tooltip, Typography, } from "@mui/material"; -import MailIcon from '@mui/icons-material/Mail'; +import MailIcon from "@mui/icons-material/Mail"; import { styled } from "@mui/material/styles"; import { Logout, GroupAdd, Person } from "@mui/icons-material"; @@ -38,7 +38,7 @@ const Root = styled("div")(({ theme }) => ({})); const ProfilePopper = (props) => { const { auth, setAuth } = useAuth(); - const allowTeams = useSelector(state => state.allow_teams); + const allowTeams = useSelector((state) => state.allow_teams); const navigate = useNavigate(); const [projectInvitations, setProjectInvitations] = React.useState([]); @@ -49,18 +49,14 @@ const ProfilePopper = (props) => { const [anchorEl, setAnchorEl] = React.useState(null); const [open, setOpen] = React.useState(false); - useQuery( - ["getProjectInvitations"], - () => TeamAPI.getProjectInvitations(), - { - onSuccess: (data) => { - setProjectInvitations((data['invited_for_projects'] || [])); - }, - onError: (data) => { - console.log('error', data); - } - } - ); + useQuery(["getProjectInvitations"], () => TeamAPI.getProjectInvitations(), { + onSuccess: (data) => { + setProjectInvitations(data["invited_for_projects"] || []); + }, + onError: (data) => { + console.log("error", data); + }, + }); const { mutate } = useMutation(AuthAPI.signout, { onSuccess: () => { @@ -89,61 +85,63 @@ const ProfilePopper = (props) => { const handleProfile = () => { setOpen(false); navigate("/profile"); - } + }; const acceptanceHandler = (project) => { // Call the API to accept the invitation, if that is successful // get list of all projects of this user and refresh the projects // list, remove from Dialog TeamAPI.acceptInvitation(project.project_id) - .then(data => { - if (data.success) { - - // success, the invite was transformed into a collaboration, get all projects - ProjectAPI.fetchProjects({}) - .then(data => { - if (data.result instanceof Array) { - // refresh project list - dispatch(setMyProjects(data.result)); - // remove project from Dialog table - const newProjectList = projectInvitations.filter((p) => p.id !== project.id); - setProjectInvitations(newProjectList); - // close modal if there are no more invitations - if (newProjectList.length === 0) { - toggleAcceptanceDialog(); - } - } else { - console.log('Could not get projects list -- DB failure'); - } - }) - .catch(err => console.log('Could not pull all projects', err)); - - } else { - console.log('Could not reject invitation -- DB failure'); - } - }) - .catch(err => console.log('Could not reject invitation', err)); - } + .then((data) => { + if (data.success) { + // success, the invite was transformed into a collaboration, get all projects + ProjectAPI.fetchProjects({}) + .then((data) => { + if (data.result instanceof Array) { + // refresh project list + dispatch(setMyProjects(data.result)); + // remove project from Dialog table + const newProjectList = projectInvitations.filter( + (p) => p.id !== project.id, + ); + setProjectInvitations(newProjectList); + // close modal if there are no more invitations + if (newProjectList.length === 0) { + toggleAcceptanceDialog(); + } + } else { + console.log("Could not get projects list -- DB failure"); + } + }) + .catch((err) => console.log("Could not pull all projects", err)); + } else { + console.log("Could not reject invitation -- DB failure"); + } + }) + .catch((err) => console.log("Could not reject invitation", err)); + }; const rejectionHandler = (project) => { // call API to remove the invitation TeamAPI.rejectInvitation(project.project_id) - .then(data => { + .then((data) => { if (data.success) { - // remove project from Dialog table and close if there are + // remove project from Dialog table and close if there are // no more invitations - const newProjectList = projectInvitations.filter((p) => p.id !== project.id); + const newProjectList = projectInvitations.filter( + (p) => p.id !== project.id, + ); setProjectInvitations(newProjectList); // close modal if there are no more invitations if (newProjectList.length === 0) { toggleAcceptanceDialog(); } } else { - console.log('Could not reject invitation -- DB failure'); + console.log("Could not reject invitation -- DB failure"); } }) - .catch(err => console.log('Could not reject invitation', err)); - } + .catch((err) => console.log("Could not reject invitation", err)); + }; return ( @@ -206,8 +204,8 @@ const ProfilePopper = (props) => { Profile - - { false && allowTeams && + + {false && allowTeams && ( @@ -215,22 +213,24 @@ const ProfilePopper = (props) => { Collaboration Invites - { projectInvitations.length > 0 && - 0 && ( + - + - } + )} - } + )} @@ -240,15 +240,14 @@ const ProfilePopper = (props) => { Sign out - - { allowTeams && - { handleAcceptance={acceptanceHandler} handleRejection={rejectionHandler} /> - } - + )} ); }; diff --git a/asreview/webapp/src/Components/RequireAuth.js b/asreview/webapp/src/Components/RequireAuth.js index 802b266bf..b763ac047 100644 --- a/asreview/webapp/src/Components/RequireAuth.js +++ b/asreview/webapp/src/Components/RequireAuth.js @@ -1,7 +1,7 @@ import { useLocation, Navigate } from "react-router-dom"; import useAuth from "../hooks/useAuth"; -const RequireAuth = ({ enforce_authentication=false, children }) => { +const RequireAuth = ({ enforce_authentication = false, children }) => { const { auth } = useAuth(); const location = useLocation(); diff --git a/asreview/webapp/src/Components/ResetPassword.js b/asreview/webapp/src/Components/ResetPassword.js index 93ab6eda7..f3e8caaea 100644 --- a/asreview/webapp/src/Components/ResetPassword.js +++ b/asreview/webapp/src/Components/ResetPassword.js @@ -1,4 +1,3 @@ - import * as React from "react"; import { useSearchParams, useNavigate } from "react-router-dom"; import { useMutation, useQueryClient } from "react-query"; @@ -21,8 +20,8 @@ import { styled } from "@mui/material/styles"; import AuthAPI from "../api/AuthAPI"; import { WordmarkState } from "../globals"; import { useToggle } from "../hooks/useToggle"; -import { useFormik } from 'formik'; -import * as Yup from 'yup'; +import { useFormik } from "formik"; +import * as Yup from "yup"; const PREFIX = "SignInForm"; @@ -72,12 +71,12 @@ const SignupSchema = Yup.object().shape({ password: Yup.string() .matches( /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&#])[A-Za-z\d@$!%*?&#]{8,}$/, - 'Use 8 or more characters with a mix of letters, numbers & symbols' + "Use 8 or more characters with a mix of letters, numbers & symbols", ) - .required('Password is required'), + .required("Password is required"), confirmPassword: Yup.string() - .required('Password confirmation is required') - .oneOf([Yup.ref('password'), null], 'Passwords must match') + .required("Password confirmation is required") + .oneOf([Yup.ref("password"), null], "Passwords must match"), }); const ResetPassword = (props) => { @@ -85,11 +84,11 @@ const ResetPassword = (props) => { const [showPassword, toggleShowPassword] = useToggle(); const [searchParams] = useSearchParams(); const navigate = useNavigate(); - const [errorMessage, setErrorMessage] = React.useState('') + const [errorMessage, setErrorMessage] = React.useState(""); const initialValues = { - password: '', - confirmPassword: '' + password: "", + confirmPassword: "", }; const formik = useFormik({ @@ -101,33 +100,30 @@ const ResetPassword = (props) => { return !showPassword ? "password" : "text"; }; - const { isLoading, mutate } = useMutation( - AuthAPI.resetPassword, - { - onMutate: () => { - // clear potential error - queryClient.resetQueries("refresh"); - }, - onSuccess: (data) => { - formik.setValues(initialValues, false); - navigate('/sigin'); - }, - onError: (data) => { - setErrorMessage(data.message); - console.error('Reset password error', data); - } - } - ); + const { isLoading, mutate } = useMutation(AuthAPI.resetPassword, { + onMutate: () => { + // clear potential error + queryClient.resetQueries("refresh"); + }, + onSuccess: (data) => { + formik.setValues(initialValues, false); + navigate("/sigin"); + }, + onError: (data) => { + setErrorMessage(data.message); + console.error("Reset password error", data); + }, + }); const handleSubmit = (event) => { event.preventDefault(); - let userId = searchParams.get('user_id') - let token = searchParams.get('token'); + let userId = searchParams.get("user_id"); + let token = searchParams.get("token"); let password = formik.values.password; console.log(userId, token, password); mutate({ userId, token, password }); //reset(); - } + }; return ( @@ -172,8 +168,13 @@ const ResetPassword = (props) => { /> - {formik.touched.password && formik.errors.password ? {formik.errors.password} : null} - {formik.touched.confirmPassword && formik.errors.confirmPassword ? {formik.errors.confirmPassword} : null} + {formik.touched.password && formik.errors.password ? ( + {formik.errors.password} + ) : null} + {formik.touched.confirmPassword && + formik.errors.confirmPassword ? ( + {formik.errors.confirmPassword} + ) : null} { label="Show password" /> - {Boolean(errorMessage) && } + {Boolean(errorMessage) && ( + + )} { - ) - -} + ); +}; -export default ResetPassword; \ No newline at end of file +export default ResetPassword; diff --git a/asreview/webapp/src/Components/SettingsDialog.js b/asreview/webapp/src/Components/SettingsDialog.js index 2f5eec1b2..6a46371e2 100644 --- a/asreview/webapp/src/Components/SettingsDialog.js +++ b/asreview/webapp/src/Components/SettingsDialog.js @@ -78,7 +78,7 @@ const SettingsDialog = (props) => { // second layer font size setting const handleFontSize = (event, newValue) => { let fontSizeSelected = fontSizeOptions.find( - (size) => size.value === newValue + (size) => size.value === newValue, ); if (fontSizeSelected !== props.fontSize) { props.handleFontSizeChange(fontSizeSelected); diff --git a/asreview/webapp/src/Components/SignIn.js b/asreview/webapp/src/Components/SignIn.js index bea9de043..82007e716 100644 --- a/asreview/webapp/src/Components/SignIn.js +++ b/asreview/webapp/src/Components/SignIn.js @@ -1,19 +1,9 @@ import * as React from "react"; -import { useSelector } from 'react-redux'; -import { - Box, - Card, - CardContent, - Fade, - Stack, - Typography, -} from "@mui/material"; +import { useSelector } from "react-redux"; +import { Box, Card, CardContent, Fade, Stack, Typography } from "@mui/material"; import { styled } from "@mui/material/styles"; -import { - HelpPrivacyTermsButton, - SignInForm, -} from "../Components"; +import { HelpPrivacyTermsButton, SignInForm } from "../Components"; import { WordmarkState } from "../globals"; import SignInOAuth from "./SignInOAuth"; @@ -61,10 +51,10 @@ const Root = styled("div")(({ theme }) => ({ }, })); - const SignIn = () => { - const oAuthData = useSelector(state => state.oAuthData); - const allowAccountCreation = useSelector(state => state.allow_account_creation) || false; + const oAuthData = useSelector((state) => state.oAuthData); + const allowAccountCreation = + useSelector((state) => state.allow_account_creation) || false; return ( @@ -85,12 +75,9 @@ const SignIn = () => { classes={classes} allowAccountCreation={allowAccountCreation} /> - { Object.keys(oAuthData.services).length > 0 && - - } + {Object.keys(oAuthData.services).length > 0 && ( + + )} diff --git a/asreview/webapp/src/Components/SignInForm.js b/asreview/webapp/src/Components/SignInForm.js index f7d2efb31..a54bac5b0 100644 --- a/asreview/webapp/src/Components/SignInForm.js +++ b/asreview/webapp/src/Components/SignInForm.js @@ -16,7 +16,6 @@ import AuthAPI from "../api/AuthAPI"; import useAuth from "../hooks/useAuth"; import { useToggle } from "../hooks/useToggle"; - const SignInForm = (props) => { const classes = props.classes; const allowAccountCreation = props.allowAccountCreation; @@ -55,13 +54,13 @@ const SignInForm = (props) => { navigate(from, { replace: true }); } } else { - console.error('Backend could not log you in.') + console.error("Backend could not log you in."); } }, onError: (data) => { - console.error('Signin error', data); - } - } + console.error("Signin error", data); + }, + }, ); const handleSubmit = (e) => { @@ -75,8 +74,8 @@ const SignInForm = (props) => { }; const handleForgotPassword = () => { - navigate("/forgot_password") - } + navigate("/forgot_password"); + }; const returnType = () => { return !showPassword ? "password" : "text"; @@ -132,11 +131,11 @@ const SignInForm = (props) => { {isError && } - { allowAccountCreation && + {allowAccountCreation && ( - } + )} { Create - diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/DashboardPage.js b/asreview/webapp/src/HomeComponents/DashboardComponents/DashboardPage.js index c121a09a9..f3feb152b 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/DashboardPage.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/DashboardPage.js @@ -5,13 +5,10 @@ import { styled } from "@mui/material/styles"; const Root = styled("div")(({ theme }) => ({})); const DashboardPage = (props) => { - return ( - - {props.children} - + {props.children} ); diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/NumberCard.js b/asreview/webapp/src/HomeComponents/DashboardComponents/NumberCard.js index 1978c4e9b..82bb246b4 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/NumberCard.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/NumberCard.js @@ -35,7 +35,7 @@ export default function NumberCard(props) { const { data, error, isError, isFetched, isSuccess } = useQuery( "fetchDashboardStats", ProjectAPI.fetchDashboardStats, - { refetchOnWindowFocus: false } + { refetchOnWindowFocus: false }, ); return ( diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/ProfilePage.js b/asreview/webapp/src/HomeComponents/DashboardComponents/ProfilePage.js index d809e6be1..3b4459fae 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/ProfilePage.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/ProfilePage.js @@ -1,12 +1,9 @@ import React from "react"; -import { - useNavigate, - useSearchParams -} from "react-router-dom"; +import { useNavigate, useSearchParams } from "react-router-dom"; import { useMutation, useQuery } from "react-query"; import DashboardPage from "./DashboardPage"; import { - Box, + Box, Checkbox, FormControl, FormControlLabel, @@ -17,34 +14,32 @@ import { } from "@mui/material"; import LoadingButton from "@mui/lab/LoadingButton"; -import { +import { TypographyH5Medium, - TypographyH6Medium + TypographyH6Medium, } from "../../StyledComponents/StyledTypography.js"; import { InlineErrorHandler } from "../../Components"; import { useToggle } from "../../hooks/useToggle"; import { AuthAPI } from "../../api"; -import { useFormik } from 'formik'; -import * as Yup from 'yup'; +import { useFormik } from "formik"; +import * as Yup from "yup"; // VALIDATION SCHEMA const SignupSchema = Yup.object().shape({ - email: Yup.string() - .email('Invalid email') - .required('Email is required'), - name: Yup.string() - .required('Full name is required'), + email: Yup.string().email("Invalid email").required("Email is required"), + name: Yup.string().required("Full name is required"), affiliation: Yup.string() - .min(2, 'Affiliation must be at least 2 characters long') + .min(2, "Affiliation must be at least 2 characters long") .nullable(), - password: Yup.string() - .matches( - /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&#])[A-Za-z\d@$!%*?&#]{8,}$/, - 'Use 8 or more characters with a mix of letters, numbers & symbols' - ), - confirmPassword: Yup.string() - .oneOf([Yup.ref('password'), null], 'Passwords must match') + password: Yup.string().matches( + /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&#])[A-Za-z\d@$!%*?&#]{8,}$/, + "Use 8 or more characters with a mix of letters, numbers & symbols", + ), + confirmPassword: Yup.string().oneOf( + [Yup.ref("password"), null], + "Passwords must match", + ), }); const ProfilePage = (props) => { @@ -54,30 +49,27 @@ const ProfilePage = (props) => { const [loadingSaveButton, setLoadingSaveButton] = React.useState(true); const [showPasswordFields, setShowPasswordFields] = React.useState(false); const [searchParams] = useSearchParams(); - const showFirstTimeMessage = searchParams.get('first_time'); + const showFirstTimeMessage = searchParams.get("first_time"); - const { error, isError, mutate } = useMutation( - AuthAPI.updateProfile, - { - onSuccess: () => { - navigate('/projects'); - }, - } - ); + const { error, isError, mutate } = useMutation(AuthAPI.updateProfile, { + onSuccess: () => { + navigate("/projects"); + }, + }); const handleSubmit = () => { if (formik.isValid) { mutate(formik.values); } - } + }; const initialValues = { - email: '', - name: '', - affiliation: '', - password: '', - confirmPassword: '', - publicAccount: true + email: "", + name: "", + affiliation: "", + password: "", + confirmPassword: "", + publicAccount: true, }; const formik = useFormik({ @@ -85,29 +77,29 @@ const ProfilePage = (props) => { validationSchema: SignupSchema, }); - const {data, isFetched} = useQuery( - "fetchProfileData", - AuthAPI.getProfile, - { - onSuccess: (data) => { - formik.setFieldValue('email', data.message.email, true); - formik.setFieldValue('name', data.message.name, true); - formik.setFieldValue('affiliation', data.message.affiliation || '', false); - formik.setFieldValue('public', data.message.public || true); - // show password field? - if (data.message.origin === "asreview") { - setShowPasswordFields(true); - } else { - setShowPasswordFields(false); - } - // stop spinner in button - setLoadingSaveButton(false); - }, - onError: (err) => { - console.log('Did not fetch profile data from backend', err) - } + const { data, isFetched } = useQuery("fetchProfileData", AuthAPI.getProfile, { + onSuccess: (data) => { + formik.setFieldValue("email", data.message.email, true); + formik.setFieldValue("name", data.message.name, true); + formik.setFieldValue( + "affiliation", + data.message.affiliation || "", + false, + ); + formik.setFieldValue("public", data.message.public || true); + // show password field? + if (data.message.origin === "asreview") { + setShowPasswordFields(true); + } else { + setShowPasswordFields(false); } - ) + // stop spinner in button + setLoadingSaveButton(false); + }, + onError: (err) => { + console.log("Did not fetch profile data from backend", err); + }, + }); const returnType = () => { return !showPassword ? "password" : "text"; @@ -140,8 +132,12 @@ const ProfilePage = (props) => { /> - {formik.touched.password && formik.errors.password ? {formik.errors.password} : null} - {formik.touched.confirmPassword && formik.errors.confirmPassword ? {formik.errors.confirmPassword} : null} + {formik.touched.password && formik.errors.password ? ( + {formik.errors.password} + ) : null} + {formik.touched.confirmPassword && formik.errors.confirmPassword ? ( + {formik.errors.confirmPassword} + ) : null} { ); - } + }; return ( - { data && isFetched && - <> - {/* Header */} - theme.palette.background.paper }} - > - - {!props.mobileScreen && ( - Profile - )} - {props.mobileScreen && ( - Profile - )} - - - - Save - - - + {data && isFetched && ( + <> + {/* Header */} + theme.palette.background.paper }} + > + + {!props.mobileScreen && ( + Profile + )} + {props.mobileScreen && ( + Profile + )} + + + + Save + + + + - - {/* Page body */} - - - { showFirstTimeMessage && - Please take a second to review your profile data: - } - - {formik.touched.email && formik.errors.email ? {formik.errors.email} : null} - - {formik.touched.name && formik.errors.name ? {formik.errors.name} : null} - - {formik.touched.affiliation && formik.errors.affiliation ? {formik.errors.affiliation} : null} - {showPasswordFields && renderPasswordFields(formik) } - { false && - <> - - } - label="Make this account public" - /> + {/* Page body */} + + + {showFirstTimeMessage && ( + + Please take a second to review your profile data: + + )} + + {formik.touched.email && formik.errors.email ? ( + {formik.errors.email} + ) : null} + + {formik.touched.name && formik.errors.name ? ( + {formik.errors.name} + ) : null} + + {formik.touched.affiliation && formik.errors.affiliation ? ( + {formik.errors.affiliation} + ) : null} + {showPasswordFields && renderPasswordFields(formik)} + {false && ( + <> + + } + label="Make this account public" + /> + + Making this account public allows you to collaborate. + + + )} + {isError && ( - Making this account public allows you to collaborate. + - - - } - {isError && } - - - - } + )} + + + + )} ); }; diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectCheckDialog.js b/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectCheckDialog.js index 343ad981c..d8a6fd675 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectCheckDialog.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectCheckDialog.js @@ -42,7 +42,7 @@ const ProjectCheckDialog = (props) => { retry: false, onSuccess: () => { navigate( - `/projects/${props.projectCheck?.project_id}/${props.projectCheck?.path}` + `/projects/${props.projectCheck?.project_id}/${props.projectCheck?.path}`, ); queryClient.invalidateQueries("fetchProjectIsOld", { refetchActive: false, @@ -58,7 +58,7 @@ const ProjectCheckDialog = (props) => { setUpgrade(false); }, refetchOnWindowFocus: false, - } + }, ); /** @@ -75,7 +75,7 @@ const ProjectCheckDialog = (props) => { enabled: exporting, refetchOnWindowFocus: false, onSettled: () => setExporting(false), - } + }, ); const returnAction = () => { @@ -195,7 +195,7 @@ const ProjectCheckDialog = (props) => { { checkText.find( - (element) => element.label === props.projectCheck?.issue + (element) => element.label === props.projectCheck?.issue, )?.text } {returnError()[0] && ( diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectTable.js b/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectTable.js index 566769a61..facb83ef7 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectTable.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectTable.js @@ -31,7 +31,7 @@ import { mapDispatchToProps, projectModes, projectStatuses, - formatDate + formatDate, } from "../../globals"; import useAuth from "../../hooks/useAuth"; import { setMyProjects } from "../../redux/actions"; @@ -118,8 +118,8 @@ const columns = [ const ProjectTable = (props) => { const navigate = useNavigate(); const queryClient = useQueryClient(); - const authenticated = useSelector(state => state.authentication); - const myProjects = useSelector(state => state.myProjects); + const authenticated = useSelector((state) => state.authentication); + const myProjects = useSelector((state) => state.myProjects); const dispatch = useDispatch(); const { auth } = useAuth(); @@ -142,7 +142,7 @@ const ProjectTable = (props) => { * Simulation status query state */ const [querySimulationFinished, setQuerySimulationFinished] = React.useState( - [] + [], ); const [querySimulationError, setQuerySimulationError] = React.useState({ isError: false, @@ -169,7 +169,7 @@ const ProjectTable = (props) => { (element) => element.mode === projectModes.SIMULATION && element.reviews[0] !== undefined && - element.reviews[0].status === projectStatuses.REVIEW + element.reviews[0].status === projectStatuses.REVIEW, ); if (!simulationProjects.length) { console.log("No simulation running"); @@ -229,9 +229,9 @@ const ProjectTable = (props) => { setTimeout( () => queryClient.invalidateQueries( - `fetchProjectStatus-${project_id[key]}` + `fetchProjectStatus-${project_id[key]}`, ), - checkIfSimulationFinishedDuration + checkIfSimulationFinishedDuration, ); } }, @@ -243,7 +243,7 @@ const ProjectTable = (props) => { } }, refetchOnWindowFocus: false, - } + }, ); /** @@ -426,7 +426,10 @@ const ProjectTable = (props) => { ?.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map((row) => { // if we do authentication, then we need to know who the owner is - row["owner_id"] = authenticated && ("owner_id" in row)? row["owner_id"] : false; + row["owner_id"] = + authenticated && "owner_id" in row + ? row["owner_id"] + : false; // A collaborator can not edit const isOwner = authenticated && row["owner_id"] === auth.id; @@ -486,7 +489,7 @@ const ProjectTable = (props) => { ) { queryClient.prefetchQuery( ["fetchExportProject", { project_id: row["id"] }], - ProjectAPI.fetchExportProject + ProjectAPI.fetchExportProject, ); } else { openProject(row, "export"); @@ -507,7 +510,11 @@ const ProjectTable = (props) => { tabIndex={-1} key={row.id} onMouseEnter={() => { - return hoverOnProject(row["id"], row["name"], row["owner_id"]); + return hoverOnProject( + row["id"], + row["name"], + row["owner_id"], + ); }} onMouseLeave={() => hoverOffProject()} > @@ -532,7 +539,9 @@ const ProjectTable = (props) => { showReviewButton={showReviewButton} onClickProjectAnalytics={onClickProjectAnalytics} onClickCollaboration={onClickCollaboration} - onClickEndCollaboration={onClickCollaboration} /* !!!!!!!!! */ + onClickEndCollaboration={ + onClickCollaboration + } /* !!!!!!!!! */ onClickProjectReview={onClickProjectReview} onClickProjectExport={onClickProjectExport} onClickProjectDetails={onClickProjectDetails} diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectsOverview.js b/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectsOverview.js index a1280a4ec..c1e72816d 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectsOverview.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/ProjectsOverview.js @@ -75,4 +75,4 @@ const ProjectsOverview = (props) => { ); }; -export default ProjectsOverview; \ No newline at end of file +export default ProjectsOverview; diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/TableRowButton.js b/asreview/webapp/src/HomeComponents/DashboardComponents/TableRowButton.js index d3d190ba0..f09e57577 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/TableRowButton.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/TableRowButton.js @@ -7,11 +7,10 @@ import { Download, GroupAdd, MoreVert, - PersonOff + PersonOff, } from "@mui/icons-material"; import { projectStatuses } from "../../globals.js"; - const PREFIX = "TableRowButton"; const classes = { @@ -79,7 +78,7 @@ export default function TableRowButton(props) { )} - { false && props.isOwner && ( + {false && props.isOwner && ( )} - { false && !props.isOwner && ( + {false && !props.isOwner && ( )} - Delete {props.isOwner ? "forever" : ""} + + Delete {props.isOwner ? "forever" : ""} + diff --git a/asreview/webapp/src/HomeComponents/DashboardComponents/index.js b/asreview/webapp/src/HomeComponents/DashboardComponents/index.js index 80c6541b1..31bb06a7b 100644 --- a/asreview/webapp/src/HomeComponents/DashboardComponents/index.js +++ b/asreview/webapp/src/HomeComponents/DashboardComponents/index.js @@ -1,7 +1,7 @@ export { default as DashboardPage } from "./DashboardPage"; export { default as DashboardPageHeader } from "./DashboardPageHeader"; export { default as NumberCard } from "./NumberCard"; -export { default as ProfilePage} from "./ProfilePage"; +export { default as ProfilePage } from "./ProfilePage"; export { default as ProjectCheckDialog } from "./ProjectCheckDialog"; export { default as ProjectTable } from "./ProjectTable"; export { default as ProjectsOverview } from "./ProjectsOverview"; diff --git a/asreview/webapp/src/HomeComponents/HomePage.js b/asreview/webapp/src/HomeComponents/HomePage.js index eb222acd3..8c11171cf 100644 --- a/asreview/webapp/src/HomeComponents/HomePage.js +++ b/asreview/webapp/src/HomeComponents/HomePage.js @@ -4,7 +4,10 @@ import clsx from "clsx"; import { Box } from "@mui/material"; import { styled } from "@mui/material/styles"; -import { ProfilePage, ProjectsOverview } from "../HomeComponents/DashboardComponents"; +import { + ProfilePage, + ProjectsOverview, +} from "../HomeComponents/DashboardComponents"; import RouteNotFound from "../RouteNotFound"; import { drawerWidth } from "../globals.js"; @@ -72,7 +75,7 @@ const HomePage = (props) => { } /> {/* Redirect root to projects */} - }/> + } /> {/* Not found */} } /> diff --git a/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.css b/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.css index 7d319007b..2f868724b 100644 --- a/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.css +++ b/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.css @@ -70,6 +70,8 @@ } .apexcharts-tooltip { - box-shadow: 0px 2px 1px -1px rgb(0 0 0 / 20%), - 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 1px 3px 0px rgb(0 0 0 / 12%); + box-shadow: + 0px 2px 1px -1px rgb(0 0 0 / 20%), + 0px 1px 1px 0px rgb(0 0 0 / 14%), + 0px 1px 3px 0px rgb(0 0 0 / 12%); } diff --git a/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.js b/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.js index 78c04aafc..1f217f9c2 100644 --- a/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.js +++ b/asreview/webapp/src/ProjectComponents/AnalyticsComponents/AnalyticsPage.js @@ -51,17 +51,17 @@ const AnalyticsPage = (props) => { const progressQuery = useQuery( ["fetchProgress", { project_id }], ProjectAPI.fetchProgress, - { refetchOnWindowFocus: false } + { refetchOnWindowFocus: false }, ); const progressDensityQuery = useQuery( ["fetchProgressDensity", { project_id }], ProjectAPI.fetchProgressDensity, - { refetchOnWindowFocus: false } + { refetchOnWindowFocus: false }, ); const progressRecallQuery = useQuery( ["fetchProgressRecall", { project_id }], ProjectAPI.fetchProgressRecall, - { refetchOnWindowFocus: false } + { refetchOnWindowFocus: false }, ); const twitterRef = React.useRef(null); diff --git a/asreview/webapp/src/ProjectComponents/AnalyticsComponents/ProgressRecallChart.js b/asreview/webapp/src/ProjectComponents/AnalyticsComponents/ProgressRecallChart.js index fbb08f598..e56d98703 100644 --- a/asreview/webapp/src/ProjectComponents/AnalyticsComponents/ProgressRecallChart.js +++ b/asreview/webapp/src/ProjectComponents/AnalyticsComponents/ProgressRecallChart.js @@ -177,7 +177,7 @@ export default function ProgressRecallChart(props) { Math, seriesArray()[0]?.data.map((element) => { return element.y; - }) + }), ); } else { return undefined; diff --git a/asreview/webapp/src/ProjectComponents/DetailsComponents/DataForm.js b/asreview/webapp/src/ProjectComponents/DetailsComponents/DataForm.js index 993e12897..ca6cd43d4 100644 --- a/asreview/webapp/src/ProjectComponents/DetailsComponents/DataForm.js +++ b/asreview/webapp/src/ProjectComponents/DetailsComponents/DataForm.js @@ -33,7 +33,7 @@ const DataForm = (props) => { ProjectAPI.fetchLabeledStats, { refetchOnWindowFocus: false, - } + }, ); const refetchData = () => { diff --git a/asreview/webapp/src/ProjectComponents/DetailsComponents/DetailsPage.js b/asreview/webapp/src/ProjectComponents/DetailsComponents/DetailsPage.js index bcae5c251..dba41c8c8 100644 --- a/asreview/webapp/src/ProjectComponents/DetailsComponents/DetailsPage.js +++ b/asreview/webapp/src/ProjectComponents/DetailsComponents/DetailsPage.js @@ -64,7 +64,7 @@ const DetailsPage = (props) => { authors: variables.authors, description: variables.description, }; - } + }, ); }, }); diff --git a/asreview/webapp/src/ProjectComponents/DetailsComponents/ModelForm.js b/asreview/webapp/src/ProjectComponents/DetailsComponents/ModelForm.js index 342159b48..cb40af303 100644 --- a/asreview/webapp/src/ProjectComponents/DetailsComponents/ModelForm.js +++ b/asreview/webapp/src/ProjectComponents/DetailsComponents/ModelForm.js @@ -49,7 +49,7 @@ const ModelForm = (props) => { { enabled: project_id !== null, refetchOnWindowFocus: false, - } + }, ); const returnModelError = () => { diff --git a/asreview/webapp/src/ProjectComponents/ExportComponents/ExportPage.js b/asreview/webapp/src/ProjectComponents/ExportComponents/ExportPage.js index aca8aec13..273d9df2b 100644 --- a/asreview/webapp/src/ProjectComponents/ExportComponents/ExportPage.js +++ b/asreview/webapp/src/ProjectComponents/ExportComponents/ExportPage.js @@ -58,7 +58,7 @@ const ExportPage = (props) => { ProjectAPI.fetchDatasetWriter, { refetchOnWindowFocus: false, - } + }, ); const exportDatasetQuery = useQuery( @@ -76,7 +76,7 @@ const ExportPage = (props) => { enabled: (file === "dataset" || file === "dataset_relevant") && exporting, refetchOnWindowFocus: false, onSettled: () => setExporting(false), - } + }, ); const exportProjectQuery = useQuery( @@ -86,7 +86,7 @@ const ExportPage = (props) => { enabled: file === "project" && exporting, refetchOnWindowFocus: false, onSettled: () => setExporting(false), - } + }, ); const selectedQuery = () => { diff --git a/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecord.js b/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecord.js index da053b72a..7f4de5cee 100644 --- a/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecord.js +++ b/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecord.js @@ -97,14 +97,14 @@ const LabeledRecord = (props) => { enabled: enableQuery(), getNextPageParam: (lastPage) => lastPage.next_page ?? false, refetchOnWindowFocus: false, - } + }, ); React.useEffect(() => { setSubset( props.filterQuery?.map((element) => { return element.value; - }) + }), ); }, [props.filterQuery]); diff --git a/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecordCard.js b/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecordCard.js index 7a8f568d4..c5c82394c 100644 --- a/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecordCard.js +++ b/asreview/webapp/src/ProjectComponents/HistoryComponents/LabeledRecordCard.js @@ -100,7 +100,7 @@ const LabeledRecordCard = (props) => { }; }), }; - } + }, ); if (variables.doc_id === recordReadMore) { setRecordReadMore(null); @@ -115,7 +115,7 @@ const LabeledRecordCard = (props) => { queryClient.invalidateQueries("fetchLabeledStats"); } }, - } + }, ); const handleClickLabelConvert = (value) => { @@ -295,29 +295,32 @@ const LabeledRecordCard = (props) => { )} - {!props.is_prior && !value.note && value.id !== note.editing && ( - - - - - - )} + {!props.is_prior && + !value.note && + value.id !== note.editing && ( + + + + + + )} { onClick={() => handleClickEditNote( props.record?.note, - props.record?.id + props.record?.id, ) } size={!props.mobileScreen ? "medium" : "small"} diff --git a/asreview/webapp/src/ProjectComponents/ImportFromFile.js b/asreview/webapp/src/ProjectComponents/ImportFromFile.js index 89ad90bf4..46264ee9a 100644 --- a/asreview/webapp/src/ProjectComponents/ImportFromFile.js +++ b/asreview/webapp/src/ProjectComponents/ImportFromFile.js @@ -88,7 +88,7 @@ const ImportFromFile = ({ // set the state such that we ca upload the file setFile(acceptedFiles[0]); }, - [setFile, isAddFileError, reset] + [setFile, isAddFileError, reset], ); const { @@ -112,7 +112,7 @@ const ImportFromFile = ({ ...(isDragAccept ? acceptStyle : {}), ...(isDragReject ? rejectStyle : {}), }), - [isDragActive, isDragReject, isDragAccept] + [isDragActive, isDragReject, isDragAccept], ); const returnAcceptFile = () => { diff --git a/asreview/webapp/src/ProjectComponents/ProjectDeleteDialog.js b/asreview/webapp/src/ProjectComponents/ProjectDeleteDialog.js index c24bd7d3f..ac600b659 100644 --- a/asreview/webapp/src/ProjectComponents/ProjectDeleteDialog.js +++ b/asreview/webapp/src/ProjectComponents/ProjectDeleteDialog.js @@ -23,7 +23,7 @@ const ProjectDeleteDialog = (props) => { const queryClient = useQueryClient(); const { auth } = useAuth(); - const authenticated = useSelector(state => state.authentication); + const authenticated = useSelector((state) => state.authentication); const dispatch = useDispatch(); const descriptionElementRef = React.useRef(null); @@ -41,32 +41,32 @@ const ProjectDeleteDialog = (props) => { navigate("/projects"); } }, - } + }, ); const endCollaboration = () => { if (authenticated && props.project_id && auth.id) { TeamAPI.endCollaboration(props.project_id, auth.id) - .then(data => { - if (data.success) { - // success, the collaboration was ended, get all projects - ProjectAPI.fetchProjects({}) - .then(data => { - if (data.result instanceof Array) { - // refresh project list - dispatch(setMyProjects(data.result)); - // close dialog - props.toggleDeleteDialog(); - } else { - console.log('Could not get projects list -- DB failure'); - } - }) - .catch(err => console.log('Could not pull all projects', err)); - } else { - console.log('Could not end collaboration -- DB failure'); - } - }) - .catch(err => console.log('Could not end collaboration', err)) + .then((data) => { + if (data.success) { + // success, the collaboration was ended, get all projects + ProjectAPI.fetchProjects({}) + .then((data) => { + if (data.result instanceof Array) { + // refresh project list + dispatch(setMyProjects(data.result)); + // close dialog + props.toggleDeleteDialog(); + } else { + console.log("Could not get projects list -- DB failure"); + } + }) + .catch((err) => console.log("Could not pull all projects", err)); + } else { + console.log("Could not end collaboration -- DB failure"); + } + }) + .catch((err) => console.log("Could not end collaboration", err)); } }; @@ -102,7 +102,7 @@ const ProjectDeleteDialog = (props) => { } else { return " from your list"; } - } + }; return ( { - { props.isOwner && + {props.isOwner && ( - } - { !props.isOwner && - - } + )} ); diff --git a/asreview/webapp/src/ProjectComponents/ProjectImportDialog.js b/asreview/webapp/src/ProjectComponents/ProjectImportDialog.js index 2c9b3cac7..1be9b84cd 100644 --- a/asreview/webapp/src/ProjectComponents/ProjectImportDialog.js +++ b/asreview/webapp/src/ProjectComponents/ProjectImportDialog.js @@ -29,7 +29,7 @@ const ProjectImportDialog = (props) => { queryClient.invalidateQueries("fetchProjects"); props.onClose(); }, - } + }, ); React.useEffect(() => { diff --git a/asreview/webapp/src/ProjectComponents/ProjectPage.js b/asreview/webapp/src/ProjectComponents/ProjectPage.js index c400482f0..3ba300b58 100644 --- a/asreview/webapp/src/ProjectComponents/ProjectPage.js +++ b/asreview/webapp/src/ProjectComponents/ProjectPage.js @@ -37,7 +37,6 @@ import { } from "../globals.js"; import useAuth from "../hooks/useAuth"; - const PREFIX = "ProjectPage"; const classes = { @@ -66,7 +65,7 @@ const Root = styled("div")(({ theme }) => ({ })); const ProjectPage = (props) => { - const authenticated = useSelector(state => state.authentication); + const authenticated = useSelector((state) => state.authentication); const { auth } = useAuth(); const queryClient = useQueryClient(); const navigate = useNavigate(); @@ -126,7 +125,7 @@ const ProjectPage = (props) => { } }, refetchOnWindowFocus: false, - } + }, ); const refetchAnalytics = () => { @@ -154,12 +153,12 @@ const ProjectPage = (props) => { // not finished yet setTimeout( () => queryClient.invalidateQueries("fetchProjectStatus"), - checkIfSimulationFinishedDuration + checkIfSimulationFinishedDuration, ); } }, refetchOnWindowFocus: false, - } + }, ); const returnError = () => { diff --git a/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPage.js b/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPage.js index 6b848b2f3..f66f2600d 100644 --- a/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPage.js +++ b/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPage.js @@ -60,7 +60,7 @@ const ReviewPage = (props) => { setActiveRecord(data["result"]); } }, - } + }, ); const { error, isError, isLoading, mutate, reset } = useMutation( @@ -81,7 +81,7 @@ const ReviewPage = (props) => { queryClient.invalidateQueries("fetchRecord"); showUndoBarIfNeeded(variables.label, variables.initial); }, - } + }, ); /** diff --git a/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPageFinished.js b/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPageFinished.js index 353fb7fbb..8db1f94de 100644 --- a/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPageFinished.js +++ b/asreview/webapp/src/ProjectComponents/ReviewComponents/ReviewPageFinished.js @@ -56,7 +56,7 @@ const ReviewPageFinished = (props) => { onSuccess: () => { queryClient.invalidateQueries("fetchInfo"); }, - } + }, ); const handleChangeStatus = () => { @@ -73,7 +73,7 @@ const ReviewPageFinished = (props) => { const ifRecordPoolEmpty = React.useCallback(async () => { const data = await queryClient.fetchQuery( ["fetchRecord", { project_id }], - ProjectAPI.fetchRecord + ProjectAPI.fetchRecord, ); setRecordEmpty(data["pool_empty"]); }, [project_id, queryClient]); diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/AddDataset.js b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/AddDataset.js index a495dbf3b..d0871d340 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/AddDataset.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/AddDataset.js @@ -66,7 +66,7 @@ const AddDataset = (props) => { queryClient.invalidateQueries("fetchLabeledStats"); props.toggleAddDataset(); }, - } + }, ); const handleDatasetSource = (event) => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DataForm.js b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DataForm.js index adc1aa74d..072cbe62e 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DataForm.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DataForm.js @@ -36,7 +36,7 @@ const DataForm = (props) => { { enabled: props.project_id !== null && props.datasetAdded, refetchOnWindowFocus: false, - } + }, ); const priorAdded = () => { @@ -54,7 +54,7 @@ const DataForm = (props) => { const refetchInfo = () => { queryClient.prefetchQuery( ["fetchInfo", { project_id: props.project_id }], - ProjectAPI.fetchInfo + ProjectAPI.fetchInfo, ); }; @@ -72,11 +72,12 @@ const DataForm = (props) => { the AI. Prior knowledge is required to warm up the AI. - {!props.isFetchInfoError && (isFetching || props.isFetchingLabeledStats) && ( - - - - )} + {!props.isFetchInfoError && + (isFetching || props.isFetchingLabeledStats) && ( + + + + )} {!isFetching && isError && ( { } = useQuery( ["fetchDatasets", { subset: props.subset }], ProjectAPI.fetchDatasets, - { refetchOnWindowFocus: false } + { refetchOnWindowFocus: false }, ); const refetchDatasets = () => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DatasetFromURL.js b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DatasetFromURL.js index 2d8ccc372..385850a58 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DatasetFromURL.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/DatasetFromURL.js @@ -45,7 +45,7 @@ const DatasetFromURL = (props) => { props.setURL(data["files"][0]["link"]); } }, - } + }, ); const handleURL = (event) => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorRandom.js b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorRandom.js index 5c2c95e5e..10b2f29ae 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorRandom.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorRandom.js @@ -101,7 +101,7 @@ const PriorRandom = (props) => { setRefresh(false); }, refetchOnWindowFocus: false, - } + }, ); const handleNRecordsChange = (event) => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorSearch.js b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorSearch.js index e8c292532..5a23b116f 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorSearch.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorSearch.js @@ -85,7 +85,7 @@ const PriorSearch = (props) => { } }, refetchOnWindowFocus: false, - } + }, ); const refetchPriorSearch = () => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorUnlabeled.js b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorUnlabeled.js index 7068861b5..e3b9dfe82 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorUnlabeled.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/DataComponents/PriorUnlabeled.js @@ -85,7 +85,7 @@ const PriorUnlabeled = (props) => { }; }), }; - } + }, ); } else { // update cached data @@ -112,11 +112,11 @@ const PriorUnlabeled = (props) => { }; }), }; - } + }, ); } }, - } + }, ); const isDebugInclusion = () => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/FinishSetup.js b/asreview/webapp/src/ProjectComponents/SetupComponents/FinishSetup.js index f70b1acf4..737fbdf70 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/FinishSetup.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/FinishSetup.js @@ -47,7 +47,7 @@ const FinishSetup = (props) => { props.handleBack(); queryClient.resetQueries("fetchProjectStatus"); }, - } + }, ); const onClickCloseSetup = async () => { @@ -55,7 +55,7 @@ const FinishSetup = (props) => { console.log("Opening existing project " + props.project_id); await queryClient.prefetchQuery( ["fetchInfo", { project_id: props.project_id }], - ProjectAPI.fetchInfo + ProjectAPI.fetchInfo, ); if (props.mode !== projectModes.SIMULATION) { navigate(`/projects/${props.project_id}/review`); diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/ModelComponents/ModelForm.js b/asreview/webapp/src/ProjectComponents/SetupComponents/ModelComponents/ModelForm.js index b904eea8c..3279c4737 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/ModelComponents/ModelForm.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/ModelComponents/ModelForm.js @@ -101,7 +101,7 @@ const ModelForm = (props) => { }); }, refetchOnWindowFocus: false, - } + }, ); const handleModel = (event) => { diff --git a/asreview/webapp/src/ProjectComponents/SetupComponents/SetupDialog.js b/asreview/webapp/src/ProjectComponents/SetupComponents/SetupDialog.js index 1f0f557df..43ff8be5c 100644 --- a/asreview/webapp/src/ProjectComponents/SetupComponents/SetupDialog.js +++ b/asreview/webapp/src/ProjectComponents/SetupComponents/SetupDialog.js @@ -188,7 +188,7 @@ const SetupDialog = (props) => { setDisableFetchInfo(true); // avoid getting all the time }, refetchOnWindowFocus: false, - } + }, ); const { @@ -276,7 +276,7 @@ const SetupDialog = (props) => { enabled: props.project_id !== null && activeStep === 1 && projectHasDataset(), refetchOnWindowFocus: false, - } + }, ); /** @@ -345,13 +345,13 @@ const SetupDialog = (props) => { // not ready yet setTimeout( () => queryClient.invalidateQueries("fetchProjectStatus"), - 12000 + 12000, ); } }, refetchOnWindowFocus: false, retry: false, - } + }, ); const restartTraining = () => { diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceContents.js b/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceContents.js index bdc18b775..2533d06c4 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceContents.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceContents.js @@ -10,7 +10,7 @@ import { TableRow, } from "@mui/material"; import { styled } from "@mui/material/styles"; -import {formatDate} from "../../globals"; +import { formatDate } from "../../globals"; const PREFIX = "ProjectTable"; @@ -35,7 +35,7 @@ const StyledPaper = styled(Paper)(({ theme }) => ({ marginLeft: "auto", marginRight: "auto", paddingLeft: 10, - paddingRight: 10 + paddingRight: 10, }, [`& .${classes.error}`]: { @@ -97,7 +97,6 @@ const columns = [ ]; const AcceptanceDialog = (props) => { - return ( @@ -112,36 +111,34 @@ const AcceptanceDialog = (props) => { - { - props.projectInvitations.map((project) => { - return ( - - {project.name} - {formatDate(project.created_at_unix)} - {project.mode} - - - - - - ); - }) - } + {props.projectInvitations.map((project) => { + return ( + + {project.name} + {formatDate(project.created_at_unix)} + {project.mode} + + + + + + ); + })} - ) + ); }; -export default AcceptanceDialog; \ No newline at end of file +export default AcceptanceDialog; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceDialog.js b/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceDialog.js index e1f83d9df..9eddcba2b 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceDialog.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/AcceptanceDialog.js @@ -1,8 +1,5 @@ -import * as React from 'react'; -import { - Dialog, - Divider, -} from "@mui/material"; +import * as React from "react"; +import { Dialog, Divider } from "@mui/material"; import { styled } from "@mui/material/styles"; import AcceptanceContents from "./AcceptanceContents"; import DialogHeader from "./DialogHeader"; @@ -41,7 +38,6 @@ const StyledDialog = styled(Dialog)(({ theme }) => ({ })); const AcceptanceDialog = (props) => { - const handleClose = () => { props.onClose(); }; @@ -67,8 +63,8 @@ const AcceptanceDialog = (props) => { handleAcceptance={props.handleAcceptance} handleRejection={props.handleRejection} /> - + ); -} +}; -export default AcceptanceDialog; \ No newline at end of file +export default AcceptanceDialog; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/ConfirmationDialog.js b/asreview/webapp/src/ProjectComponents/TeamComponents/ConfirmationDialog.js index f56e83e75..717c86c62 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/ConfirmationDialog.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/ConfirmationDialog.js @@ -1,10 +1,9 @@ import { Button } from "@mui/material"; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogContentText from '@mui/material/DialogContentText'; -import DialogTitle from '@mui/material/DialogTitle'; - +import Dialog from "@mui/material/Dialog"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import DialogContentText from "@mui/material/DialogContentText"; +import DialogTitle from "@mui/material/DialogTitle"; const ConfirmationDialog = (props) => { return ( @@ -14,9 +13,7 @@ const ConfirmationDialog = (props) => { aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" > - - {props.title} - + {props.title} {props.contents} @@ -29,9 +26,7 @@ const ConfirmationDialog = (props) => { - ) - - -} + ); +}; -export default ConfirmationDialog; \ No newline at end of file +export default ConfirmationDialog; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/DialogHeader.js b/asreview/webapp/src/ProjectComponents/TeamComponents/DialogHeader.js index 4cc24139b..c10d8bf7e 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/DialogHeader.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/DialogHeader.js @@ -1,12 +1,7 @@ -import { - DialogTitle, - Stack, - Tooltip, -} from "@mui/material"; +import { DialogTitle, Stack, Tooltip } from "@mui/material"; import { Close } from "@mui/icons-material"; import { StyledIconButton } from "../../StyledComponents/StyledButton.js"; - const DialogHeader = (props) => { return ( @@ -25,7 +20,7 @@ const DialogHeader = (props) => { - ) -} + ); +}; -export default DialogHeader; \ No newline at end of file +export default DialogHeader; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/EndCollaboration.js b/asreview/webapp/src/ProjectComponents/TeamComponents/EndCollaboration.js index 73177b586..75e6d9c4f 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/EndCollaboration.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/EndCollaboration.js @@ -1,15 +1,12 @@ -import * as React from 'react'; +import * as React from "react"; import { useNavigate, useParams } from "react-router-dom"; import { TeamAPI } from "../../api/index.js"; import useAuth from "../../hooks/useAuth"; import { Box, Button, Stack } from "@mui/material"; import { InlineErrorHandler } from "../../Components"; -import { ConfirmationDialog } from "."; - - +import { ConfirmationDialog } from "."; const EndCollaboration = (props) => { - const [dialogOpen, setDialogOpen] = React.useState(false); const { auth } = useAuth(); const navigate = useNavigate(); @@ -27,28 +24,31 @@ const EndCollaboration = (props) => { const handleEndCollaboration = () => { setDialogOpen(false); TeamAPI.endCollaboration(project_id, auth.id) - .then(data => { + .then((data) => { if (data.success) { - navigate("/projects") + navigate("/projects"); } else { - let message = "Could not end the collaboration -- DB failure" + let message = "Could not end the collaboration -- DB failure"; console.error(message); - setErrorMessage(message) + setErrorMessage(message); } }) - .catch(err => { - let message = `Could not end the collaboration: ${err.message} (${err.code})` + .catch((err) => { + let message = `Could not end the collaboration: ${err.message} (${err.code})`; console.error("Could not invite user", err); - setErrorMessage(message) + setErrorMessage(message); }); - } + }; return ( <>

You are collaborating in this project

-

If you would like to end this collaboration, please click on the button below:

+

+ If you would like to end this collaboration, please click on the + button below: +

- {(errorMessage !== undefined) && - - + {errorMessage !== undefined && ( + + - } + )}
- - ) -} + ); +}; -export default EndCollaboration; \ No newline at end of file +export default EndCollaboration; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/InvitationComponent.js b/asreview/webapp/src/ProjectComponents/TeamComponents/InvitationComponent.js index 492e85e6b..95317bed4 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/InvitationComponent.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/InvitationComponent.js @@ -1,21 +1,19 @@ -import * as React from 'react'; +import * as React from "react"; import { useQuery } from "react-query"; import { useParams } from "react-router-dom"; import { TeamAPI } from "../../api/index.js"; -import List from '@mui/material/List'; +import List from "@mui/material/List"; import { Add } from "@mui/icons-material"; -import TextField from '@mui/material/TextField'; -import Autocomplete from '@mui/material/Autocomplete'; +import TextField from "@mui/material/TextField"; +import Autocomplete from "@mui/material/Autocomplete"; import UserListEntry from "./UserListEntry"; import { Box, Fab, Stack } from "@mui/material"; import { ConfirmationDialog } from "."; - const InvitationContents = (props) => { - const [selectedUser, setSelectedUser] = React.useState(null); const [removeUser, setRemoveUser] = React.useState(null); - const [inputValue, setInputValue] = React.useState(''); + const [inputValue, setInputValue] = React.useState(""); const [collaborators, setCollaborators] = React.useState(new Set([])); const [invitedUsers, setInvitedUsers] = React.useState(new Set([])); const [allUsers, setAllUsers] = React.useState([]); @@ -42,49 +40,49 @@ const InvitationContents = (props) => { setInvitedUsers(new Set(data.invitations || [])); }, onError: (data) => { - console.log('error', data); - } - } + console.log("error", data); + }, + }, ); React.useEffect(() => { - setAssociatedUsers(state => new Set([...collaborators, ...invitedUsers])) + setAssociatedUsers((state) => new Set([...collaborators, ...invitedUsers])); }, [collaborators, invitedUsers]); const inviteUser = () => { if (selectedUser) { TeamAPI.inviteUser(project_id, selectedUser.id) - .then(data => { + .then((data) => { if (data.success) { // add this user to the invited users (ofEffect will take care of the rest // -autocomplete-) - setInvitedUsers(state => new Set([...state, selectedUser.id])) + setInvitedUsers((state) => new Set([...state, selectedUser.id])); // set selected value to null setSelectedUser(null); } else { - console.log('Could not invite user -- DB failure'); + console.log("Could not invite user -- DB failure"); } }) - .catch(err => console.log('Could not invite user', err)); + .catch((err) => console.log("Could not invite user", err)); } }; const removeInvitation = (id) => { TeamAPI.deleteInvitation(project_id, id) - .then(data => { + .then((data) => { if (data.success) { // remove from the invited users list, useEffect will take care of the rest // for the autocomplete - setInvitedUsers(state => { + setInvitedUsers((state) => { state.delete(id); return new Set([...state]); - }) + }); } else { - console.log('Could not delete invitation -- DB failure'); + console.log("Could not delete invitation -- DB failure"); } }) - .catch(err => console.log('Could not delete invitation', err)); - } + .catch((err) => console.log("Could not delete invitation", err)); + }; const removeCollaborator = () => { if (removeUser) { @@ -94,19 +92,19 @@ const InvitationContents = (props) => { } // remove from backend TeamAPI.endCollaboration(project_id, removeUser.id) - .then(data => { + .then((data) => { if (data.success) { // remove from the collabo users list, useEffect will take care of the rest // for the autocomplete - setCollaborators(state => { + setCollaborators((state) => { state.delete(removeUser.id); return new Set([...state]); - }) + }); } else { - console.log('Could not delete invitation -- DB failure'); + console.log("Could not delete invitation -- DB failure"); } }) - .catch(err => console.log('Could not delete invitation', err)); + .catch((err) => console.log("Could not delete invitation", err)); } }; @@ -118,7 +116,7 @@ const InvitationContents = (props) => { option.id === value.id} - onChange={(event, newValue=null) => { + onChange={(event, newValue = null) => { if (newValue !== null) { setSelectedUser(newValue); } @@ -128,13 +126,15 @@ const InvitationContents = (props) => { setInputValue(newInputValue); }} id="controllable-states-demo" - options={ allUsers.filter(item => !associatedUsers.has(item.id)) } - getOptionLabel={option => `${option.name}` } + options={allUsers.filter((item) => !associatedUsers.has(item.id))} + getOptionLabel={(option) => `${option.name}`} sx={{ width: 300, padding: 1 }} - renderInput={(params) => } + renderInput={(params) => ( + + )} /> {

Pending (dbl click to remove)

- { allUsers.filter(item => invitedUsers.has(item.id)).map((user) => ( - )) - } + {allUsers + .filter((item) => invitedUsers.has(item.id)) + .map((user) => ( + + ))}

Collaborators (dbl click to remove)

- { allUsers.filter(item => collaborators.has(item.id)).map((user) => ( - )) - } + {allUsers + .filter((item) => collaborators.has(item.id)) + .map((user) => ( + + ))}
- ); -} +}; -export default InvitationContents; \ No newline at end of file +export default InvitationContents; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/TeamPage.js b/asreview/webapp/src/ProjectComponents/TeamComponents/TeamPage.js index c796a4914..2b375ac81 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/TeamPage.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/TeamPage.js @@ -3,7 +3,7 @@ import { Box, Fade } from "@mui/material"; import { styled } from "@mui/material/styles"; import { PageHeader } from "../../Components"; -import { EndCollaboration, InvitationContents} from "."; +import { EndCollaboration, InvitationContents } from "."; const PREFIX = "TeamPage"; @@ -32,7 +32,6 @@ const TeamPage = (props) => { {props.isOwner && } {!props.isOwner && } -
diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/UserListEntry.js b/asreview/webapp/src/ProjectComponents/TeamComponents/UserListEntry.js index 3c703dbe8..2b0fc379a 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/UserListEntry.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/UserListEntry.js @@ -1,16 +1,13 @@ -import ListItem from '@mui/material/ListItem'; -import ListItemAvatar from '@mui/material/ListItemAvatar'; -import ListItemText from '@mui/material/ListItemText'; -import PersonIcon from '@mui/icons-material/Person'; -import Avatar from '@mui/material/Avatar'; -import { blue } from '@mui/material/colors'; +import ListItem from "@mui/material/ListItem"; +import ListItemAvatar from "@mui/material/ListItemAvatar"; +import ListItemText from "@mui/material/ListItemText"; +import PersonIcon from "@mui/icons-material/Person"; +import Avatar from "@mui/material/Avatar"; +import { blue } from "@mui/material/colors"; const UserListEntry = (props) => { return ( - props.onDoubleClick(props.user)} - > + props.onDoubleClick(props.user)}> @@ -19,6 +16,6 @@ const UserListEntry = (props) => { ); -} +}; -export default UserListEntry; \ No newline at end of file +export default UserListEntry; diff --git a/asreview/webapp/src/ProjectComponents/TeamComponents/index.js b/asreview/webapp/src/ProjectComponents/TeamComponents/index.js index 131abd3c3..532e4aa0e 100644 --- a/asreview/webapp/src/ProjectComponents/TeamComponents/index.js +++ b/asreview/webapp/src/ProjectComponents/TeamComponents/index.js @@ -1,6 +1,6 @@ export { default as AcceptanceDialog } from "./AcceptanceDialog"; -export { default as ConfirmationDialog} from "./ConfirmationDialog"; -export { default as EndCollaboration} from "./EndCollaboration"; +export { default as ConfirmationDialog } from "./ConfirmationDialog"; +export { default as EndCollaboration } from "./EndCollaboration"; export { default as DialogHeader } from "./DialogHeader"; export { default as InvitationContents } from "./InvitationComponent"; export { default as TeamPage } from "./TeamPage"; diff --git a/asreview/webapp/src/api/AuthAPI.js b/asreview/webapp/src/api/AuthAPI.js index f9cef87c5..10ae8c28c 100644 --- a/asreview/webapp/src/api/AuthAPI.js +++ b/asreview/webapp/src/api/AuthAPI.js @@ -31,7 +31,7 @@ class AuthAPI { let body = new FormData(); body.set("email", variables.email); body.set("password", variables.password); - + const url = auth_url + `signin`; return new Promise((resolve, reject) => { axios({ @@ -53,7 +53,7 @@ class AuthAPI { static forgotPassword(variables) { let body = new FormData(); body.set("email", variables.email); - + const url = auth_url + `forgot_password`; return new Promise((resolve, reject) => { axios({ diff --git a/asreview/webapp/src/api/ProjectAPI.js b/asreview/webapp/src/api/ProjectAPI.js index 60048aa8c..877cf63ed 100644 --- a/asreview/webapp/src/api/ProjectAPI.js +++ b/asreview/webapp/src/api/ProjectAPI.js @@ -412,7 +412,7 @@ class ProjectAPI { link.href = url; link.setAttribute( "download", - `asreview_dataset_${datasetLabel}_${project_title}.${fileFormat}` + `asreview_dataset_${datasetLabel}_${project_title}.${fileFormat}`, ); document.body.appendChild(link); link.click(); @@ -550,7 +550,7 @@ class ProjectAPI { console.log( `${variables.project_id} - add item ${variables.doc_id} to ${ variables.label === 1 ? "inclusions" : "exclusions" - }` + }`, ); }) .catch((error) => { diff --git a/asreview/webapp/src/api/TeamAPI.js b/asreview/webapp/src/api/TeamAPI.js index f0245db74..cdba5f8cd 100644 --- a/asreview/webapp/src/api/TeamAPI.js +++ b/asreview/webapp/src/api/TeamAPI.js @@ -2,11 +2,9 @@ import { axiosErrorHandler } from "./axiosErrorHandler"; import { api_url } from "../globals.js"; import axios from "axios"; - class TeamAPI { - static getProjectInvitations() { - const url = api_url + `invitations` + const url = api_url + `invitations`; return new Promise((resolve, reject) => { axios .get(url, { withCredentials: true }) @@ -19,7 +17,6 @@ class TeamAPI { }); } - static fetchCollaborators(projectId) { if (projectId !== null) { const url = api_url + `projects/${projectId}/users`; @@ -34,13 +31,13 @@ class TeamAPI { }); }); } else { - return {} + return {}; } } static inviteUser(projectId, userId) { if (projectId !== null && userId !== null) { - const url = api_url + `invitations/projects/${projectId}/users/${userId}` + const url = api_url + `invitations/projects/${projectId}/users/${userId}`; return new Promise((resolve, reject) => { axios({ method: "post", @@ -59,7 +56,7 @@ class TeamAPI { static deleteInvitation(projectId, userId) { if (projectId !== null && userId !== null) { - const url = api_url + `invitations/projects/${projectId}/users/${userId}` + const url = api_url + `invitations/projects/${projectId}/users/${userId}`; return new Promise((resolve, reject) => { axios({ method: "delete", @@ -76,10 +73,9 @@ class TeamAPI { } } - static rejectInvitation(projectId) { if (projectId !== null) { - const url = api_url + `invitations/projects/${projectId}/reject` + const url = api_url + `invitations/projects/${projectId}/reject`; return new Promise((resolve, reject) => { axios({ method: "delete", @@ -98,7 +94,7 @@ class TeamAPI { static acceptInvitation(projectId) { if (projectId !== null) { - const url = api_url + `invitations/projects/${projectId}/accept` + const url = api_url + `invitations/projects/${projectId}/accept`; return new Promise((resolve, reject) => { axios({ method: "post", @@ -117,7 +113,7 @@ class TeamAPI { static endCollaboration(projectId, userId) { if (userId !== null && projectId !== null) { - const url = api_url + `projects/${projectId}/users/${userId}` + const url = api_url + `projects/${projectId}/users/${userId}`; return new Promise((resolve, reject) => { axios({ method: "delete", diff --git a/asreview/webapp/src/hooks/SettingsHooks.js b/asreview/webapp/src/hooks/SettingsHooks.js index f5736bfa6..3bcace6cd 100644 --- a/asreview/webapp/src/hooks/SettingsHooks.js +++ b/asreview/webapp/src/hooks/SettingsHooks.js @@ -49,7 +49,7 @@ const useFontSize = () => { const handleFontSizeChange = (size) => { window.localStorage.setItem( "fontSize", - JSON.stringify([size.value, size.label]) + JSON.stringify([size.value, size.label]), ); setFontSize(size); }; diff --git a/asreview/webapp/src/hooks/useAuth.js b/asreview/webapp/src/hooks/useAuth.js index 0a6b3c035..dc11a8590 100644 --- a/asreview/webapp/src/hooks/useAuth.js +++ b/asreview/webapp/src/hooks/useAuth.js @@ -4,7 +4,7 @@ import AuthContext from "../context/AuthProvider"; const useAuth = () => { const { auth } = React.useContext(AuthContext); React.useDebugValue(auth, (auth) => - auth?.logged_in ? "Signed In" : "Signed Out" + auth?.logged_in ? "Signed In" : "Signed Out", ); return React.useContext(AuthContext); }; diff --git a/asreview/webapp/src/icons/Orcid.js b/asreview/webapp/src/icons/Orcid.js index 4d73ace2c..df2024a32 100644 --- a/asreview/webapp/src/icons/Orcid.js +++ b/asreview/webapp/src/icons/Orcid.js @@ -5,15 +5,35 @@ function Orcid(props) { return ( - - - - - - + + + + + + ); } -export default Orcid; \ No newline at end of file +export default Orcid; diff --git a/asreview/webapp/src/index.js b/asreview/webapp/src/index.js index 80c25e4ca..d13c976cf 100644 --- a/asreview/webapp/src/index.js +++ b/asreview/webapp/src/index.js @@ -20,5 +20,5 @@ render( , - document.getElementById("root") + document.getElementById("root"), ); diff --git a/asreview/webapp/src/redux/actions/index.js b/asreview/webapp/src/redux/actions/index.js index bdb189435..b149cb72c 100644 --- a/asreview/webapp/src/redux/actions/index.js +++ b/asreview/webapp/src/redux/actions/index.js @@ -11,7 +11,7 @@ import { // note: I am not too sure about these functions, they // shield the dispatch type from the developer, but // it complicates the API by adding yet another layer -// of functions. As far as I am concerned these can go. +// of functions. As far as I am concerned these can go. export function setASReviewVersion(data) { return { type: SET_ASREVIEW_VERSION, asreview_version: data }; } @@ -38,4 +38,4 @@ export function setMyProjects(data) { export function setOAuthServices(data) { return { type: OAUTH_SERVICES, data: data }; -} \ No newline at end of file +} diff --git a/asreview/webapp/src/redux/reducers/index.js b/asreview/webapp/src/redux/reducers/index.js index 440b32cb0..458b96b8a 100644 --- a/asreview/webapp/src/redux/reducers/index.js +++ b/asreview/webapp/src/redux/reducers/index.js @@ -17,8 +17,8 @@ const initialState = { allow_teams: undefined, oAuthData: { services: {}, - compareKey: 'oAuthCompareKey', // these 2 values are used when the oAuth - messageType: 'oAuthMessage' // popup has to communicate with the opener + compareKey: "oAuthCompareKey", // these 2 values are used when the oAuth + messageType: "oAuthMessage", // popup has to communicate with the opener }, status: undefined, project_id: null, @@ -41,7 +41,7 @@ function rootReducer(state = initialState, action) { project_id: action.project_id, }); case TOGGLE_HELP_DIALOG: - return Object.assign({}, state, { + return Object.assign({}, state, { onHelpDialog: !state.onHelpDialog, }); // set boot data @@ -58,16 +58,16 @@ function rootReducer(state = initialState, action) { // set my projects list case MY_PROJECTS: return Object.assign({}, state, { - myProjects: action.data + myProjects: action.data, }); // set OAuth services case OAUTH_SERVICES: { const newState = { ...state.oAuthData, - services: action.data - } + services: action.data, + }; return Object.assign({}, state, { - oAuthData: newState + oAuthData: newState, }); } // default diff --git a/asreview/webapp/src/serviceWorker.js b/asreview/webapp/src/serviceWorker.js index b719ab46c..0777a3fc2 100644 --- a/asreview/webapp/src/serviceWorker.js +++ b/asreview/webapp/src/serviceWorker.js @@ -16,8 +16,8 @@ const isLocalhost = Boolean( window.location.hostname === "[::1]" || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/, + ), ); export function register(config) { @@ -43,7 +43,7 @@ export function register(config) { navigator.serviceWorker.ready.then(() => { console.log( "This web app is being served cache-first by a service " + - "worker. To learn more, visit https://bit.ly/CRA-PWA" + "worker. To learn more, visit https://bit.ly/CRA-PWA", ); }); } else { @@ -71,7 +71,7 @@ function registerValidSW(swUrl, config) { // content until all client tabs are closed. console.log( "New content is available and will be used when all " + - "tabs for this page are closed. See https://bit.ly/CRA-PWA." + "tabs for this page are closed. See https://bit.ly/CRA-PWA.", ); // Execute callback @@ -121,7 +121,7 @@ function checkValidServiceWorker(swUrl, config) { }) .catch(() => { console.log( - "No internet connection found. App is running in offline mode." + "No internet connection found. App is running in offline mode.", ); }); } diff --git a/asreview/webapp/templates/emails/confirm_account.html b/asreview/webapp/templates/emails/confirm_account.html index 5e013ec82..b5ae50494 100644 --- a/asreview/webapp/templates/emails/confirm_account.html +++ b/asreview/webapp/templates/emails/confirm_account.html @@ -1,14 +1,17 @@

Hello {{ name }},

-

Thank you for joining ASReview. You are almost ready! - Please click on the following link to confirm your account:

+

+ Thank you for joining ASReview. You are almost ready! Please click on the + following link to confirm your account: +

{{ url }}

-

After confirmation you will be redirected to the signin page. - Use your account credentials and start using ASReview. +

+ After confirmation you will be redirected to the signin page. Use your account + credentials and start using ASReview.

Regards,

-

The ASReview Team

\ No newline at end of file +

The ASReview Team

diff --git a/asreview/webapp/templates/emails/forgot_password.html b/asreview/webapp/templates/emails/forgot_password.html index cb42cfffa..8e7bc97b9 100644 --- a/asreview/webapp/templates/emails/forgot_password.html +++ b/asreview/webapp/templates/emails/forgot_password.html @@ -6,4 +6,4 @@

Regards,

-

The ASReview Team

\ No newline at end of file +

The ASReview Team

diff --git a/asreview/webapp/tests/README.md b/asreview/webapp/tests/README.md index 102466601..9641a7267 100644 --- a/asreview/webapp/tests/README.md +++ b/asreview/webapp/tests/README.md @@ -2,19 +2,19 @@ This folder contains the test suite of the ASReview app. It is organized in the following folders: -* __config__: Contains data to create user accounts and a number of json files to start the ASReview app in different modes (e.g. authenticated, authentication with verification, etc). +- **config**: Contains data to create user accounts and a number of json files to start the ASReview app in different modes (e.g. authenticated, authentication with verification, etc). -* __data__: Contains project data used in tests. +- **data**: Contains project data used in tests. -* __integration_tests__: Forthcoming. +- **integration_tests**: Forthcoming. -* __test_api__: Contains API related tests. These tests are independent, unit-like tests. +- **test_api**: Contains API related tests. These tests are independent, unit-like tests. -* __test_database_and_models__: Contains tests that focus on basic database operations and several SQL_Alchemy models that are used in the authenticated version of the ASReview app. +- **test_database_and_models**: Contains tests that focus on basic database operations and several SQL_Alchemy models that are used in the authenticated version of the ASReview app. -* __test_extensions__: Forthcoming. +- **test_extensions**: Forthcoming. -* __utils__: Contains various helper files that facilitate writing tests. +- **utils**: Contains various helper files that facilitate writing tests. ## Requirements @@ -23,7 +23,7 @@ This folder contains the test suite of the ASReview app. It is organized in the ## Fixtures -If you are unfamiliar with Pytest: fixtures enable a setup and teardown mechanism for all tests. They ensure a controlled initial state and clean up afterwards to make sure the next test is conducted with a clean slate. +If you are unfamiliar with Pytest: fixtures enable a setup and teardown mechanism for all tests. They ensure a controlled initial state and clean up afterwards to make sure the next test is conducted with a clean slate. In this suite, all fixtures are defined in `conftest.py` files. These fixtures are automatically picked up by Pytest and can be found in the `/webapp/tests`-folder, but also in nested folders. The nested conftest modules use fixtures defined in the tests-folder without actually importing them: Pytest finds the required fixture if you use their function-name as parameter in either a newly defined fixture or test function. @@ -35,7 +35,8 @@ Ideally a test function tests one particular feature and can be executed indepen ## Running the tests -__Important__: if you run the entire test stuite, please make sure you have compiles the app's assets: +**Important**: if you run the entire test stuite, please make sure you have compiles the app's assets: + ``` python setup.py compile_assets ``` @@ -52,8 +53,8 @@ The `-s` option enables capturing stdout (shows your print statements if there a pytest --random-order -s -v ./asreview/webapp/tests/test_api/ ``` -If you are in the middle of writing your tests, and your module contains many tests, it is more efficient to run a small cluster or a single test. One of the many possibilities is the `-k` option that executes only tests with a function name that ends with a certain postfix. In the next example we execute only test functions that end with the 'current` postfix in the `test_projects.py` module: +If you are in the middle of writing your tests, and your module contains many tests, it is more efficient to run a small cluster or a single test. One of the many possibilities is the `-k` option that executes only tests with a function name that ends with a certain postfix. In the next example we execute only test functions that end with the 'current`postfix in the`test_projects.py` module: ``` pytest --random-order -s -v ./asreview/webapp/tests/test_api/test_projects.py -k current -``` \ No newline at end of file +```