diff --git a/front/src/generated-metadata/graphql.ts b/front/src/generated-metadata/graphql.ts index 6916048e9f..1c6f7db377 100644 --- a/front/src/generated-metadata/graphql.ts +++ b/front/src/generated-metadata/graphql.ts @@ -415,6 +415,11 @@ export type DeleteOneObjectInput = { id: Scalars['ID']['input']; }; +export type DeleteOneRelationInput = { + /** The id of the record to delete. */ + id: Scalars['ID']['input']; +}; + export type Favorite = { __typename?: 'Favorite'; company?: Maybe; @@ -463,6 +468,7 @@ export enum FieldMetadataType { Date = 'DATE', Email = 'EMAIL', Enum = 'ENUM', + FullName = 'FULL_NAME', Link = 'LINK', Number = 'NUMBER', Phone = 'PHONE', @@ -496,6 +502,7 @@ export type Mutation = { createOneRelation: Relation; deleteOneField: FieldDeleteResponse; deleteOneObject: ObjectDeleteResponse; + deleteOneRelation: RelationDeleteResponse; updateOneField: Field; updateOneObject: Object; }; @@ -526,6 +533,11 @@ export type MutationDeleteOneObjectArgs = { }; +export type MutationDeleteOneRelationArgs = { + input: DeleteOneRelationInput; +}; + + export type MutationUpdateOneFieldArgs = { input: UpdateOneFieldInput; }; @@ -711,6 +723,18 @@ export type RelationConnection = { totalCount: Scalars['Int']['output']; }; +export type RelationDeleteResponse = { + __typename?: 'RelationDeleteResponse'; + createdAt?: Maybe; + fromFieldMetadataId?: Maybe; + fromObjectMetadataId?: Maybe; + id?: Maybe; + relationType?: Maybe; + toFieldMetadataId?: Maybe; + toObjectMetadataId?: Maybe; + updatedAt?: Maybe; +}; + /** Type of the relation */ export enum RelationMetadataType { ManyToMany = 'MANY_TO_MANY', diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index 29a89919fc..0096dd770f 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -1336,6 +1336,7 @@ export enum FieldMetadataType { Date = 'DATE', Email = 'EMAIL', Enum = 'ENUM', + FullName = 'FULL_NAME', Link = 'LINK', Number = 'NUMBER', Phone = 'PHONE', @@ -1443,6 +1444,7 @@ export type Mutation = { deleteOneField: FieldDeleteResponse; deleteOneObject: ObjectDeleteResponse; deleteOnePipelineStage: PipelineStage; + deleteOneRelation: RelationDeleteResponse; deleteOneWebHook: WebHook; deleteUserAccount: User; deleteUserV2: UserV2; @@ -2626,6 +2628,18 @@ export type RelationConnection = { totalCount: Scalars['Int']; }; +export type RelationDeleteResponse = { + __typename?: 'RelationDeleteResponse'; + createdAt?: Maybe; + fromFieldMetadataId?: Maybe; + fromObjectMetadataId?: Maybe; + id?: Maybe; + relationType?: Maybe; + toFieldMetadataId?: Maybe; + toObjectMetadataId?: Maybe; + updatedAt?: Maybe; +}; + /** Type of the relation */ export enum RelationMetadataType { ManyToMany = 'MANY_TO_MANY', diff --git a/front/src/modules/activities/comment/CommentHeader.tsx b/front/src/modules/activities/comment/CommentHeader.tsx index 25d7326836..2b2a09c75a 100644 --- a/front/src/modules/activities/comment/CommentHeader.tsx +++ b/front/src/modules/activities/comment/CommentHeader.tsx @@ -67,7 +67,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => { const showDate = beautifiedCreatedAt !== ''; const author = comment.author; - const authorName = author.firstName + ' ' + author.lastName; + const authorName = author.name.firstName + ' ' + author.name.lastName; const avatarUrl = author.avatarUrl; const commentId = comment.id; diff --git a/front/src/modules/activities/comment/__stories__/mock-comment.ts b/front/src/modules/activities/comment/__stories__/mock-comment.ts index 1602346c6a..09575fb515 100644 --- a/front/src/modules/activities/comment/__stories__/mock-comment.ts +++ b/front/src/modules/activities/comment/__stories__/mock-comment.ts @@ -10,8 +10,10 @@ export const mockComment: Pick< body: 'Hello, this is a comment.', author: { id: 'fake_comment_1_author_uuid', - firstName: 'Jony' ?? '', - lastName: 'Ive' ?? '', + name: { + firstName: 'Jony' ?? '', + lastName: 'Ive' ?? '', + }, avatarUrl: null, }, 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.', author: { id: 'fake_comment_1_author_uuid', - firstName: 'Jony' ?? '', - lastName: 'Ive' ?? '', + name: { + firstName: 'Jony' ?? '', + lastName: 'Ive' ?? '', + }, avatarUrl: null, }, createdAt: DateTime.fromFormat('2021-03-12', 'yyyy-MM-dd').toISO() ?? '', diff --git a/front/src/modules/activities/components/ActivityAssigneePicker.tsx b/front/src/modules/activities/components/ActivityAssigneePicker.tsx index a7a2abd568..55cf3c3bd3 100644 --- a/front/src/modules/activities/components/ActivityAssigneePicker.tsx +++ b/front/src/modules/activities/components/ActivityAssigneePicker.tsx @@ -14,10 +14,7 @@ import { export type ActivityAssigneePickerProps = { activity: Pick & { - accountOwner?: Pick< - WorkspaceMember, - 'id' | 'firstName' | 'lastName' - > | null; + accountOwner?: Pick | null; }; onSubmit?: () => void; onCancel?: () => void; diff --git a/front/src/modules/activities/components/ActivityEditor.tsx b/front/src/modules/activities/components/ActivityEditor.tsx index b22cc30458..5deb9559cd 100644 --- a/front/src/modules/activities/components/ActivityEditor.tsx +++ b/front/src/modules/activities/components/ActivityEditor.tsx @@ -60,10 +60,7 @@ type ActivityEditorProps = { > & { comments?: Array | null; } & { - assignee?: Pick< - WorkspaceMember, - 'id' | 'firstName' | 'lastName' | 'avatarUrl' - > | null; + assignee?: Pick | null; } & { activityTargets?: Array< Pick diff --git a/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx b/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx index e709e004b9..238c38751b 100644 --- a/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx +++ b/front/src/modules/activities/editable-fields/components/ActivityAssigneeEditableField.tsx @@ -13,10 +13,7 @@ import { Company, User } from '~/generated/graphql'; type ActivityAssigneeEditableFieldProps = { activity: Pick & { - assignee?: Pick< - WorkspaceMember, - 'id' | 'firstName' | 'lastName' | 'avatarUrl' - > | null; + assignee?: Pick | null; }; }; diff --git a/front/src/modules/activities/timeline/components/TimelineActivity.tsx b/front/src/modules/activities/timeline/components/TimelineActivity.tsx index 2afce9bb18..b3c6fec578 100644 --- a/front/src/modules/activities/timeline/components/TimelineActivity.tsx +++ b/front/src/modules/activities/timeline/components/TimelineActivity.tsx @@ -7,6 +7,7 @@ import { Activity } from '@/activities/types/Activity'; import { IconNotes } from '@/ui/display/icon'; import { OverflowingTextWithTooltip } from '@/ui/display/tooltip/OverflowingTextWithTooltip'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; +import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { beautifyExactDateTime, beautifyPastDateRelativeToNow, @@ -127,11 +128,8 @@ type TimelineActivityProps = { | 'type' | 'comments' | 'dueAt' - > & { author: Pick } & { - assignee?: Pick< - Activity['author'], - 'id' | 'firstName' | 'lastName' | 'avatarUrl' - > | null; + > & { author: Pick } & { + assignee?: Pick | null; }; }; @@ -151,7 +149,9 @@ export const TimelineActivity = ({ activity }: TimelineActivityProps) => { - {activity.author.firstName + ' ' + activity.author.lastName} + {activity.author.name.firstName + + ' ' + + activity.author.name.lastName} created a {activity.type.toLowerCase()} diff --git a/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx b/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx index 20835c508b..1e5027348a 100644 --- a/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx +++ b/front/src/modules/activities/timeline/components/TimelineActivityCardFooter.tsx @@ -9,10 +9,7 @@ import { beautifyExactDate } from '~/utils/date-utils'; type TimelineActivityCardFooterProps = { activity: Pick & { - assignee?: Pick< - WorkspaceMember, - 'id' | 'firstName' | 'lastName' | 'avatarUrl' - > | null; + assignee?: Pick | null; }; }; @@ -48,9 +45,9 @@ export const TimelineActivityCardFooter = ({ diff --git a/front/src/modules/activities/types/Activity.ts b/front/src/modules/activities/types/Activity.ts index 1d75bae3cb..da3da72ebc 100644 --- a/front/src/modules/activities/types/Activity.ts +++ b/front/src/modules/activities/types/Activity.ts @@ -15,12 +15,9 @@ export type Activity = { type: ActivityType; title: string; body: string; - author: Pick; + author: Pick; authorId: string; - assignee: Pick< - WorkspaceMember, - 'id' | 'firstName' | 'lastName' | 'avatarUrl' - > | null; + assignee: Pick | null; assigneeId: string | null; comments: Comment[]; }; diff --git a/front/src/modules/activities/types/Comment.ts b/front/src/modules/activities/types/Comment.ts index ec17484cfd..3b3375cd68 100644 --- a/front/src/modules/activities/types/Comment.ts +++ b/front/src/modules/activities/types/Comment.ts @@ -6,5 +6,5 @@ export type Comment = { body: string; updatedAt: string; activityId: string; - author: Pick; + author: Pick; }; diff --git a/front/src/modules/auth/hooks/useAuth.ts b/front/src/modules/auth/hooks/useAuth.ts index 7d5bc530fb..1afc69e304 100644 --- a/front/src/modules/auth/hooks/useAuth.ts +++ b/front/src/modules/auth/hooks/useAuth.ts @@ -169,8 +169,10 @@ export const useAuth = () => { mutation: CREATE_ONE_WORKSPACE_MEMBER_V2, variables: { input: { - firstName: user.firstName ?? '', - lastName: user.lastName ?? '', + name: { + firstName: user.firstName ?? '', + lastName: user.lastName ?? '', + }, colorScheme: 'Light', userId: user.id, allowImpersonation: true, diff --git a/front/src/modules/auth/utils/getOnboardingStatus.ts b/front/src/modules/auth/utils/getOnboardingStatus.ts index ed70c21f23..dd8f23b4f9 100644 --- a/front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/front/src/modules/auth/utils/getOnboardingStatus.ts @@ -25,7 +25,10 @@ export const getOnboardingStatus = ( if (!currentWorkspace?.displayName) { return OnboardingStatus.OngoingWorkspaceCreation; } - if (!currentWorkspaceMember.firstName || !currentWorkspaceMember.lastName) { + if ( + !currentWorkspaceMember.name.firstName || + !currentWorkspaceMember.name.lastName + ) { return OnboardingStatus.OngoingProfileCreation; } diff --git a/front/src/modules/companies/table/components/companies-mock-data.ts b/front/src/modules/companies/table/components/companies-mock-data.ts index 9a1857e33c..2834731975 100644 --- a/front/src/modules/companies/table/components/companies-mock-data.ts +++ b/front/src/modules/companies/table/components/companies-mock-data.ts @@ -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< Company, @@ -15,16 +18,7 @@ type MockedCompany = Pick< | 'idealCustomerProfile' | '_activityCount' > & { - accountOwner: Pick< - User, - | 'id' - | 'email' - | 'displayName' - | 'avatarUrl' - | '__typename' - | 'firstName' - | 'lastName' - > | null; + accountOwner: Pick | null; } & { Favorite: Pick | null }; export const mockedCompaniesData: Array = [ @@ -42,13 +36,12 @@ export const mockedCompaniesData: Array = [ Favorite: null, _activityCount: 0, accountOwner: { - email: 'charles@test.com', - displayName: 'Charles Test', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: null, id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', - __typename: 'User', }, __typename: 'Company', }, diff --git a/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts b/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts index 66f17e51bd..b97ae4d429 100644 --- a/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts +++ b/front/src/modules/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery.ts @@ -91,6 +91,14 @@ export const useMapFieldMetadataToGraphQLQuery = () => { currencyCode } `; + } else if (fieldType === 'FULL_NAME') { + return ` + ${field.name} + { + firstName + lastName + } + `; } }; diff --git a/front/src/modules/object-record/components/RecordShowPage.tsx b/front/src/modules/object-record/components/RecordShowPage.tsx index 5c914ee58e..089d082d32 100644 --- a/front/src/modules/object-record/components/RecordShowPage.tsx +++ b/front/src/modules/object-record/components/RecordShowPage.tsx @@ -91,9 +91,10 @@ export const RecordShowPage = () => { if (isFavorite) deleteFavorite(object?.id); else { const additionalData = - objectNameSingular === 'peopleV2' + objectNameSingular === 'personV2' ? { - labelIdentifier: object.firstName + ' ' + object.lastName, + labelIdentifier: + object.name.firstName + ' ' + object.name.lastName, avatarUrl: object.avatarUrl, avatarType: 'rounded', link: `/object/personV2/${object.id}`, @@ -114,11 +115,16 @@ export const RecordShowPage = () => { if (!object) return <>; + const pageName = + objectNameSingular === 'personV2' + ? object.name.firstName + ' ' + object.name.lastName + : object.name; + return ( - + diff --git a/front/src/modules/object-record/graphql/mutation/createOneWorkspaceMember.ts b/front/src/modules/object-record/graphql/mutation/createOneWorkspaceMember.ts index 038f2bcb54..e105fb63b1 100644 --- a/front/src/modules/object-record/graphql/mutation/createOneWorkspaceMember.ts +++ b/front/src/modules/object-record/graphql/mutation/createOneWorkspaceMember.ts @@ -4,8 +4,10 @@ export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql` mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) { createWorkspaceMemberV2(data: $input) { id - firstName - lastName + name { + firstName + lastName + } } } `; diff --git a/front/src/modules/object-record/graphql/queries/findOneWorkspaceMember.ts b/front/src/modules/object-record/graphql/queries/findOneWorkspaceMember.ts index 31c06c32bd..57ac25a646 100644 --- a/front/src/modules/object-record/graphql/queries/findOneWorkspaceMember.ts +++ b/front/src/modules/object-record/graphql/queries/findOneWorkspaceMember.ts @@ -6,8 +6,10 @@ export const FIND_ONE_WORKSPACE_MEMBER_V2 = gql` edges { node { id - firstName - lastName + name { + firstName + lastName + } colorScheme avatarUrl locale diff --git a/front/src/modules/object-record/hooks/useRecordTableContextMenuEntries.tsx b/front/src/modules/object-record/hooks/useRecordTableContextMenuEntries.tsx index 50822c3214..13b8bb827e 100644 --- a/front/src/modules/object-record/hooks/useRecordTableContextMenuEntries.tsx +++ b/front/src/modules/object-record/hooks/useRecordTableContextMenuEntries.tsx @@ -29,7 +29,6 @@ export const useRecordTableContextMenuEntries = () => { const { scopeId: objectNamePlural, resetTableRowSelection } = useRecordTable(); - const { data } = useGetFavoritesQuery(); const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({ objectNamePlural, }); diff --git a/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts b/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts index 924c7bce8a..d6145ed856 100644 --- a/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts +++ b/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts @@ -31,7 +31,7 @@ export const useGenerateFindManyCustomObjectsQuery = ({ node { id ${objectMetadataItem.fields - .map(mapFieldMetadataToGraphQLQuery) + .map((field) => mapFieldMetadataToGraphQLQuery(field)) .join('\n')} } cursor diff --git a/front/src/modules/people/table/components/PersonTableEffect.tsx b/front/src/modules/people/table/components/PersonTableEffect.tsx index bab8750913..c97d2adb0d 100644 --- a/front/src/modules/people/table/components/PersonTableEffect.tsx +++ b/front/src/modules/people/table/components/PersonTableEffect.tsx @@ -16,7 +16,7 @@ const PeopleTableEffect = () => { setViewObjectMetadataId, } = useView(); - const { setAvailableTableColumns, setTableColumns } = useRecordTable(); + const { setAvailableTableColumns } = useRecordTable(); useEffect(() => { setAvailableSortDefinitions?.(personTableSortDefinitions); @@ -31,7 +31,6 @@ const PeopleTableEffect = () => { setAvailableFilterDefinitions, setAvailableSortDefinitions, setAvailableTableColumns, - setTableColumns, setViewObjectMetadataId, setViewType, ]); diff --git a/front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts b/front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts index 7ce4114184..cdeffeca1b 100644 --- a/front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts +++ b/front/src/modules/search/hooks/useFilteredSearchEntityQueryV2.ts @@ -62,13 +62,26 @@ export const useFilteredSearchEntityQueryV2 = ({ } return { - or: fieldNames.map((fieldName) => ({ - [fieldName]: { - like: `%${filter}%`, - // TODO: fix mode - // mode: QueryMode.Insensitive, - }, - })), + or: fieldNames.map((fieldName) => { + const fieldNameParts = fieldName.split('.'); + + if (fieldNameParts.length > 1) { + // Composite field + + return { + [fieldNameParts[0]]: { + [fieldNameParts[1]]: { + ilike: `%${filter}%`, + }, + }, + }; + } + return { + [fieldName]: { + ilike: `%${filter}%`, + }, + }; + }), }; }) .filter(isDefined); diff --git a/front/src/modules/settings/data-model/constants/dataTypes.ts b/front/src/modules/settings/data-model/constants/dataTypes.ts index 0bbd8ae06a..378e030350 100644 --- a/front/src/modules/settings/data-model/constants/dataTypes.ts +++ b/front/src/modules/settings/data-model/constants/dataTypes.ts @@ -9,6 +9,7 @@ import { IconPhone, IconPlug, IconTextSize, + IconUser, } from '@/ui/display/icon'; import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { CurrencyCode, FieldMetadataType } from '~/generated-metadata/graphql'; @@ -64,5 +65,6 @@ export const dataTypes: Record< Icon: IconNumbers, defaultValue: 50, }, + [FieldMetadataType.FullName]: { label: 'Full Name', Icon: IconUser }, [FieldMetadataType.Enum]: { label: 'Enum', Icon: IconPlug }, }; diff --git a/front/src/modules/settings/profile/components/NameFields.tsx b/front/src/modules/settings/profile/components/NameFields.tsx index c89137e8e1..c0ef940050 100644 --- a/front/src/modules/settings/profile/components/NameFields.tsx +++ b/front/src/modules/settings/profile/components/NameFields.tsx @@ -34,10 +34,10 @@ export const NameFields = ({ ); const [firstName, setFirstName] = useState( - currentWorkspaceMember?.firstName ?? '', + currentWorkspaceMember?.name.firstName ?? '', ); const [lastName, setLastName] = useState( - currentWorkspaceMember?.lastName ?? '', + currentWorkspaceMember?.name.lastName ?? '', ); const { updateOneObject, objectNotFoundInMetadata } = @@ -65,15 +65,19 @@ export const NameFields = ({ await updateOneObject({ idToUpdate: currentWorkspaceMember?.id, input: { - firstName, - lastName, + name: { + firstName: firstName, + lastName: lastName, + }, }, }); setCurrentWorkspaceMember({ ...currentWorkspaceMember, - firstName, - lastName, + name: { + firstName, + lastName, + }, }); } } catch (error) { @@ -87,8 +91,8 @@ export const NameFields = ({ } if ( - currentWorkspaceMember.firstName !== firstName || - currentWorkspaceMember.lastName !== lastName + currentWorkspaceMember.name.firstName !== firstName || + currentWorkspaceMember.name.lastName !== lastName ) { debouncedUpdate(); } diff --git a/front/src/modules/ui/navigation/navbar/components/SupportChat.tsx b/front/src/modules/ui/navigation/navbar/components/SupportChat.tsx index f5612e5b84..eaf966ffab 100644 --- a/front/src/modules/ui/navigation/navbar/components/SupportChat.tsx +++ b/front/src/modules/ui/navigation/navbar/components/SupportChat.tsx @@ -40,7 +40,7 @@ const SupportChat = () => { ( chatId: string, currentUser: Pick, - currentWorkspaceMember: Pick, + currentWorkspaceMember: Pick, ) => { const url = 'https://chat-assets.frontapp.com/v1/chat.bundle.js'; const script = document.querySelector(`script[src="${url}"]`); @@ -54,9 +54,9 @@ const SupportChat = () => { useDefaultLauncher: false, email: currentUser.email, name: - currentWorkspaceMember.firstName + + currentWorkspaceMember.name.firstName + ' ' + - currentWorkspaceMember.lastName, + currentWorkspaceMember.name.lastName, userHash: currentUser?.supportUserHash, }); setIsFrontChatLoaded(true); diff --git a/front/src/modules/ui/object/field/components/FieldDisplay.tsx b/front/src/modules/ui/object/field/components/FieldDisplay.tsx index e912566cb3..f0498a7ece 100644 --- a/front/src/modules/ui/object/field/components/FieldDisplay.tsx +++ b/front/src/modules/ui/object/field/components/FieldDisplay.tsx @@ -1,7 +1,9 @@ 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 { 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 { 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 { DateFieldDisplay } from '../meta-types/display/components/DateFieldDisplay'; 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 { LinkFieldDisplay } from '../meta-types/display/components/LinkFieldDisplay'; 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 { isFieldCurrency } from '../types/guards/isFieldCurrency'; import { isFieldDate } from '../types/guards/isFieldDate'; -import { isFieldDoubleText } from '../types/guards/isFieldDoubleText'; import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip'; import { isFieldEmail } from '../types/guards/isFieldEmail'; import { isFieldLink } from '../types/guards/isFieldLink'; @@ -56,14 +56,14 @@ export const FieldDisplay = () => { ) : isFieldCurrency(fieldDefinition) ? ( + ) : isFieldFullName(fieldDefinition) ? ( + ) : isFieldPhone(fieldDefinition) ? ( ) : isFieldChip(fieldDefinition) ? ( ) : isFieldDoubleTextChip(fieldDefinition) ? ( - ) : isFieldDoubleText(fieldDefinition) ? ( - ) : ( <> )} diff --git a/front/src/modules/ui/object/field/components/FieldInput.tsx b/front/src/modules/ui/object/field/components/FieldInput.tsx index ca7f2aca80..e4b4d5f9e1 100644 --- a/front/src/modules/ui/object/field/components/FieldInput.tsx +++ b/front/src/modules/ui/object/field/components/FieldInput.tsx @@ -8,7 +8,6 @@ import { ChipFieldInput } from '../meta-types/input/components/ChipFieldInput'; import { CurrencyFieldInput } from '../meta-types/input/components/CurrencyFieldInput'; import { DateFieldInput } from '../meta-types/input/components/DateFieldInput'; 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 { LinkFieldInput } from '../meta-types/input/components/LinkFieldInput'; 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 { isFieldCurrency } from '../types/guards/isFieldCurrency'; import { isFieldDate } from '../types/guards/isFieldDate'; -import { isFieldDoubleText } from '../types/guards/isFieldDoubleText'; import { isFieldDoubleTextChip } from '../types/guards/isFieldDoubleTextChip'; import { isFieldEmail } from '../types/guards/isFieldEmail'; import { isFieldLink } from '../types/guards/isFieldLink'; @@ -146,14 +144,6 @@ export const FieldInput = ({ onTab={onTab} onShiftTab={onShiftTab} /> - ) : isFieldDoubleText(fieldDefinition) ? ( - ) : isFieldMoney(fieldDefinition) ? ( { - const { firstValue, secondValue } = useDoubleTextField(); - - const content = [firstValue, secondValue].filter(Boolean).join(' '); - - return ; -}; diff --git a/front/src/modules/ui/object/field/meta-types/display/components/FullNameFieldDisplay.tsx b/front/src/modules/ui/object/field/meta-types/display/components/FullNameFieldDisplay.tsx new file mode 100644 index 0000000000..b6bdc2c81e --- /dev/null +++ b/front/src/modules/ui/object/field/meta-types/display/components/FullNameFieldDisplay.tsx @@ -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 ; +}; diff --git a/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DoubleTextFieldDisplay.stories.tsx b/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DoubleTextFieldDisplay.stories.tsx deleted file mode 100644 index dd9ab566b4..0000000000 --- a/front/src/modules/ui/object/field/meta-types/display/components/__stories__/DoubleTextFieldDisplay.stories.tsx +++ /dev/null @@ -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 }) => ( - - - - - ), - ComponentDecorator, - ], - component: DoubleTextFieldDisplay, - args: { - firstValue: 'Lorem', - secondValue: 'ipsum', - }, -}; - -export default meta; - -type Story = StoryObj; - -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 }, - }, -}; diff --git a/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts b/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts index 91d2c29913..a26c753a06 100644 --- a/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts +++ b/front/src/modules/ui/object/field/meta-types/display/utils/getEntityChipFromFieldMetadata.ts @@ -21,12 +21,12 @@ export const getEntityChipFromFieldMetadata = ( // TODO: use every if (fieldName === 'accountOwner' && fieldValue) { - chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName; + chipValue.name = fieldValue.name.firstName + ' ' + fieldValue.name.lastName; } else if (fieldName === 'company' && fieldValue) { chipValue.name = fieldValue.name; chipValue.pictureUrl = getLogoUrlFromDomainName(fieldValue.domainName); } else if (fieldName === 'person' && fieldValue) { - chipValue.name = fieldValue.firstName + ' ' + fieldValue.lastName; + chipValue.name = fieldValue.name.firstName + ' ' + fieldValue.name.lastName; } return chipValue; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts deleted file mode 100644 index 965950dae6..0000000000 --- a/front/src/modules/ui/object/field/meta-types/hooks/useDoubleTextField.ts +++ /dev/null @@ -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( - entityFieldsFamilySelector({ - entityId: entityId, - fieldName: fieldDefinition.metadata.firstValueFieldName, - }), - ); - - const [secondValue, setSecondValue] = useRecoilState( - 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, - }; -}; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useFullNameField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useFullNameField.ts new file mode 100644 index 0000000000..e54da17dc8 --- /dev/null +++ b/front/src/modules/ui/object/field/meta-types/hooks/useFullNameField.ts @@ -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( + 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, + }; +}; diff --git a/front/src/modules/ui/object/field/meta-types/input/components/DoubleTextFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/DoubleTextFieldInput.tsx deleted file mode 100644 index 5045b8be22..0000000000 --- a/front/src/modules/ui/object/field/meta-types/input/components/DoubleTextFieldInput.tsx +++ /dev/null @@ -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 ( - - - - ); -}; diff --git a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextFieldInput.stories.tsx b/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextFieldInput.stories.tsx deleted file mode 100644 index fe5d4bc5fd..0000000000 --- a/front/src/modules/ui/object/field/meta-types/input/components/__stories__/DoubleTextFieldInput.stories.tsx +++ /dev/null @@ -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 ( -
- - - - -
-
- ); -}; - -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 ; -}; - -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; - -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); - }); - }, -}; diff --git a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts index 376208d635..56dedbcc33 100644 --- a/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts +++ b/front/src/modules/ui/object/field/states/selectors/isEntityFieldEmptyFamilySelector.ts @@ -1,5 +1,7 @@ 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 { 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)) { const fieldName = fieldDefinition.metadata.fieldName; const fieldValue = get(entityFieldsFamilyState(entityId))?.[fieldName]; diff --git a/front/src/modules/ui/object/field/types/FieldMetadata.ts b/front/src/modules/ui/object/field/types/FieldMetadata.ts index 3c67acc3c3..448fc83270 100644 --- a/front/src/modules/ui/object/field/types/FieldMetadata.ts +++ b/front/src/modules/ui/object/field/types/FieldMetadata.ts @@ -48,6 +48,10 @@ export type FieldCurrencyMetadata = { isPositive?: boolean; }; +export type FieldFullnameMetadata = { + fieldName: string; +}; + export type FieldEmailMetadata = { fieldName: string; placeHolder: string; @@ -119,6 +123,7 @@ export type FieldLinkValue = { url: string; label: string }; export type FieldNumberValue = number | null; export type FieldMoneyValue = number | null; export type FieldCurrencyValue = { currencyCode: string; amountMicros: number }; +export type FieldFullNameValue = { firstName: string; lastName: string }; export type FieldEmailValue = string; export type FieldProbabilityValue = number; diff --git a/front/src/modules/ui/object/field/types/FieldType.ts b/front/src/modules/ui/object/field/types/FieldType.ts index dcf6534b93..9eb28cab00 100644 --- a/front/src/modules/ui/object/field/types/FieldType.ts +++ b/front/src/modules/ui/object/field/types/FieldType.ts @@ -15,4 +15,5 @@ export type FieldType = | 'PROBABILITY' | 'CURRENCY' | 'MONEY_AMOUNT' - | 'MONEY'; + | 'MONEY' + | 'FULL_NAME'; diff --git a/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts b/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts index e442492650..3207432954 100644 --- a/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts +++ b/front/src/modules/ui/object/field/types/guards/assertFieldMetadata.ts @@ -7,6 +7,7 @@ import { FieldDoubleTextChipMetadata, FieldDoubleTextMetadata, FieldEmailMetadata, + FieldFullnameMetadata, FieldLinkMetadata, FieldMetadata, FieldMoneyMetadata, @@ -28,6 +29,8 @@ type AssertFieldMetadataFunction = < ? FieldChipMetadata : E extends 'CURRENCY' ? FieldCurrencyMetadata + : E extends 'FULL_NAME' + ? FieldFullnameMetadata : E extends 'DATE' ? FieldDateMetadata : E extends 'DOUBLE_TEXT' diff --git a/front/src/modules/ui/object/field/types/guards/isFieldFullName.ts b/front/src/modules/ui/object/field/types/guards/isFieldFullName.ts new file mode 100644 index 0000000000..6455090aa0 --- /dev/null +++ b/front/src/modules/ui/object/field/types/guards/isFieldFullName.ts @@ -0,0 +1,7 @@ +import { FieldDefinition } from '../FieldDefinition'; +import { FieldCurrencyMetadata, FieldMetadata } from '../FieldMetadata'; + +export const isFieldFullName = ( + field: FieldDefinition, +): field is FieldDefinition => + field.type === 'FULL_NAME'; diff --git a/front/src/modules/ui/object/field/types/guards/isFieldFullNameValue.ts b/front/src/modules/ui/object/field/types/guards/isFieldFullNameValue.ts new file mode 100644 index 0000000000..a85ceb5104 --- /dev/null +++ b/front/src/modules/ui/object/field/types/guards/isFieldFullNameValue.ts @@ -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; diff --git a/front/src/modules/ui/theme/hooks/useColorScheme.ts b/front/src/modules/ui/theme/hooks/useColorScheme.ts index 3760ff9875..943cc5d873 100644 --- a/front/src/modules/ui/theme/hooks/useColorScheme.ts +++ b/front/src/modules/ui/theme/hooks/useColorScheme.ts @@ -6,7 +6,9 @@ import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObje import { ColorScheme } from '@/workspace-member/types/WorkspaceMember'; export const useColorScheme = () => { - const [currentWorkspaceMember] = useRecoilState(currentWorkspaceMemberState); + const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState( + currentWorkspaceMemberState, + ); const { updateOneObject: updateOneWorkspaceMember } = useUpdateOneObjectRecord({ @@ -19,6 +21,15 @@ export const useColorScheme = () => { if (!currentWorkspaceMember) { return; } + setCurrentWorkspaceMember((current) => { + if (!current) { + return current; + } + return { + ...current, + colorScheme: value, + }; + }); await updateOneWorkspaceMember?.({ idToUpdate: currentWorkspaceMember?.id, input: { @@ -26,7 +37,11 @@ export const useColorScheme = () => { }, }); }, - [currentWorkspaceMember, updateOneWorkspaceMember], + [ + currentWorkspaceMember, + setCurrentWorkspaceMember, + updateOneWorkspaceMember, + ], ); return { diff --git a/front/src/modules/users/components/UserPicker.tsx b/front/src/modules/users/components/UserPicker.tsx index 4187ad2d27..0a712376bf 100644 --- a/front/src/modules/users/components/UserPicker.tsx +++ b/front/src/modules/users/components/UserPicker.tsx @@ -33,25 +33,26 @@ export const UserPicker = ({ }, [initialSearchFilter, setRelationPickerSearchFilter]); const { findManyQuery } = useFindOneObjectMetadataItem({ - objectNamePlural: 'workspaceMembersV2', + objectNameSingular: 'workspaceMemberV2', }); const useFindManyWorkspaceMembers = (options: any) => useQuery(findManyQuery, options); - const users = useFilteredSearchEntityQueryV2({ + const workspaceMembers = useFilteredSearchEntityQueryV2({ queryHook: useFindManyWorkspaceMembers, filters: [ { - fieldNames: ['firstName', 'lastName'], + fieldNames: ['name.firstName', 'name.lastName'], filter: relationPickerSearchFilter, }, ], - orderByField: 'firstName', + orderByField: 'createdAt', mappingFunction: (workspaceMember) => ({ entityType: Entity.WorkspaceMember, id: workspaceMember.id, - name: workspaceMember.firstName, + name: + workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName, avatarType: 'rounded', avatarUrl: '', originalEntity: workspaceMember, @@ -68,11 +69,11 @@ export const UserPicker = ({ ); diff --git a/front/src/modules/views/components/ViewBarEffect.tsx b/front/src/modules/views/components/ViewBarEffect.tsx index 4277149101..c0980a72b4 100644 --- a/front/src/modules/views/components/ViewBarEffect.tsx +++ b/front/src/modules/views/components/ViewBarEffect.tsx @@ -67,7 +67,7 @@ export const ViewBarEffect = () => { useFindManyObjectRecords({ skip: !currentViewId, objectNamePlural: 'viewFieldsV2', - filter: { view: { eq: currentViewId } }, + filter: { viewId: { eq: currentViewId } }, onCompleted: useRecoilCallback( ({ snapshot, set }) => async (data: PaginatedObjectTypeResults) => { diff --git a/front/src/modules/views/hooks/internal/useViewFields.ts b/front/src/modules/views/hooks/internal/useViewFields.ts index 8e4ac50f14..cc300042d3 100644 --- a/front/src/modules/views/hooks/internal/useViewFields.ts +++ b/front/src/modules/views/hooks/internal/useViewFields.ts @@ -41,7 +41,7 @@ export const useViewFields = (viewScopeId: string) => { variables: { input: { fieldMetadataId: viewField.fieldMetadataId, - view: viewIdToPersist, + viewId: viewIdToPersist, isVisible: viewField.isVisible, size: viewField.size, position: viewField.position, diff --git a/front/src/modules/workspace-member/types/WorkspaceMember.ts b/front/src/modules/workspace-member/types/WorkspaceMember.ts index 5ed523b57c..790743cdd5 100644 --- a/front/src/modules/workspace-member/types/WorkspaceMember.ts +++ b/front/src/modules/workspace-member/types/WorkspaceMember.ts @@ -2,8 +2,10 @@ export type ColorScheme = 'Dark' | 'Light' | 'System'; export type WorkspaceMember = { id: string; - firstName: string; - lastName: string; + name: { + firstName: string; + lastName: string; + }; avatarUrl: string | null; locale: string; colorScheme: ColorScheme; diff --git a/front/src/modules/workspace/components/WorkspaceMemberCard.tsx b/front/src/modules/workspace/components/WorkspaceMemberCard.tsx index eb0115154c..3c5e3acab9 100644 --- a/front/src/modules/workspace/components/WorkspaceMemberCard.tsx +++ b/front/src/modules/workspace/components/WorkspaceMemberCard.tsx @@ -41,16 +41,18 @@ export const WorkspaceMemberCard = ({ - {workspaceMember.firstName + ' ' + workspaceMember.lastName} + {workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName} diff --git a/front/src/pages/auth/CreateProfile.tsx b/front/src/pages/auth/CreateProfile.tsx index 84f0b0bdc9..35d50ed099 100644 --- a/front/src/pages/auth/CreateProfile.tsx +++ b/front/src/pages/auth/CreateProfile.tsx @@ -73,8 +73,8 @@ export const CreateProfile = () => { } = useForm
({ mode: 'onChange', defaultValues: { - firstName: currentWorkspaceMember?.firstName ?? '', - lastName: currentWorkspaceMember?.lastName ?? '', + firstName: currentWorkspaceMember?.name.firstName ?? '', + lastName: currentWorkspaceMember?.name.lastName ?? '', }, resolver: zodResolver(validationSchema), }); @@ -95,8 +95,10 @@ export const CreateProfile = () => { await updateOneObject({ idToUpdate: currentWorkspaceMember?.id, input: { - firstName: data.firstName, - lastName: data.lastName, + name: { + firstName: data.firstName, + lastName: data.lastName, + }, }, }); @@ -104,8 +106,10 @@ export const CreateProfile = () => { (current) => ({ ...current, - firstName: data.firstName, - lastName: data.lastName, + name: { + firstName: data.firstName, + lastName: data.lastName, + }, } as any), ); diff --git a/front/src/pages/auth/CreateWorkspace.tsx b/front/src/pages/auth/CreateWorkspace.tsx index 862e297edf..3831698707 100644 --- a/front/src/pages/auth/CreateWorkspace.tsx +++ b/front/src/pages/auth/CreateWorkspace.tsx @@ -3,10 +3,12 @@ import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; import { zodResolver } from '@hookform/resolvers/zod'; +import { useSetRecoilState } from 'recoil'; import { z } from 'zod'; import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; +import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { H2Title } from '@/ui/display/typography/components/H2Title'; @@ -41,6 +43,7 @@ export const CreateWorkspace = () => { const navigate = useNavigate(); const { enqueueSnackBar } = useSnackBar(); + const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); 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) { throw result.errors ?? new Error('Unknown error'); @@ -82,7 +89,7 @@ export const CreateWorkspace = () => { }); } }, - [enqueueSnackBar, navigate, updateWorkspace], + [enqueueSnackBar, navigate, setCurrentWorkspace, updateWorkspace], ); useScopedHotkeys( diff --git a/front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeys.tsx b/front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeys.tsx index a11d17138c..7d3ea89b75 100644 --- a/front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeys.tsx +++ b/front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeys.tsx @@ -44,7 +44,9 @@ export const SettingsDevelopersApiKeys = () => { const navigate = useNavigate(); const [apiKeys, setApiKeys] = useState>([]); - const { registerOptimisticEffect } = useOptimisticEffect('apiKeyV2'); + const { registerOptimisticEffect } = useOptimisticEffect({ + objectNameSingular: 'apiKeyV2', + }); const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({ objectNameSingular: 'apiKeyV2', }); diff --git a/front/src/pages/tasks/TasksEffect.tsx b/front/src/pages/tasks/TasksEffect.tsx index 6652c9bf6a..b9e2824cde 100644 --- a/front/src/pages/tasks/TasksEffect.tsx +++ b/front/src/pages/tasks/TasksEffect.tsx @@ -22,9 +22,9 @@ export const TasksEffect = () => { value: currentWorkspaceMember.id, operand: ViewFilterOperand.Is, displayValue: - currentWorkspaceMember.firstName + + currentWorkspaceMember.name.firstName + ' ' + - currentWorkspaceMember.lastName, + currentWorkspaceMember.name.lastName, displayAvatarUrl: currentWorkspaceMember.avatarUrl ?? undefined, definition: tasksFilterDefinitions[0], }); diff --git a/front/src/testing/mock-data/activities.ts b/front/src/testing/mock-data/activities.ts index 7bac87d580..f8bf154ab2 100644 --- a/front/src/testing/mock-data/activities.ts +++ b/front/src/testing/mock-data/activities.ts @@ -17,11 +17,8 @@ type MockedActivity = Pick< | 'dueAt' | 'completedAt' > & { - author: Pick; - assignee: Pick< - WorkspaceMember, - 'id' | 'firstName' | 'lastName' | 'avatarUrl' - >; + author: Pick; + assignee: Pick; comments: Comment[]; activityTargets: Array< Pick< @@ -53,14 +50,18 @@ export const mockedTasks: Array = [ completedAt: null, author: { id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: '', }, assignee: { id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: '', }, authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', @@ -82,14 +83,18 @@ export const mockedActivities: Array = [ completedAt: null, author: { id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: '', }, assignee: { id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: '', }, authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', @@ -149,14 +154,18 @@ export const mockedActivities: Array = [ dueAt: '2029-08-26T10:12:42.33625+00:00', author: { id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: '', }, assignee: { id: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', - firstName: 'Charles', - lastName: 'Test', + name: { + firstName: 'Charles', + lastName: 'Test', + }, avatarUrl: '', }, authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e', diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata/person.ts b/server/src/database/typeorm-seeds/metadata/field-metadata/person.ts index 4d4fb6071b..535d2f52c9 100644 --- a/server/src/database/typeorm-seeds/metadata/field-metadata/person.ts +++ b/server/src/database/typeorm-seeds/metadata/field-metadata/person.ts @@ -15,7 +15,7 @@ export enum SeedPersonFieldMetadataIds { Email = '20202020-8a96-4e4b-86fd-ea126530e0c1', LinkedinLink = '20202020-dcf6-445a-b543-37e55de43c25', - XUrl = '20202020-a3a7-4f63-9303-10226f6055be', + XLink = '20202020-a3a7-4f63-9303-10226f6055be', JobTitle = '20202020-3b86-413e-ab56-0ebd1a583ff3', Phone = '20202020-486f-45f9-bbdf-aac18b1831c0', City = '20202020-78f8-4b4c-90ff-86adf77590f5', @@ -170,7 +170,7 @@ export const seedPersonFieldMetadata = async ( defaultValue: undefined, }, { - id: SeedPersonFieldMetadataIds.XUrl, + id: SeedPersonFieldMetadataIds.XLink, objectMetadataId: SeedObjectMetadataIds.Person, isCustom: false, workspaceId: SeedWorkspaceId, diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata/view-field.ts b/server/src/database/typeorm-seeds/metadata/field-metadata/view-field.ts index 12bddc5cbb..f85e747bb7 100644 --- a/server/src/database/typeorm-seeds/metadata/field-metadata/view-field.ts +++ b/server/src/database/typeorm-seeds/metadata/field-metadata/view-field.ts @@ -16,6 +16,7 @@ export enum SeedViewFieldFieldMetadataIds { Size = '20202020-b9a1-4c2e-a5af-3a6b4fef4af6', Position = '20202020-a4bb-440a-add2-81dbd9a74517', View = '20202020-8788-4508-b771-719807b60e61', + ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6a', } export const seedViewFieldFieldMetadata = async ( @@ -126,13 +127,29 @@ export const seedViewFieldFieldMetadata = async ( type: FieldMetadataType.RELATION, name: 'view', label: 'View Id', - targetColumnMap: { value: 'viewId' }, + targetColumnMap: {}, description: 'View Field related view', icon: 'IconLayoutCollage', - isNullable: false, + isNullable: true, isSystem: false, 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, objectMetadataId: SeedObjectMetadataIds.ViewField, diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata/view-filter.ts b/server/src/database/typeorm-seeds/metadata/field-metadata/view-filter.ts index 33fdafb92c..73056d454c 100644 --- a/server/src/database/typeorm-seeds/metadata/field-metadata/view-filter.ts +++ b/server/src/database/typeorm-seeds/metadata/field-metadata/view-filter.ts @@ -13,6 +13,7 @@ export enum SeedViewFilterFieldMetadataIds { FieldMetadataId = '20202020-78bb-4f2b-a052-260bc8efd694', View = '20202020-65e5-4082-829d-8c634c20e7b5', + ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6b', Operand = '20202020-1d12-465d-ab2c-8af008182730', Value = '20202020-8b37-46ae-86b8-14287ec06802', DisplayValue = '20202020-ed89-4892-83fa-d2b2929c6d52', @@ -126,15 +127,29 @@ export const seedViewFilterFieldMetadata = async ( type: FieldMetadataType.TEXT, name: 'viewId', label: 'View Id', - targetColumnMap: { - value: 'viewId', - }, + targetColumnMap: {}, description: 'View Filter related view', icon: 'IconLayoutCollage', isNullable: false, isSystem: false, 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, objectMetadataId: SeedObjectMetadataIds.ViewFilter, diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata/view-sort.ts b/server/src/database/typeorm-seeds/metadata/field-metadata/view-sort.ts index ecaa8166e6..708e312daa 100644 --- a/server/src/database/typeorm-seeds/metadata/field-metadata/view-sort.ts +++ b/server/src/database/typeorm-seeds/metadata/field-metadata/view-sort.ts @@ -13,6 +13,7 @@ export enum SeedViewSortFieldMetadataIds { FieldMetadataId = '20202020-cb2c-4c8f-a289-c9851b23d064', View = '20202020-f5d0-467f-a3d8-395ba16b8ebf', + ViewForeignKey = '20202020-c852-4c28-b13a-07788c845d6c', Direction = '20202020-077e-4451-b1d8-e602c956ebd2', } @@ -124,15 +125,29 @@ export const seedViewSortFieldMetadata = async ( type: FieldMetadataType.TEXT, name: 'viewId', label: 'View Id', - targetColumnMap: { - value: 'viewId', - }, + targetColumnMap: {}, description: 'View Sort related view', icon: 'IconLayoutCollage', isNullable: false, isSystem: false, 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, objectMetadataId: SeedObjectMetadataIds.ViewSort, diff --git a/server/src/database/typeorm-seeds/metadata/field-metadata/workspace-member.ts b/server/src/database/typeorm-seeds/metadata/field-metadata/workspace-member.ts index b7230cd167..cc969ab85d 100644 --- a/server/src/database/typeorm-seeds/metadata/field-metadata/workspace-member.ts +++ b/server/src/database/typeorm-seeds/metadata/field-metadata/workspace-member.ts @@ -3,7 +3,6 @@ import { DataSource } from 'typeorm'; import { SeedObjectMetadataIds } from 'src/database/typeorm-seeds/metadata/object-metadata'; import { SeedWorkspaceId } from 'src/database/seeds/metadata'; import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity'; -import { SeedPersonFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/person'; const fieldMetadataTableName = 'fieldMetadata'; @@ -12,8 +11,7 @@ export enum SeedWorkspaceMemberFieldMetadataIds { CreatedAt = '20202020-1cbf-4b32-8c33-fbfedcd9afa8', UpdatedAt = '20202020-1ba3-4c24-b2cd-b0789633e8d4', - FirstName = '20202020-1fa8-4d38-9fa4-0cf696909298', - LastName = '20202020-8c37-4163-ba06-1dada334ce3e', + Name = '20202020-8c37-4163-ba06-1dada334ce3e', AvatarUrl = '20202020-7ba6-40d5-934b-17146183a212', Locale = '20202020-10f6-4df9-8d6f-a760b65bd800', ColorScheme = '20202020-83f2-4c5f-96b0-0c51ecc160e3', @@ -110,7 +108,7 @@ export const seedWorkspaceMemberFieldMetadata = async ( }, // Scalar fields { - id: SeedPersonFieldMetadataIds.Name, + id: SeedWorkspaceMemberFieldMetadataIds.Name, objectMetadataId: SeedObjectMetadataIds.WorkspaceMember, isCustom: false, workspaceId: SeedWorkspaceId, diff --git a/server/src/database/typeorm-seeds/workspace/view-fields.ts b/server/src/database/typeorm-seeds/workspace/view-fields.ts index 81ab68e9a9..752bb6446d 100644 --- a/server/src/database/typeorm-seeds/workspace/view-fields.ts +++ b/server/src/database/typeorm-seeds/workspace/view-fields.ts @@ -1,6 +1,9 @@ import { DataSource } from 'typeorm'; 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'; @@ -21,112 +24,112 @@ export const seedViewFields = async ( .orIgnore() .values([ { - fieldMetadataId: 'name', + fieldMetadataId: SeedCompanyFieldMetadataIds.Name, viewId: SeedViewIds.Company, position: 0, isVisible: true, size: 180, }, { - fieldMetadataId: 'domainName', + fieldMetadataId: SeedCompanyFieldMetadataIds.DomainName, viewId: SeedViewIds.Company, position: 1, isVisible: true, size: 100, }, { - fieldMetadataId: 'accountOwner', + fieldMetadataId: SeedCompanyFieldMetadataIds.AccountOwner, viewId: SeedViewIds.Company, position: 2, isVisible: true, size: 150, }, { - fieldMetadataId: 'createdAt', + fieldMetadataId: SeedCompanyFieldMetadataIds.CreatedAt, viewId: SeedViewIds.Company, position: 3, isVisible: true, size: 150, }, { - fieldMetadataId: 'employees', + fieldMetadataId: SeedCompanyFieldMetadataIds.Employees, viewId: SeedViewIds.Company, position: 4, isVisible: true, size: 150, }, { - fieldMetadataId: 'linkedin', + fieldMetadataId: SeedCompanyFieldMetadataIds.LinkedinLink, viewId: SeedViewIds.Company, position: 5, isVisible: true, size: 170, }, { - fieldMetadataId: 'address', + fieldMetadataId: SeedCompanyFieldMetadataIds.Address, viewId: SeedViewIds.Company, position: 6, isVisible: true, size: 170, }, { - fieldMetadataId: 'displayName', + fieldMetadataId: SeedPersonFieldMetadataIds.Name, viewId: SeedViewIds.Person, position: 0, isVisible: true, size: 210, }, { - fieldMetadataId: 'email', + fieldMetadataId: SeedPersonFieldMetadataIds.Email, viewId: SeedViewIds.Person, position: 1, isVisible: true, size: 150, }, { - fieldMetadataId: 'company', + fieldMetadataId: SeedPersonFieldMetadataIds.Company, viewId: SeedViewIds.Person, position: 2, isVisible: true, size: 150, }, { - fieldMetadataId: 'phone', + fieldMetadataId: SeedPersonFieldMetadataIds.Phone, viewId: SeedViewIds.Person, position: 3, isVisible: true, size: 150, }, { - fieldMetadataId: 'createdAt', + fieldMetadataId: SeedPersonFieldMetadataIds.CreatedAt, viewId: SeedViewIds.Person, position: 4, isVisible: true, size: 150, }, { - fieldMetadataId: 'city', + fieldMetadataId: SeedPersonFieldMetadataIds.City, viewId: SeedViewIds.Person, position: 5, isVisible: true, size: 150, }, { - fieldMetadataId: 'jobTitle', + fieldMetadataId: SeedPersonFieldMetadataIds.JobTitle, viewId: SeedViewIds.Person, position: 6, isVisible: true, size: 150, }, { - fieldMetadataId: 'linkedin', + fieldMetadataId: SeedPersonFieldMetadataIds.LinkedinLink, viewId: SeedViewIds.Person, position: 7, isVisible: true, size: 150, }, { - fieldMetadataId: 'x', + fieldMetadataId: SeedPersonFieldMetadataIds.XLink, viewId: SeedViewIds.Person, position: 8, isVisible: true, @@ -134,28 +137,28 @@ export const seedViewFields = async ( }, { - fieldMetadataId: 'amount', + fieldMetadataId: SeedOpportunityFieldMetadataIds.Amount, viewId: SeedViewIds.Opportunity, position: 0, isVisible: true, size: 150, }, { - fieldMetadataId: 'closeDate', + fieldMetadataId: SeedOpportunityFieldMetadataIds.CloseDate, viewId: SeedViewIds.Opportunity, position: 1, isVisible: true, size: 150, }, { - fieldMetadataId: 'probability', + fieldMetadataId: SeedOpportunityFieldMetadataIds.Probability, viewId: SeedViewIds.Opportunity, position: 2, isVisible: true, size: 150, }, { - fieldMetadataId: 'pointOfContact', + fieldMetadataId: SeedOpportunityFieldMetadataIds.PointOfContact, viewId: SeedViewIds.Opportunity, position: 3, isVisible: true, diff --git a/server/src/workspace/workspace-manager/standard-objects-prefill-data/opportunity.ts b/server/src/workspace/workspace-manager/standard-objects-prefill-data/opportunity.ts deleted file mode 100644 index 74141faeac..0000000000 --- a/server/src/workspace/workspace-manager/standard-objects-prefill-data/opportunity.ts +++ /dev/null @@ -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(); -}; diff --git a/server/src/workspace/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts b/server/src/workspace/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts index 44840bc862..566be4e9a4 100644 --- a/server/src/workspace/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts +++ b/server/src/workspace/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts @@ -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 { 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 { opportunityPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/opportunity'; export const standardObjectsPrefillData = async ( workspaceDataSource: DataSource, @@ -28,6 +27,5 @@ export const standardObjectsPrefillData = async ( await personPrefillData(entityManager, schemaName); await viewPrefillData(entityManager, schemaName, objectMetadataMap); await pipelineStepPrefillData(entityManager, schemaName); - await opportunityPrefillData(entityManager, schemaName); }); }; diff --git a/server/src/workspace/workspace-manager/standard-objects/person.ts b/server/src/workspace/workspace-manager/standard-objects/person.ts index 3bfaad2f5a..f2b83fb338 100644 --- a/server/src/workspace/workspace-manager/standard-objects/person.ts +++ b/server/src/workspace/workspace-manager/standard-objects/person.ts @@ -125,12 +125,23 @@ const personMetadata = { type: FieldMetadataType.RELATION, name: 'company', label: 'Company', - targetColumnMap: { - value: 'companyId', - }, + targetColumnMap: {}, description: 'Contact’s company', icon: 'IconBuildingSkyscraper', 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, diff --git a/server/src/workspace/workspace-manager/standard-objects/view-field.ts b/server/src/workspace/workspace-manager/standard-objects/view-field.ts index 958ea9b066..391b783f9c 100644 --- a/server/src/workspace/workspace-manager/standard-objects/view-field.ts +++ b/server/src/workspace/workspace-manager/standard-objects/view-field.ts @@ -73,16 +73,15 @@ const viewFieldMetadata = { type: FieldMetadataType.RELATION, name: 'view', label: 'View', - targetColumnMap: { value: 'viewId' }, + targetColumnMap: {}, description: 'View Field related view', icon: 'IconLayoutCollage', - isNullable: false, + isNullable: true, }, - // Temporary hack? { isCustom: false, isActive: true, - type: FieldMetadataType.TEXT, + type: FieldMetadataType.UUID, name: 'viewId', label: 'View Id', targetColumnMap: { diff --git a/server/src/workspace/workspace-manager/standard-objects/view-filter.ts b/server/src/workspace/workspace-manager/standard-objects/view-filter.ts index 71a632af47..1fae60b02a 100644 --- a/server/src/workspace/workspace-manager/standard-objects/view-filter.ts +++ b/server/src/workspace/workspace-manager/standard-objects/view-filter.ts @@ -73,16 +73,15 @@ const viewFilterMetadata = { type: FieldMetadataType.RELATION, name: 'view', label: 'View', - targetColumnMap: { value: 'viewId' }, + targetColumnMap: {}, description: 'View Filter related view', icon: 'IconLayoutCollage', - isNullable: false, + isNullable: true, }, - // Temporary hack? { isCustom: false, isActive: true, - type: FieldMetadataType.TEXT, + type: FieldMetadataType.UUID, name: 'viewId', label: 'View Id', targetColumnMap: { diff --git a/server/src/workspace/workspace-manager/standard-objects/view-sort.ts b/server/src/workspace/workspace-manager/standard-objects/view-sort.ts index 66fb0f554d..1640bfdf98 100644 --- a/server/src/workspace/workspace-manager/standard-objects/view-sort.ts +++ b/server/src/workspace/workspace-manager/standard-objects/view-sort.ts @@ -45,18 +45,15 @@ const viewSortMetadata = { type: FieldMetadataType.RELATION, name: 'view', label: 'View', - targetColumnMap: { - value: 'viewId', - }, + targetColumnMap: {}, description: 'View Sort related view', icon: 'IconLayoutCollage', - isNullable: false, + isNullable: true, }, - // Temporary Hack? { isCustom: false, isActive: true, - type: FieldMetadataType.TEXT, + type: FieldMetadataType.UUID, name: 'viewId', label: 'View Id', targetColumnMap: { diff --git a/server/src/workspace/workspace-manager/standard-objects/workspace-member.ts b/server/src/workspace/workspace-manager/standard-objects/workspace-member.ts index 8371fb7ddd..4cd13aa7d8 100644 --- a/server/src/workspace/workspace-manager/standard-objects/workspace-member.ts +++ b/server/src/workspace/workspace-manager/standard-objects/workspace-member.ts @@ -11,19 +11,6 @@ const workspaceMemberMetadata = { isActive: true, isSystem: true, 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, isActive: true, @@ -93,6 +80,20 @@ const workspaceMemberMetadata = { isNullable: true, 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 { isCustom: false, diff --git a/server/src/workspace/workspace-manager/workspace-manager.service.ts b/server/src/workspace/workspace-manager/workspace-manager.service.ts index f0fd7bf437..cfe876b294 100644 --- a/server/src/workspace/workspace-manager/workspace-manager.service.ts +++ b/server/src/workspace/workspace-manager/workspace-manager.service.ts @@ -81,23 +81,21 @@ export class WorkspaceManagerService { workspaceId: string, ): Promise { const createdObjectMetadata = await this.objectMetadataService.createMany( - Object.values(standardObjectsMetadata).map( - (objectMetadata: ObjectMetadataEntity) => ({ - ...objectMetadata, - dataSourceId, - workspaceId, - isCustom: false, - isActive: true, - fields: [...basicFieldsMetadata, ...objectMetadata.fields].map( - (field) => ({ - ...field, - workspaceId, - isCustom: false, - isActive: true, - }), - ), - }), - ), + Object.values(standardObjectsMetadata).map((objectMetadata: any) => ({ + ...objectMetadata, + dataSourceId, + workspaceId, + isCustom: false, + isActive: true, + fields: [...basicFieldsMetadata, ...objectMetadata.fields].map( + (field) => ({ + ...field, + workspaceId, + isCustom: false, + isActive: true, + }), + ), + })), ); await this.relationMetadataService.createMany(