import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; import { Logo } from '@/auth/components/Logo'; import { Title } from '@/auth/components/Title'; import { useAuth } from '@/auth/hooks/useAuth'; import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState'; import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex'; import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; import { AppPath } from '@/types/AppPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { Modal } from '@/ui/layout/modal/components/Modal'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { zodResolver } from '@hookform/resolvers/zod'; import { Trans, useLingui } from '@lingui/react/macro'; import { isNonEmptyString } from '@sniptt/guards'; import { motion } from 'framer-motion'; import { useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; import { useParams } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { MainButton } from 'twenty-ui/input'; import { AnimatedEaseIn } from 'twenty-ui/utilities'; import { z } from 'zod'; import { useUpdatePasswordViaResetTokenMutation, useValidatePasswordResetTokenQuery, } from '~/generated-metadata/graphql'; import { useNavigateApp } from '~/hooks/useNavigateApp'; import { logError } from '~/utils/logError'; const validationSchema = z .object({ passwordResetToken: z.string(), newPassword: z .string() .regex(PASSWORD_REGEX, 'Password must be min. 8 characters'), }) .required(); type Form = z.infer; const StyledMainContainer = styled.div` display: flex; justify-content: flex-start; align-items: center; flex-direction: column; width: 100%; `; const StyledContentContainer = styled.div` margin-bottom: ${({ theme }) => theme.spacing(8)}; margin-top: ${({ theme }) => theme.spacing(4)}; width: 200px; `; const StyledForm = styled.form` align-items: center; display: flex; flex-direction: column; width: 100%; `; const StyledFullWidthMotionDiv = styled(motion.div)` width: 100%; `; const StyledInputContainer = styled.div` margin-bottom: ${({ theme }) => theme.spacing(3)}; `; const StyledMainButton = styled(MainButton)` margin-top: ${({ theme }) => theme.spacing(2)}; `; export const PasswordReset = () => { const { t } = useLingui(); const { enqueueSnackBar } = useSnackBar(); const workspacePublicData = useRecoilValue(workspacePublicDataState); const navigate = useNavigateApp(); const [email, setEmail] = useState(''); const [isTokenValid, setIsTokenValid] = useState(false); const theme = useTheme(); const passwordResetToken = useParams().passwordResetToken; const isLoggedIn = useIsLogged(); const { control, handleSubmit } = useForm
({ mode: 'onChange', defaultValues: { passwordResetToken: passwordResetToken ?? '', newPassword: '', }, resolver: zodResolver(validationSchema), }); useValidatePasswordResetTokenQuery({ variables: { token: passwordResetToken ?? '', }, skip: !passwordResetToken || isTokenValid, onError: (error) => { enqueueSnackBar(error?.message ?? 'Token Invalid', { variant: SnackBarVariant.Error, }); navigate(AppPath.Index); }, onCompleted: (data) => { setIsTokenValid(true); if (isNonEmptyString(data?.validatePasswordResetToken?.email)) { setEmail(data.validatePasswordResetToken.email); } }, }); const [updatePasswordViaToken, { loading: isUpdatingPassword }] = useUpdatePasswordViaResetTokenMutation(); const { signInWithCredentialsInWorkspace } = useAuth(); const { readCaptchaToken } = useReadCaptchaToken(); const onSubmit = async (formData: Form) => { try { const { data } = await updatePasswordViaToken({ variables: { token: formData.passwordResetToken, newPassword: formData.newPassword, }, }); if (!data?.updatePasswordViaResetToken.success) { enqueueSnackBar(t`There was an error while updating password.`, { variant: SnackBarVariant.Error, }); return; } if (isLoggedIn) { enqueueSnackBar(t`Password has been updated`, { variant: SnackBarVariant.Success, }); navigate(AppPath.Index); return; } const token = await readCaptchaToken(); await signInWithCredentialsInWorkspace( email || '', formData.newPassword, token, ); navigate(AppPath.Index); } catch (err) { logError(err); enqueueSnackBar( err instanceof Error ? err.message : t`An error occurred while updating password`, { variant: SnackBarVariant.Error, }, ); } }; return ( isTokenValid && ( <Trans>Reset Password</Trans> {!email ? ( ) : ( ( )} /> )} ) ); };