[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:
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -5,6 +5,7 @@ export enum Entity {
|
||||
Company = 'Company',
|
||||
Person = 'Person',
|
||||
User = 'User',
|
||||
WorkspaceMember = 'WorkspaceMember',
|
||||
}
|
||||
|
||||
export type EntityTypeForSelect =
|
||||
|
||||
@ -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=';
|
||||
|
||||
|
||||
@ -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 ? (
|
||||
|
||||
@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -22,6 +22,11 @@ export const useRelationField = () => {
|
||||
}),
|
||||
);
|
||||
|
||||
console.log({
|
||||
fieldDefinition,
|
||||
fieldValue,
|
||||
});
|
||||
|
||||
const fieldInitialValue = useFieldInitialValue();
|
||||
|
||||
const initialSearchValue = fieldInitialValue?.isEmpty
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -82,6 +82,8 @@ type RecordTableProps = {
|
||||
export const RecordTable = ({ updateEntityMutation }: RecordTableProps) => {
|
||||
const tableBodyRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
console.log('record table');
|
||||
|
||||
const {
|
||||
leaveTableFocus,
|
||||
setRowSelectedState,
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user