webapp/src/components/Nav/AuthModal/ForgotPasswordForm.tsx

165 lines
4.8 KiB
TypeScript
Raw Normal View History

import type { AuthModalSearchParams } from './types'
2022-10-21 18:17:04 +00:00
import { clsx } from 'clsx'
2022-10-31 16:40:55 +00:00
import { createSignal, JSX, Show } from 'solid-js'
import { useLocalize } from '../../../context/localize'
2023-12-15 13:45:34 +00:00
import { useSession } from '../../../context/session'
2023-12-24 08:16:41 +00:00
// import { ApiError } from '../../../graphql/error'
2022-10-21 18:17:04 +00:00
import { useRouter } from '../../../stores/router'
import { validateEmail } from '../../../utils/validateEmail'
2022-10-21 18:17:04 +00:00
import { email, setEmail } from './sharedLogic'
import styles from './AuthModal.module.scss'
2022-10-21 18:17:04 +00:00
type FormFields = {
email: string
}
type ValidationErrors = Partial<Record<keyof FormFields, string | JSX.Element>>
export const ForgotPasswordForm = () => {
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
2023-12-24 12:56:30 +00:00
const { t } = useLocalize()
2022-10-21 18:17:04 +00:00
const handleEmailInput = (newEmail: string) => {
setValidationErrors(({ email: _notNeeded, ...rest }) => rest)
setEmail(newEmail)
}
2023-12-14 11:49:55 +00:00
const {
actions: { authorizer },
} = useSession()
2022-10-21 18:17:04 +00:00
const [submitError, setSubmitError] = createSignal('')
const [isSubmitting, setIsSubmitting] = createSignal(false)
const [validationErrors, setValidationErrors] = createSignal<ValidationErrors>({})
2022-11-14 01:17:12 +00:00
const [isUserNotFount, setIsUserNotFound] = createSignal(false)
const authFormRef: { current: HTMLFormElement } = { current: null }
2023-12-03 10:22:42 +00:00
const [message, setMessage] = createSignal<string>('')
2022-10-21 18:17:04 +00:00
const handleSubmit = async (event: Event) => {
event.preventDefault()
setSubmitError('')
2022-11-14 01:17:12 +00:00
setIsUserNotFound(false)
2022-10-21 18:17:04 +00:00
const newValidationErrors: ValidationErrors = {}
if (!email()) {
newValidationErrors.email = t('Please enter email')
} else if (!validateEmail(email())) {
2022-10-21 18:17:04 +00:00
newValidationErrors.email = t('Invalid email')
}
setValidationErrors(newValidationErrors)
const isValid = Object.keys(newValidationErrors).length === 0
if (!isValid) {
authFormRef.current
.querySelector<HTMLInputElement>(`input[name="${Object.keys(newValidationErrors)[0]}"]`)
.focus()
2022-10-21 18:17:04 +00:00
return
}
setIsSubmitting(true)
try {
2023-12-03 10:22:42 +00:00
const response = await authorizer().forgotPassword({
email: email(),
2023-12-24 08:16:41 +00:00
redirect_uri: window.location.origin,
2023-12-03 10:22:42 +00:00
})
2023-12-24 12:56:30 +00:00
console.debug('[ForgotPasswordForm] authorizer response:', response)
2023-12-24 08:16:41 +00:00
if (response && response.message) setMessage(response.message)
2022-10-21 18:17:04 +00:00
} catch (error) {
2023-12-24 08:16:41 +00:00
console.error(error)
if (error?.code === 'user_not_found') {
2022-11-14 01:17:12 +00:00
setIsUserNotFound(true)
return
}
2023-12-24 08:16:41 +00:00
setSubmitError(error?.message)
2022-10-21 18:17:04 +00:00
} finally {
setIsSubmitting(false)
}
}
return (
<form
onSubmit={handleSubmit}
class={clsx(styles.authForm, styles.authFormForgetPassword)}
ref={(el) => (authFormRef.current = el)}
>
2023-05-18 20:02:19 +00:00
<div>
2023-12-24 08:16:41 +00:00
<h4>{t('Restore password')}</h4>
2023-12-03 10:22:42 +00:00
<div class={styles.authSubtitle}>
{t(message()) || t('Everything is ok, please give us your email address')}
</div>
2023-08-21 11:11:18 +00:00
<div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().email,
2023-08-21 11:11:18 +00:00
})}
>
<input
2023-12-24 08:16:41 +00:00
disabled={Boolean(message())}
2023-08-21 11:11:18 +00:00
id="email"
name="email"
autocomplete="email"
type="email"
value={email()}
placeholder={t('Email')}
onInput={(event) => handleEmailInput(event.currentTarget.value)}
/>
<label for="email">{t('Email')}</label>
</div>
2023-05-18 20:02:19 +00:00
<Show when={submitError()}>
<div class={styles.authInfo}>
<ul>
<li class={styles.warn}>{submitError()}</li>
</ul>
</div>
</Show>
2023-08-21 11:11:18 +00:00
2023-05-18 20:02:19 +00:00
<Show when={isUserNotFount()}>
<div class={styles.authSubtitle}>
{t("We can't find you, check email or")}{' '}
<a
href="#"
onClick={(event) => {
event.preventDefault()
changeSearchParams({
mode: 'register',
})
2023-05-18 20:02:19 +00:00
}}
>
{t('register')}
</a>
2023-08-30 21:30:15 +00:00
<Show when={validationErrors().email}>
<div class={styles.validationError}>{validationErrors().email}</div>
</Show>
2023-05-18 20:02:19 +00:00
</div>
</Show>
2023-08-21 11:11:18 +00:00
2023-05-18 20:02:19 +00:00
<div>
2023-12-24 08:16:41 +00:00
<button
class={clsx('button', styles.submitButton)}
disabled={isSubmitting() || Boolean(message())}
type="submit"
>
2023-05-18 20:02:19 +00:00
{isSubmitting() ? '...' : t('Restore password')}
</button>
</div>
<div class={styles.authControl}>
<span
class={styles.authLink}
onClick={() =>
changeSearchParams({
mode: 'login',
})
}
>
2023-05-18 20:02:19 +00:00
{t('I know the password')}
</span>
</div>
2022-10-21 18:17:04 +00:00
</div>
</form>
)
}