Files
twenty/packages/twenty-front/src/modules/workspace/components/WorkspaceInviteTeam.tsx
martmull 3986824017 5623 add an inviteteam onboarding step (#5769)
## Changes
- add a new invite Team onboarding step
- update currentUser.state to currentUser.onboardingStep

## Edge cases
We will never display invite team onboarding step 
- if number of workspaceMember > 1
- if a workspaceMember as been deleted

## Important changes
Update typeorm package version to 0.3.20 because we needed a fix on
`indexPredicates` pushed in 0.3.20 version
(https://github.com/typeorm/typeorm/issues/10191)

## Result
<img width="844" alt="image"
src="https://github.com/twentyhq/twenty/assets/29927851/0dab54cf-7c66-4c64-b0c9-b0973889a148">



https://github.com/twentyhq/twenty/assets/29927851/13268d0a-cfa7-42a4-84c6-9e1fbbe48912
2024-06-12 21:13:18 +02:00

141 lines
4.0 KiB
TypeScript

import { useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import styled from '@emotion/styled';
import { zodResolver } from '@hookform/resolvers/zod';
import { Key } from 'ts-key-enum';
import { IconMail, IconSend } from 'twenty-ui';
import { z } from 'zod';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { Button } from '@/ui/input/button/components/Button';
import { TextInput } from '@/ui/input/components/TextInput';
import { sanitizeEmailList } from '@/workspace/utils/sanitizeEmailList';
import { useSendInviteLinkMutation } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
`;
const StyledLinkContainer = styled.div`
flex: 1;
margin-right: ${({ theme }) => theme.spacing(2)};
`;
const emailValidationSchema = (email: string) =>
z.string().email(`Invalid email '${email}'`);
const validationSchema = () =>
z
.object({
emails: z.string().superRefine((value, ctx) => {
if (!value.length) {
return;
}
const emails = sanitizeEmailList(value.split(','));
if (emails.length === 0) {
ctx.addIssue({
code: z.ZodIssueCode.invalid_string,
message: 'Emails should not be empty',
validation: 'email',
});
}
const invalidEmails: string[] = [];
for (const email of emails) {
const result = emailValidationSchema(email).safeParse(email);
if (!result.success) {
invalidEmails.push(email);
}
}
if (invalidEmails.length > 0) {
ctx.addIssue({
code: z.ZodIssueCode.invalid_string,
message:
invalidEmails.length > 1
? 'Emails "' + invalidEmails.join('", "') + '" are invalid'
: 'Email "' + invalidEmails.join('", "') + '" is invalid',
validation: 'email',
});
}
}),
})
.required();
type FormInput = {
emails: string;
};
export const WorkspaceInviteTeam = () => {
const { enqueueSnackBar } = useSnackBar();
const [sendInviteLink] = useSendInviteLinkMutation();
const { reset, handleSubmit, control, formState } = useForm<FormInput>({
mode: 'onSubmit',
resolver: zodResolver(validationSchema()),
defaultValues: {
emails: '',
},
});
const submit = handleSubmit(async (data) => {
const emailsList = sanitizeEmailList(data.emails.split(','));
const result = await sendInviteLink({ variables: { emails: emailsList } });
if (isDefined(result.errors)) {
throw result.errors;
}
enqueueSnackBar('Invite link sent to email addresses', {
variant: SnackBarVariant.Success,
duration: 2000,
});
});
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === Key.Enter) {
submit();
}
};
const { isSubmitSuccessful } = formState;
useEffect(() => {
if (isSubmitSuccessful) {
reset();
}
}, [isSubmitSuccessful, reset]);
return (
<form onSubmit={submit}>
<StyledContainer>
<StyledLinkContainer>
<Controller
name="emails"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => {
return (
<TextInput
placeholder="tim@apple.com, jony.ive@apple.dev"
LeftIcon={IconMail}
value={value}
onChange={onChange}
error={error?.message}
onKeyDown={handleKeyDown}
fullWidth
/>
);
}}
/>
</StyledLinkContainer>
<Button
Icon={IconSend}
variant="primary"
accent="blue"
title="Invite"
type="submit"
/>
</StyledContainer>
</form>
);
};