fix(): include workspace in reset password flow (#10617)

Fix #10586
This commit is contained in:
Antoine Moreaux
2025-03-03 16:47:33 +01:00
committed by GitHub
parent b8d944bd6e
commit 2325e0ae0f
13 changed files with 186 additions and 90 deletions

View File

@ -1,8 +1,8 @@
import { gql } from '@apollo/client';
export const EMAIL_PASSWORD_RESET_Link = gql`
mutation EmailPasswordResetLink($email: String!) {
emailPasswordResetLink(email: $email) {
export const EMAIL_PASSWORD_RESET_LINK = gql`
mutation EmailPasswordResetLink($email: String!, $workspaceId: String!) {
emailPasswordResetLink(email: $email, workspaceId: $workspaceId) {
success
}
}

View File

@ -97,6 +97,7 @@ export const SignInUpWorkspaceScopeFormEffect = () => {
}
if (
signInUpStep !== SignInUpStep.Email &&
isDefined(email) &&
workspaceAuthProviders.password &&
loadingStatus === LoadingStatus.Done

View File

@ -7,8 +7,12 @@ import { SOURCE_LOCALE } from 'twenty-shared';
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useEmailPasswordResetLinkMutation } from '~/generated/graphql';
import {
PublicWorkspaceDataOutput,
useEmailPasswordResetLinkMutation,
} from '~/generated/graphql';
import { dynamicActivate } from '~/utils/i18n/dynamicActivate';
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
// Mocks
jest.mock('@/ui/feedback/snack-bar-manager/hooks/useSnackBar');
@ -19,7 +23,14 @@ dynamicActivate(SOURCE_LOCALE);
const renderHooks = () => {
const { result } = renderHook(() => useHandleResetPassword(), {
wrapper: ({ children }) =>
RecoilRoot({ children: I18nProvider({ i18n, children }) }),
RecoilRoot({
initializeState: ({ set }) => {
set(workspacePublicDataState, {
id: 'workspace-id',
} as PublicWorkspaceDataOutput);
},
children: I18nProvider({ i18n, children }),
}),
});
return { result };
};

View File

@ -4,14 +4,20 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useLingui } from '@lingui/react/macro';
import { useEmailPasswordResetLinkMutation } from '~/generated/graphql';
import { workspacePublicDataState } from '@/auth/states/workspacePublicDataState';
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
export const useHandleResetPassword = () => {
const { enqueueSnackBar } = useSnackBar();
const [emailPasswordResetLink] = useEmailPasswordResetLinkMutation();
const workspacePublicData = useRecoilValue(workspacePublicDataState);
const currentUser = useRecoilValue(currentUserState);
const { t } = useLingui();
const handleResetPassword = useCallback(
(email: string) => {
(email = currentUser?.email) => {
return async () => {
if (!email) {
enqueueSnackBar(t`Invalid email`, {
@ -20,9 +26,16 @@ export const useHandleResetPassword = () => {
return;
}
if (!workspacePublicData?.id) {
enqueueSnackBar(t`Invalid workspace`, {
variant: SnackBarVariant.Error,
});
return;
}
try {
const { data } = await emailPasswordResetLink({
variables: { email },
variables: { email, workspaceId: workspacePublicData.id },
});
if (data?.emailPasswordResetLink?.success === true) {
@ -41,7 +54,13 @@ export const useHandleResetPassword = () => {
}
};
},
[enqueueSnackBar, emailPasswordResetLink, t],
[
currentUser?.email,
workspacePublicData?.id,
enqueueSnackBar,
t,
emailPasswordResetLink,
],
);
return { handleResetPassword };

View File

@ -1,50 +1,12 @@
import { useRecoilValue } from 'recoil';
import { Button, H2Title } from 'twenty-ui';
import { currentUserState } from '@/auth/states/currentUserState';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useLingui } from '@lingui/react/macro';
import { useEmailPasswordResetLinkMutation } from '~/generated/graphql';
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
export const ChangePassword = () => {
const { t } = useLingui();
const { enqueueSnackBar } = useSnackBar();
const currentUser = useRecoilValue(currentUserState);
const [emailPasswordResetLink] = useEmailPasswordResetLinkMutation();
const handlePasswordResetClick = async () => {
if (!currentUser?.email) {
enqueueSnackBar(t`Invalid email`, {
variant: SnackBarVariant.Error,
});
return;
}
try {
const { data } = await emailPasswordResetLink({
variables: {
email: currentUser.email,
},
});
if (data?.emailPasswordResetLink?.success === true) {
enqueueSnackBar(t`Password reset link has been sent to the email`, {
variant: SnackBarVariant.Success,
});
} else {
enqueueSnackBar(t`There was an issue`, {
variant: SnackBarVariant.Error,
});
}
} catch (error) {
enqueueSnackBar((error as Error).message, {
variant: SnackBarVariant.Error,
});
}
};
const { handleResetPassword } = useHandleResetPassword();
return (
<>
@ -53,7 +15,7 @@ export const ChangePassword = () => {
description={t`Receive an email containing password update link`}
/>
<Button
onClick={handlePasswordResetClick}
onClick={handleResetPassword()}
variant="secondary"
title={t`Change Password`}
/>