Update Seeds while pre-fi

lling a new workspace
This commit is contained in:
Charles Bochet
2023-11-17 21:54:32 +01:00
parent e90beef91f
commit aa2596c572
66 changed files with 476 additions and 668 deletions

View File

@ -415,6 +415,11 @@ export type DeleteOneObjectInput = {
id: Scalars['ID']['input']; id: Scalars['ID']['input'];
}; };
export type DeleteOneRelationInput = {
/** The id of the record to delete. */
id: Scalars['ID']['input'];
};
export type Favorite = { export type Favorite = {
__typename?: 'Favorite'; __typename?: 'Favorite';
company?: Maybe<Company>; company?: Maybe<Company>;
@ -463,6 +468,7 @@ export enum FieldMetadataType {
Date = 'DATE', Date = 'DATE',
Email = 'EMAIL', Email = 'EMAIL',
Enum = 'ENUM', Enum = 'ENUM',
FullName = 'FULL_NAME',
Link = 'LINK', Link = 'LINK',
Number = 'NUMBER', Number = 'NUMBER',
Phone = 'PHONE', Phone = 'PHONE',
@ -496,6 +502,7 @@ export type Mutation = {
createOneRelation: Relation; createOneRelation: Relation;
deleteOneField: FieldDeleteResponse; deleteOneField: FieldDeleteResponse;
deleteOneObject: ObjectDeleteResponse; deleteOneObject: ObjectDeleteResponse;
deleteOneRelation: RelationDeleteResponse;
updateOneField: Field; updateOneField: Field;
updateOneObject: Object; updateOneObject: Object;
}; };
@ -526,6 +533,11 @@ export type MutationDeleteOneObjectArgs = {
}; };
export type MutationDeleteOneRelationArgs = {
input: DeleteOneRelationInput;
};
export type MutationUpdateOneFieldArgs = { export type MutationUpdateOneFieldArgs = {
input: UpdateOneFieldInput; input: UpdateOneFieldInput;
}; };
@ -711,6 +723,18 @@ export type RelationConnection = {
totalCount: Scalars['Int']['output']; totalCount: Scalars['Int']['output'];
}; };
export type RelationDeleteResponse = {
__typename?: 'RelationDeleteResponse';
createdAt?: Maybe<Scalars['DateTime']['output']>;
fromFieldMetadataId?: Maybe<Scalars['String']['output']>;
fromObjectMetadataId?: Maybe<Scalars['String']['output']>;
id?: Maybe<Scalars['ID']['output']>;
relationType?: Maybe<RelationMetadataType>;
toFieldMetadataId?: Maybe<Scalars['String']['output']>;
toObjectMetadataId?: Maybe<Scalars['String']['output']>;
updatedAt?: Maybe<Scalars['DateTime']['output']>;
};
/** Type of the relation */ /** Type of the relation */
export enum RelationMetadataType { export enum RelationMetadataType {
ManyToMany = 'MANY_TO_MANY', ManyToMany = 'MANY_TO_MANY',

View File

@ -1336,6 +1336,7 @@ export enum FieldMetadataType {
Date = 'DATE', Date = 'DATE',
Email = 'EMAIL', Email = 'EMAIL',
Enum = 'ENUM', Enum = 'ENUM',
FullName = 'FULL_NAME',
Link = 'LINK', Link = 'LINK',
Number = 'NUMBER', Number = 'NUMBER',
Phone = 'PHONE', Phone = 'PHONE',
@ -1443,6 +1444,7 @@ export type Mutation = {
deleteOneField: FieldDeleteResponse; deleteOneField: FieldDeleteResponse;
deleteOneObject: ObjectDeleteResponse; deleteOneObject: ObjectDeleteResponse;
deleteOnePipelineStage: PipelineStage; deleteOnePipelineStage: PipelineStage;
deleteOneRelation: RelationDeleteResponse;
deleteOneWebHook: WebHook; deleteOneWebHook: WebHook;
deleteUserAccount: User; deleteUserAccount: User;
deleteUserV2: UserV2; deleteUserV2: UserV2;
@ -2626,6 +2628,18 @@ export type RelationConnection = {
totalCount: Scalars['Int']; totalCount: Scalars['Int'];
}; };
export type RelationDeleteResponse = {
__typename?: 'RelationDeleteResponse';
createdAt?: Maybe<Scalars['DateTime']>;
fromFieldMetadataId?: Maybe<Scalars['String']>;
fromObjectMetadataId?: Maybe<Scalars['String']>;
id?: Maybe<Scalars['ID']>;
relationType?: Maybe<RelationMetadataType>;
toFieldMetadataId?: Maybe<Scalars['String']>;
toObjectMetadataId?: Maybe<Scalars['String']>;
updatedAt?: Maybe<Scalars['DateTime']>;
};
/** Type of the relation */ /** Type of the relation */
export enum RelationMetadataType { export enum RelationMetadataType {
ManyToMany = 'MANY_TO_MANY', ManyToMany = 'MANY_TO_MANY',

View File

@ -67,7 +67,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
const showDate = beautifiedCreatedAt !== ''; const showDate = beautifiedCreatedAt !== '';
const author = comment.author; const author = comment.author;
const authorName = author.firstName + ' ' + author.lastName; const authorName = author.name.firstName + ' ' + author.name.lastName;
const avatarUrl = author.avatarUrl; const avatarUrl = author.avatarUrl;
const commentId = comment.id; const commentId = comment.id;

View File

@ -10,8 +10,10 @@ export const mockComment: Pick<
body: 'Hello, this is a comment.', body: 'Hello, this is a comment.',
author: { author: {
id: 'fake_comment_1_author_uuid', id: 'fake_comment_1_author_uuid',
firstName: 'Jony' ?? '', name: {
lastName: 'Ive' ?? '', firstName: 'Jony' ?? '',
lastName: 'Ive' ?? '',
},
avatarUrl: null, avatarUrl: null,
}, },
createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '', createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '',
@ -26,8 +28,10 @@ export const mockCommentWithLongValues: Pick<
body: 'Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment.', body: 'Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment. Hello, this is a comment.',
author: { author: {
id: 'fake_comment_1_author_uuid', id: 'fake_comment_1_author_uuid',
firstName: 'Jony' ?? '', name: {
lastName: 'Ive' ?? '', firstName: 'Jony' ?? '',
lastName: 'Ive' ?? '',
},
avatarUrl: null, avatarUrl: null,
}, },
createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '', createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '',

View File

@ -14,10 +14,7 @@ import {
export type ActivityAssigneePickerProps = { export type ActivityAssigneePickerProps = {
activity: Pick<Activity, 'id'> & { activity: Pick<Activity, 'id'> & {
accountOwner?: Pick< accountOwner?: Pick<WorkspaceMember, 'id' | 'name'> | null;
WorkspaceMember,
'id' | 'firstName' | 'lastName'
> | null;
}; };
onSubmit?: () => void; onSubmit?: () => void;
onCancel?: () => void; onCancel?: () => void;

View File

@ -60,10 +60,7 @@ type ActivityEditorProps = {
> & { > & {
comments?: Array<Comment> | null; comments?: Array<Comment> | null;
} & { } & {
assignee?: Pick< assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
WorkspaceMember,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
} & { } & {
activityTargets?: Array< activityTargets?: Array<
Pick<ActivityTarget, 'id' | 'companyId' | 'personId'> Pick<ActivityTarget, 'id' | 'companyId' | 'personId'>

View File

@ -13,10 +13,7 @@ import { Company, User } from '~/generated/graphql';
type ActivityAssigneeEditableFieldProps = { type ActivityAssigneeEditableFieldProps = {
activity: Pick<Company, 'id' | 'accountOwnerId'> & { activity: Pick<Company, 'id' | 'accountOwnerId'> & {
assignee?: Pick< assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
WorkspaceMember,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
}; };
}; };

View File

@ -7,6 +7,7 @@ import { Activity } from '@/activities/types/Activity';
import { IconNotes } from '@/ui/display/icon'; import { IconNotes } from '@/ui/display/icon';
import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip'; import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { import {
beautifyExactDateTime, beautifyExactDateTime,
beautifyPastDateRelativeToNow, beautifyPastDateRelativeToNow,
@ -127,11 +128,8 @@ type TimelineActivityProps = {
| 'type' | 'type'
| 'comments' | 'comments'
| 'dueAt' | 'dueAt'
> & { author: Pick<Activity['author'], 'firstName' | 'lastName'> } & { > & { author: Pick<WorkspaceMember, 'name'> } & {
assignee?: Pick< assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
Activity['author'],
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
}; };
}; };
@ -151,7 +149,9 @@ export const TimelineActivity = ({ activity }: TimelineActivityProps) => {
</StyledIconContainer> </StyledIconContainer>
<StyledItemTitleContainer> <StyledItemTitleContainer>
<span> <span>
{activity.author.firstName + ' ' + activity.author.lastName} {activity.author.name.firstName +
' ' +
activity.author.name.lastName}
</span> </span>
created a {activity.type.toLowerCase()} created a {activity.type.toLowerCase()}
</StyledItemTitleContainer> </StyledItemTitleContainer>

View File

@ -9,10 +9,7 @@ import { beautifyExactDate } from '~/utils/date-utils';
type TimelineActivityCardFooterProps = { type TimelineActivityCardFooterProps = {
activity: Pick<Activity, 'id' | 'dueAt' | 'comments'> & { activity: Pick<Activity, 'id' | 'dueAt' | 'comments'> & {
assignee?: Pick< assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
WorkspaceMember,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
}; };
}; };
@ -48,9 +45,9 @@ export const TimelineActivityCardFooter = ({
<UserChip <UserChip
id={activity.assignee.id} id={activity.assignee.id}
name={ name={
activity.assignee.firstName + activity.assignee.name.firstName +
' ' + ' ' +
activity.assignee.lastName ?? '' activity.assignee.name.lastName ?? ''
} }
pictureUrl={activity.assignee.avatarUrl ?? ''} pictureUrl={activity.assignee.avatarUrl ?? ''}
/> />

View File

@ -15,12 +15,9 @@ export type Activity = {
type: ActivityType; type: ActivityType;
title: string; title: string;
body: string; body: string;
author: Pick<WorkspaceMember, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>; author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
authorId: string; authorId: string;
assignee: Pick< assignee: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
WorkspaceMember,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
assigneeId: string | null; assigneeId: string | null;
comments: Comment[]; comments: Comment[];
}; };

View File

@ -6,5 +6,5 @@ export type Comment = {
body: string; body: string;
updatedAt: string; updatedAt: string;
activityId: string; activityId: string;
author: Pick<WorkspaceMember, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>; author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
}; };

View File

@ -169,8 +169,10 @@ export const useAuth = () => {
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2, mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
variables: { variables: {
input: { input: {
firstName: user.firstName ?? '', name: {
lastName: user.lastName ?? '', firstName: user.firstName ?? '',
lastName: user.lastName ?? '',
},
colorScheme: 'Light', colorScheme: 'Light',
userId: user.id, userId: user.id,
allowImpersonation: true, allowImpersonation: true,

View File

@ -25,7 +25,10 @@ export const getOnboardingStatus = (
if (!currentWorkspace?.displayName) { if (!currentWorkspace?.displayName) {
return OnboardingStatus.OngoingWorkspaceCreation; return OnboardingStatus.OngoingWorkspaceCreation;
} }
if (!currentWorkspaceMember.firstName || !currentWorkspaceMember.lastName) { if (
!currentWorkspaceMember.name.firstName ||
!currentWorkspaceMember.name.lastName
) {
return OnboardingStatus.OngoingProfileCreation; return OnboardingStatus.OngoingProfileCreation;
} }

View File

@ -1,4 +1,7 @@
import { Company, Favorite, User } from '../../../../generated/graphql'; import { Favorite } from '@/favorites/types/Favorite';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { Company } from '../../../../generated/graphql';
type MockedCompany = Pick< type MockedCompany = Pick<
Company, Company,
@ -15,16 +18,7 @@ type MockedCompany = Pick<
| 'idealCustomerProfile' | 'idealCustomerProfile'
| '_activityCount' | '_activityCount'
> & { > & {
accountOwner: Pick< accountOwner: Pick<WorkspaceMember, 'id' | 'avatarUrl' | 'name'> | null;
User,
| 'id'
| 'email'
| 'displayName'
| 'avatarUrl'
| '__typename'
| 'firstName'
| 'lastName'
> | null;
} & { Favorite: Pick<Favorite, 'id'> | null }; } & { Favorite: Pick<Favorite, 'id'> | null };
export const mockedCompaniesData: Array<MockedCompany> = [ export const mockedCompaniesData: Array<MockedCompany> = [
@ -42,13 +36,12 @@ export const mockedCompaniesData: Array<MockedCompany> = [
Favorite: null, Favorite: null,
_activityCount: 0, _activityCount: 0,
accountOwner: { accountOwner: {
email: 'charles@test.com', name: {
displayName: 'Charles Test', firstName: 'Charles',
firstName: 'Charles', lastName: 'Test',
lastName: 'Test', },
avatarUrl: null, avatarUrl: null,
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
__typename: 'User',
}, },
__typename: 'Company', __typename: 'Company',
}, },

View File

@ -91,6 +91,14 @@ export const useMapFieldMetadataToGraphQLQuery = () => {
currencyCode currencyCode
} }
`; `;
} else if (fieldType === 'FULL_NAME') {
return `
${field.name}
{
firstName
lastName
}
`;
} }
}; };

View File

@ -91,9 +91,10 @@ export const RecordShowPage = () => {
if (isFavorite) deleteFavorite(object?.id); if (isFavorite) deleteFavorite(object?.id);
else { else {
const additionalData = const additionalData =
objectNameSingular === 'peopleV2' objectNameSingular === 'personV2'
? { ? {
labelIdentifier: object.firstName + ' ' + object.lastName, labelIdentifier:
object.name.firstName + ' ' + object.name.lastName,
avatarUrl: object.avatarUrl, avatarUrl: object.avatarUrl,
avatarType: 'rounded', avatarType: 'rounded',
link: `/object/personV2/${object.id}`, link: `/object/personV2/${object.id}`,
@ -114,11 +115,16 @@ export const RecordShowPage = () => {
if (!object) return <></>; if (!object) return <></>;
const pageName =
objectNameSingular === 'personV2'
? object.name.firstName + ' ' + object.name.lastName
: object.name;
return ( return (
<PageContainer> <PageContainer>
<PageTitle title={object.name || 'No Name'} /> <PageTitle title={pageName} />
<PageHeader <PageHeader
title={object.name ?? ''} title={pageName ?? ''}
hasBackButton hasBackButton
Icon={IconBuildingSkyscraper} Icon={IconBuildingSkyscraper}
> >

View File

@ -4,8 +4,10 @@ export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql`
mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) { mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) {
createWorkspaceMemberV2(data: $input) { createWorkspaceMemberV2(data: $input) {
id id
firstName name {
lastName firstName
lastName
}
} }
} }
`; `;

View File

@ -6,8 +6,10 @@ export const FIND_ONE_WORKSPACE_MEMBER_V2 = gql`
edges { edges {
node { node {
id id
firstName name {
lastName firstName
lastName
}
colorScheme colorScheme
avatarUrl avatarUrl
locale locale

View File

@ -29,7 +29,6 @@ export const useRecordTableContextMenuEntries = () => {
const { scopeId: objectNamePlural, resetTableRowSelection } = const { scopeId: objectNamePlural, resetTableRowSelection } =
useRecordTable(); useRecordTable();
const { data } = useGetFavoritesQuery();
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({ const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural, objectNamePlural,
}); });

View File

@ -31,7 +31,7 @@ export const useGenerateFindManyCustomObjectsQuery = ({
node { node {
id id
${objectMetadataItem.fields ${objectMetadataItem.fields
.map(mapFieldMetadataToGraphQLQuery) .map((field) => mapFieldMetadataToGraphQLQuery(field))
.join('\n')} .join('\n')}
} }
cursor cursor

View File

@ -16,7 +16,7 @@ const PeopleTableEffect = () => {
setViewObjectMetadataId, setViewObjectMetadataId,
} = useView(); } = useView();
const { setAvailableTableColumns, setTableColumns } = useRecordTable(); const { setAvailableTableColumns } = useRecordTable();
useEffect(() => { useEffect(() => {
setAvailableSortDefinitions?.(personTableSortDefinitions); setAvailableSortDefinitions?.(personTableSortDefinitions);
@ -31,7 +31,6 @@ const PeopleTableEffect = () => {
setAvailableFilterDefinitions, setAvailableFilterDefinitions,
setAvailableSortDefinitions, setAvailableSortDefinitions,
setAvailableTableColumns, setAvailableTableColumns,
setTableColumns,
setViewObjectMetadataId, setViewObjectMetadataId,
setViewType, setViewType,
]); ]);

View File

@ -62,13 +62,26 @@ export const useFilteredSearchEntityQueryV2 = ({
} }
return { return {
or: fieldNames.map((fieldName) => ({ or: fieldNames.map((fieldName) => {
[fieldName]: { const fieldNameParts = fieldName.split('.');
like: `%${filter}%`,
// TODO: fix mode if (fieldNameParts.length > 1) {
// mode: QueryMode.Insensitive, // Composite field
},
})), return {
[fieldNameParts[0]]: {
[fieldNameParts[1]]: {
ilike: `%${filter}%`,
},
},
};
}
return {
[fieldName]: {
ilike: `%${filter}%`,
},
};
}),
}; };
}) })
.filter(isDefined); .filter(isDefined);

View File

@ -9,6 +9,7 @@ import {
IconPhone, IconPhone,
IconPlug, IconPlug,
IconTextSize, IconTextSize,
IconUser,
} from '@/ui/display/icon'; } from '@/ui/display/icon';
import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { CurrencyCode, FieldMetadataType } from '~/generated-metadata/graphql'; import { CurrencyCode, FieldMetadataType } from '~/generated-metadata/graphql';
@ -64,5 +65,6 @@ export const dataTypes: Record<
Icon: IconNumbers, Icon: IconNumbers,
defaultValue: 50, defaultValue: 50,
}, },
[FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser },
[FieldMetadataType.Enum]: { label: 'Enum', Icon: IconPlug }, [FieldMetadataType.Enum]: { label: 'Enum', Icon: IconPlug },
}; };

View File

@ -34,10 +34,10 @@ export const NameFields = ({
); );
const [firstName, setFirstName] = useState( const [firstName, setFirstName] = useState(
currentWorkspaceMember?.firstName ?? '', currentWorkspaceMember?.name.firstName ?? '',
); );
const [lastName, setLastName] = useState( const [lastName, setLastName] = useState(
currentWorkspaceMember?.lastName ?? '', currentWorkspaceMember?.name.lastName ?? '',
); );
const { updateOneObject, objectNotFoundInMetadata } = const { updateOneObject, objectNotFoundInMetadata } =
@ -65,15 +65,19 @@ export const NameFields = ({
await updateOneObject({ await updateOneObject({
idToUpdate: currentWorkspaceMember?.id, idToUpdate: currentWorkspaceMember?.id,
input: { input: {
firstName, name: {
lastName, firstName: firstName,
lastName: lastName,
},
}, },
}); });
setCurrentWorkspaceMember({ setCurrentWorkspaceMember({
...currentWorkspaceMember, ...currentWorkspaceMember,
firstName, name: {
lastName, firstName,
lastName,
},
}); });
} }
} catch (error) { } catch (error) {
@ -87,8 +91,8 @@ export const NameFields = ({
} }
if ( if (
currentWorkspaceMember.firstName !== firstName || currentWorkspaceMember.name.firstName !== firstName ||
currentWorkspaceMember.lastName !== lastName currentWorkspaceMember.name.lastName !== lastName
) { ) {
debouncedUpdate(); debouncedUpdate();
} }

View File

@ -40,7 +40,7 @@ const SupportChat = () => {
( (
chatId: string, chatId: string,
currentUser: Pick<User, 'email' | 'supportUserHash'>, currentUser: Pick<User, 'email' | 'supportUserHash'>,
currentWorkspaceMember: Pick<WorkspaceMember, 'firstName' | 'lastName'>, currentWorkspaceMember: Pick<WorkspaceMember, 'name'>,
) => { ) => {
const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js'; const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js';
const script = document.querySelector(`script[src="${url}"]`); const script = document.querySelector(`script[src="${url}"]`);
@ -54,9 +54,9 @@ const SupportChat = () => {
useDefaultLauncher: false, useDefaultLauncher: false,
email: currentUser.email, email: currentUser.email,
name: name:
currentWorkspaceMember.firstName + currentWorkspaceMember.name.firstName +
' ' + ' ' +
currentWorkspaceMember.lastName, currentWorkspaceMember.name.lastName,
userHash: currentUser?.supportUserHash, userHash: currentUser?.supportUserHash,
}); });
setIsFrontChatLoaded(true); setIsFrontChatLoaded(true);

View File

@ -1,7 +1,9 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { FullNameFieldDisplay } from '@/ui/object/field/meta-types/display/components/FullNameFieldDisplay';
import { RelationFieldDisplay } from '@/ui/object/field/meta-types/display/components/RelationFieldDisplay'; import { RelationFieldDisplay } from '@/ui/object/field/meta-types/display/components/RelationFieldDisplay';
import { UuidFieldDisplay } from '@/ui/object/field/meta-types/display/components/UuidFieldDisplay'; import { UuidFieldDisplay } from '@/ui/object/field/meta-types/display/components/UuidFieldDisplay';
import { isFieldFullName } from '@/ui/object/field/types/guards/isFieldFullName';
import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid'; import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid';
import { FieldContext } from '../contexts/FieldContext'; import { FieldContext } from '../contexts/FieldContext';
@ -9,7 +11,6 @@ import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisp
import { CurrencyFieldDisplay } from '../meta-types/display/components/CurrencyFieldDisplay'; import { CurrencyFieldDisplay } from '../meta-types/display/components/CurrencyFieldDisplay';
import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay'; import { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay';
import { DoubleTextChipFieldDisplay } from '../meta-types/display/components/DoubleTextChipFieldDisplay'; import { DoubleTextChipFieldDisplay } from '../meta-types/display/components/DoubleTextChipFieldDisplay';
import { DoubleTextFieldDisplay } from '../meta-types/display/components/DoubleTextFieldDisplay';
import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay'; import { EmailFieldDisplay } from '../meta-types/display/components/EmailFieldDisplay';
import { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay'; import { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay';
import { MoneyFieldDisplay } from '../meta-types/display/components/MoneyFieldDisplay'; import { MoneyFieldDisplay } from '../meta-types/display/components/MoneyFieldDisplay';
@ -20,7 +21,6 @@ import { URLFieldDisplay } from '../meta-types/display/components/URLFieldDispla
import { isFieldChip } from '../types/guards/isFieldChip'; import { isFieldChip } from '../types/guards/isFieldChip';
import { isFieldCurrency } from '../types/guards/isFieldCurrency'; import { isFieldCurrency } from '../types/guards/isFieldCurrency';
import { isFieldDate } from '../types/guards/isFieldDate'; import { isFieldDate } from '../types/guards/isFieldDate';
import { isFieldDoubleText } from '../types/guards/isFieldDoubleText';
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip'; import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
import { isFieldEmail } from '../types/guards/isFieldEmail'; import { isFieldEmail } from '../types/guards/isFieldEmail';
import { isFieldLink } from '../types/guards/isFieldLink'; import { isFieldLink } from '../types/guards/isFieldLink';
@ -56,14 +56,14 @@ export const FieldDisplay = () => {
<LinkFieldDisplay /> <LinkFieldDisplay />
) : isFieldCurrency(fieldDefinition) ? ( ) : isFieldCurrency(fieldDefinition) ? (
<CurrencyFieldDisplay /> <CurrencyFieldDisplay />
) : isFieldFullName(fieldDefinition) ? (
<FullNameFieldDisplay />
) : isFieldPhone(fieldDefinition) ? ( ) : isFieldPhone(fieldDefinition) ? (
<PhoneFieldDisplay /> <PhoneFieldDisplay />
) : isFieldChip(fieldDefinition) ? ( ) : isFieldChip(fieldDefinition) ? (
<ChipFieldDisplay /> <ChipFieldDisplay />
) : isFieldDoubleTextChip(fieldDefinition) ? ( ) : isFieldDoubleTextChip(fieldDefinition) ? (
<DoubleTextChipFieldDisplay /> <DoubleTextChipFieldDisplay />
) : isFieldDoubleText(fieldDefinition) ? (
<DoubleTextFieldDisplay />
) : ( ) : (
<></> <></>
)} )}

View File

@ -8,7 +8,6 @@ import { ChipFieldInput } from '../meta-types/input/components/ChipFieldInput';
import { CurrencyFieldInput } from '../meta-types/input/components/CurrencyFieldInput'; import { CurrencyFieldInput } from '../meta-types/input/components/CurrencyFieldInput';
import { DateFieldInput } from '../meta-types/input/components/DateFieldInput'; import { DateFieldInput } from '../meta-types/input/components/DateFieldInput';
import { DoubleTextChipFieldInput } from '../meta-types/input/components/DoubleTextChipFieldInput'; import { DoubleTextChipFieldInput } from '../meta-types/input/components/DoubleTextChipFieldInput';
import { DoubleTextFieldInput } from '../meta-types/input/components/DoubleTextFieldInput';
import { EmailFieldInput } from '../meta-types/input/components/EmailFieldInput'; import { EmailFieldInput } from '../meta-types/input/components/EmailFieldInput';
import { LinkFieldInput } from '../meta-types/input/components/LinkFieldInput'; import { LinkFieldInput } from '../meta-types/input/components/LinkFieldInput';
import { MoneyFieldInput } from '../meta-types/input/components/MoneyFieldInput'; import { MoneyFieldInput } from '../meta-types/input/components/MoneyFieldInput';
@ -23,7 +22,6 @@ import { isFieldBoolean } from '../types/guards/isFieldBoolean';
import { isFieldChip } from '../types/guards/isFieldChip'; import { isFieldChip } from '../types/guards/isFieldChip';
import { isFieldCurrency } from '../types/guards/isFieldCurrency'; import { isFieldCurrency } from '../types/guards/isFieldCurrency';
import { isFieldDate } from '../types/guards/isFieldDate'; import { isFieldDate } from '../types/guards/isFieldDate';
import { isFieldDoubleText } from '../types/guards/isFieldDoubleText';
import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip'; import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip';
import { isFieldEmail } from '../types/guards/isFieldEmail'; import { isFieldEmail } from '../types/guards/isFieldEmail';
import { isFieldLink } from '../types/guards/isFieldLink'; import { isFieldLink } from '../types/guards/isFieldLink';
@ -146,14 +144,6 @@ export const FieldInput = ({
onTab={onTab} onTab={onTab}
onShiftTab={onShiftTab} onShiftTab={onShiftTab}
/> />
) : isFieldDoubleText(fieldDefinition) ? (
<DoubleTextFieldInput
onEnter={onEnter}
onEscape={onEscape}
onClickOutside={onClickOutside}
onTab={onTab}
onShiftTab={onShiftTab}
/>
) : isFieldMoney(fieldDefinition) ? ( ) : isFieldMoney(fieldDefinition) ? (
<MoneyFieldInput <MoneyFieldInput
onEnter={onEnter} onEnter={onEnter}

View File

@ -1,10 +0,0 @@
import { useDoubleTextField } from '../../hooks/useDoubleTextField';
import { TextDisplay } from '../content-display/components/TextDisplay';
export const DoubleTextFieldDisplay = () => {
const { firstValue, secondValue } = useDoubleTextField();
const content = [firstValue, secondValue].filter(Boolean).join(' ');
return <TextDisplay text={content} />;
};

View File

@ -0,0 +1,13 @@
import { useFullNameField } from '@/ui/object/field/meta-types/hooks/useFullNameField';
import { TextDisplay } from '../content-display/components/TextDisplay';
export const FullNameFieldDisplay = () => {
const { fieldValue } = useFullNameField();
const content = [fieldValue.firstName, fieldValue.lastName]
.filter(Boolean)
.join(' ');
return <TextDisplay text={content} />;
};

View File

@ -1,79 +0,0 @@
import { useEffect } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { FieldContext } from '../../../../contexts/FieldContext';
import { useDoubleTextField } from '../../../hooks/useDoubleTextField';
import { DoubleTextFieldDisplay } from '../DoubleTextFieldDisplay'; // Import your component
const DoubleTextFieldDisplayValueSetterEffect = ({
firstValue,
secondValue,
}: {
firstValue: string;
secondValue: string;
}) => {
const { setFirstValue, setSecondValue } = useDoubleTextField();
useEffect(() => {
setFirstValue(firstValue);
setSecondValue(secondValue);
}, [setFirstValue, setSecondValue, firstValue, secondValue]);
return null;
};
const meta: Meta = {
title: 'UI/Data/Field/Display/DoubleTextFieldDisplay',
decorators: [
(Story, { args }) => (
<FieldContext.Provider
value={{
entityId: '',
fieldDefinition: {
fieldMetadataId: 'double-text',
label: 'Double-Text',
type: 'DOUBLE_TEXT',
metadata: {
firstValueFieldName: 'First-text',
firstValuePlaceholder: 'First-text',
secondValueFieldName: 'Second-text',
secondValuePlaceholder: 'Second-text',
},
},
hotkeyScope: 'hotkey-scope',
}}
>
<DoubleTextFieldDisplayValueSetterEffect
firstValue={args.firstValue}
secondValue={args.secondValue}
/>
<Story />
</FieldContext.Provider>
),
ComponentDecorator,
],
component: DoubleTextFieldDisplay,
args: {
firstValue: 'Lorem',
secondValue: 'ipsum',
},
};
export default meta;
type Story = StoryObj<typeof DoubleTextFieldDisplay>;
export const Default: Story = {};
export const Elipsis: Story = {
args: {
firstValue:
'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
secondValue: 'ipsum dolor sit amet, consectetur adipiscing elit.',
},
parameters: {
container: { width: 100 },
},
};

View File

@ -21,12 +21,12 @@ export const getEntityChipFromFieldMetadata = (
// TODO: use every // TODO: use every
if (fieldName === 'accountOwner' && fieldValue) { if (fieldName === 'accountOwner' && fieldValue) {
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName; chipValue.name = fieldValue.name.firstName + ' ' + fieldValue.name.lastName;
} else if (fieldName === 'company' && fieldValue) { } else if (fieldName === 'company' && fieldValue) {
chipValue.name = fieldValue.name; chipValue.name = fieldValue.name;
chipValue.pictureUrl = getLogoUrlFromDomainName(fieldValue.domainName); chipValue.pictureUrl = getLogoUrlFromDomainName(fieldValue.domainName);
} else if (fieldName === 'person' && fieldValue) { } else if (fieldName === 'person' && fieldValue) {
chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName; chipValue.name = fieldValue.name.firstName + ' ' + fieldValue.name.lastName;
} }
return chipValue; return chipValue;

View File

@ -1,54 +0,0 @@
import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldDoubleText } from '../../types/guards/isFieldDoubleText';
export const useDoubleTextField = () => {
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
assertFieldMetadata('DOUBLE_TEXT', isFieldDoubleText, fieldDefinition);
const [firstValue, setFirstValue] = useRecoilState<string>(
entityFieldsFamilySelector({
entityId: entityId,
fieldName: fieldDefinition.metadata.firstValueFieldName,
}),
);
const [secondValue, setSecondValue] = useRecoilState<string>(
entityFieldsFamilySelector({
entityId: entityId,
fieldName: fieldDefinition.metadata.secondValueFieldName,
}),
);
const fieldInitialValue = useFieldInitialValue();
const initialFirstValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value ?? firstValue;
const initialSecondValue = fieldInitialValue?.isEmpty
? ''
: fieldInitialValue?.value
? ''
: secondValue;
const fullValue = [firstValue, secondValue].filter(Boolean).join(' ');
return {
fieldDefinition,
secondValue,
setSecondValue,
firstValue,
initialFirstValue,
initialSecondValue,
setFirstValue,
fullValue,
hotkeyScope,
};
};

View File

@ -0,0 +1,51 @@
import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { FieldFullNameValue } from '../../types/FieldMetadata';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldFullName } from '../../types/guards/isFieldFullName';
import { isFieldFullNameValue } from '../../types/guards/isFieldFullNameValue';
export const useFullNameField = () => {
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
assertFieldMetadata('FULL_NAME', isFieldFullName, fieldDefinition);
const fieldName = fieldDefinition.metadata.fieldName;
const [fieldValue, setFieldValue] = useRecoilState<FieldFullNameValue>(
entityFieldsFamilySelector({
entityId: entityId,
fieldName: fieldName,
}),
);
const persistField = usePersistField();
const persistFullNameField = (newValue: FieldFullNameValue) => {
if (!isFieldFullNameValue(newValue)) {
return;
}
persistField(newValue);
};
const fieldInitialValue = useFieldInitialValue();
const initialValue: FieldFullNameValue = fieldInitialValue?.isEmpty
? { firstName: '', lastName: '' }
: fieldValue;
return {
fieldDefinition,
fieldValue,
initialValue,
setFieldValue,
hotkeyScope,
persistFullNameField,
};
};

View File

@ -1,73 +0,0 @@
import { DoubleTextInput } from '@/ui/object/field/meta-types/input/components/internal/DoubleTextInput';
import { FieldDoubleText } from '@/ui/object/field/types/FieldDoubleText';
import { usePersistField } from '../../../hooks/usePersistField';
import { useDoubleTextField } from '../../hooks/useDoubleTextField';
import { FieldInputOverlay } from './internal/FieldInputOverlay';
import { FieldInputEvent } from './DateFieldInput';
export type DoubleTextFieldInputProps = {
onClickOutside?: FieldInputEvent;
onEnter?: FieldInputEvent;
onEscape?: FieldInputEvent;
onTab?: FieldInputEvent;
onShiftTab?: FieldInputEvent;
};
export const DoubleTextFieldInput = ({
onEnter,
onEscape,
onClickOutside,
onTab,
onShiftTab,
}: DoubleTextFieldInputProps) => {
const {
fieldDefinition,
initialFirstValue,
initialSecondValue,
hotkeyScope,
} = useDoubleTextField();
const persistField = usePersistField();
const handleEnter = (newDoubleText: FieldDoubleText) => {
onEnter?.(() => persistField(newDoubleText));
};
const handleEscape = (newDoubleText: FieldDoubleText) => {
onEscape?.(() => persistField(newDoubleText));
};
const handleClickOutside = (
event: MouseEvent | TouchEvent,
newDoubleText: FieldDoubleText,
) => {
onClickOutside?.(() => persistField(newDoubleText));
};
const handleTab = (newDoubleText: FieldDoubleText) => {
onTab?.(() => persistField(newDoubleText));
};
const handleShiftTab = (newDoubleText: FieldDoubleText) => {
onShiftTab?.(() => persistField(newDoubleText));
};
return (
<FieldInputOverlay>
<DoubleTextInput
firstValue={initialFirstValue}
secondValue={initialSecondValue}
firstValuePlaceholder={fieldDefinition.metadata.firstValuePlaceholder}
secondValuePlaceholder={fieldDefinition.metadata.secondValuePlaceholder}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onShiftTab={handleShiftTab}
onTab={handleTab}
hotkeyScope={hotkeyScope}
/>
</FieldInputOverlay>
);
};

View File

@ -1,191 +0,0 @@
import { useEffect } from 'react';
import { expect, jest } from '@storybook/jest';
import { Decorator, Meta, StoryObj } from '@storybook/react';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldContextProvider } from '../../../__stories__/FieldContextProvider';
import { useDoubleTextField } from '../../../hooks/useDoubleTextField';
import {
DoubleTextFieldInput,
DoubleTextFieldInputProps,
} from '../DoubleTextFieldInput';
const DoubleTextFieldValueSetterEffect = ({
firstValue,
secondValue,
}: {
firstValue: string;
secondValue: string;
}) => {
const { setFirstValue, setSecondValue } = useDoubleTextField();
useEffect(() => {
setFirstValue(firstValue);
setSecondValue(secondValue);
}, [firstValue, secondValue, setFirstValue, setSecondValue]);
return <></>;
};
type DoubleTextFieldInputWithContextProps = DoubleTextFieldInputProps & {
firstValue: string;
secondValue: string;
entityId?: string;
};
const DoubleTextFieldInputWithContext = ({
entityId,
firstValue,
secondValue,
onClickOutside,
onEnter,
onEscape,
onTab,
onShiftTab,
}: DoubleTextFieldInputWithContextProps) => {
const setHotKeyScope = useSetHotkeyScope();
useEffect(() => {
setHotKeyScope('hotkey-scope');
}, [setHotKeyScope]);
return (
<div>
<FieldContextProvider
fieldDefinition={{
fieldMetadataId: 'double-text',
label: 'Double-Text',
type: 'DOUBLE_TEXT',
metadata: {
firstValueFieldName: 'First-text',
firstValuePlaceholder: 'First-text',
secondValueFieldName: 'Second-text',
secondValuePlaceholder: 'Second-text',
},
}}
entityId={entityId}
>
<DoubleTextFieldValueSetterEffect {...{ firstValue, secondValue }} />
<DoubleTextFieldInput
onEnter={onEnter}
onEscape={onEscape}
onClickOutside={onClickOutside}
onTab={onTab}
onShiftTab={onShiftTab}
/>
</FieldContextProvider>
<div data-testid="data-field-input-click-outside-div" />
</div>
);
};
const enterJestFn = jest.fn();
const escapeJestfn = jest.fn();
const clickOutsideJestFn = jest.fn();
const tabJestFn = jest.fn();
const shiftTabJestFn = jest.fn();
const clearMocksDecorator: Decorator = (Story, context) => {
if (context.parameters.clearMocks) {
enterJestFn.mockClear();
escapeJestfn.mockClear();
clickOutsideJestFn.mockClear();
tabJestFn.mockClear();
shiftTabJestFn.mockClear();
}
return <Story />;
};
const meta: Meta = {
title: 'UI/Data/Field/Input/DoubleTextFieldInput',
component: DoubleTextFieldInputWithContext,
args: {
firstValue: 'first value',
secondValue: 'second value',
onEnter: enterJestFn,
onEscape: escapeJestfn,
onClickOutside: clickOutsideJestFn,
onTab: tabJestFn,
onShiftTab: shiftTabJestFn,
},
argTypes: {
onEnter: { control: false },
onEscape: { control: false },
onClickOutside: { control: false },
onTab: { control: false },
onShiftTab: { control: false },
},
decorators: [clearMocksDecorator],
parameters: {
clearMocks: true,
},
};
export default meta;
type Story = StoryObj<typeof DoubleTextFieldInputWithContext>;
export const Default: Story = {};
export const Enter: Story = {
play: async () => {
expect(enterJestFn).toHaveBeenCalledTimes(0);
await waitFor(() => {
userEvent.keyboard('{enter}');
expect(enterJestFn).toHaveBeenCalledTimes(1);
});
},
};
export const Escape: Story = {
play: async () => {
expect(escapeJestfn).toHaveBeenCalledTimes(0);
await waitFor(() => {
userEvent.keyboard('{esc}');
expect(escapeJestfn).toHaveBeenCalledTimes(1);
});
},
};
export const ClickOutside: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(clickOutsideJestFn).toHaveBeenCalledTimes(0);
const emptyDiv = await canvas.findByTestId(
'data-field-input-click-outside-div',
);
await waitFor(() => {
userEvent.click(emptyDiv);
expect(clickOutsideJestFn).toHaveBeenCalledTimes(1);
});
},
};
export const Tab: Story = {
play: async () => {
expect(tabJestFn).toHaveBeenCalledTimes(0);
await waitFor(() => {
userEvent.keyboard('{tab}');
expect(tabJestFn).toHaveBeenCalledTimes(1);
});
},
};
export const ShiftTab: Story = {
play: async () => {
expect(shiftTabJestFn).toHaveBeenCalledTimes(0);
await waitFor(() => {
userEvent.keyboard('{shift>}{tab}');
expect(shiftTabJestFn).toHaveBeenCalledTimes(1);
});
},
};

View File

@ -1,5 +1,7 @@
import { selectorFamily } from 'recoil'; import { selectorFamily } from 'recoil';
import { isFieldFullName } from '@/ui/object/field/types/guards/isFieldFullName';
import { isFieldFullNameValue } from '@/ui/object/field/types/guards/isFieldFullNameValue';
import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid'; import { isFieldUuid } from '@/ui/object/field/types/guards/isFieldUuid';
import { assertNotNull } from '~/utils/assert'; import { assertNotNull } from '~/utils/assert';
@ -108,6 +110,16 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
); );
} }
if (isFieldFullName(fieldDefinition)) {
const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];
return (
!isFieldFullNameValue(fieldValue) ||
isValueEmpty(fieldValue?.firstName + fieldValue?.lastName)
);
}
if (isFieldLink(fieldDefinition)) { if (isFieldLink(fieldDefinition)) {
const fieldName = fieldDefinition.metadata.fieldName; const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName]; const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName];

View File

@ -48,6 +48,10 @@ export type FieldCurrencyMetadata = {
isPositive?: boolean; isPositive?: boolean;
}; };
export type FieldFullnameMetadata = {
fieldName: string;
};
export type FieldEmailMetadata = { export type FieldEmailMetadata = {
fieldName: string; fieldName: string;
placeHolder: string; placeHolder: string;
@ -119,6 +123,7 @@ export type FieldLinkValue = { url: string; label: string };
export type FieldNumberValue = number | null; export type FieldNumberValue = number | null;
export type FieldMoneyValue = number | null; export type FieldMoneyValue = number | null;
export type FieldCurrencyValue = { currencyCode: string; amountMicros: number }; export type FieldCurrencyValue = { currencyCode: string; amountMicros: number };
export type FieldFullNameValue = { firstName: string; lastName: string };
export type FieldEmailValue = string; export type FieldEmailValue = string;
export type FieldProbabilityValue = number; export type FieldProbabilityValue = number;

View File

@ -15,4 +15,5 @@ export type FieldType =
| 'PROBABILITY' | 'PROBABILITY'
| 'CURRENCY' | 'CURRENCY'
| 'MONEY_AMOUNT' | 'MONEY_AMOUNT'
| 'MONEY'; | 'MONEY'
| 'FULL_NAME';

View File

@ -7,6 +7,7 @@ import {
FieldDoubleTextChipMetadata, FieldDoubleTextChipMetadata,
FieldDoubleTextMetadata, FieldDoubleTextMetadata,
FieldEmailMetadata, FieldEmailMetadata,
FieldFullnameMetadata,
FieldLinkMetadata, FieldLinkMetadata,
FieldMetadata, FieldMetadata,
FieldMoneyMetadata, FieldMoneyMetadata,
@ -28,6 +29,8 @@ type AssertFieldMetadataFunction = <
? FieldChipMetadata ? FieldChipMetadata
: E extends 'CURRENCY' : E extends 'CURRENCY'
? FieldCurrencyMetadata ? FieldCurrencyMetadata
: E extends 'FULL_NAME'
? FieldFullnameMetadata
: E extends 'DATE' : E extends 'DATE'
? FieldDateMetadata ? FieldDateMetadata
: E extends 'DOUBLE_TEXT' : E extends 'DOUBLE_TEXT'

View File

@ -0,0 +1,7 @@
import { FieldDefinition } from '../FieldDefinition';
import { FieldCurrencyMetadata, FieldMetadata } from '../FieldMetadata';
export const isFieldFullName = (
field: FieldDefinition<FieldMetadata>,
): field is FieldDefinition<FieldCurrencyMetadata> =>
field.type === 'FULL_NAME';

View File

@ -0,0 +1,13 @@
import { z } from 'zod';
import { FieldFullNameValue } from '../FieldMetadata';
const currencySchema = z.object({
firstName: z.string(),
lastName: z.string(),
});
export const isFieldFullNameValue = (
fieldValue: unknown,
): fieldValue is FieldFullNameValue =>
currencySchema.safeParse(fieldValue).success;

View File

@ -6,7 +6,9 @@ import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObje
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember'; import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
export const useColorScheme = () => { export const useColorScheme = () => {
const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState); const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState(
currentWorkspaceMemberState,
);
const { updateOneObject: updateOneWorkspaceMember } = const { updateOneObject: updateOneWorkspaceMember } =
useUpdateOneObjectRecord({ useUpdateOneObjectRecord({
@ -19,6 +21,15 @@ export const useColorScheme = () => {
if (!currentWorkspaceMember) { if (!currentWorkspaceMember) {
return; return;
} }
setCurrentWorkspaceMember((current) => {
if (!current) {
return current;
}
return {
...current,
colorScheme: value,
};
});
await updateOneWorkspaceMember?.({ await updateOneWorkspaceMember?.({
idToUpdate: currentWorkspaceMember?.id, idToUpdate: currentWorkspaceMember?.id,
input: { input: {
@ -26,7 +37,11 @@ export const useColorScheme = () => {
}, },
}); });
}, },
[currentWorkspaceMember, updateOneWorkspaceMember], [
currentWorkspaceMember,
setCurrentWorkspaceMember,
updateOneWorkspaceMember,
],
); );
return { return {

View File

@ -33,25 +33,26 @@ export const UserPicker = ({
}, [initialSearchFilter, setRelationPickerSearchFilter]); }, [initialSearchFilter, setRelationPickerSearchFilter]);
const { findManyQuery } = useFindOneObjectMetadataItem({ const { findManyQuery } = useFindOneObjectMetadataItem({
objectNamePlural: 'workspaceMembersV2', objectNameSingular: 'workspaceMemberV2',
}); });
const useFindManyWorkspaceMembers = (options: any) => const useFindManyWorkspaceMembers = (options: any) =>
useQuery(findManyQuery, options); useQuery(findManyQuery, options);
const users = useFilteredSearchEntityQueryV2({ const workspaceMembers = useFilteredSearchEntityQueryV2({
queryHook: useFindManyWorkspaceMembers, queryHook: useFindManyWorkspaceMembers,
filters: [ filters: [
{ {
fieldNames: ['firstName', 'lastName'], fieldNames: ['name.firstName', 'name.lastName'],
filter: relationPickerSearchFilter, filter: relationPickerSearchFilter,
}, },
], ],
orderByField: 'firstName', orderByField: 'createdAt',
mappingFunction: (workspaceMember) => ({ mappingFunction: (workspaceMember) => ({
entityType: Entity.WorkspaceMember, entityType: Entity.WorkspaceMember,
id: workspaceMember.id, id: workspaceMember.id,
name: workspaceMember.firstName, name:
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName,
avatarType: 'rounded', avatarType: 'rounded',
avatarUrl: '', avatarUrl: '',
originalEntity: workspaceMember, originalEntity: workspaceMember,
@ -68,11 +69,11 @@ export const UserPicker = ({
<SingleEntitySelect <SingleEntitySelect
EmptyIcon={IconUserCircle} EmptyIcon={IconUserCircle}
emptyLabel="No Owner" emptyLabel="No Owner"
entitiesToSelect={users.entitiesToSelect} entitiesToSelect={workspaceMembers.entitiesToSelect}
loading={users.loading} loading={workspaceMembers.loading}
onCancel={onCancel} onCancel={onCancel}
onEntitySelected={handleEntitySelected} onEntitySelected={handleEntitySelected}
selectedEntity={users.selectedEntities[0]} selectedEntity={workspaceMembers.selectedEntities[0]}
width={width} width={width}
/> />
); );

View File

@ -67,7 +67,7 @@ export const ViewBarEffect = () => {
useFindManyObjectRecords({ useFindManyObjectRecords({
skip: !currentViewId, skip: !currentViewId,
objectNamePlural: 'viewFieldsV2', objectNamePlural: 'viewFieldsV2',
filter: { view: { eq: currentViewId } }, filter: { viewId: { eq: currentViewId } },
onCompleted: useRecoilCallback( onCompleted: useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<ViewField>) => { async (data: PaginatedObjectTypeResults<ViewField>) => {

View File

@ -41,7 +41,7 @@ export const useViewFields = (viewScopeId: string) => {
variables: { variables: {
input: { input: {
fieldMetadataId: viewField.fieldMetadataId, fieldMetadataId: viewField.fieldMetadataId,
view: viewIdToPersist, viewId: viewIdToPersist,
isVisible: viewField.isVisible, isVisible: viewField.isVisible,
size: viewField.size, size: viewField.size,
position: viewField.position, position: viewField.position,

View File

@ -2,8 +2,10 @@ export type ColorScheme = 'Dark' | 'Light' | 'System';
export type WorkspaceMember = { export type WorkspaceMember = {
id: string; id: string;
firstName: string; name: {
lastName: string; firstName: string;
lastName: string;
};
avatarUrl: string | null; avatarUrl: string | null;
locale: string; locale: string;
colorScheme: ColorScheme; colorScheme: ColorScheme;

View File

@ -41,16 +41,18 @@ export const WorkspaceMemberCard = ({
<Avatar <Avatar
avatarUrl={workspaceMember.avatarUrl} avatarUrl={workspaceMember.avatarUrl}
colorId={workspaceMember.id} colorId={workspaceMember.id}
placeholder={workspaceMember.firstName || ''} placeholder={workspaceMember.name.firstName || ''}
type="squared" type="squared"
size="xl" size="xl"
/> />
<StyledContent> <StyledContent>
<OverflowingTextWithTooltip <OverflowingTextWithTooltip
text={workspaceMember.firstName + ' ' + workspaceMember.lastName} text={
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName
}
/> />
<StyledEmailText> <StyledEmailText>
{workspaceMember.firstName + ' ' + workspaceMember.lastName} {workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName}
</StyledEmailText> </StyledEmailText>
</StyledContent> </StyledContent>

View File

@ -73,8 +73,8 @@ export const CreateProfile = () => {
} = useForm<Form>({ } = useForm<Form>({
mode: 'onChange', mode: 'onChange',
defaultValues: { defaultValues: {
firstName: currentWorkspaceMember?.firstName ?? '', firstName: currentWorkspaceMember?.name.firstName ?? '',
lastName: currentWorkspaceMember?.lastName ?? '', lastName: currentWorkspaceMember?.name.lastName ?? '',
}, },
resolver: zodResolver(validationSchema), resolver: zodResolver(validationSchema),
}); });
@ -95,8 +95,10 @@ export const CreateProfile = () => {
await updateOneObject({ await updateOneObject({
idToUpdate: currentWorkspaceMember?.id, idToUpdate: currentWorkspaceMember?.id,
input: { input: {
firstName: data.firstName, name: {
lastName: data.lastName, firstName: data.firstName,
lastName: data.lastName,
},
}, },
}); });
@ -104,8 +106,10 @@ export const CreateProfile = () => {
(current) => (current) =>
({ ({
...current, ...current,
firstName: data.firstName, name: {
lastName: data.lastName, firstName: data.firstName,
lastName: data.lastName,
},
} as any), } as any),
); );

View File

@ -3,10 +3,12 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useSetRecoilState } from 'recoil';
import { z } from 'zod'; import { z } from 'zod';
import { SubTitle } from '@/auth/components/SubTitle'; import { SubTitle } from '@/auth/components/SubTitle';
import { Title } from '@/auth/components/Title'; import { Title } from '@/auth/components/Title';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { H2Title } from '@/ui/display/typography/components/H2Title'; import { H2Title } from '@/ui/display/typography/components/H2Title';
@ -41,6 +43,7 @@ export const CreateWorkspace = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { enqueueSnackBar } = useSnackBar(); const { enqueueSnackBar } = useSnackBar();
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
const [updateWorkspace] = useUpdateWorkspaceMutation(); const [updateWorkspace] = useUpdateWorkspaceMutation();
@ -68,6 +71,10 @@ export const CreateWorkspace = () => {
}, },
}, },
}); });
setCurrentWorkspace({
id: result.data?.updateWorkspace?.id ?? '',
displayName: data.name,
});
if (result.errors || !result.data?.updateWorkspace) { if (result.errors || !result.data?.updateWorkspace) {
throw result.errors ?? new Error('Unknown error'); throw result.errors ?? new Error('Unknown error');
@ -82,7 +89,7 @@ export const CreateWorkspace = () => {
}); });
} }
}, },
[enqueueSnackBar, navigate, updateWorkspace], [enqueueSnackBar, navigate, setCurrentWorkspace, updateWorkspace],
); );
useScopedHotkeys( useScopedHotkeys(

View File

@ -44,7 +44,9 @@ export const SettingsDevelopersApiKeys = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [apiKeys, setApiKeys] = useState<Array<ApiFieldItem>>([]); const [apiKeys, setApiKeys] = useState<Array<ApiFieldItem>>([]);
const { registerOptimisticEffect } = useOptimisticEffect('apiKeyV2'); const { registerOptimisticEffect } = useOptimisticEffect({
objectNameSingular: 'apiKeyV2',
});
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({ const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNameSingular: 'apiKeyV2', objectNameSingular: 'apiKeyV2',
}); });

View File

@ -22,9 +22,9 @@ export const TasksEffect = () => {
value: currentWorkspaceMember.id, value: currentWorkspaceMember.id,
operand: ViewFilterOperand.Is, operand: ViewFilterOperand.Is,
displayValue: displayValue:
currentWorkspaceMember.firstName + currentWorkspaceMember.name.firstName +
' ' + ' ' +
currentWorkspaceMember.lastName, currentWorkspaceMember.name.lastName,
displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined, displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined,
definition: tasksFilterDefinitions[0], definition: tasksFilterDefinitions[0],
}); });

View File

@ -17,11 +17,8 @@ type MockedActivity = Pick<
| 'dueAt' | 'dueAt'
| 'completedAt' | 'completedAt'
> & { > & {
author: Pick<WorkspaceMember, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>; author: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
assignee: Pick< assignee: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
WorkspaceMember,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
>;
comments: Comment[]; comments: Comment[];
activityTargets: Array< activityTargets: Array<
Pick< Pick<
@ -53,14 +50,18 @@ export const mockedTasks: Array<MockedActivity> = [
completedAt: null, completedAt: null,
author: { author: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles', name: {
lastName: 'Test', firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: '', avatarUrl: '',
}, },
assignee: { assignee: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles', name: {
lastName: 'Test', firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: '', avatarUrl: '',
}, },
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
@ -82,14 +83,18 @@ export const mockedActivities: Array<MockedActivity> = [
completedAt: null, completedAt: null,
author: { author: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles', name: {
lastName: 'Test', firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: '', avatarUrl: '',
}, },
assignee: { assignee: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles', name: {
lastName: 'Test', firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: '', avatarUrl: '',
}, },
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
@ -149,14 +154,18 @@ export const mockedActivities: Array<MockedActivity> = [
dueAt: '2029-08-26T10:12:42.33625+00:00', dueAt: '2029-08-26T10:12:42.33625+00:00',
author: { author: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles', name: {
lastName: 'Test', firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: '', avatarUrl: '',
}, },
assignee: { assignee: {
id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
firstName: 'Charles', name: {
lastName: 'Test', firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: '', avatarUrl: '',
}, },
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',

View File

@ -15,7 +15,7 @@ export enum SeedPersonFieldMetadataIds {
Email = '20202020-8a96-4e4b-86fd-ea126530e0c1', Email = '20202020-8a96-4e4b-86fd-ea126530e0c1',
LinkedinLink = '20202020-dcf6-445a-b543-37e55de43c25', LinkedinLink = '20202020-dcf6-445a-b543-37e55de43c25',
XUrl = '20202020-a3a7-4f63-9303-10226f6055be', XLink = '20202020-a3a7-4f63-9303-10226f6055be',
JobTitle = '20202020-3b86-413e-ab56-0ebd1a583ff3', JobTitle = '20202020-3b86-413e-ab56-0ebd1a583ff3',
Phone = '20202020-486f-45f9-bbdf-aac18b1831c0', Phone = '20202020-486f-45f9-bbdf-aac18b1831c0',
City = '20202020-78f8-4b4c-90ff-86adf77590f5', City = '20202020-78f8-4b4c-90ff-86adf77590f5',
@ -170,7 +170,7 @@ export const seedPersonFieldMetadata = async (
defaultValue: undefined, defaultValue: undefined,
}, },
{ {
id: SeedPersonFieldMetadataIds.XUrl, id: SeedPersonFieldMetadataIds.XLink,
objectMetadataId: SeedObjectMetadataIds.Person, objectMetadataId: SeedObjectMetadataIds.Person,
isCustom: false, isCustom: false,
workspaceId: SeedWorkspaceId, workspaceId: SeedWorkspaceId,

View File

@ -16,6 +16,7 @@ export enum SeedViewFieldFieldMetadataIds {
Size = '20202020-b9a1-4c2e-a5af-3a6b4fef4af6', Size = '20202020-b9a1-4c2e-a5af-3a6b4fef4af6',
Position = '20202020-a4bb-440a-add2-81dbd9a74517', Position = '20202020-a4bb-440a-add2-81dbd9a74517',
View = '20202020-8788-4508-b771-719807b60e61', View = '20202020-8788-4508-b771-719807b60e61',
ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6a',
} }
export const seedViewFieldFieldMetadata = async ( export const seedViewFieldFieldMetadata = async (
@ -126,13 +127,29 @@ export const seedViewFieldFieldMetadata = async (
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: 'view', name: 'view',
label: 'View Id', label: 'View Id',
targetColumnMap: { value: 'viewId' }, targetColumnMap: {},
description: 'View Field related view', description: 'View Field related view',
icon: 'IconLayoutCollage', icon: 'IconLayoutCollage',
isNullable: false, isNullable: true,
isSystem: false, isSystem: false,
defaultValue: undefined, defaultValue: undefined,
}, },
{
id: SeedViewFieldFieldMetadataIds.ViewForeignKey,
objectMetadataId: SeedObjectMetadataIds.ViewField,
isCustom: false,
workspaceId: SeedWorkspaceId,
isActive: true,
type: FieldMetadataType.UUID,
name: 'viewId',
label: 'View ID (foreign key)',
targetColumnMap: {},
description: 'Foreign key for view',
icon: undefined,
isNullable: false,
isSystem: true,
defaultValue: undefined,
},
{ {
id: SeedViewFieldFieldMetadataIds.IsVisible, id: SeedViewFieldFieldMetadataIds.IsVisible,
objectMetadataId: SeedObjectMetadataIds.ViewField, objectMetadataId: SeedObjectMetadataIds.ViewField,

View File

@ -13,6 +13,7 @@ export enum SeedViewFilterFieldMetadataIds {
FieldMetadataId = '20202020-78bb-4f2b-a052-260bc8efd694', FieldMetadataId = '20202020-78bb-4f2b-a052-260bc8efd694',
View = '20202020-65e5-4082-829d-8c634c20e7b5', View = '20202020-65e5-4082-829d-8c634c20e7b5',
ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6b',
Operand = '20202020-1d12-465d-ab2c-8af008182730', Operand = '20202020-1d12-465d-ab2c-8af008182730',
Value = '20202020-8b37-46ae-86b8-14287ec06802', Value = '20202020-8b37-46ae-86b8-14287ec06802',
DisplayValue = '20202020-ed89-4892-83fa-d2b2929c6d52', DisplayValue = '20202020-ed89-4892-83fa-d2b2929c6d52',
@ -126,15 +127,29 @@ export const seedViewFilterFieldMetadata = async (
type: FieldMetadataType.TEXT, type: FieldMetadataType.TEXT,
name: 'viewId', name: 'viewId',
label: 'View Id', label: 'View Id',
targetColumnMap: { targetColumnMap: {},
value: 'viewId',
},
description: 'View Filter related view', description: 'View Filter related view',
icon: 'IconLayoutCollage', icon: 'IconLayoutCollage',
isNullable: false, isNullable: false,
isSystem: false, isSystem: false,
defaultValue: undefined, defaultValue: undefined,
}, },
{
id: SeedViewFilterFieldMetadataIds.ViewForeignKey,
objectMetadataId: SeedObjectMetadataIds.ViewField,
isCustom: false,
workspaceId: SeedWorkspaceId,
isActive: true,
type: FieldMetadataType.UUID,
name: 'viewId',
label: 'View ID (foreign key)',
targetColumnMap: {},
description: 'Foreign key for view',
icon: undefined,
isNullable: false,
isSystem: true,
defaultValue: undefined,
},
{ {
id: SeedViewFilterFieldMetadataIds.Operand, id: SeedViewFilterFieldMetadataIds.Operand,
objectMetadataId: SeedObjectMetadataIds.ViewFilter, objectMetadataId: SeedObjectMetadataIds.ViewFilter,

View File

@ -13,6 +13,7 @@ export enum SeedViewSortFieldMetadataIds {
FieldMetadataId = '20202020-cb2c-4c8f-a289-c9851b23d064', FieldMetadataId = '20202020-cb2c-4c8f-a289-c9851b23d064',
View = '20202020-f5d0-467f-a3d8-395ba16b8ebf', View = '20202020-f5d0-467f-a3d8-395ba16b8ebf',
ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6c',
Direction = '20202020-077e-4451-b1d8-e602c956ebd2', Direction = '20202020-077e-4451-b1d8-e602c956ebd2',
} }
@ -124,15 +125,29 @@ export const seedViewSortFieldMetadata = async (
type: FieldMetadataType.TEXT, type: FieldMetadataType.TEXT,
name: 'viewId', name: 'viewId',
label: 'View Id', label: 'View Id',
targetColumnMap: { targetColumnMap: {},
value: 'viewId',
},
description: 'View Sort related view', description: 'View Sort related view',
icon: 'IconLayoutCollage', icon: 'IconLayoutCollage',
isNullable: false, isNullable: false,
isSystem: false, isSystem: false,
defaultValue: undefined, defaultValue: undefined,
}, },
{
id: SeedViewSortFieldMetadataIds.ViewForeignKey,
objectMetadataId: SeedObjectMetadataIds.ViewField,
isCustom: false,
workspaceId: SeedWorkspaceId,
isActive: true,
type: FieldMetadataType.UUID,
name: 'viewId',
label: 'View ID (foreign key)',
targetColumnMap: {},
description: 'Foreign key for view',
icon: undefined,
isNullable: false,
isSystem: true,
defaultValue: undefined,
},
{ {
id: SeedViewSortFieldMetadataIds.Direction, id: SeedViewSortFieldMetadataIds.Direction,
objectMetadataId: SeedObjectMetadataIds.ViewSort, objectMetadataId: SeedObjectMetadataIds.ViewSort,

View File

@ -3,7 +3,6 @@ import { DataSource } from 'typeorm';
import { SeedObjectMetadataIds } from 'src/database/typeorm-seeds/metadata/object-metadata'; import { SeedObjectMetadataIds } from 'src/database/typeorm-seeds/metadata/object-metadata';
import { SeedWorkspaceId } from 'src/database/seeds/metadata'; import { SeedWorkspaceId } from 'src/database/seeds/metadata';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
import { SeedPersonFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/person';
const fieldMetadataTableName = 'fieldMetadata'; const fieldMetadataTableName = 'fieldMetadata';
@ -12,8 +11,7 @@ export enum SeedWorkspaceMemberFieldMetadataIds {
CreatedAt = '20202020-1cbf-4b32-8c33-fbfedcd9afa8', CreatedAt = '20202020-1cbf-4b32-8c33-fbfedcd9afa8',
UpdatedAt = '20202020-1ba3-4c24-b2cd-b0789633e8d4', UpdatedAt = '20202020-1ba3-4c24-b2cd-b0789633e8d4',
FirstName = '20202020-1fa8-4d38-9fa4-0cf696909298', Name = '20202020-8c37-4163-ba06-1dada334ce3e',
LastName = '20202020-8c37-4163-ba06-1dada334ce3e',
AvatarUrl = '20202020-7ba6-40d5-934b-17146183a212', AvatarUrl = '20202020-7ba6-40d5-934b-17146183a212',
Locale = '20202020-10f6-4df9-8d6f-a760b65bd800', Locale = '20202020-10f6-4df9-8d6f-a760b65bd800',
ColorScheme = '20202020-83f2-4c5f-96b0-0c51ecc160e3', ColorScheme = '20202020-83f2-4c5f-96b0-0c51ecc160e3',
@ -110,7 +108,7 @@ export const seedWorkspaceMemberFieldMetadata = async (
}, },
// Scalar fields // Scalar fields
{ {
id: SeedPersonFieldMetadataIds.Name, id: SeedWorkspaceMemberFieldMetadataIds.Name,
objectMetadataId: SeedObjectMetadataIds.WorkspaceMember, objectMetadataId: SeedObjectMetadataIds.WorkspaceMember,
isCustom: false, isCustom: false,
workspaceId: SeedWorkspaceId, workspaceId: SeedWorkspaceId,

View File

@ -1,6 +1,9 @@
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { SeedViewIds } from 'src/database/typeorm-seeds/workspace/views'; import { SeedViewIds } from 'src/database/typeorm-seeds/workspace/views';
import { SeedCompanyFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/company';
import { SeedPersonFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/person';
import { SeedOpportunityFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/opportunity';
const tableName = 'viewField'; const tableName = 'viewField';
@ -21,112 +24,112 @@ export const seedViewFields = async (
.orIgnore() .orIgnore()
.values([ .values([
{ {
fieldMetadataId: 'name', fieldMetadataId: SeedCompanyFieldMetadataIds.Name,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 0, position: 0,
isVisible: true, isVisible: true,
size: 180, size: 180,
}, },
{ {
fieldMetadataId: 'domainName', fieldMetadataId: SeedCompanyFieldMetadataIds.DomainName,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 1, position: 1,
isVisible: true, isVisible: true,
size: 100, size: 100,
}, },
{ {
fieldMetadataId: 'accountOwner', fieldMetadataId: SeedCompanyFieldMetadataIds.AccountOwner,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 2, position: 2,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'createdAt', fieldMetadataId: SeedCompanyFieldMetadataIds.CreatedAt,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 3, position: 3,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'employees', fieldMetadataId: SeedCompanyFieldMetadataIds.Employees,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 4, position: 4,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'linkedin', fieldMetadataId: SeedCompanyFieldMetadataIds.LinkedinLink,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 5, position: 5,
isVisible: true, isVisible: true,
size: 170, size: 170,
}, },
{ {
fieldMetadataId: 'address', fieldMetadataId: SeedCompanyFieldMetadataIds.Address,
viewId: SeedViewIds.Company, viewId: SeedViewIds.Company,
position: 6, position: 6,
isVisible: true, isVisible: true,
size: 170, size: 170,
}, },
{ {
fieldMetadataId: 'displayName', fieldMetadataId: SeedPersonFieldMetadataIds.Name,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 0, position: 0,
isVisible: true, isVisible: true,
size: 210, size: 210,
}, },
{ {
fieldMetadataId: 'email', fieldMetadataId: SeedPersonFieldMetadataIds.Email,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 1, position: 1,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'company', fieldMetadataId: SeedPersonFieldMetadataIds.Company,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 2, position: 2,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'phone', fieldMetadataId: SeedPersonFieldMetadataIds.Phone,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 3, position: 3,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'createdAt', fieldMetadataId: SeedPersonFieldMetadataIds.CreatedAt,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 4, position: 4,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'city', fieldMetadataId: SeedPersonFieldMetadataIds.City,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 5, position: 5,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'jobTitle', fieldMetadataId: SeedPersonFieldMetadataIds.JobTitle,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 6, position: 6,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'linkedin', fieldMetadataId: SeedPersonFieldMetadataIds.LinkedinLink,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 7, position: 7,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'x', fieldMetadataId: SeedPersonFieldMetadataIds.XLink,
viewId: SeedViewIds.Person, viewId: SeedViewIds.Person,
position: 8, position: 8,
isVisible: true, isVisible: true,
@ -134,28 +137,28 @@ export const seedViewFields = async (
}, },
{ {
fieldMetadataId: 'amount', fieldMetadataId: SeedOpportunityFieldMetadataIds.Amount,
viewId: SeedViewIds.Opportunity, viewId: SeedViewIds.Opportunity,
position: 0, position: 0,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'closeDate', fieldMetadataId: SeedOpportunityFieldMetadataIds.CloseDate,
viewId: SeedViewIds.Opportunity, viewId: SeedViewIds.Opportunity,
position: 1, position: 1,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'probability', fieldMetadataId: SeedOpportunityFieldMetadataIds.Probability,
viewId: SeedViewIds.Opportunity, viewId: SeedViewIds.Opportunity,
position: 2, position: 2,
isVisible: true, isVisible: true,
size: 150, size: 150,
}, },
{ {
fieldMetadataId: 'pointOfContact', fieldMetadataId: SeedOpportunityFieldMetadataIds.PointOfContact,
viewId: SeedViewIds.Opportunity, viewId: SeedViewIds.Opportunity,
position: 3, position: 3,
isVisible: true, isVisible: true,

View File

@ -1,33 +0,0 @@
import { EntityManager } from 'typeorm';
export const opportunityPrefillData = async (
entityManager: EntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.opportunity`, [
'amount',
'closeDate',
'probability',
'pipelineStepId',
'pointOfContactId',
'personId',
'companyId',
])
.orIgnore()
.values([
{
amount: 100000,
closeDate: new Date(),
probability: 0.5,
pipelineStepId: '73ac09c6-2b90-4874-9e5d-553ea76912ee',
pointOfContactId: 'bb757987-ae38-4d16-96ec-b25b595e7bd8',
personId: 'a4a2b8e9-7a2b-4b6a-8c8b-7e9a0a0a0a0a',
companyId: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
},
])
.returning('*')
.execute();
};

View File

@ -5,7 +5,6 @@ import { viewPrefillData } from 'src/workspace/workspace-manager/standard-object
import { companyPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/company'; import { companyPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/company';
import { personPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/person'; import { personPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/person';
import { pipelineStepPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/pipeline-step'; import { pipelineStepPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/pipeline-step';
import { opportunityPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/opportunity';
export const standardObjectsPrefillData = async ( export const standardObjectsPrefillData = async (
workspaceDataSource: DataSource, workspaceDataSource: DataSource,
@ -28,6 +27,5 @@ export const standardObjectsPrefillData = async (
await personPrefillData(entityManager, schemaName); await personPrefillData(entityManager, schemaName);
await viewPrefillData(entityManager, schemaName, objectMetadataMap); await viewPrefillData(entityManager, schemaName, objectMetadataMap);
await pipelineStepPrefillData(entityManager, schemaName); await pipelineStepPrefillData(entityManager, schemaName);
await opportunityPrefillData(entityManager, schemaName);
}); });
}; };

View File

@ -125,12 +125,23 @@ const personMetadata = {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: 'company', name: 'company',
label: 'Company', label: 'Company',
targetColumnMap: { targetColumnMap: {},
value: 'companyId',
},
description: 'Contacts company', description: 'Contacts company',
icon: 'IconBuildingSkyscraper', icon: 'IconBuildingSkyscraper',
isNullable: false, isNullable: false,
isSystem: false,
},
{
isCustom: false,
isActive: true,
type: FieldMetadataType.UUID,
name: 'companyId',
label: 'Company ID (foreign key)',
targetColumnMap: {},
description: 'Foreign key for company',
icon: undefined,
isNullable: false,
isSystem: true,
}, },
{ {
isCustom: false, isCustom: false,

View File

@ -73,16 +73,15 @@ const viewFieldMetadata = {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: 'view', name: 'view',
label: 'View', label: 'View',
targetColumnMap: { value: 'viewId' }, targetColumnMap: {},
description: 'View Field related view', description: 'View Field related view',
icon: 'IconLayoutCollage', icon: 'IconLayoutCollage',
isNullable: false, isNullable: true,
}, },
// Temporary hack?
{ {
isCustom: false, isCustom: false,
isActive: true, isActive: true,
type: FieldMetadataType.TEXT, type: FieldMetadataType.UUID,
name: 'viewId', name: 'viewId',
label: 'View Id', label: 'View Id',
targetColumnMap: { targetColumnMap: {

View File

@ -73,16 +73,15 @@ const viewFilterMetadata = {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: 'view', name: 'view',
label: 'View', label: 'View',
targetColumnMap: { value: 'viewId' }, targetColumnMap: {},
description: 'View Filter related view', description: 'View Filter related view',
icon: 'IconLayoutCollage', icon: 'IconLayoutCollage',
isNullable: false, isNullable: true,
}, },
// Temporary hack?
{ {
isCustom: false, isCustom: false,
isActive: true, isActive: true,
type: FieldMetadataType.TEXT, type: FieldMetadataType.UUID,
name: 'viewId', name: 'viewId',
label: 'View Id', label: 'View Id',
targetColumnMap: { targetColumnMap: {

View File

@ -45,18 +45,15 @@ const viewSortMetadata = {
type: FieldMetadataType.RELATION, type: FieldMetadataType.RELATION,
name: 'view', name: 'view',
label: 'View', label: 'View',
targetColumnMap: { targetColumnMap: {},
value: 'viewId',
},
description: 'View Sort related view', description: 'View Sort related view',
icon: 'IconLayoutCollage', icon: 'IconLayoutCollage',
isNullable: false, isNullable: true,
}, },
// Temporary Hack?
{ {
isCustom: false, isCustom: false,
isActive: true, isActive: true,
type: FieldMetadataType.TEXT, type: FieldMetadataType.UUID,
name: 'viewId', name: 'viewId',
label: 'View Id', label: 'View Id',
targetColumnMap: { targetColumnMap: {

View File

@ -11,19 +11,6 @@ const workspaceMemberMetadata = {
isActive: true, isActive: true,
isSystem: true, isSystem: true,
fields: [ fields: [
{
isCustom: false,
isActive: true,
type: FieldMetadataType.TEXT,
name: 'firstName',
label: 'First name',
targetColumnMap: {
value: 'firstName',
},
description: 'Workspace member first name',
icon: 'IconCircleUser',
isNullable: false,
},
{ {
isCustom: false, isCustom: false,
isActive: true, isActive: true,
@ -93,6 +80,20 @@ const workspaceMemberMetadata = {
isNullable: true, isNullable: true,
isSystem: false, isSystem: false,
}, },
{
isCustom: false,
isActive: true,
type: FieldMetadataType.UUID,
name: 'userId',
label: 'User Id',
targetColumnMap: {
value: 'userId',
},
description: 'Associated User Id',
icon: 'IconCircleUsers',
isNullable: false,
isSystem: false,
},
// Relations // Relations
{ {
isCustom: false, isCustom: false,

View File

@ -81,23 +81,21 @@ export class WorkspaceManagerService {
workspaceId: string, workspaceId: string,
): Promise<ObjectMetadataEntity[]> { ): Promise<ObjectMetadataEntity[]> {
const createdObjectMetadata = await this.objectMetadataService.createMany( const createdObjectMetadata = await this.objectMetadataService.createMany(
Object.values(standardObjectsMetadata).map( Object.values(standardObjectsMetadata).map((objectMetadata: any) => ({
(objectMetadata: ObjectMetadataEntity) => ({ ...objectMetadata,
...objectMetadata, dataSourceId,
dataSourceId, workspaceId,
workspaceId, isCustom: false,
isCustom: false, isActive: true,
isActive: true, fields: [...basicFieldsMetadata, ...objectMetadata.fields].map(
fields: [...basicFieldsMetadata, ...objectMetadata.fields].map( (field) => ({
(field) => ({ ...field,
...field, workspaceId,
workspaceId, isCustom: false,
isCustom: false, isActive: true,
isActive: true, }),
}), ),
), })),
}),
),
); );
await this.relationMetadataService.createMany( await this.relationMetadataService.createMany(