mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-24 20:42:05 +03:00
3628 timebox separate user creation from workspace creation (#3737)
* Remove workspace schema creation from signUp * Set user workspaceMember nullable * Remove workspace creation * Handle null workspace in tokens * Update onboarding status * Generate types * Move createWorkspace to workspace resolver * Create workspace after signup * Update createWorkspace return type * Update createWorkspace return type * Create core.workspace at signup * WIP * Fix create workspace * Fix create workspace * Clean code * Remove useless recoil set * Simplify create workspace request * Set currentWorkspace at login * Fix tests * Create a recoil value for is workspaceSchema created * Rename createWorkspace to createWorkspaceSchema * Code review returns * Use AppPath when possible * Try without state * Fix * Fixes * Rename createWorkspaceSchema to activateWorkspace * Remove defaultAvatarUrl from user * Add defaultAvatarUrl to core user This reverts commit1701c30eb1
. * Add defaultAvatarUrl to core user This reverts commit1701c30eb1
. * Fix ci * Fix tests * Fix storybook * Fix test * Remove useless query * Fix test * Fix test * Fix mock data * Fix test * Clean Mock Requests * Fix tentative * Revert "Clean Mock Requests" This reverts commit8aa20a3436
. * Fix * Revert "Fix" This reverts commit2df7e9b656
. * Revert "Revert "Clean Mock Requests"" This reverts commit3aefef8e96
. * Revert "Fix tentative" This reverts commit13e7748d6f
. * Update filename --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
parent
3fc18aeec1
commit
7425223f83
@ -88,7 +88,7 @@ export const PageChangeEffect = () => {
|
||||
) {
|
||||
navigate(AppPath.PlanRequired);
|
||||
} else if (
|
||||
onboardingStatus === OnboardingStatus.OngoingWorkspaceCreation &&
|
||||
onboardingStatus === OnboardingStatus.OngoingWorkspaceActivation &&
|
||||
!isMatchingLocation(AppPath.CreateWorkspace)
|
||||
) {
|
||||
navigate(AppPath.CreateWorkspace);
|
||||
@ -101,7 +101,7 @@ export const PageChangeEffect = () => {
|
||||
onboardingStatus === OnboardingStatus.Completed &&
|
||||
isMatchingOnboardingRoute
|
||||
) {
|
||||
navigate('/');
|
||||
navigate(AppPath.Index);
|
||||
} else if (isMatchingLocation(AppPath.Invite)) {
|
||||
const inviteHash =
|
||||
matchPath({ path: '/invite/:workspaceInviteHash' }, location.pathname)
|
||||
|
@ -19,6 +19,10 @@ export type Scalars = {
|
||||
Upload: any;
|
||||
};
|
||||
|
||||
export type ActivateWorkspaceInput = {
|
||||
displayName?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type Analytics = {
|
||||
__typename?: 'Analytics';
|
||||
/** Boolean that confirms query was dispatched */
|
||||
@ -77,15 +81,6 @@ export type ClientConfig = {
|
||||
telemetry: Telemetry;
|
||||
};
|
||||
|
||||
export type CreateOneRefreshTokenInput = {
|
||||
/** The record to create */
|
||||
refreshToken: CreateRefreshTokenInput;
|
||||
};
|
||||
|
||||
export type CreateRefreshTokenInput = {
|
||||
expiresAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type CursorPaging = {
|
||||
/** Paginate after opaque cursor */
|
||||
after?: InputMaybe<Scalars['ConnectionCursor']>;
|
||||
@ -223,6 +218,7 @@ export type LoginToken = {
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
activateWorkspace: Workspace;
|
||||
challenge: LoginToken;
|
||||
createEvent: Analytics;
|
||||
createOneObject: Object;
|
||||
@ -247,6 +243,11 @@ export type Mutation = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationActivateWorkspaceArgs = {
|
||||
data: ActivateWorkspaceInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationChallengeArgs = {
|
||||
email: Scalars['String'];
|
||||
password: Scalars['String'];
|
||||
@ -259,11 +260,6 @@ export type MutationCreateEventArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOneRefreshTokenArgs = {
|
||||
input: CreateOneRefreshTokenInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteOneObjectArgs = {
|
||||
input: DeleteOneObjectInput;
|
||||
};
|
||||
@ -532,6 +528,7 @@ export type User = {
|
||||
__typename?: 'User';
|
||||
canImpersonate: Scalars['Boolean'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
defaultAvatarUrl?: Maybe<Scalars['String']>;
|
||||
defaultWorkspace: Workspace;
|
||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||
disabled?: Maybe<Scalars['Boolean']>;
|
||||
@ -545,7 +542,7 @@ export type User = {
|
||||
passwordResetTokenExpiresAt?: Maybe<Scalars['DateTime']>;
|
||||
supportUserHash?: Maybe<Scalars['String']>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
workspaceMember: WorkspaceMember;
|
||||
workspaceMember?: Maybe<WorkspaceMember>;
|
||||
};
|
||||
|
||||
export type UserEdge = {
|
||||
@ -778,7 +775,7 @@ export type ImpersonateMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type RenewTokenMutationVariables = Exact<{
|
||||
refreshToken: Scalars['String'];
|
||||
@ -809,7 +806,7 @@ export type VerifyMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type CheckUserExistsQueryVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@ -846,7 +843,7 @@ export type UploadImageMutationVariables = Exact<{
|
||||
|
||||
export type UploadImageMutation = { __typename?: 'Mutation', uploadImage: string };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } };
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } };
|
||||
|
||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@ -860,6 +857,18 @@ export type UploadProfilePictureMutationVariables = Exact<{
|
||||
|
||||
export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProfilePicture: string };
|
||||
|
||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: string, key: string, value: boolean, workspaceId: string }> | null } } };
|
||||
|
||||
export type ActivateWorkspaceMutationVariables = Exact<{
|
||||
input: ActivateWorkspaceInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type ActivateWorkspaceMutation = { __typename?: 'Mutation', activateWorkspace: { __typename?: 'Workspace', id: string } };
|
||||
|
||||
export type DeleteCurrentWorkspaceMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@ -1677,6 +1686,103 @@ export function useUploadProfilePictureMutation(baseOptions?: Apollo.MutationHoo
|
||||
export type UploadProfilePictureMutationHookResult = ReturnType<typeof useUploadProfilePictureMutation>;
|
||||
export type UploadProfilePictureMutationResult = Apollo.MutationResult<UploadProfilePictureMutation>;
|
||||
export type UploadProfilePictureMutationOptions = Apollo.BaseMutationOptions<UploadProfilePictureMutation, UploadProfilePictureMutationVariables>;
|
||||
export const GetCurrentUserDocument = gql`
|
||||
query GetCurrentUser {
|
||||
currentUser {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
email
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
workspaceMember {
|
||||
id
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
colorScheme
|
||||
avatarUrl
|
||||
locale
|
||||
}
|
||||
defaultWorkspace {
|
||||
id
|
||||
displayName
|
||||
logo
|
||||
domainName
|
||||
inviteHash
|
||||
allowImpersonation
|
||||
subscriptionStatus
|
||||
featureFlags {
|
||||
id
|
||||
key
|
||||
value
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetCurrentUserQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetCurrentUserQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetCurrentUserQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetCurrentUserQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetCurrentUserQuery(baseOptions?: Apollo.QueryHookOptions<GetCurrentUserQuery, GetCurrentUserQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<GetCurrentUserQuery, GetCurrentUserQueryVariables>(GetCurrentUserDocument, options);
|
||||
}
|
||||
export function useGetCurrentUserLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetCurrentUserQuery, GetCurrentUserQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<GetCurrentUserQuery, GetCurrentUserQueryVariables>(GetCurrentUserDocument, options);
|
||||
}
|
||||
export type GetCurrentUserQueryHookResult = ReturnType<typeof useGetCurrentUserQuery>;
|
||||
export type GetCurrentUserLazyQueryHookResult = ReturnType<typeof useGetCurrentUserLazyQuery>;
|
||||
export type GetCurrentUserQueryResult = Apollo.QueryResult<GetCurrentUserQuery, GetCurrentUserQueryVariables>;
|
||||
export const ActivateWorkspaceDocument = gql`
|
||||
mutation ActivateWorkspace($input: ActivateWorkspaceInput!) {
|
||||
activateWorkspace(data: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type ActivateWorkspaceMutationFn = Apollo.MutationFunction<ActivateWorkspaceMutation, ActivateWorkspaceMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useActivateWorkspaceMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useActivateWorkspaceMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useActivateWorkspaceMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [activateWorkspaceMutation, { data, loading, error }] = useActivateWorkspaceMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useActivateWorkspaceMutation(baseOptions?: Apollo.MutationHookOptions<ActivateWorkspaceMutation, ActivateWorkspaceMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<ActivateWorkspaceMutation, ActivateWorkspaceMutationVariables>(ActivateWorkspaceDocument, options);
|
||||
}
|
||||
export type ActivateWorkspaceMutationHookResult = ReturnType<typeof useActivateWorkspaceMutation>;
|
||||
export type ActivateWorkspaceMutationResult = Apollo.MutationResult<ActivateWorkspaceMutation>;
|
||||
export type ActivateWorkspaceMutationOptions = Apollo.BaseMutationOptions<ActivateWorkspaceMutation, ActivateWorkspaceMutationVariables>;
|
||||
export const DeleteCurrentWorkspaceDocument = gql`
|
||||
mutation DeleteCurrentWorkspace {
|
||||
deleteCurrentWorkspace {
|
||||
|
@ -66,16 +66,6 @@ describe('useOnboardingStatus', () => {
|
||||
expect(result.current.onboardingStatus).toBe('ongoing_user_creation');
|
||||
});
|
||||
|
||||
it('should return undefined when currentWorkspaceMember in undefined', async () => {
|
||||
const { result } = renderHooks();
|
||||
|
||||
act(() => {
|
||||
result.current.setTokenPair(tokenPair);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return "incomplete"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
@ -120,14 +110,9 @@ describe('useOnboardingStatus', () => {
|
||||
expect(result.current.onboardingStatus).toBe('canceled');
|
||||
});
|
||||
|
||||
it('should return "ongoing_workspace_creation"', async () => {
|
||||
it('should return "ongoing_workspace_activation"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const {
|
||||
setTokenPair,
|
||||
setBilling,
|
||||
setCurrentWorkspace,
|
||||
setCurrentWorkspaceMember,
|
||||
} = result.current;
|
||||
const { setTokenPair, setBilling, setCurrentWorkspace } = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
@ -135,12 +120,31 @@ describe('useOnboardingStatus', () => {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
displayName: '',
|
||||
subscriptionStatus: 'completed',
|
||||
subscriptionStatus: 'active',
|
||||
});
|
||||
setCurrentWorkspaceMember(currentWorkspaceMember);
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe('ongoing_workspace_creation');
|
||||
expect(result.current.onboardingStatus).toBe(
|
||||
'ongoing_workspace_activation',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return "ongoing_workspace_activation"', async () => {
|
||||
const { result } = renderHooks();
|
||||
const { setTokenPair, setBilling, setCurrentWorkspace } = result.current;
|
||||
|
||||
act(() => {
|
||||
setTokenPair(tokenPair);
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'active',
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.onboardingStatus).toBe(
|
||||
'ongoing_workspace_activation',
|
||||
);
|
||||
});
|
||||
|
||||
it('should return "ongoing_profile_creation"', async () => {
|
||||
@ -157,7 +161,7 @@ describe('useOnboardingStatus', () => {
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'completed',
|
||||
subscriptionStatus: 'active',
|
||||
});
|
||||
setCurrentWorkspaceMember(currentWorkspaceMember);
|
||||
});
|
||||
@ -179,7 +183,7 @@ describe('useOnboardingStatus', () => {
|
||||
setBilling(billing);
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
subscriptionStatus: 'completed',
|
||||
subscriptionStatus: 'active',
|
||||
});
|
||||
setCurrentWorkspaceMember({
|
||||
...currentWorkspaceMember,
|
||||
|
@ -89,13 +89,16 @@ export const useAuth = () => {
|
||||
setTokenPair(verifyResult.data?.verify.tokens);
|
||||
|
||||
const user = verifyResult.data?.verify.user;
|
||||
const workspaceMember = {
|
||||
...user.workspaceMember,
|
||||
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
|
||||
};
|
||||
const workspace = user.defaultWorkspace ?? null;
|
||||
let workspaceMember = null;
|
||||
setCurrentUser(user);
|
||||
setCurrentWorkspaceMember(workspaceMember);
|
||||
if (user.workspaceMember) {
|
||||
workspaceMember = {
|
||||
...user.workspaceMember,
|
||||
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
|
||||
};
|
||||
setCurrentWorkspaceMember(workspaceMember);
|
||||
}
|
||||
const workspace = user.defaultWorkspace ?? null;
|
||||
setCurrentWorkspace(workspace);
|
||||
return {
|
||||
user,
|
||||
|
@ -143,16 +143,15 @@ export const useSignInUp = () => {
|
||||
billing?.isBillingEnabled &&
|
||||
currentWorkspace.subscriptionStatus !== 'active'
|
||||
) {
|
||||
navigate('/plan-required');
|
||||
navigate(AppPath.PlanRequired);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentWorkspace.displayName) {
|
||||
navigate('/');
|
||||
navigate(AppPath.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
navigate('/create/workspace');
|
||||
navigate(AppPath.CreateWorkspace);
|
||||
} catch (err: any) {
|
||||
enqueueSnackBar(err?.message, {
|
||||
variant: 'error',
|
||||
|
@ -0,0 +1,11 @@
|
||||
import { selector } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
|
||||
export const isCurrentWorkspaceActiveSelector = selector({
|
||||
key: 'isCurrentWorkspaceActiveSelector',
|
||||
get: ({ get }) => {
|
||||
const currentWorkspaceMember = get(currentWorkspaceMemberState);
|
||||
return !!currentWorkspaceMember;
|
||||
},
|
||||
});
|
@ -11,27 +11,24 @@ describe('getOnboardingStatus', () => {
|
||||
currentWorkspace: null,
|
||||
});
|
||||
|
||||
const unknownStatus = getOnboardingStatus({
|
||||
const ongoingWorkspaceActivation = getOnboardingStatus({
|
||||
isLoggedIn: true,
|
||||
currentWorkspaceMember: null,
|
||||
currentWorkspace: null,
|
||||
});
|
||||
|
||||
const ongoingWorkspaceCreation = getOnboardingStatus({
|
||||
isLoggedIn: true,
|
||||
currentWorkspaceMember: {
|
||||
id: '1',
|
||||
name: {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
} as WorkspaceMember,
|
||||
currentWorkspace: {
|
||||
id: '1',
|
||||
displayName: null,
|
||||
} as CurrentWorkspace,
|
||||
});
|
||||
|
||||
const ongoingWorkspaceActivationPreviouslyActive = getOnboardingStatus({
|
||||
isLoggedIn: true,
|
||||
currentWorkspaceMember: null,
|
||||
currentWorkspace: {
|
||||
id: '1',
|
||||
displayName: 'My Workspace',
|
||||
} as CurrentWorkspace,
|
||||
});
|
||||
|
||||
const ongoingProfileCreation = getOnboardingStatus({
|
||||
isLoggedIn: true,
|
||||
currentWorkspaceMember: {
|
||||
@ -110,8 +107,10 @@ describe('getOnboardingStatus', () => {
|
||||
});
|
||||
|
||||
expect(ongoingUserCreation).toBe('ongoing_user_creation');
|
||||
expect(unknownStatus).toBe(undefined);
|
||||
expect(ongoingWorkspaceCreation).toBe('ongoing_workspace_creation');
|
||||
expect(ongoingWorkspaceActivation).toBe('ongoing_workspace_activation');
|
||||
expect(ongoingWorkspaceActivationPreviouslyActive).toBe(
|
||||
'ongoing_workspace_activation',
|
||||
);
|
||||
expect(ongoingProfileCreation).toBe('ongoing_profile_creation');
|
||||
expect(completed).toBe('completed');
|
||||
expect(incomplete).toBe('incomplete');
|
||||
|
@ -5,7 +5,7 @@ export enum OnboardingStatus {
|
||||
Incomplete = 'incomplete',
|
||||
Canceled = 'canceled',
|
||||
OngoingUserCreation = 'ongoing_user_creation',
|
||||
OngoingWorkspaceCreation = 'ongoing_workspace_creation',
|
||||
OngoingWorkspaceActivation = 'ongoing_workspace_activation',
|
||||
OngoingProfileCreation = 'ongoing_profile_creation',
|
||||
Completed = 'completed',
|
||||
}
|
||||
@ -28,11 +28,6 @@ export const getOnboardingStatus = ({
|
||||
return OnboardingStatus.OngoingUserCreation;
|
||||
}
|
||||
|
||||
// if the user has not been fetched yet, we can't know the onboarding status
|
||||
if (!currentWorkspaceMember) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (
|
||||
isBillingEnabled &&
|
||||
currentWorkspace?.subscriptionStatus === 'incomplete'
|
||||
@ -44,9 +39,10 @@ export const getOnboardingStatus = ({
|
||||
return OnboardingStatus.Canceled;
|
||||
}
|
||||
|
||||
if (!currentWorkspace?.displayName) {
|
||||
return OnboardingStatus.OngoingWorkspaceCreation;
|
||||
if (!currentWorkspaceMember) {
|
||||
return OnboardingStatus.OngoingWorkspaceActivation;
|
||||
}
|
||||
|
||||
if (
|
||||
!currentWorkspaceMember.name.firstName ||
|
||||
!currentWorkspaceMember.name.lastName
|
||||
|
@ -3,6 +3,7 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, userEvent, within } from '@storybook/test';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { CommandType } from '@/command-menu/types/Command';
|
||||
@ -11,7 +12,10 @@ import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWith
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockDefaultWorkspace } from '~/testing/mock-data/users';
|
||||
import {
|
||||
mockDefaultWorkspace,
|
||||
mockedWorkspaceMemberData,
|
||||
} from '~/testing/mock-data/users';
|
||||
import { sleep } from '~/testing/sleep';
|
||||
|
||||
import { CommandMenu } from '../CommandMenu';
|
||||
@ -24,10 +28,14 @@ const meta: Meta<typeof CommandMenu> = {
|
||||
decorators: [
|
||||
(Story) => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
const { addToCommandMenu, setToIntitialCommandMenu, openCommandMenu } =
|
||||
useCommandMenu();
|
||||
|
||||
setCurrentWorkspace(mockDefaultWorkspace);
|
||||
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
|
||||
|
||||
useEffect(() => {
|
||||
setToIntitialCommandMenu();
|
||||
|
@ -3,6 +3,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import {
|
||||
IconCheckbox,
|
||||
IconList,
|
||||
@ -71,7 +72,7 @@ export const MobileNavigationBar = () => {
|
||||
onClick: () => {
|
||||
closeCommandMenu();
|
||||
setIsNavigationDrawerOpen(false);
|
||||
navigate('/tasks');
|
||||
navigate(AppPath.TasksPage);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentWorkspaceActiveSelector } from '@/auth/states/selectors/isCurrentWorkspaceActiveSelector';
|
||||
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
|
||||
@ -9,12 +9,14 @@ export const ObjectMetadataItemsProvider = ({
|
||||
children,
|
||||
}: React.PropsWithChildren) => {
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isCurrentWorkspaceActive = useRecoilValue(
|
||||
isCurrentWorkspaceActiveSelector,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ObjectMetadataItemsLoadEffect />
|
||||
{(!currentWorkspace || !!objectMetadataItems.length) && (
|
||||
{(!isCurrentWorkspaceActive || !!objectMetadataItems.length) && (
|
||||
<RelationPickerScope relationPickerScopeId="relation-picker">
|
||||
{children}
|
||||
</RelationPickerScope>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { gql } from '@apollo/client';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentWorkspaceActiveSelector } from '@/auth/states/selectors/isCurrentWorkspaceActiveSelector';
|
||||
import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError';
|
||||
import { useGetObjectOrderByField } from '@/object-metadata/hooks/useGetObjectOrderByField';
|
||||
import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
|
||||
@ -40,7 +40,9 @@ export const useObjectMetadataItem = (
|
||||
{ objectNameSingular }: ObjectMetadataItemIdentifier,
|
||||
depth?: number,
|
||||
) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isCurrentWorkspaceActive = useRecoilValue(
|
||||
isCurrentWorkspaceActiveSelector,
|
||||
);
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
@ -52,7 +54,7 @@ export const useObjectMetadataItem = (
|
||||
|
||||
let objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
if (!currentWorkspace) {
|
||||
if (!isCurrentWorkspaceActive) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentWorkspaceActiveSelector } from '@/auth/states/selectors/isCurrentWorkspaceActiveSelector';
|
||||
import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
@ -12,7 +12,9 @@ import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentif
|
||||
export const useObjectMetadataItemOnly = ({
|
||||
objectNameSingular,
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isCurrentWorkspaceActive = useRecoilValue(
|
||||
isCurrentWorkspaceActiveSelector,
|
||||
);
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
@ -24,7 +26,7 @@ export const useObjectMetadataItemOnly = ({
|
||||
|
||||
let objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
if (!currentWorkspace) {
|
||||
if (!isCurrentWorkspaceActive) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentWorkspaceActiveSelector } from '@/auth/states/selectors/isCurrentWorkspaceActiveSelector';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
@ -10,7 +10,9 @@ export const useObjectNamePluralFromSingular = ({
|
||||
}: {
|
||||
objectNameSingular: string;
|
||||
}) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isCurrentWorkspaceActive = useRecoilValue(
|
||||
isCurrentWorkspaceActiveSelector,
|
||||
);
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
@ -20,7 +22,7 @@ export const useObjectNamePluralFromSingular = ({
|
||||
}),
|
||||
);
|
||||
|
||||
if (!currentWorkspace) {
|
||||
if (!isCurrentWorkspaceActive) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentWorkspaceActiveSelector } from '@/auth/states/selectors/isCurrentWorkspaceActiveSelector';
|
||||
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
@ -10,7 +10,9 @@ export const useObjectNameSingularFromPlural = ({
|
||||
}: {
|
||||
objectNamePlural: string;
|
||||
}) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const isCurrentWorkspaceActive = useRecoilValue(
|
||||
isCurrentWorkspaceActiveSelector,
|
||||
);
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
||||
let objectMetadataItem = useRecoilValue(
|
||||
@ -20,7 +22,7 @@ export const useObjectNameSingularFromPlural = ({
|
||||
}),
|
||||
);
|
||||
|
||||
if (!currentWorkspace) {
|
||||
if (!isCurrentWorkspaceActive) {
|
||||
objectMetadataItem =
|
||||
mockObjectMetadataItems.find(
|
||||
(objectMetadataItem) =>
|
||||
|
@ -3,7 +3,7 @@ import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import {
|
||||
@ -39,7 +39,7 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
);
|
||||
|
||||
describe('useFindManyRecords', () => {
|
||||
it('should skip fetch if currentWorkspace is undefined', async () => {
|
||||
it('should skip fetch if currentWorkspaceMember is undefined', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useFindManyRecords({ objectNameSingular: 'person' }),
|
||||
{
|
||||
@ -56,12 +56,12 @@ describe('useFindManyRecords', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
setCurrentWorkspace({
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember({
|
||||
id: '32219445-f587-4c40-b2b1-6d3205ed96da',
|
||||
displayName: 'cool-workspace',
|
||||
allowImpersonation: false,
|
||||
subscriptionStatus: 'incomplete',
|
||||
name: { firstName: 'John', lastName: 'Connor' },
|
||||
});
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
@ -4,7 +4,7 @@ import { isNonEmptyArray } from '@apollo/client/utilities';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { useMapConnectionToRecords } from '@/object-record/hooks/useMapConnectionToRecords';
|
||||
@ -65,12 +65,12 @@ export const useFindManyRecords = <T extends ObjectRecord = ObjectRecord>({
|
||||
);
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { data, loading, error, fetchMore } = useQuery<
|
||||
ObjectRecordQueryResult<T>
|
||||
>(findManyRecordsQuery, {
|
||||
skip: skip || !objectMetadataItem || !currentWorkspace,
|
||||
skip: skip || !objectMetadataItem || !currentWorkspaceMember,
|
||||
variables: {
|
||||
filter,
|
||||
limit,
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
} from '@storybook/test';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
@ -17,7 +18,10 @@ import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/Componen
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockDefaultWorkspace } from '~/testing/mock-data/users';
|
||||
import {
|
||||
mockDefaultWorkspace,
|
||||
mockedWorkspaceMemberData,
|
||||
} from '~/testing/mock-data/users';
|
||||
|
||||
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
|
||||
import {
|
||||
@ -27,10 +31,14 @@ import {
|
||||
|
||||
const RelationWorkspaceSetterEffect = () => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentWorkspace(mockDefaultWorkspace);
|
||||
}, [setCurrentWorkspace]);
|
||||
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
|
||||
}, [setCurrentWorkspace, setCurrentWorkspaceMember]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { isCurrentWorkspaceActiveSelector } from '@/auth/states/selectors/isCurrentWorkspaceActiveSelector';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
||||
@ -13,8 +13,10 @@ import { useFindManyRecords } from '../../hooks/useFindManyRecords';
|
||||
export const useLoadRecordIndexTable = (objectNameSingular: string) => {
|
||||
const { setRecordTableData, setIsRecordTableInitialLoading } =
|
||||
useRecordTable();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const isCurrentWorkspaceActive = useRecoilValue(
|
||||
isCurrentWorkspaceActiveSelector,
|
||||
);
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
@ -51,7 +53,7 @@ export const useLoadRecordIndexTable = (objectNameSingular: string) => {
|
||||
});
|
||||
|
||||
return {
|
||||
records: currentWorkspace ? records : signInBackgroundMockCompanies,
|
||||
records: isCurrentWorkspaceActive ? records : signInBackgroundMockCompanies,
|
||||
loading,
|
||||
fetchMoreRecords,
|
||||
queryStateIdentifier,
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isCreateModeScopedState = atomFamily<boolean, string>({
|
||||
key: 'isCreateModeScopedState',
|
||||
default: false,
|
||||
});
|
@ -3,7 +3,7 @@ import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
|
||||
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
|
||||
@ -66,12 +66,12 @@ describe('useFilteredSearchEntityQuery', () => {
|
||||
it('returns the correct result when everything is provided', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
setCurrentWorkspace({
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
setCurrentWorkspaceMember({
|
||||
id: '32219445-f587-4c40-b2b1-6d3205ed96da',
|
||||
displayName: 'cool-workspace',
|
||||
allowImpersonation: false,
|
||||
subscriptionStatus: 'incomplete',
|
||||
name: { firstName: 'John', lastName: 'Connor' },
|
||||
});
|
||||
|
||||
const mockObjectMetadataItems = getObjectMetadataItemsMock();
|
||||
|
@ -28,7 +28,6 @@ export const SignInBackgroundMockContainerEffect = ({
|
||||
const {
|
||||
setAvailableTableColumns,
|
||||
setOnEntityCountChange,
|
||||
setRecordTableData,
|
||||
setTableColumns,
|
||||
resetTableRowSelection,
|
||||
} = useRecordTable({
|
||||
@ -77,7 +76,6 @@ export const SignInBackgroundMockContainerEffect = ({
|
||||
setAvailableFieldDefinitions,
|
||||
objectMetadataItem,
|
||||
setAvailableTableColumns,
|
||||
setRecordTableData,
|
||||
setTableColumns,
|
||||
]);
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { useSetRecoilState } from 'recoil';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { GET_CURRENT_USER_AND_VIEWS } from '@/users/graphql/queries/getCurrentUserAndViews';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
export const UserProvider = ({ children }: React.PropsWithChildren) => {
|
||||
@ -13,21 +13,22 @@ export const UserProvider = ({ children }: React.PropsWithChildren) => {
|
||||
|
||||
const setCurrentUser = useSetRecoilState(currentUserState);
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
|
||||
const setCurrentWorkspaceMember = useSetRecoilState(
|
||||
currentWorkspaceMemberState,
|
||||
);
|
||||
|
||||
const { loading: queryLoading, data: queryData } = useQuery(
|
||||
GET_CURRENT_USER_AND_VIEWS,
|
||||
);
|
||||
const { loading: queryLoading, data: queryData } = useQuery(GET_CURRENT_USER);
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryLoading) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
if (queryData?.currentUser?.workspaceMember) {
|
||||
if (queryData?.currentUser) {
|
||||
setCurrentUser(queryData.currentUser);
|
||||
setCurrentWorkspace(queryData.currentUser.defaultWorkspace);
|
||||
}
|
||||
if (queryData?.currentUser?.workspaceMember) {
|
||||
const workspaceMember = queryData.currentUser.workspaceMember;
|
||||
setCurrentWorkspaceMember({
|
||||
...workspaceMember,
|
||||
|
@ -0,0 +1,40 @@
|
||||
// This query cannot be put in the graphQL folder because it cannot be generated by the graphQL codegen.
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_CURRENT_USER = gql`
|
||||
query GetCurrentUser {
|
||||
currentUser {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
email
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
workspaceMember {
|
||||
id
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
colorScheme
|
||||
avatarUrl
|
||||
locale
|
||||
}
|
||||
defaultWorkspace {
|
||||
id
|
||||
displayName
|
||||
logo
|
||||
domainName
|
||||
inviteHash
|
||||
allowImpersonation
|
||||
subscriptionStatus
|
||||
featureFlags {
|
||||
id
|
||||
key
|
||||
value
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@ -1,103 +0,0 @@
|
||||
// This query cannot be put in the graphQL folder because it cannot be generated by the graphQL codegen.
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_CURRENT_USER_AND_VIEWS = gql`
|
||||
query GetCurrentUserAndViews {
|
||||
currentUser {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
email
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
workspaceMember {
|
||||
id
|
||||
name {
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
colorScheme
|
||||
avatarUrl
|
||||
locale
|
||||
}
|
||||
defaultWorkspace {
|
||||
id
|
||||
displayName
|
||||
logo
|
||||
domainName
|
||||
inviteHash
|
||||
allowImpersonation
|
||||
subscriptionStatus
|
||||
featureFlags {
|
||||
id
|
||||
key
|
||||
value
|
||||
workspaceId
|
||||
}
|
||||
}
|
||||
}
|
||||
views {
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
name
|
||||
objectMetadataId
|
||||
type
|
||||
deletedAt
|
||||
viewFilters {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
fieldMetadataId
|
||||
operand
|
||||
value
|
||||
displayValue
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
viewSorts {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
fieldMetadataId
|
||||
direction
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
viewFields {
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
fieldMetadataId
|
||||
isVisible
|
||||
size
|
||||
position
|
||||
deletedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@ -0,0 +1,9 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const ACTIVATE_WORKSPACE = gql`
|
||||
mutation ActivateWorkspace($input: ActivateWorkspaceInput!) {
|
||||
activateWorkspace(data: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
@ -1,6 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useRecoilState } from 'recoil';
|
||||
@ -54,7 +53,6 @@ const validationSchema = z
|
||||
type Form = z.infer<typeof validationSchema>;
|
||||
|
||||
export const CreateProfile = () => {
|
||||
const navigate = useNavigate();
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
@ -114,8 +112,6 @@ export const CreateProfile = () => {
|
||||
colorScheme: 'System',
|
||||
}) as any,
|
||||
);
|
||||
|
||||
navigate('/');
|
||||
} catch (error: any) {
|
||||
enqueueSnackBar(error?.message, {
|
||||
variant: 'error',
|
||||
@ -125,7 +121,6 @@ export const CreateProfile = () => {
|
||||
[
|
||||
currentWorkspaceMember?.id,
|
||||
enqueueSnackBar,
|
||||
navigate,
|
||||
setCurrentWorkspaceMember,
|
||||
updateOneRecord,
|
||||
],
|
||||
|
@ -3,22 +3,24 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { SubTitle } from '@/auth/components/SubTitle';
|
||||
import { Title } from '@/auth/components/Title';
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
|
||||
import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient';
|
||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useUpdateWorkspaceMutation } from '~/generated/graphql';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { useActivateWorkspaceMutation } from '~/generated/graphql';
|
||||
|
||||
const StyledContentContainer = styled.div`
|
||||
width: 100%;
|
||||
@ -46,9 +48,9 @@ export const CreateWorkspace = () => {
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
const [activateWorkspace] = useActivateWorkspaceMutation();
|
||||
const apolloMetadataClient = useApolloMetadataClient();
|
||||
|
||||
// Form
|
||||
const {
|
||||
@ -67,28 +69,25 @@ export const CreateWorkspace = () => {
|
||||
const onSubmit: SubmitHandler<Form> = useCallback(
|
||||
async (data) => {
|
||||
try {
|
||||
const result = await updateWorkspace({
|
||||
const result = await activateWorkspace({
|
||||
variables: {
|
||||
input: {
|
||||
displayName: data.name,
|
||||
},
|
||||
},
|
||||
});
|
||||
setCurrentWorkspace({
|
||||
id: result.data?.updateWorkspace?.id ?? '',
|
||||
displayName: data.name,
|
||||
subscriptionStatus:
|
||||
result.data?.updateWorkspace?.subscriptionStatus ?? 'incomplete',
|
||||
allowImpersonation:
|
||||
result.data?.updateWorkspace?.allowImpersonation ?? false,
|
||||
refetchQueries: [GET_CURRENT_USER],
|
||||
});
|
||||
|
||||
if (result.errors || !result.data?.updateWorkspace) {
|
||||
await apolloMetadataClient?.refetchQueries({
|
||||
include: [FIND_MANY_OBJECT_METADATA_ITEMS],
|
||||
});
|
||||
|
||||
if (result.errors) {
|
||||
throw result.errors ?? new Error('Unknown error');
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
navigate('/create/profile');
|
||||
navigate(AppPath.CreateProfile);
|
||||
}, 20);
|
||||
} catch (error: any) {
|
||||
enqueueSnackBar(error?.message, {
|
||||
@ -96,7 +95,7 @@ export const CreateWorkspace = () => {
|
||||
});
|
||||
}
|
||||
},
|
||||
[enqueueSnackBar, navigate, setCurrentWorkspace, updateWorkspace],
|
||||
[enqueueSnackBar, navigate, apolloMetadataClient, activateWorkspace],
|
||||
);
|
||||
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
@ -115,7 +114,7 @@ export const CreateWorkspace = () => {
|
||||
[onSubmit],
|
||||
);
|
||||
|
||||
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceCreation) {
|
||||
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceActivation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
|
||||
import { AppPath } from '../../modules/types/AppPath';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
|
||||
export const VerifyEffect = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
@ -4,7 +4,7 @@ import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER_AND_VIEWS } from '@/users/graphql/queries/getCurrentUserAndViews';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
@ -22,16 +22,13 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query(
|
||||
getOperationName(GET_CURRENT_USER_AND_VIEWS) ?? '',
|
||||
() => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
},
|
||||
});
|
||||
},
|
||||
),
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
|
@ -1,14 +1,18 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedOnboardingUsersData } from '~/testing/mock-data/users';
|
||||
|
||||
import { CreateWorkspace } from '../CreateWorkspace';
|
||||
|
||||
@ -25,7 +29,18 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
],
|
||||
args: { routePath: AppPath.CreateWorkspace },
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[1],
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER_AND_VIEWS } from '@/users/graphql/queries/getCurrentUserAndViews';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
@ -22,22 +22,19 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query(
|
||||
getOperationName(GET_CURRENT_USER_AND_VIEWS) ?? '',
|
||||
() => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: {
|
||||
...mockedOnboardingUsersData[0],
|
||||
defaultWorkspace: {
|
||||
...mockedOnboardingUsersData[0].defaultWorkspace,
|
||||
subscriptionStatus: 'incomplete',
|
||||
},
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: {
|
||||
...mockedOnboardingUsersData[0],
|
||||
defaultWorkspace: {
|
||||
...mockedOnboardingUsersData[0].defaultWorkspace,
|
||||
subscriptionStatus: 'incomplete',
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
),
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ import { fireEvent, within } from '@storybook/test';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { GET_CURRENT_USER_AND_VIEWS } from '@/users/graphql/queries/getCurrentUserAndViews';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import {
|
||||
PageDecorator,
|
||||
PageDecoratorArgs,
|
||||
@ -22,16 +22,13 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
parameters: {
|
||||
msw: {
|
||||
handlers: [
|
||||
graphql.query(
|
||||
getOperationName(GET_CURRENT_USER_AND_VIEWS) ?? '',
|
||||
() => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
},
|
||||
});
|
||||
},
|
||||
),
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedOnboardingUsersData[0],
|
||||
},
|
||||
});
|
||||
}),
|
||||
graphqlMocks.handlers,
|
||||
],
|
||||
},
|
||||
|
@ -6,10 +6,9 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { useImpersonateMutation } from '~/generated/graphql';
|
||||
|
||||
import { AppPath } from '../../modules/types/AppPath';
|
||||
|
||||
export const ImpersonateEffect = () => {
|
||||
const navigate = useNavigate();
|
||||
const { userId } = useParams();
|
||||
|
@ -2,6 +2,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { SignInBackgroundMockPage } from '@/sign-in-background-mock/components/SignInBackgroundMockPage';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { MainButton } from '@/ui/input/button/components/MainButton';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import { StyledEmptyTextContainer } from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
@ -49,7 +50,7 @@ export const NotFound = () => {
|
||||
<MainButton
|
||||
title="Back to content"
|
||||
fullWidth
|
||||
onClick={() => navigate('/')}
|
||||
onClick={() => navigate(AppPath.Index)}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
</StyledErrorContainer>
|
||||
|
@ -1,10 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
@ -32,10 +28,6 @@ export const RecordIndexPage = () => {
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { findObjectMetadataItemByNamePlural } =
|
||||
useObjectMetadataItemForSettings();
|
||||
|
||||
@ -44,15 +36,6 @@ export const RecordIndexPage = () => {
|
||||
findObjectMetadataItemByNamePlural(objectNamePlural)?.icon,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isNonEmptyString(objectNamePlural) &&
|
||||
onboardingStatus === OnboardingStatus.Completed
|
||||
) {
|
||||
navigate('/');
|
||||
}
|
||||
}, [objectNamePlural, navigate, onboardingStatus]);
|
||||
|
||||
const { createOneRecord: createOneObject } = useCreateOneRecord({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ import { graphql, HttpResponse } from 'msw';
|
||||
import { CREATE_EVENT } from '@/analytics/graphql/queries/createEvent';
|
||||
import { GET_CLIENT_CONFIG } from '@/client-config/graphql/queries/getClientConfig';
|
||||
import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries';
|
||||
import { GET_CURRENT_USER_AND_VIEWS } from '@/users/graphql/queries/getCurrentUserAndViews';
|
||||
import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { mockedActivities } from '~/testing/mock-data/activities';
|
||||
import { mockedCompaniesData } from '~/testing/mock-data/companies';
|
||||
@ -22,22 +22,10 @@ const metadataGraphql = graphql.link(`${REACT_APP_SERVER_BASE_URL}/metadata`);
|
||||
|
||||
export const graphqlMocks = {
|
||||
handlers: [
|
||||
graphql.query(getOperationName(GET_CURRENT_USER_AND_VIEWS) ?? '', () => {
|
||||
graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => {
|
||||
return HttpResponse.json({
|
||||
data: {
|
||||
currentUser: mockedUsersData[0],
|
||||
views: {
|
||||
edges: mockedViewsData.map((view) => ({
|
||||
node: view,
|
||||
cursor: null,
|
||||
})),
|
||||
pageInfo: {
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
startCursor: null,
|
||||
endCursor: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@ type MockedUser = Pick<
|
||||
| '__typename'
|
||||
| 'supportUserHash'
|
||||
> & {
|
||||
workspaceMember: WorkspaceMember;
|
||||
workspaceMember: WorkspaceMember | null;
|
||||
locale: string;
|
||||
defaultWorkspace: Workspace;
|
||||
};
|
||||
@ -34,7 +34,7 @@ export const mockDefaultWorkspace: Workspace = {
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
};
|
||||
|
||||
const workspaceMember: WorkspaceMember = {
|
||||
export const mockedWorkspaceMemberData: WorkspaceMember = {
|
||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
||||
colorScheme: 'Light',
|
||||
avatarUrl,
|
||||
@ -59,7 +59,7 @@ export const mockedUsersData: Array<MockedUser> = [
|
||||
canImpersonate: false,
|
||||
supportUserHash:
|
||||
'a95afad9ff6f0b364e2a3fd3e246a1a852c22b6e55a3ca33745a86c201f9c10d',
|
||||
workspaceMember,
|
||||
workspaceMember: mockedWorkspaceMemberData,
|
||||
defaultWorkspace: mockDefaultWorkspace,
|
||||
locale: 'en',
|
||||
},
|
||||
@ -73,7 +73,7 @@ export const mockedUsersData: Array<MockedUser> = [
|
||||
supportUserHash:
|
||||
'54ac3986035961724cdb9a7a30c70e6463a4b68f0ecd2014c727171a82144b74',
|
||||
workspaceMember: {
|
||||
...workspaceMember,
|
||||
...mockedWorkspaceMemberData,
|
||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6c',
|
||||
name: {
|
||||
firstName: 'Felix',
|
||||
@ -97,7 +97,7 @@ export const mockedOnboardingUsersData: Array<MockedUser> = [
|
||||
supportUserHash:
|
||||
'4fb61d34ed3a4aeda2476d4b308b5162db9e1809b2b8277e6fdc6efc4a609254',
|
||||
workspaceMember: {
|
||||
...workspaceMember,
|
||||
...mockedWorkspaceMemberData,
|
||||
id: 'd454f075-c72f-4ebe-bac7-d28e75e74a23',
|
||||
name: {
|
||||
firstName: '',
|
||||
@ -116,7 +116,7 @@ export const mockedOnboardingUsersData: Array<MockedUser> = [
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
canImpersonate: false,
|
||||
workspaceMember,
|
||||
workspaceMember: null,
|
||||
defaultWorkspace: {
|
||||
...mockDefaultWorkspace,
|
||||
displayName: '',
|
||||
|
@ -101,6 +101,10 @@ export class AuthResolver {
|
||||
@AuthUser() user: User,
|
||||
): Promise<TransientToken | void> {
|
||||
const workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
|
||||
if (!workspaceMember) {
|
||||
return;
|
||||
}
|
||||
const transientToken = await this.tokenService.generateTransientToken(
|
||||
workspaceMember.id,
|
||||
user.defaultWorkspace.id,
|
||||
|
@ -7,11 +7,11 @@ import {
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
|
||||
import FileType from 'file-type';
|
||||
import { Repository } from 'typeorm';
|
||||
import { v4 } from 'uuid';
|
||||
import { render } from '@react-email/components';
|
||||
import { PasswordUpdateNotifyEmail } from 'twenty-emails';
|
||||
import FileType from 'file-type';
|
||||
|
||||
import { FileFolder } from 'src/core/file/interfaces/file-folder.interface';
|
||||
|
||||
@ -29,11 +29,11 @@ import { User } from 'src/core/user/user.entity';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { UserService } from 'src/core/user/services/user.service';
|
||||
import { WorkspaceManagerService } from 'src/workspace/workspace-manager/workspace-manager.service';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
import { FileUploadService } from 'src/core/file/services/file-upload.service';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { EmailService } from 'src/integrations/email/email.service';
|
||||
import { UpdatePassword } from 'src/core/auth/dto/update-password.entity';
|
||||
import { getImageBufferFromUrl } from 'src/utils/image';
|
||||
|
||||
import { TokenService } from './token.service';
|
||||
|
||||
@ -135,18 +135,8 @@ export class AuthService {
|
||||
});
|
||||
|
||||
workspace = await this.workspaceRepository.save(workspaceToCreate);
|
||||
await this.workspaceManagerService.init(workspace.id);
|
||||
}
|
||||
|
||||
const userToCreate = this.userRepository.create({
|
||||
email: email,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
canImpersonate: false,
|
||||
passwordHash,
|
||||
defaultWorkspace: workspace,
|
||||
});
|
||||
const user = await this.userRepository.save(userToCreate);
|
||||
let imagePath: string | undefined = undefined;
|
||||
|
||||
if (picture) {
|
||||
@ -166,9 +156,18 @@ export class AuthService {
|
||||
|
||||
imagePath = paths[0];
|
||||
}
|
||||
await this.userService.createWorkspaceMember(user, imagePath);
|
||||
|
||||
return user;
|
||||
const userToCreate = this.userRepository.create({
|
||||
email: email,
|
||||
firstName: firstName,
|
||||
lastName: lastName,
|
||||
defaultAvatarUrl: imagePath,
|
||||
canImpersonate: false,
|
||||
passwordHash,
|
||||
defaultWorkspace: workspace,
|
||||
});
|
||||
|
||||
return await this.userRepository.save(userToCreate);
|
||||
}
|
||||
|
||||
async verify(email: string): Promise<Verify> {
|
||||
@ -189,7 +188,11 @@ export class AuthService {
|
||||
|
||||
// passwordHash is hidden for security reasons
|
||||
user.passwordHash = '';
|
||||
user.workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
const workspaceMember = await this.userService.loadWorkspaceMember(user);
|
||||
|
||||
if (workspaceMember) {
|
||||
user.workspaceMember = workspaceMember;
|
||||
}
|
||||
|
||||
const accessToken = await this.tokenService.generateAccessToken(user.id);
|
||||
const refreshToken = await this.tokenService.generateRefreshToken(user.id);
|
||||
|
@ -21,11 +21,23 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
}
|
||||
|
||||
async loadWorkspaceMember(user: User) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
const dataSourcesMetadata =
|
||||
await this.dataSourceService.getDataSourcesMetadataFromWorkspaceId(
|
||||
user.defaultWorkspace.id,
|
||||
);
|
||||
|
||||
if (!dataSourcesMetadata.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataSourcesMetadata.length > 1) {
|
||||
throw new Error(
|
||||
`user '${user.id}' default workspace '${user.defaultWorkspace.id}' has multiple data source metadata`,
|
||||
);
|
||||
}
|
||||
|
||||
const dataSourceMetadata = dataSourcesMetadata[0];
|
||||
|
||||
const workspaceDataSource =
|
||||
await this.typeORMService.connectToDataSource(dataSourceMetadata);
|
||||
|
||||
@ -33,6 +45,10 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
`SELECT * FROM ${dataSourceMetadata.schema}."workspaceMember" WHERE "userId" = '${user.id}'`,
|
||||
);
|
||||
|
||||
if (!workspaceMembers.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(workspaceMembers.length === 1, 'WorkspaceMember not found');
|
||||
|
||||
const userWorkspaceMember = new WorkspaceMember();
|
||||
@ -63,7 +79,7 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
);
|
||||
}
|
||||
|
||||
async createWorkspaceMember(user: User, avatarUrl?: string) {
|
||||
async createWorkspaceMember(user: User) {
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
user.defaultWorkspace.id,
|
||||
@ -77,7 +93,7 @@ export class UserService extends TypeOrmQueryService<User> {
|
||||
("nameFirstName", "nameLastName", "colorScheme", "userId", "userEmail", "avatarUrl")
|
||||
VALUES ('${user.firstName}', '${user.lastName}', 'Light', '${
|
||||
user.id
|
||||
}', '${user.email}', '${avatarUrl ?? ''}')`,
|
||||
}', '${user.email}', '${user.defaultAvatarUrl ?? ''}')`,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,10 @@ export class User {
|
||||
@Column()
|
||||
email: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@Column({ nullable: true })
|
||||
defaultAvatarUrl: string;
|
||||
|
||||
@Field()
|
||||
@Column({ default: false })
|
||||
emailVerified: boolean;
|
||||
@ -81,6 +85,6 @@ export class User {
|
||||
})
|
||||
refreshTokens: RefreshToken[];
|
||||
|
||||
@Field(() => WorkspaceMember, { nullable: false })
|
||||
@Field(() => WorkspaceMember, { nullable: true })
|
||||
workspaceMember: WorkspaceMember;
|
||||
}
|
||||
|
@ -55,9 +55,11 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@ResolveField(() => WorkspaceMember, {
|
||||
nullable: false,
|
||||
nullable: true,
|
||||
})
|
||||
async workspaceMember(@Parent() user: User): Promise<WorkspaceMember> {
|
||||
async workspaceMember(
|
||||
@Parent() user: User,
|
||||
): Promise<WorkspaceMember | undefined> {
|
||||
return this.userService.loadWorkspaceMember(user);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class ActivateWorkspaceInput {
|
||||
@Field({ nullable: true })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
displayName?: string;
|
||||
}
|
@ -3,6 +3,7 @@ import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { WorkspaceManagerService } from 'src/workspace/workspace-manager/workspace-manager.service';
|
||||
import { UserService } from 'src/core/user/services/user.service';
|
||||
|
||||
import { WorkspaceService } from './workspace.service';
|
||||
|
||||
@ -21,6 +22,10 @@ describe('WorkspaceService', () => {
|
||||
provide: WorkspaceManagerService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: UserService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
@ -7,16 +8,33 @@ import { Repository } from 'typeorm';
|
||||
|
||||
import { WorkspaceManagerService } from 'src/workspace/workspace-manager/workspace-manager.service';
|
||||
import { Workspace } from 'src/core/workspace/workspace.entity';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { UserService } from 'src/core/user/services/user.service';
|
||||
import { ActivateWorkspaceInput } from 'src/core/workspace/dtos/activate-workspace-input';
|
||||
|
||||
export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
constructor(
|
||||
@InjectRepository(Workspace, 'core')
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
private readonly workspaceManagerService: WorkspaceManagerService,
|
||||
private readonly userService: UserService,
|
||||
) {
|
||||
super(workspaceRepository);
|
||||
}
|
||||
|
||||
async activateWorkspace(user: User, data: ActivateWorkspaceInput) {
|
||||
if (!data.displayName || !data.displayName.length) {
|
||||
throw new BadRequestException("'displayName' not provided");
|
||||
}
|
||||
await this.workspaceRepository.update(user.defaultWorkspace.id, {
|
||||
displayName: data.displayName,
|
||||
});
|
||||
await this.workspaceManagerService.init(user.defaultWorkspace.id);
|
||||
await this.userService.createWorkspaceMember(user);
|
||||
|
||||
return user.defaultWorkspace;
|
||||
}
|
||||
|
||||
async deleteWorkspace(id: string, shouldDeleteCoreWorkspace = true) {
|
||||
const workspace = await this.workspaceRepository.findOneBy({ id });
|
||||
|
||||
|
@ -8,6 +8,7 @@ import { WorkspaceManagerModule } from 'src/workspace/workspace-manager/workspac
|
||||
import { WorkspaceResolver } from 'src/core/workspace/workspace.resolver';
|
||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||
import { FeatureFlagEntity } from 'src/core/feature-flag/feature-flag.entity';
|
||||
import { UserModule } from 'src/core/user/user.module';
|
||||
|
||||
import { Workspace } from './workspace.entity';
|
||||
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
|
||||
@ -24,6 +25,7 @@ import { WorkspaceService } from './services/workspace.service';
|
||||
'core',
|
||||
),
|
||||
WorkspaceManagerModule,
|
||||
UserModule,
|
||||
FileModule,
|
||||
],
|
||||
services: [WorkspaceService],
|
||||
|
@ -12,6 +12,9 @@ import { assert } from 'src/utils/assert';
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { UpdateWorkspaceInput } from 'src/core/workspace/dtos/update-workspace-input';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
import { User } from 'src/core/user/user.entity';
|
||||
import { AuthUser } from 'src/decorators/auth/auth-user.decorator';
|
||||
import { ActivateWorkspaceInput } from 'src/core/workspace/dtos/activate-workspace-input';
|
||||
|
||||
import { Workspace } from './workspace.entity';
|
||||
|
||||
@ -35,6 +38,15 @@ export class WorkspaceResolver {
|
||||
return workspace;
|
||||
}
|
||||
|
||||
@Mutation(() => Workspace)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
async activateWorkspace(
|
||||
@Args('data') data: ActivateWorkspaceInput,
|
||||
@AuthUser() user: User,
|
||||
) {
|
||||
return await this.workspaceService.activateWorkspace(user, data);
|
||||
}
|
||||
|
||||
@Mutation(() => Workspace)
|
||||
async updateWorkspace(
|
||||
@Args('data') data: UpdateWorkspaceInput,
|
||||
|
Loading…
Reference in New Issue
Block a user