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 commit 1701c30eb18804558293cc42043aedf96ea888df.

* Add defaultAvatarUrl to core user

This reverts commit 1701c30eb18804558293cc42043aedf96ea888df.

* 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 commit 8aa20a34363ffddfdee24f18fc80b27ea0ad5e1d.

* Fix

* Revert "Fix"

This reverts commit 2df7e9b6569b8bfb53f6a45391db725e28d16a18.

* Revert "Revert "Clean Mock Requests""

This reverts commit 3aefef8e9600d161434a047e845563d1b8e0692e.

* Revert "Fix tentative"

This reverts commit 13e7748d6f3b3858d30fb08adbc8ad347c5556ee.

* Update filename

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
martmull
2024-02-09 12:06:11 +01:00
committed by GitHub
parent 3fc18aeec1
commit 7425223f83
49 changed files with 769 additions and 355 deletions

View File

@ -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,
],

View File

@ -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;
}

View File

@ -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();

View File

@ -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,
],
},

View File

@ -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,
],
},
},
};

View File

@ -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,
],
},

View File

@ -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,
],
},

View File

@ -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();

View File

@ -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>

View File

@ -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,
});