mirror of
https://github.com/sasjs/server.git
synced 2025-12-10 19:34:34 +00:00
feat(web): add the UI for updating user password
This commit is contained in:
141
web/src/components/passwordModal.tsx
Normal file
141
web/src/components/passwordModal.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import {
|
||||
Grid,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
OutlinedInput,
|
||||
InputAdornment,
|
||||
IconButton,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
FormHelperText
|
||||
} from '@mui/material'
|
||||
import Visibility from '@mui/icons-material/Visibility'
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff'
|
||||
|
||||
import { BootstrapDialogTitle } from './dialogTitle'
|
||||
import { BootstrapDialog } from './modal'
|
||||
|
||||
type Props = {
|
||||
open: boolean
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>
|
||||
title: string
|
||||
updatePassword: (currentPassword: string, newPassword: string) => void
|
||||
}
|
||||
|
||||
const UpdatePasswordModal = (props: Props) => {
|
||||
const { open, setOpen, title, updatePassword } = props
|
||||
const [currentPassword, setCurrentPassword] = useState('')
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
const [hasError, setHasError] = useState(false)
|
||||
const [errorText, setErrorText] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (newPassword.length >= 6) {
|
||||
setErrorText('')
|
||||
setHasError(false)
|
||||
}
|
||||
}, [newPassword])
|
||||
|
||||
const handleBlur = () => {
|
||||
if (newPassword.length < 6) {
|
||||
setErrorText('Password length should be at least 6 characters.')
|
||||
setHasError(true)
|
||||
} else {
|
||||
setErrorText('')
|
||||
setHasError(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapDialog onClose={() => setOpen(false)} open={open}>
|
||||
<BootstrapDialogTitle id="abort-modal" handleOpen={setOpen}>
|
||||
{title}
|
||||
</BootstrapDialogTitle>
|
||||
<DialogContent dividers>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<PasswordInput
|
||||
label="Current Password"
|
||||
password={currentPassword}
|
||||
setPassword={setCurrentPassword}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<PasswordInput
|
||||
label="New Password"
|
||||
password={newPassword}
|
||||
setPassword={setNewPassword}
|
||||
hasError={hasError}
|
||||
errorText={errorText}
|
||||
handleBlur={handleBlur}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => updatePassword(currentPassword, newPassword)}
|
||||
disabled={hasError || !currentPassword || !newPassword}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</BootstrapDialog>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UpdatePasswordModal
|
||||
|
||||
type PasswordInputProps = {
|
||||
label: string
|
||||
password: string
|
||||
setPassword: React.Dispatch<React.SetStateAction<string>>
|
||||
hasError?: boolean
|
||||
errorText?: string
|
||||
handleBlur?: () => void
|
||||
}
|
||||
|
||||
const PasswordInput = ({
|
||||
label,
|
||||
password,
|
||||
setPassword,
|
||||
hasError,
|
||||
errorText,
|
||||
handleBlur
|
||||
}: PasswordInputProps) => {
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
|
||||
return (
|
||||
<FormControl sx={{ width: '100%' }} variant="outlined" error={hasError}>
|
||||
<InputLabel htmlFor="outlined-adornment-password">{label}</InputLabel>
|
||||
<OutlinedInput
|
||||
id="outlined-adornment-password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
label={label}
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
onBlur={handleBlur}
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={() => setShowPassword((val) => !val)}
|
||||
edge="end"
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
{errorText && <FormHelperText>{errorText}</FormHelperText>}
|
||||
</FormControl>
|
||||
)
|
||||
}
|
||||
@@ -17,11 +17,13 @@ import {
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
import { AppContext, ModeType } from '../../context/appContext'
|
||||
import UpdatePasswordModal from '../../components/passwordModal'
|
||||
|
||||
const Profile = () => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const appContext = useContext(AppContext)
|
||||
const [user, setUser] = useState({} as any)
|
||||
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true)
|
||||
@@ -36,7 +38,7 @@ const Profile = () => {
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}, [])
|
||||
}, [appContext.userId])
|
||||
|
||||
const handleChange = (event: any) => {
|
||||
const { name, value } = event.target
|
||||
@@ -68,82 +70,124 @@ const Profile = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const updatePassword = (currentPassword: string, newPassword: string) => {
|
||||
setIsLoading(true)
|
||||
setIsPasswordModalOpen(false)
|
||||
axios
|
||||
.patch(`/SASjsApi/auth/updatePassword`, {
|
||||
currentPassword,
|
||||
newPassword
|
||||
})
|
||||
.then((res: any) => {
|
||||
toast.success('Password updated', {
|
||||
theme: 'dark',
|
||||
position: toast.POSITION.BOTTOM_RIGHT
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('Failed: ' + err.response?.data || err.text, {
|
||||
theme: 'dark',
|
||||
position: toast.POSITION.BOTTOM_RIGHT
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
return isLoading ? (
|
||||
<CircularProgress
|
||||
style={{ position: 'absolute', left: '50%', top: '50%' }}
|
||||
/>
|
||||
) : (
|
||||
<Card>
|
||||
<CardHeader title="Profile Information" />
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<Grid container spacing={4}>
|
||||
<Grid item md={6} xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
error={user.displayName?.length === 0}
|
||||
helperText="Please specify display name"
|
||||
label="Display Name"
|
||||
name="displayName"
|
||||
onChange={handleChange}
|
||||
required
|
||||
value={user.displayName}
|
||||
variant="outlined"
|
||||
disabled={appContext.mode === ModeType.Desktop}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item md={6} xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
error={user.username?.length === 0}
|
||||
helperText="Please specify username"
|
||||
label="Username"
|
||||
name="username"
|
||||
onChange={handleChange}
|
||||
required
|
||||
value={user.username}
|
||||
variant="outlined"
|
||||
disabled={appContext.mode === ModeType.Desktop}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item lg={6} md={8} sm={12} xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="autoExec"
|
||||
name="autoExec"
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
rows="10"
|
||||
value={user.autoExec}
|
||||
variant="outlined"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6}>
|
||||
<FormGroup row>
|
||||
<FormControlLabel
|
||||
disabled
|
||||
control={<Checkbox checked={user.isActive} />}
|
||||
label="isActive"
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader title="Profile Information" />
|
||||
<Divider />
|
||||
<CardContent>
|
||||
<Grid container spacing={4}>
|
||||
<Grid item md={6} xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
error={user.displayName?.length === 0}
|
||||
helperText="Please specify display name"
|
||||
label="Display Name"
|
||||
name="displayName"
|
||||
onChange={handleChange}
|
||||
required
|
||||
value={user.displayName}
|
||||
variant="outlined"
|
||||
disabled={appContext.mode === ModeType.Desktop}
|
||||
/>
|
||||
<FormControlLabel
|
||||
disabled
|
||||
control={<Checkbox checked={user.isAdmin} />}
|
||||
label="isAdmin"
|
||||
</Grid>
|
||||
|
||||
<Grid item md={6} xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
error={user.username?.length === 0}
|
||||
helperText="Please specify username"
|
||||
label="Username"
|
||||
name="username"
|
||||
onChange={handleChange}
|
||||
required
|
||||
value={user.username}
|
||||
variant="outlined"
|
||||
disabled={appContext.mode === ModeType.Desktop}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Grid>
|
||||
|
||||
<Grid item lg={6} md={8} sm={12} xs={12}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="autoExec"
|
||||
name="autoExec"
|
||||
onChange={handleChange}
|
||||
multiline
|
||||
rows="10"
|
||||
value={user.autoExec}
|
||||
variant="outlined"
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6}>
|
||||
<FormGroup row>
|
||||
<FormControlLabel
|
||||
disabled
|
||||
control={<Checkbox checked={user.isActive} />}
|
||||
label="isActive"
|
||||
/>
|
||||
<FormControlLabel
|
||||
disabled
|
||||
control={<Checkbox checked={user.isAdmin} />}
|
||||
label="isAdmin"
|
||||
/>
|
||||
</FormGroup>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => setIsPasswordModalOpen(true)}
|
||||
>
|
||||
Update Password
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardActions>
|
||||
<Button type="submit" variant="contained" onClick={handleSubmit}>
|
||||
Save Changes
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</CardContent>
|
||||
<Divider />
|
||||
<CardActions>
|
||||
<Button type="submit" variant="contained" onClick={handleSubmit}>
|
||||
Save Changes
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
<UpdatePasswordModal
|
||||
open={isPasswordModalOpen}
|
||||
setOpen={setIsPasswordModalOpen}
|
||||
title="Update Password"
|
||||
updatePassword={updatePassword}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user