Files
twenty_crm/packages/twenty-front/src/pages/auth/CreateWorkspace.tsx
Marie 68662fa543 [refacto] Introduce stateless TextInputV2 (#5013)
## Context
As discussed with @lucasbordeau and @charlesBochet we are looking at
making low level UI components stateless when possible.
Therefore TextInput should not handle a hotkey state. Instead hotkeys
should be defined in the parent component (as done here in
CreateProfile).

Introducing here TextInputV2 that is stateless and that can already
replace TextInput without any behaviour change everywhere it is used
with `disableHotkey` prop.

## How was it tested?
Locally + Storybook

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
2024-04-22 11:19:41 +02:00

170 lines
5.0 KiB
TypeScript

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 { useSetRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
import { z } from 'zod';
import { SubTitle } from '@/auth/components/SubTitle';
import { Title } from '@/auth/components/Title';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
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 { H2Title } from '@/ui/display/typography/components/H2Title';
import { Loader } from '@/ui/feedback/loader/components/Loader.tsx';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { MainButton } from '@/ui/input/button/components/MainButton';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { useActivateWorkspaceMutation } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
const StyledContentContainer = styled.div`
width: 100%;
`;
const StyledSectionContainer = styled.div`
margin-top: ${({ theme }) => theme.spacing(8)};
`;
const StyledButtonContainer = styled.div`
margin-top: ${({ theme }) => theme.spacing(8)};
width: 200px;
`;
const validationSchema = z
.object({
name: z.string().min(1, { message: 'Name can not be empty' }),
})
.required();
type Form = z.infer<typeof validationSchema>;
export const CreateWorkspace = () => {
const navigate = useNavigate();
const { enqueueSnackBar } = useSnackBar();
const onboardingStatus = useOnboardingStatus();
const [activateWorkspace] = useActivateWorkspaceMutation();
const apolloMetadataClient = useApolloMetadataClient();
const setIsCurrentUserLoaded = useSetRecoilState(isCurrentUserLoadedState);
// Form
const {
control,
handleSubmit,
formState: { isValid, isSubmitting },
} = useForm<Form>({
mode: 'onChange',
defaultValues: {
name: '',
},
resolver: zodResolver(validationSchema),
});
const onSubmit: SubmitHandler<Form> = useCallback(
async (data) => {
try {
const result = await activateWorkspace({
variables: {
input: {
displayName: data.name,
},
},
});
setIsCurrentUserLoaded(false);
await apolloMetadataClient?.refetchQueries({
include: [FIND_MANY_OBJECT_METADATA_ITEMS],
});
if (isDefined(result.errors)) {
throw result.errors ?? new Error('Unknown error');
}
setTimeout(() => {
navigate(AppPath.CreateProfile);
}, 20);
} catch (error: any) {
enqueueSnackBar(error?.message, {
variant: 'error',
});
}
},
[
activateWorkspace,
setIsCurrentUserLoaded,
apolloMetadataClient,
navigate,
enqueueSnackBar,
],
);
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === Key.Enter) {
event.preventDefault();
handleSubmit(onSubmit)();
}
};
if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceActivation) {
return null;
}
return (
<>
<Title withMarginTop={false}>Create your workspace</Title>
<SubTitle>
A shared environment where you will be able to manage your customer
relations with your team.
</SubTitle>
<StyledContentContainer>
<StyledSectionContainer>
<H2Title title="Workspace logo" />
<WorkspaceLogoUploader />
</StyledSectionContainer>
<StyledSectionContainer>
<H2Title
title="Workspace name"
description="The name of your organization"
/>
<Controller
name="name"
control={control}
render={({
field: { onChange, onBlur, value },
fieldState: { error },
}) => (
<TextInputV2
autoFocus
value={value}
placeholder="Apple"
onBlur={onBlur}
onChange={onChange}
error={error?.message}
onKeyDown={handleKeyDown}
fullWidth
/>
)}
/>
</StyledSectionContainer>
</StyledContentContainer>
<StyledButtonContainer>
<MainButton
title="Continue"
onClick={handleSubmit(onSubmit)}
disabled={!isValid || isSubmitting}
Icon={() => isSubmitting && <Loader />}
fullWidth
/>
</StyledButtonContainer>
</>
);
};