Skip to content

Commit eb1d443

Browse files
committed
add: confirm email change page. update: change email behavior in account settings
1 parent fd366d0 commit eb1d443

4 files changed

Lines changed: 165 additions & 4 deletions

File tree

packages/ui/src/api/account.api.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import client from '@/api/client'
33
const inviteAccount = (body) => client.post(`/account/invite`, body)
44
const registerAccount = (body) => client.post(`/account/register`, body)
55
const verifyAccountEmail = (body) => client.post('/account/verify', body)
6+
const confirmEmailChange = (body) => client.post('/account/confirm-email-change', body)
67
const resendVerificationEmail = (body) => client.post('/account/resend-verification', body)
78
const forgotPassword = (body) => client.post('/account/forgot-password', body)
89
const resetPassword = (body) => client.post('/account/reset-password', body)
@@ -16,6 +17,7 @@ export default {
1617
inviteAccount,
1718
registerAccount,
1819
verifyAccountEmail,
20+
confirmEmailChange,
1921
resendVerificationEmail,
2022
forgotPassword,
2123
resetPassword,

packages/ui/src/routes/AuthRoutes.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const ResolveLoginPage = Loadable(lazy(() => import('@/views/auth/login')))
77
const SignInPage = Loadable(lazy(() => import('@/views/auth/signIn')))
88
const RegisterPage = Loadable(lazy(() => import('@/views/auth/register')))
99
const VerifyEmailPage = Loadable(lazy(() => import('@/views/auth/verify-email')))
10+
const ConfirmEmailChangePage = Loadable(lazy(() => import('@/views/auth/confirm-email-change')))
1011
const ForgotPasswordPage = Loadable(lazy(() => import('@/views/auth/forgotPassword')))
1112
const ResetPasswordPage = Loadable(lazy(() => import('@/views/auth/resetPassword')))
1213
const UnauthorizedPage = Loadable(lazy(() => import('@/views/auth/unauthorized')))
@@ -34,6 +35,10 @@ const AuthRoutes = {
3435
path: '/verify',
3536
element: <VerifyEmailPage />
3637
},
38+
{
39+
path: '/confirm-email-change',
40+
element: <ConfirmEmailChangePage />
41+
},
3742
{
3843
path: '/forgot-password',
3944
element: <ForgotPasswordPage />

packages/ui/src/views/account/index.jsx

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,29 @@ const AccountSettings = () => {
221221
email: email
222222
}
223223
const saveProfileResp = await userApi.updateUser(obj)
224-
if (saveProfileResp.data) {
225-
store.dispatch(userProfileUpdated(saveProfileResp.data))
224+
const payload = saveProfileResp.data
225+
if (payload?.user) {
226+
store.dispatch(userProfileUpdated(payload.user))
227+
const pendingMsg =
228+
payload.emailChangePending &&
229+
`Check your current email (${payload.user.email}) to confirm the change to ${payload.pendingEmail}.`
230+
enqueueSnackbar({
231+
message: pendingMsg || 'Profile updated',
232+
options: {
233+
key: new Date().getTime() + Math.random(),
234+
variant: 'success',
235+
action: (key) => (
236+
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
237+
<IconX />
238+
</Button>
239+
)
240+
}
241+
})
242+
if (payload.user.email) {
243+
setEmail(payload.user.email)
244+
}
245+
} else if (payload) {
246+
store.dispatch(userProfileUpdated(payload))
226247
enqueueSnackbar({
227248
message: 'Profile updated',
228249
options: {
@@ -235,6 +256,9 @@ const AccountSettings = () => {
235256
)
236257
}
237258
})
259+
if (payload.email) {
260+
setEmail(payload.email)
261+
}
238262
}
239263
} catch (error) {
240264
enqueueSnackbar({
@@ -292,8 +316,10 @@ const AccountSettings = () => {
292316
confirmPassword
293317
}
294318
const saveProfileResp = await userApi.updateUser(obj)
295-
if (saveProfileResp.data) {
296-
store.dispatch(userProfileUpdated(saveProfileResp.data))
319+
const pwdPayload = saveProfileResp.data
320+
const updatedUser = pwdPayload?.user ?? pwdPayload
321+
if (updatedUser) {
322+
store.dispatch(userProfileUpdated(updatedUser))
297323
setOldPassword('')
298324
setNewPassword('')
299325
setConfirmPassword('')
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { useEffect, useState } from 'react'
2+
import { useNavigate, useSearchParams } from 'react-router-dom'
3+
4+
// material-ui
5+
import { Stack, Typography, Box, useTheme, CircularProgress } from '@mui/material'
6+
7+
// project imports
8+
import MainCard from '@/ui-component/cards/MainCard'
9+
10+
// API
11+
import accountApi from '@/api/account.api'
12+
13+
// Hooks
14+
import useApi from '@/hooks/useApi'
15+
16+
// icons
17+
import { IconCheck, IconX } from '@tabler/icons-react'
18+
19+
const ConfirmEmailChange = () => {
20+
const confirmApi = useApi(accountApi.confirmEmailChange)
21+
22+
const [searchParams] = useSearchParams()
23+
const [loading, setLoading] = useState(false)
24+
const [errorMessage, setErrorMessage] = useState('')
25+
const [success, setSuccess] = useState(false)
26+
const navigate = useNavigate()
27+
28+
const theme = useTheme()
29+
30+
useEffect(() => {
31+
if (confirmApi.data) {
32+
setLoading(false)
33+
setErrorMessage('')
34+
setSuccess(true)
35+
setTimeout(() => {
36+
navigate('/signin')
37+
}, 3000)
38+
}
39+
// eslint-disable-next-line react-hooks/exhaustive-deps
40+
}, [confirmApi.data])
41+
42+
useEffect(() => {
43+
if (confirmApi.error) {
44+
setLoading(false)
45+
setErrorMessage(confirmApi.error)
46+
setSuccess(false)
47+
}
48+
}, [confirmApi.error])
49+
50+
useEffect(() => {
51+
const token = searchParams.get('token')
52+
if (token) {
53+
setLoading(true)
54+
setErrorMessage('')
55+
setSuccess(false)
56+
confirmApi.request({ user: { tempToken: token } })
57+
}
58+
// eslint-disable-next-line react-hooks/exhaustive-deps
59+
}, [])
60+
61+
return (
62+
<MainCard>
63+
<Stack flexDirection='column' sx={{ width: '480px', gap: 3 }}>
64+
<Stack sx={{ width: '100%', alignItems: 'center', justifyContent: 'center', gap: 4 }}>
65+
<Stack sx={{ alignItems: 'center', gap: 2 }}>
66+
{loading && (
67+
<>
68+
<CircularProgress
69+
sx={{
70+
width: '48px',
71+
height: '48px'
72+
}}
73+
/>
74+
<Typography variant='h1'>Confirming email change...</Typography>
75+
</>
76+
)}
77+
{errorMessage && (
78+
<>
79+
<Box
80+
sx={{
81+
width: '48px',
82+
height: '48px',
83+
borderRadius: '100%',
84+
backgroundColor: theme.palette.error.main,
85+
color: 'white',
86+
display: 'flex',
87+
alignItems: 'center',
88+
justifyContent: 'center'
89+
}}
90+
>
91+
<IconX />
92+
</Box>
93+
<Typography variant='h1'>Confirmation failed.</Typography>
94+
<Typography variant='body2' color='textSecondary' sx={{ textAlign: 'center' }}>
95+
{errorMessage}
96+
</Typography>
97+
</>
98+
)}
99+
{success && (
100+
<>
101+
<Box
102+
sx={{
103+
width: '48px',
104+
height: '48px',
105+
borderRadius: '100%',
106+
backgroundColor: theme.palette.success.main,
107+
color: 'white',
108+
display: 'flex',
109+
alignItems: 'center',
110+
justifyContent: 'center'
111+
}}
112+
>
113+
<IconCheck />
114+
</Box>
115+
<Typography variant='h1'>Email updated successfully.</Typography>
116+
<Typography variant='body2' color='textSecondary' sx={{ textAlign: 'center' }}>
117+
Please sign in with your new email address.
118+
</Typography>
119+
</>
120+
)}
121+
</Stack>
122+
</Stack>
123+
</Stack>
124+
</MainCard>
125+
)
126+
}
127+
128+
export default ConfirmEmailChange

0 commit comments

Comments
 (0)