[WIP] Whole FE migrated (#2517)

* Wip

* WIP

* Removed concole log

* Add relations to workspace init (#2511)

* Add relations to workspace init

* remove logs

* update prefill

* add missing isSystem

* comment relation fields

* Migrate v2 core models to graphql schema (#2509)

* migrate v2 core models to graphql schema

* Migrate to new workspace member schema

* Continue work

* migrated-main

* Finished accountOwner nested field integration on companies

* Introduce bug

* Fix

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Charles Bochet
2023-11-15 15:46:06 +01:00
committed by GitHub
parent 1f49ed2acf
commit 6129444c5c
129 changed files with 3468 additions and 1497 deletions

View File

@ -9,49 +9,5 @@ export const USER_QUERY_FRAGMENT = gql`
lastName
canImpersonate
supportUserHash
avatarUrl
workspaceMember {
id
allowImpersonation
workspace {
id
domainName
displayName
logo
inviteHash
}
assignedActivities {
id
title
}
authoredActivities {
id
title
}
authoredAttachments {
id
name
type
}
settings {
id
colorScheme
locale
}
companies {
id
name
domainName
}
comments {
id
body
}
}
settings {
id
colorScheme
locale
}
}
`;

View File

@ -4,12 +4,19 @@ import {
snapshot_UNSTABLE,
useGotoRecoilSnapshot,
useRecoilState,
useSetRecoilState,
} from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
import { CREATE_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/mutation/createOneWorkspaceMember';
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
import { REACT_APP_SERVER_AUTH_URL } from '~/config';
import {
useChallengeMutation,
useCheckUserExistsLazyQuery,
useGetCurrentWorkspaceLazyQuery,
useSignUpMutation,
useVerifyMutation,
} from '~/generated/graphql';
@ -19,13 +26,20 @@ import { tokenPairState } from '../states/tokenPairState';
export const useAuth = () => {
const [, setTokenPair] = useRecoilState(tokenPairState);
const [, setCurrentUser] = useRecoilState(currentUserState);
const setCurrentUser = useSetRecoilState(currentUserState);
const setCurrentWorkspaceMember = useSetRecoilState(
currentWorkspaceMemberState,
);
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
const setIsVerifyPendingState = useSetRecoilState(isVerifyPendingState);
const [challenge] = useChallengeMutation();
const [signUp] = useSignUpMutation();
const [verify] = useVerifyMutation();
const [checkUserExistsQuery, { data: checkUserExistsData }] =
useCheckUserExistsLazyQuery();
const [getCurrentWorkspaceQuery, { data: getCurrentWorkspaceData }] =
useGetCurrentWorkspaceLazyQuery();
const client = useApolloClient();
@ -67,36 +81,56 @@ export const useAuth = () => {
throw new Error('No verify result');
}
if (!verifyResult.data?.verify.user.workspaceMember) {
throw new Error('No workspace member');
}
if (!verifyResult.data?.verify.user.workspaceMember.settings) {
throw new Error('No settings');
}
setCurrentUser({
...verifyResult.data?.verify.user,
workspaceMember: {
...verifyResult.data?.verify.user.workspaceMember,
settings: verifyResult.data?.verify.user.workspaceMember.settings,
setTokenPair(verifyResult.data?.verify.tokens);
const workspaceMember = await client.query({
query: FIND_ONE_WORKSPACE_MEMBER_V2,
variables: {
filter: {
userId: { eq: verifyResult.data?.verify.user.id },
},
},
});
setTokenPair(verifyResult.data?.verify.tokens);
const currentWorkspace = await getCurrentWorkspaceQuery();
return verifyResult.data?.verify;
setCurrentUser(verifyResult.data?.verify.user);
setCurrentWorkspaceMember(workspaceMember.data?.findMany);
setCurrentWorkspace(currentWorkspace.data?.currentWorkspace ?? null);
return {
user: verifyResult.data?.verify.user,
workspaceMember: workspaceMember.data?.findMany,
workspace: currentWorkspace.data?.currentWorkspace,
tokens: verifyResult.data?.verify.tokens,
};
},
[setTokenPair, verify, setCurrentUser],
[
verify,
setTokenPair,
client,
getCurrentWorkspaceQuery,
setCurrentUser,
setCurrentWorkspaceMember,
setCurrentWorkspace,
],
);
const handleCrendentialsSignIn = useCallback(
async (email: string, password: string) => {
const { loginToken } = await handleChallenge(email, password);
setIsVerifyPendingState(true);
const { user } = await handleVerify(loginToken.token);
return user;
const { user, workspaceMember, workspace } = await handleVerify(
loginToken.token,
);
setIsVerifyPendingState(false);
return {
user,
workspaceMember,
workspace,
};
},
[handleChallenge, handleVerify],
[handleChallenge, handleVerify, setIsVerifyPendingState],
);
const handleSignOut = useCallback(() => {
@ -110,6 +144,8 @@ export const useAuth = () => {
const handleCredentialsSignUp = useCallback(
async (email: string, password: string, workspaceInviteHash?: string) => {
setIsVerifyPendingState(true);
const signUpResult = await signUp({
variables: {
email,
@ -126,13 +162,30 @@ export const useAuth = () => {
throw new Error('No login token');
}
const { user } = await handleVerify(
const { user, workspace } = await handleVerify(
signUpResult.data?.signUp.loginToken.token,
);
return user;
const workspaceMember = await client.mutate({
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
variables: {
input: {
firstName: user.firstName ?? '',
lastName: user.lastName ?? '',
colorScheme: 'Light',
userId: user.id,
allowImpersonation: true,
locale: 'en',
},
},
});
setCurrentWorkspaceMember(workspaceMember.data?.createWorkspaceMemberV2);
setIsVerifyPendingState(false);
return { user, workspaceMember, workspace };
},
[signUp, handleVerify],
[setIsVerifyPendingState, signUp, handleVerify, client],
);
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {

View File

@ -1,9 +1,12 @@
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
import { tokenPairState } from '../states/tokenPairState';
export const useIsLogged = (): boolean => {
const [tokenPair] = useRecoilState(tokenPairState);
const isVerifyPending = useRecoilValue(isVerifyPendingState);
return !!tokenPair;
return !!tokenPair && !isVerifyPending;
};

View File

@ -1,15 +1,26 @@
import { useRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useIsLogged } from '../hooks/useIsLogged';
import { currentUserState } from '../states/currentUserState';
import {
getOnboardingStatus,
OnboardingStatus,
} from '../utils/getOnboardingStatus';
export const useOnboardingStatus = (): OnboardingStatus | undefined => {
const [currentUser] = useRecoilState(currentUserState);
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const isLoggedIn = useIsLogged();
return getOnboardingStatus(isLoggedIn, currentUser);
console.log(
getOnboardingStatus(isLoggedIn, currentWorkspaceMember, currentWorkspace),
);
return getOnboardingStatus(
isLoggedIn,
currentWorkspaceMember,
currentWorkspace,
);
};

View File

@ -62,7 +62,7 @@ export const useSignInUp = () => {
});
const [showErrors, setShowErrors] = useState(false);
const { data: workspace } = useGetWorkspaceFromInviteHashQuery({
const { data: workspaceFromInviteHash } = useGetWorkspaceFromInviteHashQuery({
variables: { inviteHash: workspaceInviteHash || '' },
});
@ -119,20 +119,23 @@ export const useSignInUp = () => {
if (!data.email || !data.password) {
throw new Error('Email and password are required');
}
let user;
let currentWorkspace;
if (signInUpMode === SignInUpMode.SignIn) {
user = await signInWithCredentials(
const { workspace } = await signInWithCredentials(
data.email.toLowerCase(),
data.password,
);
currentWorkspace = workspace;
} else {
user = await signUpWithCredentials(
const { workspace } = await signUpWithCredentials(
data.email.toLowerCase(),
data.password,
workspaceInviteHash,
);
currentWorkspace = workspace;
}
if (user?.workspaceMember?.workspace?.displayName) {
if (currentWorkspace?.displayName) {
navigate('/');
} else {
navigate('/create/workspace');
@ -144,12 +147,12 @@ export const useSignInUp = () => {
}
},
[
navigate,
signInUpMode,
signInWithCredentials,
signUpWithCredentials,
workspaceInviteHash,
navigate,
enqueueSnackBar,
signInUpMode,
],
);
@ -188,6 +191,6 @@ export const useSignInUp = () => {
goBackToEmailStep,
submitCredentials,
form,
workspace: workspace?.findWorkspaceFromInviteHash,
workspace: workspaceFromInviteHash?.findWorkspaceFromInviteHash,
};
};

View File

@ -1,32 +1,11 @@
import { atom } from 'recoil';
import {
User,
UserSettings,
Workspace,
WorkspaceMember,
} from '~/generated/graphql';
import { User } from '~/generated/graphql';
export type CurrentUser = Pick<
User,
| 'id'
| 'email'
| 'displayName'
| 'firstName'
| 'lastName'
| 'avatarUrl'
| 'canImpersonate'
| 'supportUserHash'
> & {
workspaceMember: Pick<WorkspaceMember, 'id' | 'allowImpersonation'> & {
workspace: Pick<
Workspace,
'id' | 'displayName' | 'domainName' | 'inviteHash' | 'logo'
>;
settings: Pick<UserSettings, 'id' | 'colorScheme' | 'locale'>;
};
settings: Pick<UserSettings, 'id' | 'colorScheme' | 'locale'>;
};
'id' | 'email' | 'supportUserHash' | 'canImpersonate'
>;
export const currentUserState = atom<CurrentUser | null>({
key: 'currentUserState',

View File

@ -0,0 +1,18 @@
import { atom } from 'recoil';
import { ColorScheme } from '~/generated-metadata/graphql';
export type CurrentWorkspaceMember = {
id: string;
locale: string;
colorScheme: ColorScheme;
allowImpersonation: boolean;
firstName: string;
lastName: string;
avatarUrl: string;
};
export const currentWorkspaceMemberState = atom<CurrentWorkspaceMember | null>({
key: 'currentWorkspaceMemberState',
default: null,
});

View File

@ -0,0 +1,13 @@
import { atom } from 'recoil';
import { Workspace } from '~/generated-metadata/graphql';
export type CurrentWorkspace = Pick<
Workspace,
'id' | 'inviteHash' | 'logo' | 'displayName'
>;
export const currentWorkspaceState = atom<CurrentWorkspace | null>({
key: 'currentWorkspaceState',
default: null,
});

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const isVerifyPendingState = atom<boolean>({
key: 'isVerifyPendingState',
default: false,
});

View File

@ -1,4 +1,5 @@
import { CurrentUser } from '../states/currentUserState';
import { CurrentWorkspaceMember } from '@/auth/states/currentWorkspaceMemberState';
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
export enum OnboardingStatus {
OngoingUserCreation = 'ongoing_user_creation',
@ -9,21 +10,22 @@ export enum OnboardingStatus {
export const getOnboardingStatus = (
isLoggedIn: boolean,
currentUser: CurrentUser | null,
currentWorkspaceMember: CurrentWorkspaceMember | null,
currentWorkspace: CurrentWorkspace | null,
) => {
if (!isLoggedIn) {
return OnboardingStatus.OngoingUserCreation;
}
// if the user has not been fetched yet, we can't know the onboarding status
if (!currentUser) {
if (!currentWorkspaceMember) {
return undefined;
}
if (!currentUser.workspaceMember?.workspace.displayName) {
if (!currentWorkspace?.displayName) {
return OnboardingStatus.OngoingWorkspaceCreation;
}
if (!currentUser.firstName || !currentUser.lastName) {
if (!currentWorkspaceMember.firstName || !currentWorkspaceMember.lastName) {
return OnboardingStatus.OngoingProfileCreation;
}