[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

@ -8,7 +8,7 @@ import { Avatar, AvatarType } from '@/users/components/Avatar';
import { Chip, ChipVariant } from './Chip';
type EntityChipProps = {
export type EntityChipProps = {
linkToEntity?: string;
entityId: string;
name: string;

View File

@ -62,9 +62,13 @@ export const SingleEntitySelectBase = <
const entitiesInDropdown = [selectedEntity, ...entitiesToSelect].filter(
(entity): entity is CustomEntityForSelect =>
assertNotNull(entity) && isNonEmptyString(entity.name.trim()),
assertNotNull(entity) && isNonEmptyString(entity.name),
);
console.log({
entitiesInDropdown,
});
const { preselectedOptionId, resetScroll } = useEntitySelectScroll({
selectableOptionIds: [
EmptyButtonId,

View File

@ -5,6 +5,7 @@ export enum Entity {
Company = 'Company',
Person = 'Person',
User = 'User',
WorkspaceMember = 'WorkspaceMember',
}
export type EntityTypeForSelect =

View File

@ -2,6 +2,7 @@ import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { getImageAbsoluteURIOrBase64 } from '@/users/utils/getProfilePictureAbsoluteURI';
import NavCollapseButton from './NavCollapseButton';
@ -53,8 +54,8 @@ const NavWorkspaceButton = ({
showCollapseButton,
}: NavWorkspaceButtonProps) => {
const currentUser = useRecoilValue(currentUserState);
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const currentWorkspace = currentUser?.workspaceMember?.workspace;
const DEFAULT_LOGO =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=';

View File

@ -3,6 +3,10 @@ import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import {
CurrentWorkspaceMember,
currentWorkspaceMemberState,
} from '@/auth/states/currentWorkspaceMemberState';
import { supportChatState } from '@/client-config/states/supportChatState';
import { IconHelpCircle } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
@ -30,13 +34,18 @@ const insertScript = ({
const SupportChat = () => {
const currentUser = useRecoilValue(currentUserState);
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const supportChat = useRecoilValue(supportChatState);
const [isFrontChatLoaded, setIsFrontChatLoaded] = useState(false);
const configureFront = useCallback(
(
chatId: string,
currentUser: Pick<User, 'email' | 'displayName' | 'supportUserHash'>,
currentUser: Pick<User, 'email' | 'supportUserHash'>,
currentWorkspaceMember: Pick<
CurrentWorkspaceMember,
'firstName' | 'lastName'
>,
) => {
const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js';
const script = document.querySelector(`script[src="${url}"]`);
@ -49,7 +58,10 @@ const SupportChat = () => {
chatId,
useDefaultLauncher: false,
email: currentUser.email,
name: currentUser.displayName,
name:
currentWorkspaceMember.firstName +
' ' +
currentWorkspaceMember.lastName,
userHash: currentUser?.supportUserHash,
});
setIsFrontChatLoaded(true);
@ -65,9 +77,14 @@ const SupportChat = () => {
supportChat?.supportDriver === 'front' &&
supportChat.supportFrontChatId &&
currentUser?.email &&
currentWorkspaceMember &&
!isFrontChatLoaded
) {
configureFront(supportChat.supportFrontChatId, currentUser);
configureFront(
supportChat.supportFrontChatId,
currentUser,
currentWorkspaceMember,
);
}
}, [
configureFront,
@ -75,6 +92,7 @@ const SupportChat = () => {
isFrontChatLoaded,
supportChat?.supportDriver,
supportChat.supportFrontChatId,
currentWorkspaceMember,
]);
return isFrontChatLoaded ? (

View File

@ -105,13 +105,16 @@ export const usePersistField = () => {
valueToPersist,
);
console.log({
fieldName,
valueToPersist,
});
updateEntity?.({
variables: {
where: { id: entityId },
data: {
[fieldName]: valueToPersist
? { connect: { id: valueToPersist.id } }
: { disconnect: true },
[fieldName]: valueToPersist?.id,
},
},
});

View File

@ -1,24 +1,27 @@
import { EntityChip } from '@/ui/display/chip/components/EntityChip';
import { getEntityChipFromFieldMetadata } from '@/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata';
import { useRelationField } from '../../hooks/useRelationField';
export const RelationFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useRelationField();
const { entityChipDisplayMapper } = fieldDefinition;
if (!entityChipDisplayMapper) {
throw new Error(
"Missing entityChipDisplayMapper in FieldContext. Please provide it in the FieldContextProvider's value prop.",
);
}
const { name, pictureUrl, avatarType } =
entityChipDisplayMapper?.(fieldValue);
console.log({
fieldDefinition,
fieldValue,
});
const entityChipProps = getEntityChipFromFieldMetadata(
fieldDefinition,
fieldValue,
);
return (
<EntityChip
entityId={fieldValue?.id}
name={name}
pictureUrl={pictureUrl}
avatarType={avatarType}
entityId={entityChipProps.entityId}
name={entityChipProps.name}
pictureUrl={entityChipProps.pictureUrl}
avatarType={entityChipProps.avatarType}
/>
);
};

View File

@ -0,0 +1,32 @@
import { EntityChipProps } from '@/ui/display/chip/components/EntityChip';
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
export const getEntityChipFromFieldMetadata = (
fieldDefinition: FieldDefinition<FieldRelationMetadata>,
fieldValue: any,
) => {
const { fieldName } = fieldDefinition.metadata;
const chipValue: Pick<
EntityChipProps,
'name' | 'pictureUrl' | 'avatarType' | 'entityId'
> = {
name: '',
pictureUrl: '',
avatarType: 'rounded',
entityId: fieldValue?.id,
};
console.log({
fieldName,
fieldValue,
});
// TODO: use every
if (fieldName === 'accountOwner' && fieldValue) {
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName;
}
return chipValue;
};

View File

@ -22,6 +22,11 @@ export const useRelationField = () => {
}),
);
console.log({
fieldDefinition,
fieldValue,
});
const fieldInitialValue = useFieldInitialValue();
const initialSearchValue = fieldInitialValue?.isEmpty

View File

@ -40,7 +40,7 @@ export const RelationFieldInput = ({
return (
<StyledRelationPickerContainer>
{fieldDefinition.metadata.relationType === Entity.Person ? (
{fieldDefinition.metadata.fieldName === 'person' ? (
<PeoplePicker
personId={initialValue?.id ?? ''}
companyId={initialValue?.companyId ?? ''}
@ -48,7 +48,7 @@ export const RelationFieldInput = ({
onCancel={onCancel}
initialSearchFilter={initialSearchValue}
/>
) : fieldDefinition.metadata.relationType === Entity.User ? (
) : fieldDefinition.metadata.fieldName === 'accountOwner' ? (
<UserPicker
userId={initialValue?.id ?? ''}
onSubmit={handleSubmit}

View File

@ -82,6 +82,8 @@ type RecordTableProps = {
export const RecordTable = ({ updateEntityMutation }: RecordTableProps) => {
const tableBodyRef = useRef<HTMLDivElement>(null);
console.log('record table');
const {
leaveTableFocus,
setRowSelectedState,

View File

@ -1,105 +1,32 @@
import { useCallback } from 'react';
import { useRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import {
ColorScheme,
useUpdateOneWorkspaceMemberMutation,
useUpdateUserMutation,
} from '~/generated/graphql';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { ColorScheme } from '~/generated/graphql';
export const useColorScheme = () => {
const [currentUser, setCurrentUser] = useRecoilState(currentUserState);
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState);
const [updateUser] = useUpdateUserMutation();
const [updateWorkspaceMember] = useUpdateOneWorkspaceMemberMutation();
const colorScheme =
!currentUser?.workspaceMember.settings?.colorScheme &&
!currentUser?.settings?.colorScheme
? ColorScheme.System
: currentUser.workspaceMember.settings?.colorScheme ??
currentUser.settings.colorScheme;
const { updateOneObject: updateOneWorkspaceMember } =
useUpdateOneObjectRecord({
objectNamePlural: 'workspaceMembersV2',
});
const colorScheme = currentWorkspaceMember?.colorScheme ?? ColorScheme.System;
const setColorScheme = useCallback(
async (value: ColorScheme) => {
try {
// connect settings to workspace member if not already connected
await updateWorkspaceMember({
variables: {
where: { id: currentUser?.workspaceMember.id },
data: { settings: { connect: { id: currentUser?.settings.id } } },
},
});
const result = await updateUser({
variables: {
where: {
id: currentUser?.id,
},
data: {
settings: {
update: {
colorScheme: value,
},
},
},
},
optimisticResponse: currentUser
? {
__typename: 'Mutation',
updateUser: {
__typename: 'User',
...currentUser,
workspaceMember: {
...currentUser.workspaceMember,
settings: {
__typename: 'UserSettings',
id: currentUser.settings.id,
colorScheme: value,
locale: currentUser.settings.locale,
},
},
settings: {
__typename: 'UserSettings',
id: currentUser.settings.id,
colorScheme: value,
locale: currentUser.settings.locale,
},
},
}
: undefined,
update: (_cache, { data }) => {
if (
data?.updateUser.workspaceMember?.settings?.colorScheme &&
currentUser
) {
setCurrentUser({
...currentUser,
workspaceMember: {
...currentUser.workspaceMember,
settings: {
...currentUser.workspaceMember.settings,
colorScheme:
data.updateUser.workspaceMember.settings.colorScheme,
},
},
settings: {
...currentUser.settings,
colorScheme:
data.updateUser.workspaceMember.settings.colorScheme,
},
});
}
},
});
if (!result.data || result.errors) {
throw result.errors;
}
} catch (err) {}
if (!currentWorkspaceMember) {
return;
}
await updateOneWorkspaceMember?.({
idToUpdate: currentWorkspaceMember?.id,
input: {
colorScheme: value,
},
});
},
[updateWorkspaceMember, currentUser, updateUser, setCurrentUser],
[currentWorkspaceMember, updateOneWorkspaceMember],
);
return {