[WIP] Whole FE migrated (#2517)

* Wip

* WIP

* Removed concole log

* Add relations to workspace init (#2511)

* Add relations to workspace init

* remove logs

* update prefill

* add missing isSystem

* comment relation fields

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

* migrate v2 core models to graphql schema

* Migrate to new workspace member schema

* Continue work

* migrated-main

* Finished accountOwner nested field integration on companies

* Introduce bug

* Fix

---------

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

View File

@ -11,6 +11,7 @@ import { z } from 'zod';
import { SubTitle } from '@/auth/components/SubTitle';
import { Title } from '@/auth/components/Title';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { H2Title } from '@/ui/display/typography/components/H2Title';
@ -57,6 +58,7 @@ export const CreateProfile = () => {
const { enqueueSnackBar } = useSnackBar();
const [currentUser] = useRecoilState(currentUserState);
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
const [updateUser] = useUpdateUserMutation();
@ -69,8 +71,8 @@ export const CreateProfile = () => {
} = useForm<Form>({
mode: 'onChange',
defaultValues: {
firstName: currentUser?.firstName ?? '',
lastName: currentUser?.lastName ?? '',
firstName: currentWorkspaceMember?.firstName ?? '',
lastName: currentWorkspaceMember?.lastName ?? '',
},
resolver: zodResolver(validationSchema),
});

View File

@ -1,15 +1,18 @@
import { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { isNonEmptyString } from '@sniptt/guards';
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';
export const VerifyEffect = () => {
const [searchParams] = useSearchParams();
const loginToken = searchParams.get('loginToken');
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const isLogged = useIsLogged();
const navigate = useNavigate();
@ -21,13 +24,9 @@ export const VerifyEffect = () => {
if (!loginToken) {
navigate(AppPath.SignIn);
} else {
const verifyResponse = await verify(loginToken);
await verify(loginToken);
if (
isNonEmptyString(
verifyResponse.user.workspaceMember?.workspace.displayName,
)
) {
if (isNonEmptyString(currentWorkspace?.displayName)) {
navigate(AppPath.Index);
} else {
navigate(AppPath.CreateWorkspace);

View File

@ -38,21 +38,9 @@ export const ImpersonateEffect = () => {
throw new Error('No impersonate result');
}
if (!impersonateResult.data?.impersonate.user.workspaceMember) {
throw new Error('No workspace member');
}
if (!impersonateResult.data?.impersonate.user.workspaceMember.settings) {
throw new Error('No workspace member settings');
}
setCurrentUser({
...impersonateResult.data.impersonate.user,
workspaceMember: {
...impersonateResult.data.impersonate.user.workspaceMember,
settings:
impersonateResult.data.impersonate.user.workspaceMember.settings,
},
// Todo also set WorkspaceMember
});
setTokenPair(impersonateResult.data?.impersonate.tokens);

View File

@ -1,8 +1,10 @@
import { useState } from 'react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { IconSettings, IconTrash } from '@/ui/display/icon';
import { H1Title } from '@/ui/display/typography/components/H1Title';
@ -13,10 +15,7 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'
import { Section } from '@/ui/layout/section/components/Section';
import { WorkspaceInviteLink } from '@/workspace/components/WorkspaceInviteLink';
import { WorkspaceMemberCard } from '@/workspace/components/WorkspaceMemberCard';
import {
useGetWorkspaceMembersQuery,
useRemoveWorkspaceMemberMutation,
} from '~/generated/graphql';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
const StyledH1Title = styled(H1Title)`
margin-bottom: 0;
@ -31,51 +30,22 @@ const StyledButtonContainer = styled.div`
export const SettingsWorkspaceMembers = () => {
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
const [userToDelete, setUserToDelete] = useState<string | undefined>();
const [workspaceMemberToDelete, setWorkspaceMemberToDelete] = useState<
string | undefined
>();
const [currentUser] = useRecoilState(currentUserState);
const workspace = currentUser?.workspaceMember?.workspace;
const { data } = useGetWorkspaceMembersQuery();
const [removeWorkspaceMember] = useRemoveWorkspaceMemberMutation();
const handleRemoveWorkspaceMember = async (userId: string) => {
await removeWorkspaceMember({
variables: {
where: {
userId,
},
},
optimisticResponse: {
__typename: 'Mutation',
deleteWorkspaceMember: {
__typename: 'WorkspaceMember',
id: userId,
},
},
update: (cache, { data: responseData }) => {
if (!responseData) {
return;
}
cache.evict({
id: cache.identify({
id: responseData.deleteWorkspaceMember.id,
__typename: 'WorkspaceMember',
}),
});
cache.evict({
id: cache.identify({
id: userId,
__typename: 'User',
}),
});
cache.gc();
},
const { objects: workspaceMembers } =
useFindManyObjectRecords<WorkspaceMember>({
objectNamePlural: 'workspaceMembersV2',
});
const { deleteOneObject: deleteOneWorkspaceMember } =
useDeleteOneObjectRecord({
objectNamePlural: 'workspaceMembersV2',
});
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const handleRemoveWorkspaceMember = async (workspaceMemberId: string) => {
await deleteOneWorkspaceMember?.(workspaceMemberId);
setIsConfirmationModalOpen(false);
};
@ -83,14 +53,14 @@ export const SettingsWorkspaceMembers = () => {
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
<SettingsPageContainer width={350}>
<StyledH1Title title="Members" />
{workspace?.inviteHash && (
{currentWorkspace?.inviteHash && (
<Section>
<H2Title
title="Invite"
description="Send an invitation to use Twenty"
/>
<WorkspaceInviteLink
inviteLink={`${window.location.origin}/invite/${workspace?.inviteHash}`}
inviteLink={`${window.location.origin}/invite/${currentWorkspace?.inviteHash}`}
/>
</Section>
)}
@ -99,17 +69,17 @@ export const SettingsWorkspaceMembers = () => {
title="Members"
description="Manage the members of your space here"
/>
{data?.workspaceMembers?.map((member) => (
{workspaceMembers?.map((member) => (
<WorkspaceMemberCard
key={member.user.id}
workspaceMember={{ user: member.user }}
key={member.id}
workspaceMember={member as WorkspaceMember}
accessory={
currentUser?.id !== member.user.id && (
currentWorkspace?.id !== member.id && (
<StyledButtonContainer>
<IconButton
onClick={() => {
setIsConfirmationModalOpen(true);
setUserToDelete(member.user.id);
setWorkspaceMemberToDelete(member.id);
}}
variant="tertiary"
size="medium"
@ -133,7 +103,8 @@ export const SettingsWorkspaceMembers = () => {
</>
}
onConfirmClick={() =>
userToDelete && handleRemoveWorkspaceMember(userToDelete)
workspaceMemberToDelete &&
handleRemoveWorkspaceMember(workspaceMemberToDelete)
}
deleteButtonText="Delete account"
/>

View File

@ -1,14 +1,14 @@
import { useEffect } from 'react';
import { useRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { tasksFilterDefinitions } from './tasks-filter-definitions';
export const TasksEffect = () => {
const [currentUser] = useRecoilState(currentUserState);
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { setSelectedFilter, setAvailableFilterDefinitions } = useFilter();
useEffect(() => {
@ -16,16 +16,19 @@ export const TasksEffect = () => {
}, [setAvailableFilterDefinitions]);
useEffect(() => {
if (currentUser) {
if (currentWorkspaceMember) {
setSelectedFilter({
fieldMetadataId: 'assigneeId',
value: currentUser.id,
value: currentWorkspaceMember.id,
operand: ViewFilterOperand.Is,
displayValue: currentUser.displayName,
displayAvatarUrl: currentUser.avatarUrl ?? undefined,
displayValue:
currentWorkspaceMember.firstName +
' ' +
currentWorkspaceMember.lastName,
displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined,
definition: tasksFilterDefinitions[0],
});
}
}, [currentUser, setSelectedFilter]);
}, [currentWorkspaceMember, setSelectedFilter]);
return <></>;
};