From ab6bf192021285e51cd1a18fbba6c2d9bf2ec1c8 Mon Sep 17 00:00:00 2001 From: Zoltan Runyo <zoltan.runyo@ess.eu> Date: Mon, 22 Nov 2021 14:58:52 +0000 Subject: [PATCH] ICSHWI-8272: Refine error handling --- src/api/SwaggerApi.js | 52 ++++++++++++------- src/components/IOC/CreateIOC.js | 13 +++-- src/components/IOC/DeployIOC.js | 12 +++-- src/components/IOC/IOCCreateDialog.js | 12 +++-- src/components/IOC/IOCDelete.js | 32 +++++++++--- src/components/IOC/IOCDeployDialog.js | 17 ++++-- src/components/IOC/IOCDetailAdmin.js | 19 +++++-- src/components/IOC/IOCUndeployDialog.js | 6 ++- src/components/IOC/UndeployIOC.js | 10 ++-- src/components/auth/Login.js | 11 ++-- .../navigation/GlobalAppBar/GlobalAppBar.js | 12 +++-- 11 files changed, 140 insertions(+), 56 deletions(-) diff --git a/src/api/SwaggerApi.js b/src/api/SwaggerApi.js index 56ee34bb..9045bd21 100644 --- a/src/api/SwaggerApi.js +++ b/src/api/SwaggerApi.js @@ -37,13 +37,20 @@ export const apiContext = createContext(null); export const userContext = createContext({ user: null, userRoles:[], getUserRoles: ()=>{}, login: ()=>{}, logout: ()=>{} }); export function UserProvider({ children }) { + function onLoginError(message) { + setLoginError(message); + } + const [user, setUser] = useState(); + const [loginError, setLoginError] = useState(); const [userRoles, getUserRoles, resetUserRoles] = useUserRoles(); const [userInfo, getUserInfo] = useUserInfo(); - const [loginResponse, loginFcn] = useLogin(); + const [loginResponse, loginFcn] = useLogin(onLoginError); const logoutFcn = useLogout(); const history = useHistory(); + const resetLoginError = () => setLoginError(null); + console.log("rendering UserProvider") console.log(user); @@ -69,7 +76,7 @@ export function UserProvider({ children }) { } return ( - <userContext.Provider value={{ user, userRoles, getUserRoles, login, logout }}> + <userContext.Provider value={{ user, userRoles, getUserRoles, login, loginError, resetLoginError, logout }}> {children} </userContext.Provider> ); @@ -90,7 +97,7 @@ export function APIProvider({ children }) { <CircularProgress />; } -export function useAPIErrorHandler() { +export function useAPIErrorHandler(onError) { const {logout} = useContext(userContext); const showError = useCustomSnackbar(); @@ -100,6 +107,9 @@ export function useAPIErrorHandler() { if (status === 401) { logout(); } + else if (onError){ + onError(obj?.description ?? message); + } else { showError(obj?.description ?? message); } @@ -110,7 +120,7 @@ export function useAPIErrorHandler() { export function useAsync({ fcn, call = true, init = null, onError = null }) { const [response, setResponse] = useState(init); - const errorHandler = useAPIErrorHandler(); + const errorHandler = useAPIErrorHandler(onError); const reset = () => setResponse(init); @@ -136,7 +146,7 @@ function callAndUnpack(fcn, unpacker = x => x) { return async (...args) => { console.log(...args); const response = await fcn(...args); - const unpacked = unpacker(response.obj); + const unpacked = unpacker(response.obj, response.status); return unpacked; }; } @@ -205,13 +215,13 @@ export function useIOC(id) { return useAsync({ fcn: method.bind(null, { iocId: id }) }); } -export function useCreateIOC() { +export function useCreateIOC(onError) { const api = useContext(apiContext); const method = callAndUnpack( body => api.apis.IOCs.createIoc({}, { requestBody: body }), unpackIOC ); - return useAsync({ fcn: method, call: false }); + return useAsync({ fcn: method, call: false, onError: onError }); } export function unpackDeployment(deployment) { @@ -303,22 +313,22 @@ export function useCommandJob(awxCommandId) { return useAsync({ fcn: method.bind(null, { awxCommandId: awxCommandId }), call: true }); } -export function useUpdateAndDeployIoc(id) { +export function useUpdateAndDeployIoc(id, onError) { const api = useContext(apiContext); const method = callAndUnpack( body => api.apis.Deployments.updateAndDeployIoc({ iocId: id }, { requestBody: body }), unpackDeployment ); - return useAsync({ fcn: method, call: false }); + return useAsync({ fcn: method, call: false, onError: onError }); } -export function useCreateUndeployment(id) { +export function useCreateUndeployment(id, onError) { const api = useContext(apiContext); const method = callAndUnpack( body => api.apis.Deployments.createUndeployment({iocId: id}, { requestBody: body }), unpackDeployment ); - return useAsync({ fcn: method, call: false }); + return useAsync({ fcn: method, call: false, onError: onError }); } export function unpackHost(host) { @@ -381,7 +391,7 @@ export function unpackLogin(loginResponse) { return { ...loginResponse }; } -export function useLogin() { +export function useLogin(onError) { const api = useContext(apiContext); const method = callAndUnpack( (username, password) => api.apis.Authentication.login({},{ @@ -392,7 +402,7 @@ export function useLogin() { }), unpackLogin ); - return useAsync({ fcn: method, call: false }); + return useAsync({ fcn: method, call: false, onError: onError }); } export function unpackUser(user) { @@ -560,19 +570,25 @@ export function useAllowedGitProjects() { return useAsync({ fcn: method, call: false, init: [] }); } -export function useDeleteIOC(id) { +export function useDeleteIOC(id, onError) { const api = useContext(apiContext); - const method = callAndUnpack(api.apis.IOCs.deleteIoc); - return useAsync({ fcn: method.bind(null, {iocId: id}), call: false }); + const method = callAndUnpack(api.apis.IOCs.deleteIoc, unpackDeleteIocResponse); + return useAsync({ fcn: method.bind(null, {iocId: id}), call: false, onError: onError }); +} + +export function unpackDeleteIocResponse(_, status) { + if (status === 204) { + return "Successful delete"; + } } -export function useUpdateIoc(id) { +export function useUpdateIoc(id, onError) { const api = useContext(apiContext); const method = callAndUnpack( body => api.apis.IOCs.updateIoc({ iocId: id }, { requestBody: body }), unpackUpdateIoc ); - return useAsync({ fcn: method, call: false }); + return useAsync({ fcn: method, call: false, onError: onError }); } export function unpackUpdateIoc(updateIoc) { diff --git a/src/components/IOC/CreateIOC.js b/src/components/IOC/CreateIOC.js index f24f32f1..fa768b03 100644 --- a/src/components/IOC/CreateIOC.js +++ b/src/components/IOC/CreateIOC.js @@ -1,13 +1,18 @@ -import React from "react"; +import React, {useState} from "react"; import { Redirect } from "react-router-dom"; import { IOCCreateDialog } from "./IOCCreateDialog"; // Process component -export function CreateIOC({ open, setOpen, submitCallback, hook, title, buttonText, isUpdateIoc, init={}}) { - const [ioc, action] = hook(); +export function CreateIOC({ open, setOpen, submitCallback, hook}) { + function onError(message) { + setError(message); + } + + const [ioc, action] = hook(onError); + const [error, setError] = useState(); if (!ioc) { - return (<IOCCreateDialog open={open} setOpen={setOpen} submitCallback={action} />); + return (<IOCCreateDialog open={open} setOpen={setOpen} submitCallback={action} error={error} resetError={() => setError(null)} />); } else { submitCallback(); // This works but throws a warning because I am changing state in the parent while the child is rerendering. Not sure yet how to fix. diff --git a/src/components/IOC/DeployIOC.js b/src/components/IOC/DeployIOC.js index 6c9f8196..ba394b72 100644 --- a/src/components/IOC/DeployIOC.js +++ b/src/components/IOC/DeployIOC.js @@ -1,15 +1,21 @@ -import React, { useContext} from "react"; +import React, { useContext, useState} from "react"; import { Redirect } from "react-router-dom"; import { IOCDeployDialog } from "./IOCDeployDialog"; import { notificationContext } from "../../components/common/notification/Notifications"; // Process component export function DeployIOC({ open, setOpen, submitCallback, hook, hasActiveDeployment, init={}}) { - const [deployment, action] = hook(); + function onError(message) { + setError(message); + } + + const [deployment, action] = hook(onError); + const [error, setError] = useState(); const {watchDeployment} = useContext(notificationContext); if (!deployment) { - return (<IOCDeployDialog open={open} setOpen={setOpen} submitCallback={action} init={init} hasActiveDeployment={hasActiveDeployment}/>); + return (<IOCDeployDialog open={open} setOpen={setOpen} submitCallback={action} init={init} + hasActiveDeployment={hasActiveDeployment} error={error} resetError={() => setError(null)}/>); } else { submitCallback(); // This works but throws a warning because I am changing state in the parent while the child is rerendering. Not sure yet how to fix. diff --git a/src/components/IOC/IOCCreateDialog.js b/src/components/IOC/IOCCreateDialog.js index 85ae5c80..e6cfabc5 100644 --- a/src/components/IOC/IOCCreateDialog.js +++ b/src/components/IOC/IOCCreateDialog.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { Button, TextField, Dialog, DialogActions, DialogContent, DialogTitle, makeStyles } from "@material-ui/core"; -import { Autocomplete } from "@material-ui/lab"; +import { Autocomplete, Alert } from "@material-ui/lab"; import { useNamingNames, useAllowedGitProjects } from "../../api/SwaggerApi"; import { useTypingTimer } from "../common/SearchBoxFilter/TypingTimer"; @@ -10,7 +10,7 @@ const useStyles = makeStyles((theme) => ({ }, })); -export function IOCCreateDialog({ open, setOpen, submitCallback }) { +export function IOCCreateDialog({ open, setOpen, submitCallback, error, resetError }) { const classes = useStyles(); const [names, getNames] = useNamingNames(); const [name, setName] = useState(); @@ -50,7 +50,7 @@ export function IOCCreateDialog({ open, setOpen, submitCallback }) { options={names} getOptionLabel={(option) => {console.log(option?.name); return option?.name}} renderInput={(params) => <TextField {...params} label="IOC name" variant="outlined" required/>} - onChange={(event, value, reason) => setName(value)} + onChange={(event, value, reason) => {setName(value); resetError();}} onInputChange={(event, value, reason) => {event && onNameKeyUp(event.nativeEvent)}} /> @@ -64,10 +64,14 @@ export function IOCCreateDialog({ open, setOpen, submitCallback }) { options={allowedGitProjects} defaultValue = { "" } getOptionLabel={(option) => {return option}} - onChange={(event, value, reason) => setGitUrl(value)} + onChange={(event, value, reason) => {setGitUrl(value); resetError();}} renderInput={(params) => <TextField {...params} label="Git repository" variant="outlined" fullWidth required />} /> + {error ? + <Alert severity="error">{error}</Alert> + : <></>} + </DialogContent> <DialogActions> <Button onClick={handleClose} color="primary">Cancel</Button> diff --git a/src/components/IOC/IOCDelete.js b/src/components/IOC/IOCDelete.js index 614319ed..7080a317 100644 --- a/src/components/IOC/IOCDelete.js +++ b/src/components/IOC/IOCDelete.js @@ -1,12 +1,13 @@ -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { useHistory } from "react-router-dom"; import { makeStyles } from '@material-ui/core/styles'; -import { Button, Typography } from "@material-ui/core"; +import { Button, Typography, Grid } from "@material-ui/core"; import { theme } from "../../Theme"; import { useDeleteIOC } from '../../api/SwaggerApi'; import { SimpleAccordion } from "../common/Accordion/SimpleAccordion"; import { ConfirmationDialog } from "../dialog/ConfirmationDialog"; import { SimpleModal } from "../../components/common/SimpleModal/SimpleModal"; +import { Alert } from "@material-ui/lab"; const useStyles = makeStyles({ deleteButton: { @@ -28,13 +29,18 @@ export default function IOCDelete({ ioc }) { const history = useHistory(); + function onError(message) { + setError(message); + } + //for the dialog + const [error, setError] = useState(); const [adHocDialogOpen, setAdHocDialogOpen] = useState(false); const [adHocDialogTitle, setAdHocDiatlogTitle] = useState(); const [adHocDialogDescription, setAdHocDialogDescription] = useState(); const callbackRef = useRef(); - const [, deleteIOC] = useDeleteIOC(ioc.id); + const [response, deleteIOC] = useDeleteIOC(ioc.id, onError); //Setting up dialog properties const openDeleteModal = () => { @@ -50,10 +56,15 @@ export default function IOCDelete({ ioc }) { //what to do when deleting IOC const deleteIoc = async () => { + deleteIOC(); + } - await deleteIOC(); + useEffect(() => + { + if (response) { history.goBack(); - } + } + }, [response, history]); return ( @@ -63,7 +74,16 @@ export default function IOCDelete({ ioc }) { </SimpleModal> <SimpleAccordion summary="Delete IOC" defaultExpanded> - <Button className={classes.deleteButton} onClick={openDeleteModal}>Delete IOC</Button> + <Grid container spacing={1}> + {error ? + <Grid item xs={12}> + <Alert severity="error">{error}</Alert> + </Grid> + : <></>} + <Grid item xs={12}> + <Button className={classes.deleteButton} onClick={openDeleteModal}>Delete IOC</Button> + </Grid> + </Grid> </SimpleAccordion> </> ); diff --git a/src/components/IOC/IOCDeployDialog.js b/src/components/IOC/IOCDeployDialog.js index 9480e205..2c5a7554 100644 --- a/src/components/IOC/IOCDeployDialog.js +++ b/src/components/IOC/IOCDeployDialog.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import { Button, TextField, Dialog, DialogActions, DialogContent, DialogTitle, Typography, makeStyles } from "@material-ui/core"; -import { Autocomplete } from "@material-ui/lab"; +import { Autocomplete, Alert } from "@material-ui/lab"; import { useCSEntrySearch, useTagsAndCommitIds } from "../../api/SwaggerApi"; import { useTypingTimer } from "../common/SearchBoxFilter/TypingTimer"; @@ -8,9 +8,12 @@ const useStyles = makeStyles((theme) => ({ textField: { marginBottom: theme.spacing(1), }, + alert: { + marginTop: theme.spacing(1), + }, })); -export function IOCDeployDialog({ open, setOpen, submitCallback, hasActiveDeployment, init = {}}) { +export function IOCDeployDialog({ open, setOpen, submitCallback, hasActiveDeployment, init = {}, error, resetError}) { const classes = useStyles(); const [hosts, getHosts] = useCSEntrySearch(); const [host, setHost] = useState(null); @@ -50,7 +53,7 @@ export function IOCDeployDialog({ open, setOpen, submitCallback, hasActiveDeploy <DialogContent> <TextField autoComplete="off" className={classes.textField} id="comment" label="Deployment comment" variant="outlined" fullWidth/> <TextField autoComplete="off" className={classes.textField} id="git" label="Git repository" variant="standard" defaultValue={init.git || ""} fullWidth - onChange={(event) => setGitRepo(event.target.value)} disabled required/> + onChange={(event) => {setGitRepo(event.target.value); resetError();}} disabled required/> <Autocomplete className={classes.textField} @@ -60,7 +63,7 @@ export function IOCDeployDialog({ open, setOpen, submitCallback, hasActiveDeploy defaultValue = { init.version || "" } onFocus={(event) => {getTagOrCommitId(gitRepo);}} getOptionLabel={(option) => {return option}} - onChange={(event, value, reason) => setGitVersion(value)} + onChange={(event, value, reason) => {setGitVersion(value); resetError();}} disabled={(!gitRepo) || (gitRepo.trim() === "")} renderInput={(params) => <TextField {...params} label="Git reference" variant="outlined" fullWidth required />} /> @@ -73,7 +76,7 @@ export function IOCDeployDialog({ open, setOpen, submitCallback, hasActiveDeploy defaultValue = { init } getOptionLabel={option => {console.log(option); return option?.csEntryHost?.fqdn}} renderInput={(params) => <TextField {...params} label="host" variant="outlined" required/>} - onChange={(event, value, reason) => setHost(value)} + onChange={(event, value, reason) => {setHost(value); resetError();}} onInputChange={(event, value, reason) => {event && onHostKeyUp(event.nativeEvent)}} disabled={hasActiveDeployment} /> @@ -84,6 +87,10 @@ export function IOCDeployDialog({ open, setOpen, submitCallback, hasActiveDeploy </Typography> : <></> } + + {error ? + <Alert severity="error"className={classes.alert}>{error}</Alert> + : <></>} </DialogContent> <DialogActions> <Button onClick={handleClose} color="primary">Cancel</Button> diff --git a/src/components/IOC/IOCDetailAdmin.js b/src/components/IOC/IOCDetailAdmin.js index 34ed4801..5cb1fc4f 100644 --- a/src/components/IOC/IOCDetailAdmin.js +++ b/src/components/IOC/IOCDetailAdmin.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import { SimpleAccordion } from "../common/Accordion/SimpleAccordion"; import { Button, makeStyles, TextField, Typography } from "@material-ui/core"; -import { Autocomplete } from "@material-ui/lab"; +import { Autocomplete, Alert } from "@material-ui/lab"; import { useAllowedGitProjects, useNamingNames, useUpdateIoc } from "../../api/SwaggerApi"; import { useTypingTimer } from "../common/SearchBoxFilter/TypingTimer"; import { ConfirmationDialog } from "../dialog/ConfirmationDialog"; @@ -27,9 +27,14 @@ export default function IOCDetailAdmin({ ioc, getIOC, resetTab }) { const [adHocDialogOpen, setAdHocDialogOpen] = useState(false); const [adHocDialogTitle, setAdHocDiatlogTitle] = useState(); const [adHocDialogDescription, setAdHocDialogDescription] = useState(); + const [error, setError] = useState(); const callbackRef = useRef(); - const [uioc, actionUpdateIoc] = useUpdateIoc(ioc.id); + function onError(message) { + setError(message); + } + + const [uioc, actionUpdateIoc] = useUpdateIoc(ioc.id, onError); useEffect(() => { getAllowedGitProjects(); @@ -90,7 +95,7 @@ export default function IOCDetailAdmin({ ioc, getIOC, resetTab }) { defaultValue={name} getOptionLabel={(option) => { console.log(option?.name); return option?.name }} renderInput={(params) => <TextField {...params} label="IOC name" variant="outlined" required />} - onChange={(event, value, reason) => setName(value)} + onChange={(event, value, reason) => {setName(value); setError(null);}} onInputChange={(event, value, reason) => { event && onNameKeyUp(event.nativeEvent) }} /> @@ -101,10 +106,14 @@ export default function IOCDetailAdmin({ ioc, getIOC, resetTab }) { options={allowedGitProjects} defaultValue={gitUrl} getOptionLabel={(option) => { return option }} - onChange={(event, value, reason) => setGitUrl(value)} + onChange={(event, value, reason) => {setGitUrl(value); setError(null);}} renderInput={(params) => <TextField {...params} label="Git repository" variant="outlined" fullWidth required />} /> - <TextField id="owner" label="Owner" variant="outlined" fullWidth required value={owner} onChange={(event) => setOwner(event.target.value)} /> + <TextField id="owner" className={classes.textField} label="Owner" variant="outlined" fullWidth required value={owner} onChange={(event) => {setOwner(event.target.value); setError(null);}} /> + + {error ? + <Alert severity="error">{error}</Alert> + : <></>} <br /><br /> <Button color="primary" variant="contained" type="submit">Modify IOC</Button> diff --git a/src/components/IOC/IOCUndeployDialog.js b/src/components/IOC/IOCUndeployDialog.js index be0781e2..426c7e7f 100644 --- a/src/components/IOC/IOCUndeployDialog.js +++ b/src/components/IOC/IOCUndeployDialog.js @@ -1,5 +1,6 @@ import React from "react"; import { Button, TextField, Dialog, DialogActions, DialogContent, DialogTitle, DialogContentText, makeStyles } from "@material-ui/core"; +import { Alert } from "@material-ui/lab"; const useStyles = makeStyles((theme) => ({ textField: { @@ -7,7 +8,7 @@ const useStyles = makeStyles((theme) => ({ }, })); -export function IOCUndeployDialog({ open, setOpen, submitCallback, ioc }) { +export function IOCUndeployDialog({ open, setOpen, submitCallback, ioc , error }) { const classes = useStyles(); const handleClose = () => { @@ -32,6 +33,9 @@ export function IOCUndeployDialog({ open, setOpen, submitCallback, ioc }) { Do you really want to undeploy {ioc.latestVersion.namingName} from {ioc.activeDeployment.host.host}? </DialogContentText> <TextField autoComplete="off" className={classes.textField} id="comment" label="Undeployment comment" variant="outlined" fullWidth/> + {error ? + <Alert severity="error"className={classes.alert}>{error}</Alert> + : <></>} </DialogContent> <DialogActions> <Button onClick={handleClose} color="primary">Cancel</Button> diff --git a/src/components/IOC/UndeployIOC.js b/src/components/IOC/UndeployIOC.js index e5e9e208..cc05b5a6 100644 --- a/src/components/IOC/UndeployIOC.js +++ b/src/components/IOC/UndeployIOC.js @@ -1,15 +1,19 @@ -import React, { useContext} from "react"; +import React, { useContext, useState} from "react"; import { Redirect } from "react-router-dom"; import { IOCUndeployDialog } from "./IOCUndeployDialog"; import { notificationContext } from "../../components/common/notification/Notifications"; // Process component export function UndeployIOC({ open, setOpen, submitCallback, hook, ioc }) { - const [deployment, action] = hook(); + function onError(message) { + setError(message); + } + const [deployment, action] = hook(onError); + const [error, setError] = useState(); const {watchDeployment} = useContext(notificationContext); if (!deployment) { - return (<IOCUndeployDialog open={open} setOpen={setOpen} submitCallback={action} ioc={ioc}/>); + return (<IOCUndeployDialog open={open} setOpen={setOpen} submitCallback={action} ioc={ioc} error={error}/>); } else { submitCallback(); // This works but throws a warning because I am changing state in the parent while the child is rerendering. Not sure yet how to fix. diff --git a/src/components/auth/Login.js b/src/components/auth/Login.js index 690dfe60..a185181b 100644 --- a/src/components/auth/Login.js +++ b/src/components/auth/Login.js @@ -8,6 +8,7 @@ import { DialogContent, makeStyles } from '@material-ui/core'; +import { Alert } from "@material-ui/lab"; const useStyles = makeStyles((theme) => ({ textField: { @@ -15,7 +16,7 @@ const useStyles = makeStyles((theme) => ({ }, })); -export default function Login({ auth, open, setOpen }) { +export default function Login({ auth, open, setOpen, error, resetError }) { const classes = useStyles(); const handleClose = () => { @@ -27,7 +28,6 @@ export default function Login({ auth, open, setOpen }) { const username = document.getElementById('username').value; const password = document.getElementById('password').value; await auth(username, password); - setOpen(false); } return ( @@ -35,8 +35,11 @@ export default function Login({ auth, open, setOpen }) { <form> <DialogTitle id="form-dialog-title">Login</DialogTitle> <DialogContent> - <TextField id="username" className={classes.textField} label="Username" variant="filled" fullWidth autoFocus /> - <TextField id="password" className={classes.textField} label="Password" variant="filled" type="password" fullWidth /> + <TextField id="username" className={classes.textField} label="Username" variant="filled" onChange={resetError} fullWidth autoFocus required /> + <TextField id="password" className={classes.textField} label="Password" variant="filled" type="password" onChange={resetError} fullWidth required /> + {error ? + <Alert severity="error">{error}</Alert> + : <></>} </DialogContent> <DialogActions> <Button onClick={handleClose} color="primary">Cancel</Button> diff --git a/src/components/navigation/GlobalAppBar/GlobalAppBar.js b/src/components/navigation/GlobalAppBar/GlobalAppBar.js index 5ba11efc..3a23e3e6 100644 --- a/src/components/navigation/GlobalAppBar/GlobalAppBar.js +++ b/src/components/navigation/GlobalAppBar/GlobalAppBar.js @@ -178,7 +178,7 @@ export function UserInfo({ user }) { export function ProfileMenu() { const theme = useTheme(); - const { user, logout, login } = useContext(userContext); + const { user, logout, login, loginError, resetLoginError } = useContext(userContext); const [anchorEl, setAnchorEl] = useState(null); const [loginFormOpen, setLoginFormOpen] = useState(false); const history = useHistory(); @@ -195,6 +195,12 @@ export function ProfileMenu() { history.push("/home"); } + useEffect(() => { + if (user) { + setLoginFormOpen(false); + } + }, [user, setLoginFormOpen]); + return ( <> { user ? @@ -219,9 +225,9 @@ export function ProfileMenu() { </> : <> - <Button color="inherit" onClick={() => { setLoginFormOpen(true) }}>Login</Button> + <Button color="inherit" onClick={() => { resetLoginError(); setLoginFormOpen(true) }}>Login</Button> <SimpleModal open={loginFormOpen} setOpen={setLoginFormOpen}> - <Login open={loginFormOpen} setOpen={setLoginFormOpen} auth={login}/> + <Login open={loginFormOpen} setOpen={setLoginFormOpen} auth={login} error={loginError} resetError={resetLoginError}/> </SimpleModal> </> } -- GitLab