Added Linaria for performance optimization (#5693)

- Added Linaria to have compiled CSS on our optimized field displays
- Refactored mocks for performance stories on fields
- Refactored generateRecordChipData into a global context, computed only
when we fetch object metadata items.
- Refactored ChipFieldDisplay 
- Refactored PhoneFieldDisplay
This commit is contained in:
Lucas Bordeau 2024-06-12 16:31:07 +02:00 committed by GitHub
parent 30d3ebc68a
commit 732e8912da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 2166 additions and 519 deletions

View File

@ -23,6 +23,8 @@
"@hello-pangea/dnd": "^16.2.0",
"@hookform/resolvers": "^3.1.1",
"@jsdevtools/rehype-toc": "^3.0.2",
"@linaria/core": "^6.2.0",
"@linaria/react": "^6.2.1",
"@mdx-js/react": "^3.0.0",
"@nestjs/apollo": "^11.0.5",
"@nestjs/axios": "^3.0.1",
@ -61,6 +63,7 @@
"@types/lodash.pick": "^4.3.7",
"@types/nodemailer": "^6.4.14",
"@types/passport-microsoft": "^1.0.3",
"@wyw-in-js/vite": "^0.5.3",
"add": "^2.0.6",
"addressparser": "^1.0.1",
"afterframe": "^1.0.2",
@ -192,6 +195,7 @@
"devDependencies": {
"@babel/core": "^7.14.5",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.24.6",
"@crxjs/vite-plugin": "^1.0.14",
"@docusaurus/module-type-aliases": "^3.1.0",
"@docusaurus/tsconfig": "3.1.0",

View File

@ -8,7 +8,7 @@ const globalCoverage = {
const modulesCoverage = {
branches: 25,
statements: 50,
statements: 49,
lines: 50,
functions: 40,
include: ['src/modules/**/*'],

View File

@ -2,15 +2,20 @@ export type Company = {
__typename: 'Company';
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
updatedAt?: string;
deletedAt?: string | null;
name: string;
domainName: string;
address: string;
accountOwnerId: string | null;
linkedinLink: { url: string; label: string };
xLink: { url: string; label: string };
annualRecurringRevenue: { amountMicros: number | null; currencyCode: string };
accountOwnerId?: string | null;
position?: number;
linkedinLink: { __typename?: 'Link'; url: string; label: string };
xLink?: { __typename?: 'Link'; url: string; label: string };
annualRecurringRevenue: {
__typename?: 'Currency';
amountMicros: number | null;
currencyCode: string;
};
employees: number | null;
idealCustomerProfile: boolean;
idealCustomerProfile?: boolean;
};

View File

@ -1,9 +1,11 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useRecoilValue } from 'recoil';
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';
import { getRecordChipGeneratorPerObjectPerField } from '@/object-record/utils/getRecordChipGeneratorPerObjectPerField';
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
export const ObjectMetadataItemsProvider = ({
@ -13,13 +15,23 @@ export const ObjectMetadataItemsProvider = ({
const shouldDisplayChildren = objectMetadataItems.length > 0;
const chipGeneratorPerObjectPerField = useMemo(() => {
return getRecordChipGeneratorPerObjectPerField(objectMetadataItems);
}, [objectMetadataItems]);
return (
<>
<ObjectMetadataItemsLoadEffect />
{shouldDisplayChildren ? (
<RelationPickerScope relationPickerScopeId="relation-picker">
{children}
</RelationPickerScope>
<PreComputedChipGeneratorsContext.Provider
value={{
chipGeneratorPerObjectPerField,
}}
>
<RelationPickerScope relationPickerScopeId="relation-picker">
{children}
</RelationPickerScope>
</PreComputedChipGeneratorsContext.Provider>
) : (
<UserOrMetadataLoader />
)}

View File

@ -0,0 +1,18 @@
import { createContext } from 'react';
import { RecordChipData } from '@/object-record/record-field/types/RecordChipData';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
export type ChipGeneratorPerObjectPerField = Record<
string,
Record<string, (record: ObjectRecord) => RecordChipData>
>;
export type PreComputedChipGeneratorsContextProps = {
chipGeneratorPerObjectPerField: ChipGeneratorPerObjectPerField;
};
export const PreComputedChipGeneratorsContext =
createContext<PreComputedChipGeneratorsContextProps>(
{} as PreComputedChipGeneratorsContextProps,
);

View File

@ -5,6 +5,7 @@ import { LinksFieldDisplay } from '@/object-record/record-field/meta-types/displ
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
import { isFieldDisplayedAsPhone } from '@/object-record/record-field/types/guards/isFieldDisplayedAsPhone';
import { isFieldLinks } from '@/object-record/record-field/types/guards/isFieldLinks';
import { isFieldChipDisplay } from '@/object-record/utils/getRecordChipGeneratorPerObjectPerField';
import { FieldContext } from '../contexts/FieldContext';
import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay';
@ -42,11 +43,7 @@ import { isFieldUuid } from '../types/guards/isFieldUuid';
export const FieldDisplay = () => {
const { fieldDefinition, isLabelIdentifier } = useContext(FieldContext);
const isChipDisplay =
isLabelIdentifier &&
(isFieldText(fieldDefinition) ||
isFieldFullName(fieldDefinition) ||
isFieldNumber(fieldDefinition));
const isChipDisplay = isFieldChipDisplay(fieldDefinition, isLabelIdentifier);
return isChipDisplay ? (
<ChipFieldDisplay />

View File

@ -1,12 +1,24 @@
import { RecordChip } from '@/object-record/components/RecordChip';
import { useChipField } from '@/object-record/record-field/meta-types/hooks/useChipField';
import { EntityChip } from 'twenty-ui';
import { useChipFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useChipFieldDisplay';
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
export const ChipFieldDisplay = () => {
const { objectNameSingular, record } = useChipField();
const { recordValue, generateRecordChipData } = useChipFieldDisplay();
if (!record) return null;
if (!recordValue) {
return null;
}
const recordChipData = generateRecordChipData(recordValue);
return (
<RecordChip objectNameSingular={objectNameSingular || ''} record={record} />
<EntityChip
entityId={recordValue.id}
name={recordChipData.name as any}
avatarType={recordChipData.avatarType}
avatarUrl={getImageAbsoluteURIOrBase64(recordChipData.avatarUrl) ?? ''}
linkToEntity={recordChipData.linkToShowPage}
/>
);
};

View File

@ -1,9 +1,8 @@
import { usePhoneFieldDisplay } from '@/object-record/record-field/meta-types/hooks/usePhoneFieldDisplay';
import { PhoneDisplay } from '@/ui/field/display/components/PhoneDisplay';
import { usePhoneField } from '../../hooks/usePhoneField';
export const PhoneFieldDisplay = () => {
const { fieldValue } = usePhoneField();
const { fieldValue } = usePhoneFieldDisplay();
return <PhoneDisplay value={fieldValue} />;
};

View File

@ -23,8 +23,8 @@ export const RelationFromManyFieldDisplay = ({
{recordChipsData.map((record) => {
return (
<EntityChip
key={record.id}
entityId={record.id}
key={record.recordId}
entityId={record.recordId}
name={record.name as any}
avatarType={record.avatarType}
avatarUrl={getImageAbsoluteURIOrBase64(record.avatarUrl) || ''}

View File

@ -1,67 +0,0 @@
import { useEffect } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useSetRecoilState } from 'recoil';
import { ComponentDecorator } from 'twenty-ui';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { ChipFieldDisplay } from '@/object-record/record-field/meta-types/display/components/ChipFieldDisplay';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { FieldMetadataType } from '~/generated/graphql';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
const ChipFieldValueSetterEffect = () => {
const setEntityFields = useSetRecoilState(recordStoreFamilyState('123'));
useEffect(() => {
setEntityFields({
id: 'henry',
name: {
firstName: 'Henry',
lastName: 'Cavill',
},
__typename: 'Person',
});
}, [setEntityFields]);
return null;
};
const meta: Meta = {
title: 'UI/Data/Field/Display/ChipFieldDisplay',
decorators: [
MemoryRouterDecorator,
(Story) => (
<FieldContext.Provider
value={{
entityId: '123',
basePathToShowPage: '/object-record/',
isLabelIdentifier: false,
fieldDefinition: {
fieldMetadataId: 'full name',
label: 'Henry Cavill',
type: FieldMetadataType.FullName,
iconName: 'IconCalendarEvent',
metadata: {
fieldName: 'full name',
objectMetadataNameSingular: 'person',
},
},
hotkeyScope: 'hotkey-scope',
}}
>
<ChipFieldValueSetterEffect />
<Story />
</FieldContext.Provider>
),
ComponentDecorator,
],
component: ChipFieldDisplay,
argTypes: { value: { control: 'date' } },
args: {},
};
export default meta;
type Story = StoryObj<typeof ChipFieldDisplay>;
export const Default: Story = {};

View File

@ -1,68 +0,0 @@
import { useEffect } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { usePhoneField } from '@/object-record/record-field/meta-types/hooks/usePhoneField';
import { FieldMetadataType } from '~/generated/graphql';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { PhoneFieldDisplay } from '../PhoneFieldDisplay';
const PhoneFieldValueSetterEffect = ({ value }: { value: string }) => {
const { setFieldValue } = usePhoneField();
useEffect(() => {
setFieldValue(value);
}, [setFieldValue, value]);
return null;
};
const meta: Meta = {
title: 'UI/Data/Field/Display/PhoneFieldDisplay',
decorators: [
MemoryRouterDecorator,
(Story, { args }) => (
<FieldContext.Provider
value={{
entityId: '',
isLabelIdentifier: false,
fieldDefinition: {
fieldMetadataId: 'phone',
label: 'Phone',
type: FieldMetadataType.Phone,
iconName: 'IconPhone',
metadata: {
fieldName: 'phone',
placeHolder: 'Phone',
objectMetadataNameSingular: 'person',
},
},
hotkeyScope: 'hotkey-scope',
useUpdateRecord: () => [() => undefined, {}],
}}
>
<PhoneFieldValueSetterEffect value={args.value} />
<Story />
</FieldContext.Provider>
),
ComponentDecorator,
],
component: PhoneFieldDisplay,
args: {
value: '362763872687362',
},
};
export default meta;
type Story = StoryObj<typeof PhoneFieldDisplay>;
export const Default: Story = {};
export const Elipsis: Story = {
parameters: {
container: { width: 50 },
},
};

View File

@ -0,0 +1,36 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { ChipFieldDisplay } from '@/object-record/record-field/meta-types/display/components/ChipFieldDisplay';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
const meta: Meta = {
title: 'UI/Data/Field/Display/ChipFieldDisplay',
decorators: [
MemoryRouterDecorator,
ChipGeneratorsDecorator,
getFieldDecorator('person', 'name'),
ComponentDecorator,
],
component: ChipFieldDisplay,
args: {},
parameters: {
chromatic: { disableSnapshot: true },
},
};
export default meta;
type Story = StoryObj<typeof ChipFieldDisplay>;
export const Default: Story = {};
export const Performance = getProfilingStory({
componentName: 'ChipFieldDisplay',
averageThresholdInMs: 0.2,
numberOfRuns: 20,
numberOfTestsPerRun: 100,
});

View File

@ -0,0 +1,47 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { PhoneFieldDisplay } from '@/object-record/record-field/meta-types/display/components/PhoneFieldDisplay';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
const meta: Meta = {
title: 'UI/Data/Field/Display/PhoneFieldDisplay',
decorators: [
MemoryRouterDecorator,
getFieldDecorator('person', 'phone'),
ComponentDecorator,
],
component: PhoneFieldDisplay,
args: {},
parameters: {
chromatic: { disableSnapshot: true },
},
};
export default meta;
type Story = StoryObj<typeof PhoneFieldDisplay>;
export const Default: Story = {};
export const Elipsis: Story = {
parameters: {
container: { width: 50 },
},
};
export const WrongNumber: Story = {
parameters: {
container: { width: 50 },
},
decorators: [getFieldDecorator('person', 'phone', 'sdklaskdj')],
};
export const Performance = getProfilingStory({
componentName: 'PhoneFieldDisplay',
averageThresholdInMs: 0.5,
numberOfRuns: 20,
numberOfTestsPerRun: 100,
});

View File

@ -1,74 +1,21 @@
import { useEffect } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useSetRecoilState } from 'recoil';
import { ComponentDecorator } from 'twenty-ui';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { RelationFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RelationFieldDisplay';
import {
RecordFieldValueSelectorContextProvider,
useSetRecordValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
import { relationFieldDisplayMock } from './mock';
const RelationFieldValueSetterEffect = () => {
const setEntity = useSetRecoilState(
recordStoreFamilyState(relationFieldDisplayMock.entityId),
);
const setRelationEntity = useSetRecoilState(
recordStoreFamilyState(relationFieldDisplayMock.relationEntityId),
);
const setRecordValue = useSetRecordValue();
useEffect(() => {
setEntity(relationFieldDisplayMock.entityValue);
setRelationEntity(relationFieldDisplayMock.relationFieldValue);
setRecordValue(
relationFieldDisplayMock.entityValue.id,
relationFieldDisplayMock.entityValue,
);
setRecordValue(
relationFieldDisplayMock.relationFieldValue.id,
relationFieldDisplayMock.relationFieldValue,
);
}, [setEntity, setRelationEntity, setRecordValue]);
return null;
};
const meta: Meta = {
title: 'UI/Data/Field/Display/RelationFieldDisplay',
decorators: [
MemoryRouterDecorator,
(Story) => (
<RecordFieldValueSelectorContextProvider>
<FieldContext.Provider
value={{
entityId: relationFieldDisplayMock.entityId,
basePathToShowPage: '/object-record/',
isLabelIdentifier: false,
fieldDefinition: {
...relationFieldDisplayMock.fieldDefinition,
},
hotkeyScope: 'hotkey-scope',
}}
>
<RelationFieldValueSetterEffect />
<Story />
</FieldContext.Provider>
</RecordFieldValueSelectorContextProvider>
),
ChipGeneratorsDecorator,
getFieldDecorator('person', 'company'),
ComponentDecorator,
],
component: RelationFieldDisplay,
argTypes: { value: { control: 'date' } },
args: {},
parameters: {
chromatic: { disableSnapshot: true },
@ -83,7 +30,7 @@ export const Default: Story = {};
export const Performance = getProfilingStory({
componentName: 'RelationFieldDisplay',
averageThresholdInMs: 0.4,
averageThresholdInMs: 0.2,
numberOfRuns: 20,
numberOfTestsPerRun: 100,
});

View File

@ -12,6 +12,7 @@ import {
useSetRecordValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
@ -52,6 +53,7 @@ const meta: Meta = {
title: 'UI/Data/Field/Display/RelationFromManyFieldDisplay',
decorators: [
MemoryRouterDecorator,
ChipGeneratorsDecorator,
(Story) => (
<RecordFieldValueSelectorContextProvider>
<FieldContext.Provider

View File

@ -1,113 +0,0 @@
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const relationFieldDisplayMock = {
entityId: '20202020-2d40-4e49-8df4-9c6a049191df',
relationEntityId: '20202020-c21e-4ec2-873b-de4264d89025',
entityValue: {
__typename: 'Person',
asd: '',
city: 'Seattle',
jobTitle: '',
name: {
__typename: 'FullName',
firstName: 'Lorie',
lastName: 'Vladim',
},
createdAt: '2024-05-01T13:16:29.046Z',
company: {
__typename: 'Company',
domainName: 'google.com',
xLink: {
__typename: 'Link',
label: '',
url: '',
},
name: 'Google',
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
employees: null,
accountOwnerId: null,
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 6,
updatedAt: '2024-05-01T13:16:29.046Z',
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
},
id: '20202020-2d40-4e49-8df4-9c6a049191df',
email: 'lorie.vladim@google.com',
phone: '+33788901235',
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
xLink: {
__typename: 'Link',
label: '',
url: '',
},
tEst: '',
position: 15,
},
relationFieldValue: {
__typename: 'Company',
domainName: 'microsoft.com',
xLink: {
__typename: 'Link',
label: '',
url: '',
},
name: 'Microsoft',
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
employees: null,
accountOwnerId: null,
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-ed89-413a-b31a-962986e67bb4',
position: 4,
updatedAt: '2024-05-01T13:16:29.046Z',
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
},
fieldDefinition: {
fieldMetadataId: '4e79f0b7-d100-4e89-a07b-315a710b8059',
label: 'Company',
metadata: {
fieldName: 'company',
placeHolder: 'Company',
relationType: 'TO_ONE_OBJECT',
relationFieldMetadataId: '01fa2247-7937-4493-b7e2-3d72f05d6d25',
relationObjectMetadataNameSingular: 'company',
relationObjectMetadataNamePlural: 'companies',
objectMetadataNameSingular: 'person',
options: null,
},
iconName: 'IconBuildingSkyscraper',
type: FieldMetadataType.Relation,
position: 2,
size: 150,
isLabelIdentifier: false,
isVisible: true,
viewFieldId: '924f4c94-cbcd-4de5-b7a2-ebae2f0b2c3b',
isSortable: false,
isFilterable: true,
defaultValue: null,
},
};

View File

@ -0,0 +1,47 @@
import { useContext } from 'react';
import { isNonEmptyString } from '@sniptt/guards';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
import { useRecordValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { isDefined } from '~/utils/isDefined';
import { FieldContext } from '../../contexts/FieldContext';
export const useChipFieldDisplay = () => {
const { entityId, fieldDefinition } = useContext(FieldContext);
const { chipGeneratorPerObjectPerField } = useContext(
PreComputedChipGeneratorsContext,
);
if (!isDefined(chipGeneratorPerObjectPerField)) {
throw new Error('Chip generator per object per field is not defined');
}
const objectNameSingular =
isFieldText(fieldDefinition) ||
isFieldFullName(fieldDefinition) ||
isFieldNumber(fieldDefinition)
? fieldDefinition.metadata.objectMetadataNameSingular
: undefined;
const recordValue = useRecordValue(entityId);
if (!isNonEmptyString(fieldDefinition.metadata.objectMetadataNameSingular)) {
throw new Error('Object metadata name singular is not a non-empty string');
}
const generateRecordChipData =
chipGeneratorPerObjectPerField[
fieldDefinition.metadata.objectMetadataNameSingular
][fieldDefinition.metadata.fieldName];
return {
objectNameSingular,
recordValue,
generateRecordChipData,
};
};

View File

@ -0,0 +1,19 @@
import { useContext } from 'react';
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { FieldContext } from '../../contexts/FieldContext';
export const usePhoneFieldDisplay = () => {
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = useRecordFieldValue(entityId, fieldName);
return {
fieldDefinition,
fieldValue,
hotkeyScope,
};
};

View File

@ -1,9 +1,8 @@
import { useContext } from 'react';
import { isNonEmptyString } from '@sniptt/guards';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { FIELD_EDIT_BUTTON_WIDTH } from '@/ui/field/display/constants/FieldEditButtonWidth';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
@ -15,6 +14,14 @@ import { isFieldRelation } from '../../types/guards/isFieldRelation';
export const useRelationFieldDisplay = () => {
const { entityId, fieldDefinition, maxWidth } = useContext(FieldContext);
const { chipGeneratorPerObjectPerField } = useContext(
PreComputedChipGeneratorsContext,
);
if (!isDefined(chipGeneratorPerObjectPerField)) {
throw new Error('Chip generator per object per field is not defined');
}
assertFieldMetadata(
FieldMetadataType.Relation,
isFieldRelation,
@ -32,18 +39,14 @@ export const useRelationFieldDisplay = () => {
? maxWidth - FIELD_EDIT_BUTTON_WIDTH
: maxWidth;
const { objectMetadataItem: relationObjectMetadataItem } =
useObjectMetadataItem({
objectNameSingular:
fieldDefinition.metadata.relationObjectMetadataNameSingular,
});
if (!isNonEmptyString(fieldDefinition.metadata.objectMetadataNameSingular)) {
throw new Error('Object metadata name singular is not a non-empty string');
}
const generateRecordChipData = (record: ObjectRecord) => {
return getObjectRecordIdentifier({
objectMetadataItem: relationObjectMetadataItem,
record,
});
};
const generateRecordChipData =
chipGeneratorPerObjectPerField[
fieldDefinition.metadata.objectMetadataNameSingular
][fieldDefinition.metadata.fieldName];
return {
fieldDefinition,

View File

@ -1,6 +1,7 @@
import { AvatarType } from 'twenty-ui';
export type RecordChipData = {
recordId: string;
name: string | number;
avatarType: AvatarType;
avatarUrl: string;

View File

@ -1,6 +1,8 @@
import { Dispatch, SetStateAction, useState } from 'react';
import { createContext, useContextSelector } from 'use-context-selector';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
export type RecordFieldValue = {
[recordId: string]: {
[fieldName: string]: any;
@ -31,7 +33,7 @@ export const useRecordValue = (recordId: string) => {
(value) => value[0],
);
return tableValue?.[recordId];
return tableValue?.[recordId] as ObjectRecord | undefined;
};
export const useRecordFieldValue = (recordId: string, fieldName: string) => {

View File

@ -17,6 +17,7 @@ import { RecordTableCellContext } from '@/object-record/record-table/contexts/Re
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
import { RecordTableScope } from '@/object-record/record-table/scopes/RecordTableScope';
import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
@ -57,6 +58,7 @@ const meta: Meta = {
title: 'RecordIndex/Table/RecordTableCell',
decorators: [
MemoryRouterDecorator,
ChipGeneratorsDecorator,
(Story) => {
return (
<RecordFieldValueSelectorContextProvider>
@ -143,7 +145,7 @@ export const Default: Story = {};
export const Performance = getProfilingStory({
componentName: 'RecordTableCell',
averageThresholdInMs: 0.6,
averageThresholdInMs: 0.3,
numberOfRuns: 50,
numberOfTestsPerRun: 200,
warmUpRounds: 20,

View File

@ -1,86 +0,0 @@
import { useMemo } from 'react';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { getAvatarType } from '@/object-metadata/utils/getAvatarType';
import { getAvatarUrl } from '@/object-metadata/utils/getAvatarUrl';
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
import { getLabelIdentifierFieldValue } from '@/object-metadata/utils/getLabelIdentifierFieldValue';
import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { RecordChipData } from '@/object-record/record-field/types/RecordChipData';
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
export const useRecordChipDataGenerator = ({
objectNameSingular,
visibleTableColumns,
}: {
objectNameSingular: string;
visibleTableColumns: ColumnDefinition<FieldMetadata>[];
}) => {
const { objectMetadataItems } = useObjectMetadataItems();
return useMemo(() => {
return Object.fromEntries<(record: ObjectRecord) => RecordChipData>(
visibleTableColumns
.filter(
(tableColumn) =>
tableColumn.isLabelIdentifier ||
tableColumn.type === FieldMetadataType.Relation,
)
.map((tableColumn) => {
const objectNameSingularToFind = tableColumn.isLabelIdentifier
? objectNameSingular
: isFieldRelation(tableColumn)
? tableColumn.metadata.relationObjectMetadataNameSingular
: undefined;
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.nameSingular === objectNameSingularToFind,
);
if (
!isDefined(objectMetadataItem) ||
!isDefined(objectNameSingularToFind)
) {
return ['', () => ({}) as any];
}
const labelIdentifierFieldMetadataItem =
getLabelIdentifierFieldMetadataItem(objectMetadataItem);
const imageIdentifierFieldMetadata = objectMetadataItem.fields.find(
(field) =>
field.id === objectMetadataItem.imageIdentifierFieldMetadataId,
);
const avatarType = getAvatarType(objectNameSingularToFind);
return [
tableColumn.metadata.fieldName,
(record: ObjectRecord) => ({
name: getLabelIdentifierFieldValue(
record,
labelIdentifierFieldMetadataItem,
objectMetadataItem.nameSingular,
),
avatarUrl: getAvatarUrl(
objectMetadataItem.nameSingular,
record,
imageIdentifierFieldMetadata,
),
avatarType,
linkToShowPage: getLinkToShowPage(
objectMetadataItem.nameSingular,
record,
),
}),
];
}),
);
}, [objectNameSingular, visibleTableColumns, objectMetadataItems]);
};

View File

@ -0,0 +1,116 @@
import { ChipGeneratorPerObjectPerField } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getAvatarType } from '@/object-metadata/utils/getAvatarType';
import { getAvatarUrl } from '@/object-metadata/utils/getAvatarUrl';
import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem';
import { getLabelIdentifierFieldValue } from '@/object-metadata/utils/getLabelIdentifierFieldValue';
import { getLinkToShowPage } from '@/object-metadata/utils/getLinkToShowPage';
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
import { RecordChipData } from '@/object-record/record-field/types/RecordChipData';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
export const isFieldChipDisplay = (
field: Pick<FieldMetadataItem, 'type'>,
isLabelIdentifier: boolean,
) =>
isLabelIdentifier &&
(isFieldText(field) || isFieldFullName(field) || isFieldNumber(field));
export const getRecordChipGeneratorPerObjectPerField = (
objectMetadataItems: ObjectMetadataItem[],
) => {
const recordChipGeneratorPerObjectPerField: ChipGeneratorPerObjectPerField =
{};
for (const objectMetadataItem of objectMetadataItems) {
const generatorPerField = Object.fromEntries<
(record: ObjectRecord) => RecordChipData
>(
objectMetadataItem.fields
.filter(
(fieldMetadataItem) =>
isLabelIdentifierField({
fieldMetadataItem: fieldMetadataItem,
objectMetadataItem,
}) ||
fieldMetadataItem.type === FieldMetadataType.Relation ||
isFieldChipDisplay(
fieldMetadataItem,
isLabelIdentifierField({
fieldMetadataItem: fieldMetadataItem,
objectMetadataItem,
}),
),
)
.map((fieldMetadataItem) => {
const objectNameSingularToFind = isLabelIdentifierField({
fieldMetadataItem: fieldMetadataItem,
objectMetadataItem: objectMetadataItem,
})
? objectMetadataItem.nameSingular
: isFieldRelation(fieldMetadataItem)
? fieldMetadataItem.relationDefinition?.targetObjectMetadata
.nameSingular ?? undefined
: undefined;
const objectMetadataItemToUse = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.nameSingular === objectNameSingularToFind,
);
if (
!isDefined(objectMetadataItemToUse) ||
!isDefined(objectNameSingularToFind)
) {
return ['', () => ({}) as any];
}
const labelIdentifierFieldMetadataItem =
getLabelIdentifierFieldMetadataItem(objectMetadataItemToUse);
const imageIdentifierFieldMetadata =
objectMetadataItemToUse.fields.find(
(field) =>
field.id ===
objectMetadataItemToUse.imageIdentifierFieldMetadataId,
);
const avatarType = getAvatarType(objectNameSingularToFind);
return [
fieldMetadataItem.name,
(record: ObjectRecord) => ({
recordId: record.id,
name: getLabelIdentifierFieldValue(
record,
labelIdentifierFieldMetadataItem,
objectMetadataItemToUse.nameSingular,
),
avatarUrl: getAvatarUrl(
objectMetadataItemToUse.nameSingular,
record,
imageIdentifierFieldMetadata,
),
avatarType,
linkToShowPage: getLinkToShowPage(
objectMetadataItemToUse.nameSingular,
record,
),
}),
];
}),
);
recordChipGeneratorPerObjectPerField[objectMetadataItem.nameSingular] =
generatorPerField;
}
return recordChipGeneratorPerObjectPerField;
};

View File

@ -2,24 +2,39 @@ export type Person = {
__typename: 'Person';
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
updatedAt?: string;
deletedAt?: string | null;
name: {
__typename?: 'FullName';
firstName: string;
lastName: string;
};
avatarUrl: string;
avatarUrl?: string;
jobTitle: string;
linkedinLink: {
__typename?: 'Link';
url: string;
label: string;
};
xLink: {
__typename?: 'Link';
url: string;
label: string;
};
city: string;
email: string;
phone: string;
companyId: string;
companyId?: string;
position?: number;
links?: {
__typename: 'Links';
primaryLinkUrl: string;
primaryLinkLabel: '';
secondaryLinks?:
| {
url: string;
label: string;
}[]
| null;
};
};

View File

@ -1,4 +1,4 @@
import styled from '@emotion/styled';
import { styled } from '@linaria/react';
const StyledEllipsisDisplay = styled.div<{ maxWidth?: number }>`
max-width: ${({ maxWidth }) => maxWidth ?? '100%'};
@ -19,7 +19,7 @@ export const EllipsisDisplay = ({
maxWidth,
className,
}: EllipsisDisplayProps) => (
<StyledEllipsisDisplay style={{ maxWidth }} className={className}>
<StyledEllipsisDisplay maxWidth={maxWidth} className={className}>
{children}
</StyledEllipsisDisplay>
);

View File

@ -1,27 +1,39 @@
import { MouseEvent } from 'react';
import { isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-js';
import { parsePhoneNumber, PhoneNumber } from 'libphonenumber-js';
import { ContactLink } from '@/ui/navigation/link/components/ContactLink';
import { EllipsisDisplay } from './EllipsisDisplay';
import { isDefined } from '~/utils/isDefined';
type PhoneDisplayProps = {
value: string | null;
};
export const PhoneDisplay = ({ value }: PhoneDisplayProps) => (
<EllipsisDisplay>
{value && isValidPhoneNumber(value) ? (
<ContactLink
href={parsePhoneNumber(value, 'FR')?.getURI()}
onClick={(event: MouseEvent<HTMLElement>) => {
event.stopPropagation();
}}
>
{parsePhoneNumber(value, 'FR')?.formatNational() || value}
</ContactLink>
) : (
<ContactLink href="#">{value}</ContactLink>
)}
</EllipsisDisplay>
);
// TODO: see if we can find a faster way to format the phone number
export const PhoneDisplay = ({ value }: PhoneDisplayProps) => {
if (!isDefined(value)) {
return <ContactLink href="#">{value}</ContactLink>;
}
let parsedPhoneNumber: PhoneNumber | null = null;
try {
// TODO: parse according to locale not hard coded FR
parsedPhoneNumber = parsePhoneNumber(value, 'FR');
} catch (error) {
return <ContactLink href="#">{value}</ContactLink>;
}
const URI = parsedPhoneNumber.getURI();
const formattedNational = parsedPhoneNumber?.formatNational();
return (
<ContactLink
href={URI}
onClick={(event: MouseEvent<HTMLElement>) => {
event.stopPropagation();
}}
>
{formattedNational || value}
</ContactLink>
);
};

View File

@ -1,20 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
const meta: Meta = {
title: 'UI/Input/EllipsisDisplay/EllipsisDisplay',
component: EllipsisDisplay,
decorators: [ComponentDecorator],
args: {
maxWidth: 100,
children: 'This is a long text that should be truncated',
},
};
export default meta;
type Story = StoryObj<typeof EllipsisDisplay>;
export const Default: Story = {};

View File

@ -1,4 +1,4 @@
import { Meta } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
@ -19,6 +19,10 @@ const meta: Meta = {
export default meta;
type Story = StoryObj<typeof EllipsisDisplay>;
export const Default: Story = {};
export const Performance = getProfilingStory({
componentName: 'EllipsisDisplay',
averageThresholdInMs: 0.1,

View File

@ -1,43 +1,47 @@
import * as React from 'react';
import { Link as ReactLink } from 'react-router-dom';
import styled from '@emotion/styled';
import { Theme, withTheme } from '@emotion/react';
import { styled } from '@linaria/react';
const StyledClickableLink = withTheme(styled.a<{
theme: Theme;
maxWidth?: number;
}>`
color: inherit;
overflow: hidden;
text-decoration: underline;
text-decoration-color: ${({ theme }) => theme.border.color.strong};
text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
max-width: ${({ maxWidth }) => maxWidth ?? '100%'};
&:hover {
text-decoration-color: ${({ theme }) => theme.font.color.primary};
}
`);
type ContactLinkProps = {
className?: string;
href: string;
children?: React.ReactNode;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
maxWidth?: number;
};
const StyledClickable = styled.div`
display: flex;
overflow: hidden;
white-space: nowrap;
a {
color: inherit;
overflow: hidden;
text-decoration: underline;
text-decoration-color: ${({ theme }) => theme.border.color.strong};
text-overflow: ellipsis;
&:hover {
text-decoration-color: ${({ theme }) => theme.font.color.primary};
}
}
`;
export const ContactLink = ({
className,
href,
children,
onClick,
maxWidth,
}: ContactLinkProps) => (
<div>
<StyledClickable className={className}>
<ReactLink target="_blank" onClick={onClick} to={href}>
{children}
</ReactLink>
</StyledClickable>
</div>
<StyledClickableLink
maxWidth={maxWidth}
target="_blank"
onClick={onClick}
href={href}
>
{children}
</StyledClickableLink>
);

View File

@ -10,7 +10,6 @@ const meta: Meta<typeof ContactLink> = {
component: ContactLink,
decorators: [ComponentWithRouterDecorator],
args: {
className: 'ContactLink',
href: '/test',
children: 'Contact Link',
},

View File

@ -4,6 +4,7 @@ export type WorkspaceMember = {
__typename: 'WorkspaceMember';
id: string;
name: {
__typename?: 'FullName';
firstName: string;
lastName: string;
};

View File

@ -0,0 +1,24 @@
import { useMemo } from 'react';
import { Decorator } from '@storybook/react';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { getRecordChipGeneratorPerObjectPerField } from '@/object-record/utils/getRecordChipGeneratorPerObjectPerField';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
export const ChipGeneratorsDecorator: Decorator = (Story) => {
const chipGeneratorPerObjectPerField = useMemo(() => {
return getRecordChipGeneratorPerObjectPerField(
generatedMockObjectMetadataItems,
);
}, []);
return (
<PreComputedChipGeneratorsContext.Provider
value={{
chipGeneratorPerObjectPerField,
}}
>
<Story />
</PreComputedChipGeneratorsContext.Provider>
);
};

View File

@ -1,11 +1,13 @@
import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';
import { Decorator } from '@storybook/react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { getRecordChipGeneratorPerObjectPerField } from '@/object-record/utils/getRecordChipGeneratorPerObjectPerField';
import { mockedUsersData } from '~/testing/mock-data/users';
import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members';
@ -21,10 +23,20 @@ export const ObjectMetadataItemsDecorator: Decorator = (Story) => {
setCurrentUser(mockedUsersData[0]);
}, [setCurrentUser, setCurrentWorkspaceMember]);
const chipGeneratorPerObjectPerField = useMemo(() => {
return getRecordChipGeneratorPerObjectPerField(objectMetadataItems);
}, [objectMetadataItems]);
return (
<>
<ObjectMetadataItemsLoadEffect />
{!!objectMetadataItems.length && <Story />}
<PreComputedChipGeneratorsContext.Provider
value={{
chipGeneratorPerObjectPerField,
}}
>
{!!objectMetadataItems.length && <Story />}
</PreComputedChipGeneratorsContext.Provider>
</>
);
};

View File

@ -0,0 +1,125 @@
import { useEffect } from 'react';
import { Decorator } from '@storybook/react';
import { useRecoilCallback } from 'recoil';
import { Company } from '@/companies/types/Company';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import {
RecordFieldValueSelectorContextProvider,
useSetRecordValue,
} from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { Person } from '@/people/types/Person';
import { mockedCompaniesDataV2 } from '~/testing/mock-data/companiesV2';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/objectMetadataItems';
import { mockPeopleDataV2 } from '~/testing/mock-data/peopleV2';
import { isDefined } from '~/utils/isDefined';
const RecordMockSetterEffect = ({
companies,
people,
}: {
companies: Company[];
people: Person[];
}) => {
const setRecordValue = useSetRecordValue();
const setRecordInBothStores = useRecoilCallback(
({ set }) =>
(record: ObjectRecord) => {
set(recordStoreFamilyState(record.id), record);
setRecordValue(record.id, record);
},
[setRecordValue],
);
useEffect(() => {
for (const company of companies) {
setRecordInBothStores(company);
}
for (const person of people) {
setRecordInBothStores(person);
}
}, [setRecordInBothStores, companies, people]);
return null;
};
export const getFieldDecorator =
(
objectNameSingular: 'company' | 'person',
fieldName: string,
fieldValue?: any,
): Decorator =>
(Story) => {
const companies =
objectNameSingular === 'company' && isDefined(fieldValue)
? [
{ ...mockedCompaniesDataV2[0], [fieldName]: fieldValue },
...mockedCompaniesDataV2.slice(1),
]
: mockedCompaniesDataV2;
const people =
objectNameSingular === 'person' && isDefined(fieldValue)
? [
{ ...mockPeopleDataV2[0], [fieldName]: fieldValue },
...mockPeopleDataV2.slice(1),
]
: mockPeopleDataV2;
const record = objectNameSingular === 'company' ? companies[0] : people[0];
if (isDefined(fieldValue)) {
(record as any)[fieldName] = fieldValue;
}
const objectMetadataItem = generatedMockObjectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.nameSingular === objectNameSingular,
);
const fieldMetadataItem = objectMetadataItem?.fields.find(
(field) => field.name === fieldName,
);
if (!isDefined(objectMetadataItem)) {
throw new Error(`Object ${objectNameSingular} not found`);
}
if (!isDefined(fieldMetadataItem)) {
throw new Error(
`Field ${fieldName} not found in object ${objectNameSingular}`,
);
}
const isLabelIdentifier = isLabelIdentifierField({
fieldMetadataItem,
objectMetadataItem,
});
return (
<RecordFieldValueSelectorContextProvider>
<FieldContext.Provider
value={{
entityId: record.id,
basePathToShowPage: '/object-record/',
isLabelIdentifier,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
position: record.position ?? 0,
objectMetadataItem,
}),
hotkeyScope: 'hotkey-scope',
}}
>
<RecordMockSetterEffect companies={companies} people={people} />
<Story />
</FieldContext.Provider>
</RecordFieldValueSelectorContextProvider>
);
};

View File

@ -0,0 +1,78 @@
import { ReactNode } from 'react';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { getBasePathToShowPage } from '@/object-metadata/utils/getBasePathToShowPage';
import {
FieldContext,
RecordUpdateHook,
} from '@/object-record/record-field/contexts/FieldContext';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
export const useMockFieldContext = ({
clearable,
fieldMetadataName,
fieldPosition,
isLabelIdentifier = false,
objectNameSingular,
objectRecordId,
customHotkeyScope,
}: {
clearable?: boolean;
fieldMetadataName: string;
fieldPosition: number;
isLabelIdentifier?: boolean;
objectNameSingular: string;
objectRecordId: string;
customHotkeyScope?: string;
}) => {
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const basePathToShowPage = getBasePathToShowPage({
objectNameSingular,
});
const fieldMetadataItem = objectMetadataItem?.fields.find(
(field) => field.name === fieldMetadataName,
);
const useUpdateOneObjectMutation: RecordUpdateHook = () => {
const updateEntity = () => {};
return [updateEntity, { loading: false }];
};
const FieldContextProvider =
fieldMetadataItem && objectMetadataItem
? ({ children }: { children: ReactNode }) => (
<FieldContext.Provider
key={objectRecordId + fieldMetadataItem.id}
value={{
basePathToShowPage: isLabelIdentifier
? basePathToShowPage
: undefined,
entityId: objectRecordId,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem,
position: fieldPosition,
objectMetadataItem,
}),
useUpdateRecord: useUpdateOneObjectMutation,
hotkeyScope:
customHotkeyScope ?? InlineCellHotkeyScope.InlineCell,
clearable,
}}
>
{children}
</FieldContext.Provider>
)
: undefined;
return {
FieldContextProvider,
};
};

View File

@ -0,0 +1,493 @@
import { Company } from '@/companies/types/Company';
import { Favorite } from '@/favorites/types/Favorite';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { mockedCompaniesData } from '~/testing/mock-data/companies';
type MockedCompanyV2 = Omit<Company, 'deletedAt'> & {
accountOwner: WorkspaceMember | null;
Favorite?: Pick<Favorite, 'id'> | null;
};
export const mockedCompaniesDataV2: Array<MockedCompanyV2> = [
{
__typename: 'Company',
domainName: 'paris.com',
name: 'Test',
employees: null,
address: 'Paris France',
createdAt: '2024-05-27T11:23:05.954Z',
id: 'd55c240e-e4e0-4248-b56d-8004d1218a9c',
position: 6.109375,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1000000000,
currencyCode: 'USD',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: 'paris.com',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1553-45c6-a028-5a9064cce07f',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-7169-42cf-bc47-1cfef15264b8',
userEmail: 'phil.schiler@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Phil',
lastName: 'Shiler',
},
},
},
{
__typename: 'Company',
domainName: 'google.com',
name: 'Google',
employees: 10202,
address: 'Paris France',
createdAt: '2024-05-21T13:16:29.000Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 7.5,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1001000000,
currencyCode: 'USD',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-0687-4c41-b707-ed1bfca972a7',
colorScheme: 'Light',
updatedAt: '2024-05-30T09:00:31.127Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-9e3b-46d4-a556-88b9ddc2b034',
userEmail: 'tim@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Tim',
lastName: 'Apple',
},
},
},
{
__typename: 'Company',
domainName: 'hasura.io',
name: 'Hasura',
employees: 102938102938,
address: '',
createdAt: '2024-05-16T13:16:29.000Z',
id: '20202020-f86b-419f-b794-02319abe8637',
position: 10,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-77d5-4cb6-b60a-f4a835a85d61',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-3957-4908-9c36-2929a23f8357',
userEmail: 'jony.ive@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Jony',
lastName: 'Ive',
},
},
},
{
__typename: 'Company',
domainName: 'netflix.com',
name: 'Netflix',
employees: null,
address: '',
createdAt: '2024-05-15T13:16:29.000Z',
id: '20202020-707e-44dc-a1d2-30030bf1a944',
position: 7,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 2000000000,
currencyCode: 'USD',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1553-45c6-a028-5a9064cce07f',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-7169-42cf-bc47-1cfef15264b8',
userEmail: 'phil.schiler@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Phil',
lastName: 'Shiler',
},
},
},
{
__typename: 'Company',
domainName: 'claap.io',
name: 'Claap',
employees: 2131920,
address: 'asdasd',
createdAt: '2024-05-10T13:16:29.000Z',
id: '20202020-cfbf-4156-a790-e39854dcd4eb',
position: 9,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: 'asmdlkasd',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-0687-4c41-b707-ed1bfca972a7',
colorScheme: 'Light',
updatedAt: '2024-05-30T09:00:31.127Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-9e3b-46d4-a556-88b9ddc2b034',
userEmail: 'tim@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Tim',
lastName: 'Apple',
},
},
},
{
__typename: 'Company',
domainName: 'libeo.io',
name: 'Libeo',
employees: 1239819238,
address: '',
createdAt: '2024-05-09T13:16:29.000Z',
id: '20202020-3f74-492d-a101-2a70f50a1645',
position: 8,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1553-45c6-a028-5a9064cce07f',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-7169-42cf-bc47-1cfef15264b8',
userEmail: 'phil.schiler@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Phil',
lastName: 'Shiler',
},
},
},
{
__typename: 'Company',
domainName: 'qonto.com',
name: 'Qonto',
employees: 123123123,
address: '',
createdAt: '2024-05-08T13:16:29.000Z',
id: '20202020-0713-40a5-8216-82802401d33e',
position: 9.5,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1553-45c6-a028-5a9064cce07f',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-7169-42cf-bc47-1cfef15264b8',
userEmail: 'phil.schiler@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Phil',
lastName: 'Shiler',
},
},
},
{
__typename: 'Company',
domainName: 'wework.com',
name: 'Wework',
employees: 123123123,
address: '',
createdAt: '2024-05-08T13:16:29.000Z',
id: '20202020-5518-4553-9433-42d8eb82834b',
position: 11,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-0687-4c41-b707-ed1bfca972a7',
colorScheme: 'Light',
updatedAt: '2024-05-30T09:00:31.127Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-9e3b-46d4-a556-88b9ddc2b034',
userEmail: 'tim@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Tim',
lastName: 'Apple',
},
},
},
{
__typename: 'Company',
domainName: 'linkedin.com',
name: 'Linkedin',
employees: 10102,
accountOwner: null,
address: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-3ec3-4fe3-8997-b76aa0bfa408',
position: 1,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: 'adasd',
url: 'adasd',
},
},
{
__typename: 'Company',
domainName: 'airbnb.com',
name: 'Airbnb',
employees: 123333,
accountOwner: null,
address: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-171e-4bcc-9cf7-43448d6fb278',
position: 5,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
},
{
__typename: 'Company',
domainName: 'samsung.com',
name: 'Samsung',
employees: 10000,
address: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-f79e-40dd-bd06-c36e6abb4678',
position: 12,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1553-45c6-a028-5a9064cce07f',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-7169-42cf-bc47-1cfef15264b8',
userEmail: 'phil.schiler@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Phil',
lastName: 'Shiler',
},
},
},
{
__typename: 'Company',
domainName: 'algolia.com',
name: 'Algolia',
employees: 10000,
address: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1455-4c57-afaf-dd5dc086361d',
position: 13,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-77d5-4cb6-b60a-f4a835a85d61',
colorScheme: 'Light',
updatedAt: '2024-05-01T13:16:29.046Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-3957-4908-9c36-2929a23f8357',
userEmail: 'jony.ive@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Jony',
lastName: 'Ive',
},
},
},
{
__typename: 'Company',
domainName: 'facebook.com',
name: 'Facebook',
employees: 220323,
accountOwner: null,
address: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-5d81-46d6-bf83-f7fd33ea6102',
position: 6.0625,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: 'asdasd',
},
},
{
__typename: 'Company',
domainName: 'microsoft.com',
name: 'Microsoft',
employees: 10000,
address: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-ed89-413a-b31a-962986e67bb4',
position: 6.09375,
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 10000000000,
currencyCode: 'USD',
},
linkedinLink: {
__typename: 'Link',
label: '',
url: '',
},
accountOwner: {
__typename: 'WorkspaceMember',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-0687-4c41-b707-ed1bfca972a7',
colorScheme: 'Light',
updatedAt: '2024-05-30T09:00:31.127Z',
locale: 'en',
avatarUrl: '',
userId: '20202020-9e3b-46d4-a556-88b9ddc2b034',
userEmail: 'tim@apple.dev',
name: {
__typename: 'FullName',
firstName: 'Tim',
lastName: 'Apple',
},
},
},
];
export const mockedDuplicateCompanyData: MockedCompanyV2 = {
...mockedCompaniesData[0],
id: '8b40856a-2ec9-4c03-8bc0-c032c89e1824',
};
export const mockedEmptyCompanyData = {
id: '9231e6ee-4cc2-4c7b-8c55-dff16f4d968a',
name: '',
domainName: '',
address: '',
accountOwner: null,
annualRecurringRevenue: null,
createdAt: null,
updatedAt: null,
employees: null,
idealCustomerProfile: null,
linkedinLink: null,
xLink: null,
_activityCount: null,
__typename: 'Company',
};

View File

@ -13249,3 +13249,4 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery =
] as ObjectEdge[],
},
} as ObjectMetadataItemsQuery;

View File

@ -3,6 +3,13 @@ import {
FieldMetadataType,
RelationMetadataType,
} from '~/generated-metadata/graphql';
import { mockedStandardObjectMetadataQueryResult } from '~/testing/mock-data/generated/standard-metadata-query-result';
export const generatedMockObjectMetadataItems: ObjectMetadataItem[] =
mockedStandardObjectMetadataQueryResult.objects.edges.map((edge) => ({
...edge.node,
fields: edge.node.fields.edges.map((edge) => edge.node),
}));
export const mockObjectMetadataItem: ObjectMetadataItem = {
__typename: 'object',

View File

@ -1,11 +1,11 @@
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
type RequiredAndNotNull<T> = {
export type RequiredAndNotNull<T> = {
[P in keyof T]-?: Exclude<T[P], null | undefined>;
};
type MockedPerson = RequiredAndNotNull<
export type MockedPerson = RequiredAndNotNull<
Pick<
Person,
| '__typename'

View File

@ -0,0 +1,533 @@
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
export type MockedPersonV2 = Pick<
Person,
| '__typename'
| 'id'
| 'name'
| 'linkedinLink'
| 'xLink'
| 'links'
| 'jobTitle'
| 'email'
| 'phone'
| 'city'
| 'avatarUrl'
| 'createdAt'
| 'updatedAt'
| 'companyId'
| 'position'
> & {
company?: Company;
};
export const mockPeopleDataV2: MockedPersonV2[] = [
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1c0e-494c-a1b6-85b1c6fefaa5',
email: 'christoph.calisto@linkedin.com',
phone: '+33789012345',
position: 1,
name: {
__typename: 'FullName',
firstName: 'Christoph',
lastName: 'Callisto',
},
linkedinLink: { __typename: 'Link', label: '', url: 'asd' },
xLink: { __typename: 'Link', label: '', url: 'asd' },
company: {
__typename: 'Company',
domainName: 'linkedin.com',
name: 'Linkedin',
employees: 10102,
accountOwnerId: null,
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-3ec3-4fe3-8997-b76aa0bfa408',
position: 1,
updatedAt: '2024-05-23T13:21:41.159Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: 'adasd', url: 'adasd' },
},
},
{
__typename: 'Person',
city: 'Los Angeles',
jobTitle: '@',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-ac73-4797-824e-87a1f5aea9e0',
email: 'sylvie.palmer@linkedin.com',
phone: '+33780123456',
position: 2,
name: { __typename: 'FullName', firstName: 'Sylvie', lastName: 'Palmer' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'algolia.com',
name: 'Algolia',
employees: 10000,
accountOwnerId: '20202020-77d5-4cb6-b60a-f4a835a85d61',
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-1455-4c57-afaf-dd5dc086361d',
position: 13,
updatedAt: '2024-05-28T15:52:31.839Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-f517-42fd-80ae-14173b3b70ae',
email: 'christopher.gonzalez@qonto.com',
phone: '+33789012345',
position: 3,
name: {
__typename: 'FullName',
firstName: 'Christopher',
lastName: 'Gonzalez',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'qonto.com',
name: 'Qonto',
employees: 123123123,
accountOwnerId: '20202020-1553-45c6-a028-5a9064cce07f',
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-08T13:16:29.000Z',
id: '20202020-0713-40a5-8216-82802401d33e',
position: 9.5,
updatedAt: '2024-05-28T15:52:46.961Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Los Angeles',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-eee1-4690-ad2c-8619e5b56a2e',
email: 'ashley.parker@qonto.com',
phone: '+33780123456',
position: 4,
name: { __typename: 'FullName', firstName: 'Ashley', lastName: 'Parker' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'qonto.com',
name: 'Qonto',
employees: 123123123,
accountOwnerId: '20202020-1553-45c6-a028-5a9064cce07f',
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-08T13:16:29.000Z',
id: '20202020-0713-40a5-8216-82802401d33e',
position: 9.5,
updatedAt: '2024-05-28T15:52:46.961Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-6784-4449-afdf-dc62cb8702f2',
email: 'nicholas.wright@microsoft.com',
phone: '+33781234567',
position: 5,
name: { __typename: 'FullName', firstName: 'Nicholas', lastName: 'Wright' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'microsoft.com',
name: 'Microsoft',
employees: 10000,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-ed89-413a-b31a-962986e67bb4',
position: 6.09375,
updatedAt: '2024-05-28T15:52:35.621Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 10000000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'New York',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-490f-4466-8391-733cfd66a0c8',
email: 'isabella.scott@microsoft.com',
phone: '+33782345678',
position: 6,
name: { __typename: 'FullName', firstName: 'Isabella', lastName: 'Scott' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'microsoft.com',
name: 'Microsoft',
employees: 10000,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-ed89-413a-b31a-962986e67bb4',
position: 6.09375,
updatedAt: '2024-05-28T15:52:35.621Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 10000000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-80f1-4dff-b570-a74942528de3',
email: 'matthew.green@microsoft.com',
phone: '+33783456789',
position: 7,
name: { __typename: 'FullName', firstName: 'Matthew', lastName: 'Green' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'microsoft.com',
name: 'Microsoft',
employees: 10000,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-ed89-413a-b31a-962986e67bb4',
position: 6.09375,
updatedAt: '2024-05-28T15:52:35.621Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 10000000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'New York',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-338b-46df-8811-fa08c7d19d35',
email: 'elizabeth.baker@airbnb.com',
phone: '+33784567890',
position: 8,
name: { __typename: 'FullName', firstName: 'Elizabeth', lastName: 'Baker' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'airbnb.com',
name: 'Airbnb',
employees: 123333,
accountOwnerId: null,
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-171e-4bcc-9cf7-43448d6fb278',
position: 5,
updatedAt: '2024-05-28T15:52:27.902Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'San Francisco',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-64ad-4b0e-bbfd-e9fd795b7016',
email: 'christopher.nelson@airbnb.com',
phone: '+33785678901',
position: 9,
name: {
__typename: 'FullName',
firstName: 'Christopher',
lastName: 'Nelson',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'airbnb.com',
name: 'Airbnb',
employees: 123333,
accountOwnerId: null,
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-171e-4bcc-9cf7-43448d6fb278',
position: 5,
updatedAt: '2024-05-28T15:52:27.902Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'New York',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-5d54-41b7-ba36-f0d20e1417ae',
email: 'avery.carter@airbnb.com',
phone: '+33786789012',
position: 10,
name: { __typename: 'FullName', firstName: 'Avery', lastName: 'Carter' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'airbnb.com',
name: 'Airbnb',
employees: 123333,
accountOwnerId: null,
address: '',
idealCustomerProfile: false,
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-171e-4bcc-9cf7-43448d6fb278',
position: 5,
updatedAt: '2024-05-28T15:52:27.902Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: null,
currencyCode: '',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Los Angeles',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-623d-41fe-92e7-dd45b7c568e1',
email: 'ethan.mitchell@google.com',
phone: '+33787890123',
position: 11,
name: { __typename: 'FullName', firstName: 'Ethan', lastName: 'Mitchell' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'google.com',
name: 'Google',
employees: 10202,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: 'Paris France',
idealCustomerProfile: false,
createdAt: '2024-05-21T13:16:29.000Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 7.5,
updatedAt: '2024-05-28T15:53:28.838Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1001000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-2d40-4e49-8df4-9c6a049190ef',
email: 'madison.perez@google.com',
phone: '+33788901234',
position: 12,
name: { __typename: 'FullName', firstName: 'Madison', lastName: 'Perez' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'google.com',
name: 'Google',
employees: 10202,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: 'Paris France',
idealCustomerProfile: false,
createdAt: '2024-05-21T13:16:29.000Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 7.5,
updatedAt: '2024-05-28T15:53:28.838Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1001000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-2d40-4e49-8df4-9c6a049190df',
email: 'bertrand.voulzy@google.com',
phone: '+33788901234',
position: 13,
name: { __typename: 'FullName', firstName: 'Bertrand', lastName: 'Voulzy' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'google.com',
name: 'Google',
employees: 10202,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: 'Paris France',
idealCustomerProfile: false,
createdAt: '2024-05-21T13:16:29.000Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 7.5,
updatedAt: '2024-05-28T15:53:28.838Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1001000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-2d40-4e49-8df4-9c6a049191de',
email: 'louis.duss@google.com',
phone: '+33788901234',
position: 14,
name: { __typename: 'FullName', firstName: 'Louis', lastName: 'Duss' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'google.com',
name: 'Google',
employees: 10202,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: 'Paris France',
idealCustomerProfile: false,
createdAt: '2024-05-21T13:16:29.000Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 7.5,
updatedAt: '2024-05-28T15:53:28.838Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1001000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
{
__typename: 'Person',
city: 'Seattle',
jobTitle: '',
createdAt: '2024-05-01T13:16:29.046Z',
id: '20202020-2d40-4e49-8df4-9c6a049191df',
email: 'lorie.vladim@google.com',
phone: '+33788901235',
position: 15,
name: { __typename: 'FullName', firstName: 'Lorie', lastName: 'Vladim' },
linkedinLink: { __typename: 'Link', label: '', url: '' },
xLink: { __typename: 'Link', label: '', url: '' },
company: {
__typename: 'Company',
domainName: 'google.com',
name: 'Google',
employees: 10202,
accountOwnerId: '20202020-0687-4c41-b707-ed1bfca972a7',
address: 'Paris France',
idealCustomerProfile: false,
createdAt: '2024-05-21T13:16:29.000Z',
id: '20202020-c21e-4ec2-873b-de4264d89025',
position: 7.5,
updatedAt: '2024-05-28T15:53:28.838Z',
xLink: { __typename: 'Link', label: '', url: '' },
annualRecurringRevenue: {
__typename: 'Currency',
amountMicros: 1001000000,
currencyCode: 'USD',
},
linkedinLink: { __typename: 'Link', label: '', url: '' },
},
},
];

View File

@ -1,4 +1,5 @@
import react from '@vitejs/plugin-react-swc';
import wyw from '@wyw-in-js/vite';
import path from 'path';
import { defineConfig, loadEnv } from 'vite';
import checker from 'vite-plugin-checker';
@ -48,6 +49,12 @@ export default defineConfig(({ command, mode }) => {
}),
svgr(),
checker(checkers),
wyw({
include: ['**/EllipsisDisplay.tsx', '**/ContactLink.tsx'],
babelOptions: {
presets: ['@babel/preset-typescript', '@babel/preset-react'],
},
}),
],
build: {

425
yarn.lock
View File

@ -1516,6 +1516,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/code-frame@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/code-frame@npm:7.24.6"
dependencies:
"@babel/highlight": "npm:^7.24.6"
picocolors: "npm:^1.0.0"
checksum: c93c6d1763530f415218c31d07359364397f19b70026abdff766164c21ed352a931cf07f3102c5fb9e04792de319e332d68bcb1f7debef601a02197f90f9ba24
languageName: node
linkType: hard
"@babel/compat-data@npm:^7.20.5, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5":
version: 7.23.5
resolution: "@babel/compat-data@npm:7.23.5"
@ -1648,6 +1658,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-annotate-as-pure@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-annotate-as-pure@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 3fe446e3bd37e5e32152279c84ace4e83815e5b88b9e09a82a83974a0bb22e941d89db26b23aaab4c9eb0f9713772c2f6163feffc1bcb055c4cdb6b67e5dc82f
languageName: node
linkType: hard
"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.22.15":
version: 7.22.15
resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.22.15"
@ -1708,6 +1727,25 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-create-class-features-plugin@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-create-class-features-plugin@npm:7.24.6"
dependencies:
"@babel/helper-annotate-as-pure": "npm:^7.24.6"
"@babel/helper-environment-visitor": "npm:^7.24.6"
"@babel/helper-function-name": "npm:^7.24.6"
"@babel/helper-member-expression-to-functions": "npm:^7.24.6"
"@babel/helper-optimise-call-expression": "npm:^7.24.6"
"@babel/helper-replace-supers": "npm:^7.24.6"
"@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.6"
"@babel/helper-split-export-declaration": "npm:^7.24.6"
semver: "npm:^6.3.1"
peerDependencies:
"@babel/core": ^7.0.0
checksum: e6734671bc6a5f3cca4ec46e4cc70238e5a2fa063e51225c2be572f157119002af419b33ea0f846dbb1307370fe9f3aa92d199449abbea5e88e0262513c8a821
languageName: node
linkType: hard
"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.22.15, @babel/helper-create-regexp-features-plugin@npm:^7.22.5":
version: 7.22.15
resolution: "@babel/helper-create-regexp-features-plugin@npm:7.22.15"
@ -1773,6 +1811,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-environment-visitor@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-environment-visitor@npm:7.24.6"
checksum: fdcd18ac505ed71f40c05cc992b648a4495b0aa5310a774492a0f74d8dcf3579691102f516561a651d3de6c3a44fe64bfb3049d11c14c5857634ef1823ea409a
languageName: node
linkType: hard
"@babel/helper-function-name@npm:^7.22.5, @babel/helper-function-name@npm:^7.23.0":
version: 7.23.0
resolution: "@babel/helper-function-name@npm:7.23.0"
@ -1783,6 +1828,16 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-function-name@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-function-name@npm:7.24.6"
dependencies:
"@babel/template": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
checksum: 5ba2f8db789b3f5a2b2239300a217aa212e303cd7bfad9c8b90563807f49215e8c679e8f8f177b6aaca2038038e29bc702b83839e1f7b4896d79c44a75cac97a
languageName: node
linkType: hard
"@babel/helper-hoist-variables@npm:^7.22.5":
version: 7.22.5
resolution: "@babel/helper-hoist-variables@npm:7.22.5"
@ -1801,6 +1856,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-member-expression-to-functions@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-member-expression-to-functions@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 7595f62978f55921b24de6ed5252fcedbffacfb8271f71e092f38724179ba554cb3a24a4764a1a3890b8a53504c2bee9c99eab81f1f365582739f566c8e28eaa
languageName: node
linkType: hard
"@babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.15":
version: 7.22.15
resolution: "@babel/helper-module-imports@npm:7.22.15"
@ -1810,6 +1874,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-module-imports@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-module-imports@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: e0db3fbfcd963d138f0792ff626f940a576fcf212d02b8fe6478dccf3421bd1c2a76f8e69c7450c049985e7b63b30be309a24eeeb6ad7c2137a31b676a095a84
languageName: node
linkType: hard
"@babel/helper-module-transforms@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/helper-module-transforms@npm:7.23.3"
@ -1825,6 +1898,21 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-module-transforms@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-module-transforms@npm:7.24.6"
dependencies:
"@babel/helper-environment-visitor": "npm:^7.24.6"
"@babel/helper-module-imports": "npm:^7.24.6"
"@babel/helper-simple-access": "npm:^7.24.6"
"@babel/helper-split-export-declaration": "npm:^7.24.6"
"@babel/helper-validator-identifier": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0
checksum: 9e2e3d0ddb397b36b9e8c7d94e175a36be8cb888ef370cefef2cdfd53ae1f87d567b268bd90ed9a6c706485a8de3da19cac577657613e9cd17210b91cbdfb00b
languageName: node
linkType: hard
"@babel/helper-optimise-call-expression@npm:^7.22.5":
version: 7.22.5
resolution: "@babel/helper-optimise-call-expression@npm:7.22.5"
@ -1834,6 +1922,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-optimise-call-expression@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-optimise-call-expression@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 7fce2c4ce22c4ba3c2178d1ce85f34fc9bbe286af5ec153b4b6ea9bf2212390359c4a1e8a54551c4daa4688022d619668bdb8c8060cb185c0c9ad02c5247efc9
languageName: node
linkType: hard
"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.20.2, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3":
version: 7.22.5
resolution: "@babel/helper-plugin-utils@npm:7.22.5"
@ -1848,6 +1945,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-plugin-utils@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-plugin-utils@npm:7.24.6"
checksum: 636d3ce8cabc0621c1f78187e1d95f1087209921fa452f76aad06224ef5dffb3d934946f5183109920f32a4b94dd75ac91c63bc52813fee639d10cd54d49ba1f
languageName: node
linkType: hard
"@babel/helper-remap-async-to-generator@npm:^7.22.20":
version: 7.22.20
resolution: "@babel/helper-remap-async-to-generator@npm:7.22.20"
@ -1874,6 +1978,19 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-replace-supers@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-replace-supers@npm:7.24.6"
dependencies:
"@babel/helper-environment-visitor": "npm:^7.24.6"
"@babel/helper-member-expression-to-functions": "npm:^7.24.6"
"@babel/helper-optimise-call-expression": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0
checksum: aaf2dfaf25360da1525ecea5979d5afed201b96f0feeed2e15f90883a97776132a720b25039e67fee10a5c537363aea5cc2a46c0f1d13fdb86d0e920244f2da7
languageName: node
linkType: hard
"@babel/helper-simple-access@npm:^7.22.5":
version: 7.22.5
resolution: "@babel/helper-simple-access@npm:7.22.5"
@ -1883,6 +2000,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-simple-access@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-simple-access@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: b17e404dd6c9787fc7d558aea5222471a77e29596705f0d10b4c2a58b9d71ff7eae915094204848cc1af99b771553caa69337a768b9abdd82b54a0050ba83eb9
languageName: node
linkType: hard
"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0, @babel/helper-skip-transparent-expression-wrappers@npm:^7.22.5":
version: 7.22.5
resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.22.5"
@ -1892,6 +2018,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-skip-transparent-expression-wrappers@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 6928f698362d6082a67ee2bc73991ef6b0cc6b5f2854177389bc8f3c09296580f0ee20134dd1a29dfcb1906ad9e346fa0f7c6fcd7589ab3ff176d4f09504577f
languageName: node
linkType: hard
"@babel/helper-split-export-declaration@npm:^7.22.6":
version: 7.22.6
resolution: "@babel/helper-split-export-declaration@npm:7.22.6"
@ -1901,6 +2036,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-split-export-declaration@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-split-export-declaration@npm:7.24.6"
dependencies:
"@babel/types": "npm:^7.24.6"
checksum: 53a5dd8691fdffc89cc7fcf5aed0ad1d8bc39796a5782a3d170dcbf249eb5c15cc8a290e8d09615711d18798ad04a7d0694ab5195d35fa651abbc1b9c885d6a8
languageName: node
linkType: hard
"@babel/helper-string-parser@npm:^7.23.4":
version: 7.23.4
resolution: "@babel/helper-string-parser@npm:7.23.4"
@ -1908,6 +2052,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-string-parser@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-string-parser@npm:7.24.6"
checksum: 95115bf676e92c4e99166395649108d97447e6cabef1fabaec8cdbc53a43f27b5df2268ff6534439d405bc1bd06685b163eb3b470455bd49f69159dada414145
languageName: node
linkType: hard
"@babel/helper-validator-identifier@npm:^7.22.20":
version: 7.22.20
resolution: "@babel/helper-validator-identifier@npm:7.22.20"
@ -1915,6 +2066,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-validator-identifier@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-validator-identifier@npm:7.24.6"
checksum: d29d2e3fca66c31867a009014169b93f7bc21c8fc1dd7d0b9d85d7a4000670526ff2222d966febb75a6e12f9859a31d1e75b558984e28ecb69651314dd0a6fd1
languageName: node
linkType: hard
"@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5":
version: 7.23.5
resolution: "@babel/helper-validator-option@npm:7.23.5"
@ -1922,6 +2080,13 @@ __metadata:
languageName: node
linkType: hard
"@babel/helper-validator-option@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/helper-validator-option@npm:7.24.6"
checksum: 787268dff5cf77f3b704454b96ab7b58aa4f43b2808247e51859a103a1c28a9c252100f830433f4b37a73f4a61ba745bbeef4cdccbab48c1e9adf037f4ca3491
languageName: node
linkType: hard
"@babel/helper-wrap-function@npm:^7.22.20":
version: 7.22.20
resolution: "@babel/helper-wrap-function@npm:7.22.20"
@ -1988,6 +2153,18 @@ __metadata:
languageName: node
linkType: hard
"@babel/highlight@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/highlight@npm:7.24.6"
dependencies:
"@babel/helper-validator-identifier": "npm:^7.24.6"
chalk: "npm:^2.4.2"
js-tokens: "npm:^4.0.0"
picocolors: "npm:^1.0.0"
checksum: 5bbc31695e5d44e97feb267f7aaf4c52908560d184ffeb2e2e57aae058d40125592931883889413e19def3326895ddb41ff45e090fa90b459d8c294b4ffc238c
languageName: node
linkType: hard
"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.22.7, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.5":
version: 7.23.5
resolution: "@babel/parser@npm:7.23.5"
@ -2015,6 +2192,15 @@ __metadata:
languageName: node
linkType: hard
"@babel/parser@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/parser@npm:7.24.6"
bin:
parser: ./bin/babel-parser.js
checksum: cbef70923078a20fe163b03f4a6482be65ed99d409a57f3091a23ce3a575ee75716c30e7ea9f40b692ac5660f34055f4cbeb66a354fad15a6cf1fca35c3496c5
languageName: node
linkType: hard
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.23.3"
@ -2268,6 +2454,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-syntax-jsx@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/plugin-syntax-jsx@npm:7.24.6"
dependencies:
"@babel/helper-plugin-utils": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: f00d783a9e2d52f0a8797823a3cbdbe2d0dc09c7235fe8c88e6dce3a02f234f52fb5e976a001cc30b0e2b330590b5680f54436e56d67f9ab05d1e4bdeb3992cd
languageName: node
linkType: hard
"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4, @babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3":
version: 7.10.4
resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4"
@ -2367,6 +2564,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-syntax-typescript@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/plugin-syntax-typescript@npm:7.24.6"
dependencies:
"@babel/helper-plugin-utils": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: b1eeabf8bebfa78cea559c0a0d55e480fe2ebd799472d1f6bd5afbd2759d02b362d29ad30009c81d5b112797beb987e58a3000d2331adaa4bf03862e1ed18cef
languageName: node
linkType: hard
"@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6":
version: 7.18.6
resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6"
@ -2691,6 +2899,19 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-modules-commonjs@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.6"
dependencies:
"@babel/helper-module-transforms": "npm:^7.24.6"
"@babel/helper-plugin-utils": "npm:^7.24.6"
"@babel/helper-simple-access": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 4fc790136d066105fa773ffc7e249d88c6f0d0126984ede36fedd51ac2b622b46c08565bcdd1ab62ac10195eeedeaba0d26e7e4c676ed50906cbed16540a4e22
languageName: node
linkType: hard
"@babel/plugin-transform-modules-systemjs@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/plugin-transform-modules-systemjs@npm:7.23.3"
@ -3071,6 +3292,20 @@ __metadata:
languageName: node
linkType: hard
"@babel/plugin-transform-typescript@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/plugin-transform-typescript@npm:7.24.6"
dependencies:
"@babel/helper-annotate-as-pure": "npm:^7.24.6"
"@babel/helper-create-class-features-plugin": "npm:^7.24.6"
"@babel/helper-plugin-utils": "npm:^7.24.6"
"@babel/plugin-syntax-typescript": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: 46b054e4d4253187403e392ef30f4dd624d8486a1992703f5ff1b415d4e8d00f474e35fb77bc7a3a16a17330873cadcd5af4a8493c61b16da2dde212b2788ccd
languageName: node
linkType: hard
"@babel/plugin-transform-unicode-escapes@npm:^7.23.3":
version: 7.23.3
resolution: "@babel/plugin-transform-unicode-escapes@npm:7.23.3"
@ -3355,6 +3590,21 @@ __metadata:
languageName: node
linkType: hard
"@babel/preset-typescript@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/preset-typescript@npm:7.24.6"
dependencies:
"@babel/helper-plugin-utils": "npm:^7.24.6"
"@babel/helper-validator-option": "npm:^7.24.6"
"@babel/plugin-syntax-jsx": "npm:^7.24.6"
"@babel/plugin-transform-modules-commonjs": "npm:^7.24.6"
"@babel/plugin-transform-typescript": "npm:^7.24.6"
peerDependencies:
"@babel/core": ^7.0.0-0
checksum: bfcef91ed80d67301301e17a799814457b57bfd0d85d9897dce6df6ed0b0af155c0f5b2af7a1a122a3f36faaaa1de87ccf9954ce06d2f440898ffdfaf18aab86
languageName: node
linkType: hard
"@babel/register@npm:^7.13.16, @babel/register@npm:^7.22.15":
version: 7.23.7
resolution: "@babel/register@npm:7.23.7"
@ -3446,6 +3696,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/template@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/template@npm:7.24.6"
dependencies:
"@babel/code-frame": "npm:^7.24.6"
"@babel/parser": "npm:^7.24.6"
"@babel/types": "npm:^7.24.6"
checksum: a4d5805770de908b445f7cdcebfcb6eaa07b1ec9c7b78fd3f375a911b1522c249bddae6b96bc4aac24247cc603e3e6cffcf2fe50b4c929dfeb22de289b517525
languageName: node
linkType: hard
"@babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.18.9, @babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.5":
version: 7.23.5
resolution: "@babel/traverse@npm:7.23.5"
@ -3551,6 +3812,17 @@ __metadata:
languageName: node
linkType: hard
"@babel/types@npm:^7.24.6":
version: 7.24.6
resolution: "@babel/types@npm:7.24.6"
dependencies:
"@babel/helper-string-parser": "npm:^7.24.6"
"@babel/helper-validator-identifier": "npm:^7.24.6"
to-fast-properties: "npm:^2.0.0"
checksum: 1d94d92d97ef49030ad7f9e14cfccfeb70b1706dabcaa69037e659ec9d2c3178fb005d2088cce40d88dfc1306153d9157fe038a79ea2be92e5e6b99a59ef80cc
languageName: node
linkType: hard
"@base2/pretty-print-object@npm:1.0.1":
version: 1.0.1
resolution: "@base2/pretty-print-object@npm:1.0.1"
@ -4760,6 +5032,15 @@ __metadata:
languageName: node
linkType: hard
"@emotion/is-prop-valid@npm:^1.2.0":
version: 1.2.2
resolution: "@emotion/is-prop-valid@npm:1.2.2"
dependencies:
"@emotion/memoize": "npm:^0.8.1"
checksum: bb1530dcb4e0e5a4fabb219279f2d0bc35796baf66f6241f98b0d03db1985c890a8cafbea268e0edefd5eeda143dbd5c09a54b5fba74cee8c69b98b13194af50
languageName: node
linkType: hard
"@emotion/is-prop-valid@npm:^1.2.1":
version: 1.2.1
resolution: "@emotion/is-prop-valid@npm:1.2.1"
@ -7479,6 +7760,33 @@ __metadata:
languageName: node
linkType: hard
"@linaria/core@npm:^6.2.0":
version: 6.2.0
resolution: "@linaria/core@npm:6.2.0"
dependencies:
"@wyw-in-js/processor-utils": "npm:^0.5.3"
checksum: 4eb6083c0988f19aa3721e34bf14093dab8a0c6a784237ae4276a0b4e68bb82e91c594d7dd1d12173aaa2b38400a6bf5e0bd8b961a9ae05cdba854e2c566a134
languageName: node
linkType: hard
"@linaria/react@npm:^6.2.1":
version: 6.2.1
resolution: "@linaria/react@npm:6.2.1"
dependencies:
"@emotion/is-prop-valid": "npm:^1.2.0"
"@linaria/core": "npm:^6.2.0"
"@wyw-in-js/processor-utils": "npm:^0.5.3"
"@wyw-in-js/shared": "npm:^0.5.3"
minimatch: "npm:^9.0.3"
react-html-attributes: "npm:^1.4.6"
resolve: "npm:^1.22.8"
ts-invariant: "npm:^0.10.3"
peerDependencies:
react: ">=16"
checksum: 102480195bc6e5f41cf3da4c7181640cf617256fcdd48847238cdc7def30536f132f29f35530bd261cbf5580a87981f5a6f7717e791b73cc309552141a10d256
languageName: node
linkType: hard
"@ljharb/through@npm:^2.3.9":
version: 2.3.12
resolution: "@ljharb/through@npm:2.3.12"
@ -18685,6 +18993,62 @@ __metadata:
languageName: node
linkType: hard
"@wyw-in-js/processor-utils@npm:0.5.3, @wyw-in-js/processor-utils@npm:^0.5.3":
version: 0.5.3
resolution: "@wyw-in-js/processor-utils@npm:0.5.3"
dependencies:
"@babel/generator": "npm:^7.23.5"
"@wyw-in-js/shared": "npm:0.5.3"
checksum: d9d12fe96a423551876b00cd715c61945a11770b448cb9363d435f1cd3506f7f57c068558ea8cb87eb78d39ff7cdb255ed055af540ee289f8b4e4798f12dd66d
languageName: node
linkType: hard
"@wyw-in-js/shared@npm:0.5.3, @wyw-in-js/shared@npm:^0.5.3":
version: 0.5.3
resolution: "@wyw-in-js/shared@npm:0.5.3"
dependencies:
debug: "npm:^4.3.4"
find-up: "npm:^5.0.0"
minimatch: "npm:^9.0.3"
checksum: 0e02195f113793231953f8a2b0a65e8641f851d2627d961949fa925c60a72ce35a20dd477b12f5ac3f0cfedc30c8507a4540d0f5acf7983fd5c636acba50d293
languageName: node
linkType: hard
"@wyw-in-js/transform@npm:0.5.3":
version: 0.5.3
resolution: "@wyw-in-js/transform@npm:0.5.3"
dependencies:
"@babel/core": "npm:^7.23.5"
"@babel/generator": "npm:^7.23.5"
"@babel/helper-module-imports": "npm:^7.22.15"
"@babel/plugin-transform-modules-commonjs": "npm:^7.23.3"
"@babel/template": "npm:^7.22.15"
"@babel/traverse": "npm:^7.23.5"
"@babel/types": "npm:^7.23.5"
"@wyw-in-js/processor-utils": "npm:0.5.3"
"@wyw-in-js/shared": "npm:0.5.3"
babel-merge: "npm:^3.0.0"
cosmiconfig: "npm:^8.0.0"
happy-dom: "npm:^12.5.0"
source-map: "npm:^0.7.4"
stylis: "npm:^4.3.0"
ts-invariant: "npm:^0.10.3"
checksum: 947633c7ed6fbbd2546a88db7ba5e70e558eb460eb60a479031f5edae07234f4e7eda5bc960aabee755e154d7cf6e3f191cd291666f3c5735b65d469896e8574
languageName: node
linkType: hard
"@wyw-in-js/vite@npm:^0.5.3":
version: 0.5.3
resolution: "@wyw-in-js/vite@npm:0.5.3"
dependencies:
"@wyw-in-js/shared": "npm:0.5.3"
"@wyw-in-js/transform": "npm:0.5.3"
peerDependencies:
vite: ">=3.2.7"
checksum: 0fbbdfe2fde2061b160052c7607d15f4fc3254058888b03f4affe42f4fd5bc059784732e1d372e79dcd8b40e9fa4f92c98805c018d5912d4227236328c32ea30
languageName: node
linkType: hard
"@xobotyi/scrollbar-width@npm:^1.9.5":
version: 1.9.5
resolution: "@xobotyi/scrollbar-width@npm:1.9.5"
@ -20244,6 +20608,18 @@ __metadata:
languageName: node
linkType: hard
"babel-merge@npm:^3.0.0":
version: 3.0.0
resolution: "babel-merge@npm:3.0.0"
dependencies:
deepmerge: "npm:^2.2.1"
object.omit: "npm:^3.0.0"
peerDependencies:
"@babel/core": ^7.0.0
checksum: 8337358ba69553305ba0ee6ddfdeb79b58d37012d5c99953d87e608eb22299a565501a854e451c2daa8f7b2310d1a52c66ed92f17e18a9858153e9fd9f3090e5
languageName: node
linkType: hard
"babel-messages@npm:^6.23.0":
version: 6.23.0
resolution: "babel-messages@npm:6.23.0"
@ -23714,7 +24090,7 @@ __metadata:
languageName: node
linkType: hard
"cosmiconfig@npm:8.3.6, cosmiconfig@npm:^8.1.3, cosmiconfig@npm:^8.2.0, cosmiconfig@npm:^8.3.5":
"cosmiconfig@npm:8.3.6, cosmiconfig@npm:^8.0.0, cosmiconfig@npm:^8.1.3, cosmiconfig@npm:^8.2.0, cosmiconfig@npm:^8.3.5":
version: 8.3.6
resolution: "cosmiconfig@npm:8.3.6"
dependencies:
@ -24836,6 +25212,13 @@ __metadata:
languageName: node
linkType: hard
"deepmerge@npm:^2.2.1":
version: 2.2.1
resolution: "deepmerge@npm:2.2.1"
checksum: 4379288cabd817587cee92a095ea65d18317b45e48010a2e0d87982b5f432239a144f9c8ebd4ab090cc21f0cb47e51ebfe32921f329b3b3084a2711d5d63e450
languageName: node
linkType: hard
"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.1":
version: 4.3.1
resolution: "deepmerge@npm:4.3.1"
@ -29608,6 +29991,20 @@ __metadata:
languageName: node
linkType: hard
"happy-dom@npm:^12.5.0":
version: 12.10.3
resolution: "happy-dom@npm:12.10.3"
dependencies:
css.escape: "npm:^1.5.1"
entities: "npm:^4.5.0"
iconv-lite: "npm:^0.6.3"
webidl-conversions: "npm:^7.0.0"
whatwg-encoding: "npm:^2.0.0"
whatwg-mimetype: "npm:^3.0.0"
checksum: fbf8647e17c4af5c166d7c4b6963f4bbc9d1c279e94a4c77234b1fecca98c59989b894c7b186f5107e1062d40ffd84f12350b757f51330a5fc1c5228eb199517
languageName: node
linkType: hard
"har-schema@npm:^2.0.0":
version: 2.0.0
resolution: "har-schema@npm:2.0.0"
@ -30391,6 +30788,13 @@ __metadata:
languageName: node
linkType: hard
"html-element-attributes@npm:^1.0.0":
version: 1.3.1
resolution: "html-element-attributes@npm:1.3.1"
checksum: caf9704577854275bfe4b6f5a77c9b5f2d988b6bc86f48aa1ee043421950c314c43d8f2915178f5cb7a00409e1f2690c1ddb0f4758f9d63b875172cbaa32bc9f
languageName: node
linkType: hard
"html-encoding-sniffer@npm:^3.0.0":
version: 3.0.0
resolution: "html-encoding-sniffer@npm:3.0.0"
@ -30857,7 +31261,7 @@ __metadata:
languageName: node
linkType: hard
"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2":
"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
dependencies:
@ -41673,6 +42077,15 @@ __metadata:
languageName: node
linkType: hard
"react-html-attributes@npm:^1.4.6":
version: 1.4.6
resolution: "react-html-attributes@npm:1.4.6"
dependencies:
html-element-attributes: "npm:^1.0.0"
checksum: f2ef5b80c6254a0bc4cc802d7412bee0bd5008a1ac1a96eb52cdfde9b7e5017e9373ee613361d6c83df3faa8743c71fb42d1fb238bb797ece481d97848c02be2
languageName: node
linkType: hard
"react-icons@npm:^4.12.0, react-icons@npm:^4.3.1":
version: 4.12.0
resolution: "react-icons@npm:4.12.0"
@ -43180,7 +43593,7 @@ __metadata:
languageName: node
linkType: hard
"resolve@npm:^1.1.4, resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.4.0, resolve@npm:~1.22.1":
"resolve@npm:^1.1.4, resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.12.0, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:^1.4.0, resolve@npm:~1.22.1":
version: 1.22.8
resolution: "resolve@npm:1.22.8"
dependencies:
@ -43216,7 +43629,7 @@ __metadata:
languageName: node
linkType: hard
"resolve@patch:resolve@npm%3A^1.1.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.1.6#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.4.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin<compat/resolve>":
"resolve@patch:resolve@npm%3A^1.1.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.1.6#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.12.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.4.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin<compat/resolve>":
version: 1.22.8
resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin<compat/resolve>::version=1.22.8&hash=c3c19d"
dependencies:
@ -46796,6 +47209,7 @@ __metadata:
"@aws-sdk/credential-providers": "npm:^3.363.0"
"@babel/core": "npm:^7.14.5"
"@babel/preset-react": "npm:^7.14.5"
"@babel/preset-typescript": "npm:^7.24.6"
"@blocknote/core": "npm:^0.12.1"
"@blocknote/react": "npm:^0.12.2"
"@chakra-ui/accordion": "npm:^2.3.0"
@ -46822,6 +47236,8 @@ __metadata:
"@hello-pangea/dnd": "npm:^16.2.0"
"@hookform/resolvers": "npm:^3.1.1"
"@jsdevtools/rehype-toc": "npm:^3.0.2"
"@linaria/core": "npm:^6.2.0"
"@linaria/react": "npm:^6.2.1"
"@mdx-js/react": "npm:^3.0.0"
"@nestjs/apollo": "npm:^11.0.5"
"@nestjs/axios": "npm:^3.0.1"
@ -46934,6 +47350,7 @@ __metadata:
"@typescript-eslint/utils": "npm:6.21.0"
"@vitejs/plugin-react-swc": "npm:^3.5.0"
"@vitest/ui": "npm:1.4.0"
"@wyw-in-js/vite": "npm:^0.5.3"
add: "npm:^2.0.6"
addressparser: "npm:^1.0.1"
afterframe: "npm:^1.0.2"