From 549335054a2f9ba1161a03f8ad2e6afdf0676038 Mon Sep 17 00:00:00 2001 From: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Date: Sat, 16 Sep 2023 01:32:58 +0100 Subject: [PATCH] Chore: Duplicate certain user fields to workspaceMember (#1514) * Move certain user fields to workspaceMember Co-authored-by: v1b3m Co-authored-by: chiazokam * Merge main Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Update the generated GraphQL Co-authored-by: v1b3m Co-authored-by: chiazokam * Update hooks Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Refactor according to review Co-authored-by: v1b3m Co-authored-by: chiazokam * Rework typing * Fix tests * Remove console logs --------- Co-authored-by: v1b3m Co-authored-by: chiazokam Co-authored-by: Charles Bochet --- front/src/__stories__/App.stories.tsx | 33 +- front/src/generated/graphql.tsx | 374 +++++++++++++++++- .../components/ActivityAssigneePicker.tsx | 17 +- .../hooks/useOpenCreateActivityDrawer.ts | 6 + .../graphql/fragments/userQueryFragment.ts | 27 ++ front/src/modules/auth/hooks/useAuth.ts | 16 +- .../modules/auth/states/currentUserState.ts | 28 +- .../table/components/CompanyTable.tsx | 48 ++- .../modules/ui/theme/hooks/useColorScheme.ts | 68 +++- .../modules/users/components/UserProvider.tsx | 10 +- .../users/graphql/mutations/updateUser.ts | 27 ++ .../users/graphql/queries/getCurrentUser.ts | 27 ++ .../mutations/updateWorkspaceMember.ts | 47 +++ .../graphql/queries/getWorkspaceMembers.ts | 4 +- .../pages/impersonate/ImpersonateEffect.tsx | 17 +- front/src/testing/mock-data/users.ts | 60 ++- server/src/ability/ability.factory.ts | 4 + server/src/core/auth/services/auth.service.ts | 10 - server/src/core/user/user.service.ts | 8 + .../resolvers/workspace-member.resolver.ts | 18 + .../migration.sql | 40 ++ server/src/database/schema.prisma | 56 ++- server/src/database/seeds/users.ts | 60 ++- 23 files changed, 890 insertions(+), 115 deletions(-) create mode 100644 front/src/modules/workspace/graphql/mutations/updateWorkspaceMember.ts create mode 100644 server/src/database/migrations/20230905121813_add_fields_to_workspace_member/migration.sql diff --git a/front/src/__stories__/App.stories.tsx b/front/src/__stories__/App.stories.tsx index 75624836a..361348d7b 100644 --- a/front/src/__stories__/App.stories.tsx +++ b/front/src/__stories__/App.stories.tsx @@ -1,36 +1,29 @@ import { HelmetProvider } from 'react-helmet-async'; import { MemoryRouter } from 'react-router-dom'; import type { Meta, StoryObj } from '@storybook/react'; -import { useRecoilState } from 'recoil'; -import { currentUserState } from '@/auth/states/currentUserState'; +import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider'; +import { UserProvider } from '@/users/components/UserProvider'; import { App } from '~/App'; import { FullHeightStorybookLayout } from '~/testing/FullHeightStorybookLayout'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedUsersData } from '~/testing/mock-data/users'; - -const MockedAuth: React.FC = ({ children }) => { - const [, setCurrentUser] = useRecoilState(currentUserState); - - setCurrentUser(mockedUsersData[0]); - - return <>{children}; -}; const meta: Meta = { title: 'App/App', component: App, decorators: [ (Story) => ( - - - - - - - - - + + + + + + + + + + + ), ], parameters: { diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index e0c1cc13b..afdda833a 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -36,6 +36,10 @@ export type Activity = { title?: Maybe; type: ActivityType; updatedAt: Scalars['DateTime']; + workspaceMemberAssignee?: Maybe; + workspaceMemberAssigneeId?: Maybe; + workspaceMemberAuthor?: Maybe; + workspaceMemberAuthorId?: Maybe; }; export type ActivityCreateInput = { @@ -53,6 +57,8 @@ export type ActivityCreateInput = { title?: InputMaybe; type?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAssignee?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; }; export type ActivityCreateNestedOneWithoutActivityTargetsInput = { @@ -90,6 +96,10 @@ export type ActivityOrderByWithRelationInput = { title?: InputMaybe; type?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAssignee?: InputMaybe; + workspaceMemberAssigneeId?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; + workspaceMemberAuthorId?: InputMaybe; }; export type ActivityRelationFilter = { @@ -110,7 +120,9 @@ export enum ActivityScalarFieldEnum { Title = 'title', Type = 'type', UpdatedAt = 'updatedAt', - WorkspaceId = 'workspaceId' + WorkspaceId = 'workspaceId', + WorkspaceMemberAssigneeId = 'workspaceMemberAssigneeId', + WorkspaceMemberAuthorId = 'workspaceMemberAuthorId' } export type ActivityTarget = { @@ -358,6 +370,8 @@ export type ActivityUpdateInput = { title?: InputMaybe; type?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAssignee?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; }; export type ActivityUpdateManyWithoutAssigneeNestedInput = { @@ -372,6 +386,18 @@ export type ActivityUpdateManyWithoutAuthorNestedInput = { set?: InputMaybe>; }; +export type ActivityUpdateManyWithoutWorkspaceMemberAssigneeNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + +export type ActivityUpdateManyWithoutWorkspaceMemberAuthorNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type ActivityUpdateManyWithoutWorkspaceNestedInput = { connect?: InputMaybe>; disconnect?: InputMaybe>; @@ -398,6 +424,10 @@ export type ActivityWhereInput = { title?: InputMaybe; type?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAssignee?: InputMaybe; + workspaceMemberAssigneeId?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; + workspaceMemberAuthorId?: InputMaybe; }; export type ActivityWhereUniqueInput = { @@ -428,6 +458,8 @@ export type Attachment = { type: AttachmentType; updatedAt: Scalars['DateTime']; workspace: Workspace; + workspaceMemberAuthor?: Maybe; + workspaceMemberAuthorId?: Maybe; }; export type AttachmentCreateNestedManyWithoutActivityInput = { @@ -466,6 +498,12 @@ export type AttachmentUpdateManyWithoutAuthorNestedInput = { set?: InputMaybe>; }; +export type AttachmentUpdateManyWithoutWorkspaceMemberAuthorNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type AttachmentUpdateManyWithoutWorkspaceNestedInput = { connect?: InputMaybe>; disconnect?: InputMaybe>; @@ -486,6 +524,8 @@ export type AttachmentWhereInput = { name?: InputMaybe; type?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; + workspaceMemberAuthorId?: InputMaybe; }; export type AttachmentWhereUniqueInput = { @@ -547,6 +587,8 @@ export type Comment = { createdAt: Scalars['DateTime']; id: Scalars['ID']; updatedAt: Scalars['DateTime']; + workspaceMemberAuthor?: Maybe; + workspaceMemberAuthorId?: Maybe; }; export type CommentCreateInput = { @@ -557,6 +599,7 @@ export type CommentCreateInput = { createdAt?: InputMaybe; id?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; }; export type CommentCreateNestedManyWithoutActivityInput = { @@ -585,6 +628,12 @@ export type CommentUpdateManyWithoutAuthorNestedInput = { set?: InputMaybe>; }; +export type CommentUpdateManyWithoutWorkspaceMemberAuthorNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type CommentUpdateManyWithoutWorkspaceNestedInput = { connect?: InputMaybe>; disconnect?: InputMaybe>; @@ -604,6 +653,8 @@ export type CommentWhereInput = { createdAt?: InputMaybe; id?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAuthor?: InputMaybe; + workspaceMemberAuthorId?: InputMaybe; }; export type CommentWhereUniqueInput = { @@ -631,6 +682,8 @@ export type Company = { name: Scalars['String']; people?: Maybe>; updatedAt: Scalars['DateTime']; + workspaceMemberAccountOwner?: Maybe; + workspaceMemberAccountOwnerId?: Maybe; xUrl?: Maybe; }; @@ -650,6 +703,7 @@ export type CompanyCreateInput = { name: Scalars['String']; people?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAccountOwner?: InputMaybe; xUrl?: InputMaybe; }; @@ -665,6 +719,7 @@ export type CompanyCreateManyInput = { linkedinUrl?: InputMaybe; name: Scalars['String']; updatedAt?: InputMaybe; + workspaceMemberAccountOwnerId?: InputMaybe; xUrl?: InputMaybe; }; @@ -707,6 +762,8 @@ export type CompanyOrderByWithRelationInput = { name?: InputMaybe; people?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAccountOwner?: InputMaybe; + workspaceMemberAccountOwnerId?: InputMaybe; xUrl?: InputMaybe; }; @@ -729,6 +786,7 @@ export enum CompanyScalarFieldEnum { Name = 'name', UpdatedAt = 'updatedAt', WorkspaceId = 'workspaceId', + WorkspaceMemberAccountOwnerId = 'workspaceMemberAccountOwnerId', XUrl = 'xUrl' } @@ -748,6 +806,7 @@ export type CompanyUpdateInput = { name?: InputMaybe; people?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAccountOwner?: InputMaybe; xUrl?: InputMaybe; }; @@ -757,6 +816,12 @@ export type CompanyUpdateManyWithoutAccountOwnerNestedInput = { set?: InputMaybe>; }; +export type CompanyUpdateManyWithoutWorkspaceMemberAccountOwnerNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type CompanyUpdateManyWithoutWorkspaceNestedInput = { connect?: InputMaybe>; disconnect?: InputMaybe>; @@ -793,6 +858,8 @@ export type CompanyWhereInput = { name?: InputMaybe; people?: InputMaybe; updatedAt?: InputMaybe; + workspaceMemberAccountOwner?: InputMaybe; + workspaceMemberAccountOwnerId?: InputMaybe; xUrl?: InputMaybe; }; @@ -921,6 +988,12 @@ export type FavoriteUpdateManyWithoutPersonNestedInput = { set?: InputMaybe>; }; +export type FavoriteUpdateManyWithoutWorkspaceMemberNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type FavoriteWhereInput = { AND?: InputMaybe>; NOT?: InputMaybe>; @@ -988,6 +1061,7 @@ export type LoginToken = { export type Mutation = { __typename?: 'Mutation'; + UpdateOneWorkspaceMember: WorkspaceMember; allowImpersonation: WorkspaceMember; challenge: LoginToken; createEvent: Analytics; @@ -1044,6 +1118,12 @@ export type Mutation = { }; +export type MutationUpdateOneWorkspaceMemberArgs = { + data: WorkspaceMemberUpdateInput; + where: WorkspaceMemberWhereUniqueInput; +}; + + export type MutationAllowImpersonationArgs = { allowImpersonation: Scalars['Boolean']; }; @@ -2334,6 +2414,7 @@ export enum UserScalarFieldEnum { export type UserSettings = { __typename?: 'UserSettings'; + WorkspaceMember?: Maybe>; colorScheme: ColorScheme; createdAt: Scalars['DateTime']; id: Scalars['ID']; @@ -2343,6 +2424,7 @@ export type UserSettings = { }; export type UserSettingsOrderByWithRelationInput = { + WorkspaceMember?: InputMaybe; colorScheme?: InputMaybe; createdAt?: InputMaybe; id?: InputMaybe; @@ -2360,7 +2442,13 @@ export type UserSettingsUpdateOneRequiredWithoutUserNestedInput = { update?: InputMaybe; }; +export type UserSettingsUpdateOneWithoutWorkspaceMemberNestedInput = { + connect?: InputMaybe; + disconnect?: InputMaybe; +}; + export type UserSettingsUpdateWithoutUserInput = { + WorkspaceMember?: InputMaybe; colorScheme?: InputMaybe; createdAt?: InputMaybe; id?: InputMaybe; @@ -2372,6 +2460,7 @@ export type UserSettingsWhereInput = { AND?: InputMaybe>; NOT?: InputMaybe>; OR?: InputMaybe>; + WorkspaceMember?: InputMaybe; colorScheme?: InputMaybe; createdAt?: InputMaybe; id?: InputMaybe; @@ -2380,6 +2469,10 @@ export type UserSettingsWhereInput = { user?: InputMaybe; }; +export type UserSettingsWhereUniqueInput = { + id?: InputMaybe; +}; + export type UserUpdateInput = { assignedActivities?: InputMaybe; authoredActivities?: InputMaybe; @@ -2407,6 +2500,10 @@ export type UserUpdateOneRequiredWithoutAuthoredActivitiesNestedInput = { connect?: InputMaybe; }; +export type UserUpdateOneRequiredWithoutWorkspaceMemberNestedInput = { + connect?: InputMaybe; +}; + export type UserUpdateOneWithoutAssignedActivitiesNestedInput = { connect?: InputMaybe; disconnect?: InputMaybe; @@ -2904,48 +3001,137 @@ export type WorkspaceMember = { __typename?: 'WorkspaceMember'; Favorite?: Maybe>; allowImpersonation: Scalars['Boolean']; + assignedActivities?: Maybe>; + authoredActivities?: Maybe>; + authoredAttachments?: Maybe>; + comments?: Maybe>; + companies?: Maybe>; createdAt: Scalars['DateTime']; id: Scalars['ID']; + settings?: Maybe; + settingsId?: Maybe; updatedAt: Scalars['DateTime']; user: User; userId: Scalars['String']; workspace: Workspace; }; +export type WorkspaceMemberCreateNestedOneWithoutAssignedActivitiesInput = { + connect?: InputMaybe; +}; + +export type WorkspaceMemberCreateNestedOneWithoutAuthoredActivitiesInput = { + connect?: InputMaybe; +}; + +export type WorkspaceMemberCreateNestedOneWithoutCommentsInput = { + connect?: InputMaybe; +}; + +export type WorkspaceMemberCreateNestedOneWithoutCompaniesInput = { + connect?: InputMaybe; +}; + +export type WorkspaceMemberListRelationFilter = { + every?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; +}; + +export type WorkspaceMemberOrderByRelationAggregateInput = { + _count?: InputMaybe; +}; + export type WorkspaceMemberOrderByWithRelationInput = { Favorite?: InputMaybe; allowImpersonation?: InputMaybe; + assignedActivities?: InputMaybe; + authoredActivities?: InputMaybe; + authoredAttachments?: InputMaybe; + comments?: InputMaybe; + companies?: InputMaybe; createdAt?: InputMaybe; id?: InputMaybe; + settings?: InputMaybe; + settingsId?: InputMaybe; updatedAt?: InputMaybe; user?: InputMaybe; userId?: InputMaybe; }; +export type WorkspaceMemberRelationFilter = { + is?: InputMaybe; + isNot?: InputMaybe; +}; + export enum WorkspaceMemberScalarFieldEnum { AllowImpersonation = 'allowImpersonation', CreatedAt = 'createdAt', DeletedAt = 'deletedAt', Id = 'id', + SettingsId = 'settingsId', UpdatedAt = 'updatedAt', UserId = 'userId', WorkspaceId = 'workspaceId' } +export type WorkspaceMemberUpdateInput = { + Favorite?: InputMaybe; + allowImpersonation?: InputMaybe; + assignedActivities?: InputMaybe; + authoredActivities?: InputMaybe; + authoredAttachments?: InputMaybe; + comments?: InputMaybe; + companies?: InputMaybe; + createdAt?: InputMaybe; + id?: InputMaybe; + settings?: InputMaybe; + updatedAt?: InputMaybe; + user?: InputMaybe; +}; + +export type WorkspaceMemberUpdateManyWithoutSettingsNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type WorkspaceMemberUpdateManyWithoutWorkspaceNestedInput = { connect?: InputMaybe>; disconnect?: InputMaybe>; set?: InputMaybe>; }; +export type WorkspaceMemberUpdateOneWithoutAssignedActivitiesNestedInput = { + connect?: InputMaybe; + disconnect?: InputMaybe; +}; + +export type WorkspaceMemberUpdateOneWithoutAuthoredActivitiesNestedInput = { + connect?: InputMaybe; + disconnect?: InputMaybe; +}; + +export type WorkspaceMemberUpdateOneWithoutCompaniesNestedInput = { + connect?: InputMaybe; + disconnect?: InputMaybe; +}; + export type WorkspaceMemberWhereInput = { AND?: InputMaybe>; Favorite?: InputMaybe; NOT?: InputMaybe>; OR?: InputMaybe>; allowImpersonation?: InputMaybe; + assignedActivities?: InputMaybe; + authoredActivities?: InputMaybe; + authoredAttachments?: InputMaybe; + comments?: InputMaybe; + companies?: InputMaybe; createdAt?: InputMaybe; id?: InputMaybe; + settings?: InputMaybe; + settingsId?: InputMaybe; updatedAt?: InputMaybe; user?: InputMaybe; userId?: InputMaybe; @@ -3072,7 +3258,7 @@ export type CreateEventMutationVariables = Exact<{ export type CreateEventMutation = { __typename?: 'Mutation', createEvent: { __typename?: 'Analytics', success: boolean } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null } } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }; export type ChallengeMutationVariables = Exact<{ email: Scalars['String']; @@ -3087,7 +3273,7 @@ export type ImpersonateMutationVariables = Exact<{ }>; -export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null } } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type RenewTokenMutationVariables = Exact<{ refreshToken: Scalars['String']; @@ -3110,7 +3296,7 @@ export type VerifyMutationVariables = Exact<{ }>; -export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null } } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, canImpersonate: boolean, supportUserHash?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type CheckUserExistsQueryVariables = Exact<{ email: Scalars['String']; @@ -3124,7 +3310,7 @@ export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>; export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, debugMode: boolean, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean }, telemetry: { __typename?: 'Telemetry', enabled: boolean, anonymizationEnabled: boolean }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null } } }; -export type CompanyFieldsFragmentFragment = { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }; +export type CompanyFieldsFragmentFragment = { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }; export type DeleteManyCompaniesMutationVariables = Exact<{ ids?: InputMaybe | Scalars['String']>; @@ -3145,7 +3331,7 @@ export type InsertOneCompanyMutationVariables = Exact<{ }>; -export type InsertOneCompanyMutation = { __typename?: 'Mutation', createOneCompany: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } }; +export type InsertOneCompanyMutation = { __typename?: 'Mutation', createOneCompany: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } }; export type UpdateOneCompanyMutationVariables = Exact<{ where: CompanyWhereUniqueInput; @@ -3153,7 +3339,7 @@ export type UpdateOneCompanyMutationVariables = Exact<{ }>; -export type UpdateOneCompanyMutation = { __typename?: 'Mutation', updateOneCompany?: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } | null }; +export type UpdateOneCompanyMutation = { __typename?: 'Mutation', updateOneCompany?: { __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null } | null }; export type GetCompaniesQueryVariables = Exact<{ orderBy?: InputMaybe | CompanyOrderByWithRelationInput>; @@ -3393,7 +3579,7 @@ export type SearchCompanyQueryVariables = Exact<{ }>; -export type SearchCompanyQuery = { __typename?: 'Query', searchResults: Array<{ __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }> }; +export type SearchCompanyQuery = { __typename?: 'Query', searchResults: Array<{ __typename?: 'Company', address: string, createdAt: string, domainName: string, employees?: number | null, linkedinUrl?: string | null, xUrl?: string | null, annualRecurringRevenue?: number | null, idealCustomerProfile: boolean, id: string, name: string, _activityCount: number, accountOwner?: { __typename?: 'User', id: string, email: string, displayName: string, avatarUrl?: string | null } | null }> }; export type SearchPeopleQueryVariables = Exact<{ where?: InputMaybe; @@ -3445,12 +3631,12 @@ export type UpdateUserMutationVariables = Exact<{ }>; -export type UpdateUserMutation = { __typename?: 'Mutation', updateUser: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null } } | null, settings: { __typename?: 'UserSettings', id: string, locale: string, colorScheme: ColorScheme } } }; +export type UpdateUserMutation = { __typename?: 'Mutation', updateUser: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, locale: string, colorScheme: ColorScheme } } }; export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; -export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null } } | null, settings: { __typename?: 'UserSettings', id: string, locale: string, colorScheme: ColorScheme } } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, email: string, displayName: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null, canImpersonate: boolean, supportUserHash?: string | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } | null, settings: { __typename?: 'UserSettings', id: string, locale: string, colorScheme: ColorScheme } } }; export type GetUsersQueryVariables = Exact<{ [key: string]: never; }>; @@ -3598,6 +3784,14 @@ export type UploadWorkspaceLogoMutationVariables = Exact<{ export type UploadWorkspaceLogoMutation = { __typename?: 'Mutation', uploadWorkspaceLogo: string }; +export type UpdateOneWorkspaceMemberMutationVariables = Exact<{ + data: WorkspaceMemberUpdateInput; + where: WorkspaceMemberWhereUniqueInput; +}>; + + +export type UpdateOneWorkspaceMemberMutation = { __typename?: 'Mutation', UpdateOneWorkspaceMember: { __typename?: 'WorkspaceMember', id: string, allowImpersonation: boolean, workspace: { __typename?: 'Workspace', id: string, domainName?: string | null, displayName?: string | null, logo?: string | null, inviteHash?: string | null }, assignedActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredActivities?: Array<{ __typename?: 'Activity', id: string, title?: string | null }> | null, authoredAttachments?: Array<{ __typename?: 'Attachment', id: string, name: string, type: AttachmentType }> | null, settings?: { __typename?: 'UserSettings', id: string, colorScheme: ColorScheme, locale: string } | null, companies?: Array<{ __typename?: 'Company', id: string, name: string, domainName: string }> | null, comments?: Array<{ __typename?: 'Comment', id: string, body: string }> | null } }; + export type GetWorkspaceFromInviteHashQueryVariables = Exact<{ inviteHash: Scalars['String']; }>; @@ -3605,7 +3799,9 @@ export type GetWorkspaceFromInviteHashQueryVariables = Exact<{ export type GetWorkspaceFromInviteHashQuery = { __typename?: 'Query', findWorkspaceFromInviteHash: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null } }; -export type GetWorkspaceMembersQueryVariables = Exact<{ [key: string]: never; }>; +export type GetWorkspaceMembersQueryVariables = Exact<{ + where?: InputMaybe; +}>; export type GetWorkspaceMembersQuery = { __typename?: 'Query', workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: string, user: { __typename?: 'User', id: string, email: string, avatarUrl?: string | null, firstName?: string | null, lastName?: string | null, displayName: string } }> }; @@ -3698,6 +3894,33 @@ export const UserQueryFragmentFragmentDoc = gql` logo inviteHash } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } } settings { id @@ -3724,6 +3947,7 @@ export const CompanyFieldsFragmentFragmentDoc = gql` idealCustomerProfile id name + _activityCount } `; export const PersonFieldsFragmentFragmentDoc = gql` @@ -6138,6 +6362,33 @@ export const UpdateUserDocument = gql` logo inviteHash } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } } settings { id @@ -6194,6 +6445,33 @@ export const GetCurrentUserDocument = gql` logo inviteHash } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } } settings { id @@ -6965,6 +7243,75 @@ export function useUploadWorkspaceLogoMutation(baseOptions?: Apollo.MutationHook export type UploadWorkspaceLogoMutationHookResult = ReturnType; export type UploadWorkspaceLogoMutationResult = Apollo.MutationResult; export type UploadWorkspaceLogoMutationOptions = Apollo.BaseMutationOptions; +export const UpdateOneWorkspaceMemberDocument = gql` + mutation UpdateOneWorkspaceMember($data: WorkspaceMemberUpdateInput!, $where: WorkspaceMemberWhereUniqueInput!) { + UpdateOneWorkspaceMember(data: $data, where: $where) { + id + allowImpersonation + workspace { + id + domainName + displayName + logo + inviteHash + } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } + } +} + `; +export type UpdateOneWorkspaceMemberMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateOneWorkspaceMemberMutation__ + * + * To run a mutation, you first call `useUpdateOneWorkspaceMemberMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateOneWorkspaceMemberMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateOneWorkspaceMemberMutation, { data, loading, error }] = useUpdateOneWorkspaceMemberMutation({ + * variables: { + * data: // value for 'data' + * where: // value for 'where' + * }, + * }); + */ +export function useUpdateOneWorkspaceMemberMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateOneWorkspaceMemberDocument, options); + } +export type UpdateOneWorkspaceMemberMutationHookResult = ReturnType; +export type UpdateOneWorkspaceMemberMutationResult = Apollo.MutationResult; +export type UpdateOneWorkspaceMemberMutationOptions = Apollo.BaseMutationOptions; export const GetWorkspaceFromInviteHashDocument = gql` query GetWorkspaceFromInviteHash($inviteHash: String!) { findWorkspaceFromInviteHash(inviteHash: $inviteHash) { @@ -7003,8 +7350,8 @@ export type GetWorkspaceFromInviteHashQueryHookResult = ReturnType; export type GetWorkspaceFromInviteHashQueryResult = Apollo.QueryResult; export const GetWorkspaceMembersDocument = gql` - query GetWorkspaceMembers { - workspaceMembers: findManyWorkspaceMember { + query GetWorkspaceMembers($where: WorkspaceMemberWhereInput) { + workspaceMembers: findManyWorkspaceMember(where: $where) { id user { id @@ -7030,6 +7377,7 @@ export const GetWorkspaceMembersDocument = gql` * @example * const { data, loading, error } = useGetWorkspaceMembersQuery({ * variables: { + * where: // value for 'where' * }, * }); */ diff --git a/front/src/modules/activities/components/ActivityAssigneePicker.tsx b/front/src/modules/activities/components/ActivityAssigneePicker.tsx index f6b6f429c..d0d064cad 100644 --- a/front/src/modules/activities/components/ActivityAssigneePicker.tsx +++ b/front/src/modules/activities/components/ActivityAssigneePicker.tsx @@ -9,6 +9,7 @@ import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { Activity, + useGetWorkspaceMembersLazyQuery, User, useSearchUserQuery, useUpdateActivityMutation, @@ -38,6 +39,7 @@ export function ActivityAssigneePicker({ relationPickerSearchFilterScopedState, ); const [updateActivity] = useUpdateActivityMutation(); + const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery(); const users = useFilteredSearchEntityQuery({ queryHook: useSearchUserQuery, @@ -66,15 +68,28 @@ export function ActivityAssigneePicker({ fragment: ACTIVITY_UPDATE_FRAGMENT, }); - function handleEntitySelected( + async function handleEntitySelected( selectedUser: UserForSelect | null | undefined, ) { if (selectedUser) { + const workspaceMemberAssignee = ( + await getWorkspaceMember({ + variables: { + where: { + userId: { equals: selectedUser.id }, + }, + }, + }) + ).data?.workspaceMembers?.[0]; + updateActivity({ variables: { where: { id: activity.id }, data: { assignee: { connect: { id: selectedUser.id } }, + workspaceMemberAssignee: { + connect: { id: workspaceMemberAssignee?.id }, + }, }, }, optimisticResponse: { diff --git a/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts b/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts index c8a2c8237..e6beae913 100644 --- a/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts +++ b/front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts @@ -48,7 +48,13 @@ export function useOpenCreateActivityDrawer() { createdAt: now, updatedAt: now, author: { connect: { id: currentUser?.id ?? '' } }, + workspaceMemberAuthor: { + connect: { id: currentUser?.workspaceMember?.id ?? '' }, + }, assignee: { connect: { id: assigneeId ?? currentUser?.id ?? '' } }, + workspaceMemberAssignee: { + connect: { id: currentUser?.workspaceMember?.id ?? '' }, + }, type: type, activityTargets: { createMany: { diff --git a/front/src/modules/auth/graphql/fragments/userQueryFragment.ts b/front/src/modules/auth/graphql/fragments/userQueryFragment.ts index ab3841a0f..5e46d946c 100644 --- a/front/src/modules/auth/graphql/fragments/userQueryFragment.ts +++ b/front/src/modules/auth/graphql/fragments/userQueryFragment.ts @@ -20,6 +20,33 @@ export const USER_QUERY_FRAGMENT = gql` logo inviteHash } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } } settings { id diff --git a/front/src/modules/auth/hooks/useAuth.ts b/front/src/modules/auth/hooks/useAuth.ts index fa800f057..6e2ecb1ce 100644 --- a/front/src/modules/auth/hooks/useAuth.ts +++ b/front/src/modules/auth/hooks/useAuth.ts @@ -60,7 +60,21 @@ export function useAuth() { throw new Error('No verify result'); } - setCurrentUser(verifyResult.data?.verify.user); + if (!verifyResult.data?.verify.user.workspaceMember) { + throw new Error('No workspace member'); + } + + if (!verifyResult.data?.verify.user.workspaceMember.settings) { + throw new Error('No settings'); + } + + setCurrentUser({ + ...verifyResult.data?.verify.user, + workspaceMember: { + ...verifyResult.data?.verify.user.workspaceMember, + settings: verifyResult.data?.verify.user.workspaceMember.settings, + }, + }); setTokenPair(verifyResult.data?.verify.tokens); return verifyResult.data?.verify; diff --git a/front/src/modules/auth/states/currentUserState.ts b/front/src/modules/auth/states/currentUserState.ts index 54aeceb25..7c32b1b20 100644 --- a/front/src/modules/auth/states/currentUserState.ts +++ b/front/src/modules/auth/states/currentUserState.ts @@ -1,8 +1,32 @@ import { atom } from 'recoil'; -import { GetCurrentUserQuery } from '~/generated/graphql'; +import { + User, + UserSettings, + Workspace, + WorkspaceMember, +} from '~/generated/graphql'; -export type CurrentUser = GetCurrentUserQuery['currentUser']; +export type CurrentUser = Pick< + User, + | 'id' + | 'email' + | 'displayName' + | 'firstName' + | 'lastName' + | 'avatarUrl' + | 'canImpersonate' + | 'supportUserHash' +> & { + workspaceMember: Pick & { + workspace: Pick< + Workspace, + 'id' | 'displayName' | 'domainName' | 'inviteHash' | 'logo' + >; + settings: Pick; + }; + settings: Pick; +}; export const currentUserState = atom({ key: 'currentUserState', diff --git a/front/src/modules/companies/table/components/CompanyTable.tsx b/front/src/modules/companies/table/components/CompanyTable.tsx index cfda4eb76..ac95e9dfa 100644 --- a/front/src/modules/companies/table/components/CompanyTable.tsx +++ b/front/src/modules/companies/table/components/CompanyTable.tsx @@ -15,6 +15,7 @@ import { useTableViews } from '@/views/hooks/useTableViews'; import { UpdateOneCompanyMutationVariables, useGetCompaniesQuery, + useGetWorkspaceMembersLazyQuery, useUpdateOneCompanyMutation, } from '~/generated/graphql'; import { companiesFilters } from '~/pages/companies/companies-filters'; @@ -33,6 +34,7 @@ export function CompanyTable() { const [updateEntityMutation] = useUpdateOneCompanyMutation(); const upsertEntityTableItem = useUpsertEntityTableItem(); + const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery(); const { createView, deleteView, submitCurrentView, updateView } = useTableViews({ objectId: 'company', @@ -44,6 +46,40 @@ export function CompanyTable() { const { setContextMenuEntries } = useCompanyTableContextMenuEntries(); const { setActionBarEntries } = useCompanyTableActionBarEntries(); + async function updateCompany(variables: UpdateOneCompanyMutationVariables) { + const workspaceMemberAccountOwner = variables.data.accountOwner + ? ( + await getWorkspaceMember({ + variables: { + where: { + userId: { equals: variables.data.accountOwner.connect?.id }, + }, + }, + }) + ).data?.workspaceMembers?.[0] + : undefined; + + const data = { + ...variables.data, + workspaceMemberAccountOwner: { + connect: { id: workspaceMemberAccountOwner?.id }, + }, + }; + + updateEntityMutation({ + variables: { + ...variables, + data, + }, + onCompleted: (data) => { + if (!data.updateOneCompany) { + return; + } + upsertEntityTableItem(data.updateOneCompany); + }, + }); + } + return ( <> - updateEntityMutation({ - variables, - onCompleted: (data) => { - if (!data.updateOneCompany) { - return; - } - upsertEntityTableItem(data.updateOneCompany); - }, - }) - } + }) => updateCompany(variables)} /> diff --git a/front/src/modules/ui/theme/hooks/useColorScheme.ts b/front/src/modules/ui/theme/hooks/useColorScheme.ts index 0fc523fdc..147908383 100644 --- a/front/src/modules/ui/theme/hooks/useColorScheme.ts +++ b/front/src/modules/ui/theme/hooks/useColorScheme.ts @@ -2,20 +2,36 @@ import { useCallback } from 'react'; import { useRecoilState } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; -import { ColorScheme, useUpdateUserMutation } from '~/generated/graphql'; +import { + ColorScheme, + useUpdateOneWorkspaceMemberMutation, + useUpdateUserMutation, +} from '~/generated/graphql'; export function useColorScheme() { const [currentUser, setCurrentUser] = useRecoilState(currentUserState); const [updateUser] = useUpdateUserMutation(); + const [updateWorkspaceMember] = useUpdateOneWorkspaceMemberMutation(); - const colorScheme = !currentUser?.settings?.colorScheme - ? ColorScheme.System - : currentUser.settings.colorScheme; + const colorScheme = + !currentUser?.workspaceMember.settings?.colorScheme && + !currentUser?.settings?.colorScheme + ? ColorScheme.System + : currentUser.workspaceMember.settings?.colorScheme ?? + currentUser.settings.colorScheme; const setColorScheme = useCallback( async (value: ColorScheme) => { try { + // connect settings to workspace member if not already connected + await updateWorkspaceMember({ + variables: { + where: { id: currentUser?.workspaceMember.id }, + data: { settings: { connect: { id: currentUser?.settings.id } } }, + }, + }); + const result = await updateUser({ variables: { where: { @@ -29,13 +45,14 @@ export function useColorScheme() { }, }, }, - optimisticResponse: - currentUser && currentUser.settings - ? { - __typename: 'Mutation', - updateUser: { - __typename: 'User', - ...currentUser, + optimisticResponse: currentUser + ? { + __typename: 'Mutation', + updateUser: { + __typename: 'User', + ...currentUser, + workspaceMember: { + ...currentUser.workspaceMember, settings: { __typename: 'UserSettings', id: currentUser.settings.id, @@ -43,15 +60,34 @@ export function useColorScheme() { locale: currentUser.settings.locale, }, }, - } - : undefined, + settings: { + __typename: 'UserSettings', + id: currentUser.settings.id, + colorScheme: value, + locale: currentUser.settings.locale, + }, + }, + } + : undefined, update: (_cache, { data }) => { - if (data?.updateUser && currentUser) { + if ( + data?.updateUser.workspaceMember?.settings?.colorScheme && + currentUser + ) { setCurrentUser({ ...currentUser, + workspaceMember: { + ...currentUser.workspaceMember, + settings: { + ...currentUser.workspaceMember.settings, + colorScheme: + data.updateUser.workspaceMember.settings.colorScheme, + }, + }, settings: { ...currentUser.settings, - colorScheme: data?.updateUser.settings.colorScheme, + colorScheme: + data.updateUser.workspaceMember.settings.colorScheme, }, }); } @@ -63,7 +99,7 @@ export function useColorScheme() { } } catch (err) {} }, - [currentUser, updateUser, setCurrentUser], + [updateWorkspaceMember, currentUser, updateUser, setCurrentUser], ); return { diff --git a/front/src/modules/users/components/UserProvider.tsx b/front/src/modules/users/components/UserProvider.tsx index e8440237d..3e8aa31ac 100644 --- a/front/src/modules/users/components/UserProvider.tsx +++ b/front/src/modules/users/components/UserProvider.tsx @@ -14,8 +14,14 @@ export function UserProvider({ children }: React.PropsWithChildren) { if (!loading) { setIsLoading(false); } - if (data?.currentUser) { - setCurrentUser(data?.currentUser); + if (data?.currentUser?.workspaceMember?.settings) { + setCurrentUser({ + ...data.currentUser, + workspaceMember: { + ...data.currentUser.workspaceMember, + settings: data.currentUser.workspaceMember.settings, + }, + }); } }, [setCurrentUser, data, isLoading, loading]); diff --git a/front/src/modules/users/graphql/mutations/updateUser.ts b/front/src/modules/users/graphql/mutations/updateUser.ts index 993b1cef7..c741349b6 100644 --- a/front/src/modules/users/graphql/mutations/updateUser.ts +++ b/front/src/modules/users/graphql/mutations/updateUser.ts @@ -18,6 +18,33 @@ export const UPDATE_USER = gql` logo inviteHash } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } } settings { id diff --git a/front/src/modules/users/graphql/queries/getCurrentUser.ts b/front/src/modules/users/graphql/queries/getCurrentUser.ts index 6d326fb17..f587020b7 100644 --- a/front/src/modules/users/graphql/queries/getCurrentUser.ts +++ b/front/src/modules/users/graphql/queries/getCurrentUser.ts @@ -20,6 +20,33 @@ export const GET_CURRENT_USER = gql` logo inviteHash } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } } settings { id diff --git a/front/src/modules/workspace/graphql/mutations/updateWorkspaceMember.ts b/front/src/modules/workspace/graphql/mutations/updateWorkspaceMember.ts new file mode 100644 index 000000000..25c49c4c7 --- /dev/null +++ b/front/src/modules/workspace/graphql/mutations/updateWorkspaceMember.ts @@ -0,0 +1,47 @@ +import { gql } from '@apollo/client'; + +export const UPDATE_WORKSPACE_MEMBER = gql` + mutation UpdateOneWorkspaceMember( + $data: WorkspaceMemberUpdateInput! + $where: WorkspaceMemberWhereUniqueInput! + ) { + UpdateOneWorkspaceMember(data: $data, where: $where) { + id + allowImpersonation + workspace { + id + domainName + displayName + logo + inviteHash + } + assignedActivities { + id + title + } + authoredActivities { + id + title + } + authoredAttachments { + id + name + type + } + settings { + id + colorScheme + locale + } + companies { + id + name + domainName + } + comments { + id + body + } + } + } +`; diff --git a/front/src/modules/workspace/graphql/queries/getWorkspaceMembers.ts b/front/src/modules/workspace/graphql/queries/getWorkspaceMembers.ts index 5a670d7c5..8cb53ac6d 100644 --- a/front/src/modules/workspace/graphql/queries/getWorkspaceMembers.ts +++ b/front/src/modules/workspace/graphql/queries/getWorkspaceMembers.ts @@ -1,8 +1,8 @@ import { gql } from '@apollo/client'; export const GET_WORKSPACE_MEMBERS = gql` - query GetWorkspaceMembers { - workspaceMembers: findManyWorkspaceMember { + query GetWorkspaceMembers($where: WorkspaceMemberWhereInput) { + workspaceMembers: findManyWorkspaceMember(where: $where) { id user { id diff --git a/front/src/pages/impersonate/ImpersonateEffect.tsx b/front/src/pages/impersonate/ImpersonateEffect.tsx index fccf04ddf..6b8bb31df 100644 --- a/front/src/pages/impersonate/ImpersonateEffect.tsx +++ b/front/src/pages/impersonate/ImpersonateEffect.tsx @@ -38,7 +38,22 @@ export function ImpersonateEffect() { throw new Error('No impersonate result'); } - setCurrentUser(impersonateResult.data?.impersonate.user); + if (!impersonateResult.data?.impersonate.user.workspaceMember) { + throw new Error('No workspace member'); + } + + if (!impersonateResult.data?.impersonate.user.workspaceMember.settings) { + throw new Error('No workspace member settings'); + } + + setCurrentUser({ + ...impersonateResult.data.impersonate.user, + workspaceMember: { + ...impersonateResult.data.impersonate.user.workspaceMember, + settings: + impersonateResult.data.impersonate.user.workspaceMember.settings, + }, + }); setTokenPair(impersonateResult.data?.impersonate.tokens); return impersonateResult.data?.impersonate; diff --git a/front/src/testing/mock-data/users.ts b/front/src/testing/mock-data/users.ts index 24d4d9e7d..0b9ea41b6 100644 --- a/front/src/testing/mock-data/users.ts +++ b/front/src/testing/mock-data/users.ts @@ -1,6 +1,38 @@ -import { ColorScheme, GetCurrentUserQuery } from '~/generated/graphql'; +import { + ColorScheme, + User, + UserSettings, + Workspace, + WorkspaceMember, +} from '~/generated/graphql'; -type MockedUser = GetCurrentUserQuery['currentUser']; +type MockedUser = Pick< + User, + | 'id' + | 'email' + | 'displayName' + | 'firstName' + | 'lastName' + | 'avatarUrl' + | 'canImpersonate' + | 'supportUserHash' + | '__typename' +> & { + workspaceMember: Pick< + WorkspaceMember, + 'id' | 'allowImpersonation' | '__typename' + > & { + workspace: Pick< + Workspace, + 'id' | 'displayName' | 'domainName' | 'inviteHash' | 'logo' | '__typename' + >; + settings: Pick< + UserSettings, + 'id' | 'colorScheme' | 'locale' | '__typename' + >; + }; + settings: Pick; +}; export const avatarUrl = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMACwgICggHCwoJCg0MCw0RHBIRDw8RIhkaFBwpJCsqKCQnJy0yQDctMD0wJyc4TDk9Q0VISUgrNk9VTkZUQEdIRf/bAEMBDA0NEQ8RIRISIUUuJy5FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRf/dAAQAAv/aAAwDAQACEQMRAD8Ava1q728otYY98joSCTgZrnbXWdTtrhrfVZXWLafmcAEkdgR/hVltQku9Q8+OIEBcGOT+ID0PY1ka1KH2u8ToqnPLbmIqG7u6LtbQ7RXBRec4Uck9eKXcPWsKDWVnhWSL5kYcFelSf2m3901POh8jP//QoyIAnTuKpXsY82NsksUyWPU5q/L9z8RVK++/F/uCsVsaEURwgA4HtT9x9TUcf3KfUGh//9k='; @@ -30,6 +62,12 @@ export const mockedUsersData: Array = [ inviteHash: 'twenty.com-invite-hash', logo: workspaceLogoUrl, }, + settings: { + id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', + __typename: 'UserSettings', + locale: 'en', + colorScheme: ColorScheme.System, + }, }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', @@ -59,6 +97,12 @@ export const mockedUsersData: Array = [ inviteHash: 'twenty.com-invite-hash', logo: workspaceLogoUrl, }, + settings: { + id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', + __typename: 'UserSettings', + locale: 'en', + colorScheme: ColorScheme.System, + }, }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cdt7a', @@ -92,6 +136,12 @@ export const mockedOnboardingUsersData: Array = [ inviteHash: 'twenty.com-invite-hash-1', logo: '', }, + settings: { + id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', + __typename: 'UserSettings', + locale: 'en', + colorScheme: ColorScheme.System, + }, }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', @@ -122,6 +172,12 @@ export const mockedOnboardingUsersData: Array = [ inviteHash: 'twenty.com-invite-hash-2', logo: '', }, + settings: { + id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', + __typename: 'UserSettings', + locale: 'en', + colorScheme: ColorScheme.System, + }, }, settings: { id: '7dfbc3f7-6e5e-4128-957e-8d86808cde9y', diff --git a/server/src/ability/ability.factory.ts b/server/src/ability/ability.factory.ts index aa54d8f5f..5fca3d3a5 100644 --- a/server/src/ability/ability.factory.ts +++ b/server/src/ability/ability.factory.ts @@ -78,6 +78,10 @@ export class AbilityFactory { can(AbilityAction.Read, 'WorkspaceMember', { workspaceId: workspace.id }); can(AbilityAction.Delete, 'WorkspaceMember', { workspaceId: workspace.id }); cannot(AbilityAction.Delete, 'WorkspaceMember', { userId: user.id }); + can(AbilityAction.Update, 'WorkspaceMember', { + userId: user.id, + workspaceId: workspace.id, + }); // Company can(AbilityAction.Read, 'Company', { workspaceId: workspace.id }); diff --git a/server/src/core/auth/services/auth.service.ts b/server/src/core/auth/services/auth.service.ts index f725c4431..e68556228 100644 --- a/server/src/core/auth/services/auth.service.ts +++ b/server/src/core/auth/services/auth.service.ts @@ -89,11 +89,6 @@ export class AuthService { email: signUpInput.email, passwordHash, locale: 'en', - settings: { - create: { - locale: 'en', - }, - }, }, } as Prisma.UserCreateArgs, workspace.id, @@ -105,11 +100,6 @@ export class AuthService { email: signUpInput.email, passwordHash, locale: 'en', - settings: { - create: { - locale: 'en', - }, - }, }, } as Prisma.UserCreateArgs); } diff --git a/server/src/core/user/user.service.ts b/server/src/core/user/user.service.ts index 234575639..1256e4ae8 100644 --- a/server/src/core/user/user.service.ts +++ b/server/src/core/user/user.service.ts @@ -67,6 +67,12 @@ export class UserService { assert(workspace, 'workspace is missing', BadRequestException); + const userSettings = await this.prismaService.client.userSettings.create({ + data: { locale: 'en' }, + }); + + const settings = { connect: { id: userSettings.id } }; + // Create user const user = await this.prismaService.client.user.upsert({ where: { @@ -74,12 +80,14 @@ export class UserService { }, create: { ...(args.data as Prisma.UserCreateInput), + settings, workspaceMember: { create: { workspace: { connect: { id: workspace.id }, }, + settings, }, }, locale: 'en', diff --git a/server/src/core/workspace/resolvers/workspace-member.resolver.ts b/server/src/core/workspace/resolvers/workspace-member.resolver.ts index e7045bcf6..30a0f2ed1 100644 --- a/server/src/core/workspace/resolvers/workspace-member.resolver.ts +++ b/server/src/core/workspace/resolvers/workspace-member.resolver.ts @@ -2,6 +2,7 @@ import { Args, Query, Resolver, Mutation } from '@nestjs/graphql'; import { UseGuards } from '@nestjs/common'; import { accessibleBy } from '@casl/prisma'; +import { Prisma } from '@prisma/client'; import { WorkspaceMember } from 'src/core/@generated/workspace-member/workspace-member.model'; import { AbilityGuard } from 'src/guards/ability.guard'; @@ -9,6 +10,7 @@ import { CheckAbilities } from 'src/decorators/check-abilities.decorator'; import { DeleteWorkspaceMemberAbilityHandler, ReadWorkspaceMemberAbilityHandler, + UpdateWorkspaceMemberAbilityHandler, } from 'src/ability/handlers/workspace-member.ability-handler'; import { FindManyWorkspaceMemberArgs } from 'src/core/@generated/workspace-member/find-many-workspace-member.args'; import { UserAbility } from 'src/decorators/user-ability.decorator'; @@ -22,6 +24,7 @@ import { DeleteOneWorkspaceMemberArgs } from 'src/core/@generated/workspace-memb import { JwtAuthGuard } from 'src/guards/jwt.auth.guard'; import { AuthUser } from 'src/decorators/auth-user.decorator'; import { User } from 'src/core/@generated/user/user.model'; +import { UpdateOneWorkspaceMemberArgs } from 'src/core/@generated/workspace-member/update-one-workspace-member.args'; @UseGuards(JwtAuthGuard) @Resolver(() => WorkspaceMember) @@ -81,4 +84,19 @@ export class WorkspaceMemberResolver { select: prismaSelect.value, }); } + + @Mutation(() => WorkspaceMember) + @UseGuards(AbilityGuard) + @CheckAbilities(UpdateWorkspaceMemberAbilityHandler) + async UpdateOneWorkspaceMember( + @Args() args: UpdateOneWorkspaceMemberArgs, + @PrismaSelector({ modelName: 'WorkspaceMember' }) + prismaSelect: PrismaSelect<'WorkspaceMember'>, + ): Promise> { + return this.workspaceMemberService.update({ + data: args.data, + where: args.where, + select: prismaSelect.value, + } as Prisma.WorkspaceMemberUpdateArgs); + } } diff --git a/server/src/database/migrations/20230905121813_add_fields_to_workspace_member/migration.sql b/server/src/database/migrations/20230905121813_add_fields_to_workspace_member/migration.sql new file mode 100644 index 000000000..b50fd78ec --- /dev/null +++ b/server/src/database/migrations/20230905121813_add_fields_to_workspace_member/migration.sql @@ -0,0 +1,40 @@ +/* + Warnings: + + - Made the column `idealCustomerProfile` on table `companies` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "activities" ADD COLUMN "workspaceMemberAssigneeId" TEXT, +ADD COLUMN "workspaceMemberAuthorId" TEXT; + +-- AlterTable +ALTER TABLE "attachments" ADD COLUMN "workspaceMemberAuthorId" TEXT; + +-- AlterTable +ALTER TABLE "comments" ADD COLUMN "workspaceMemberAuthorId" TEXT; + +-- AlterTable +ALTER TABLE "companies" ADD COLUMN "workspaceMemberAccountOwnerId" TEXT, +ALTER COLUMN "idealCustomerProfile" SET NOT NULL; + +-- AlterTable +ALTER TABLE "workspace_members" ADD COLUMN "settingsId" TEXT; + +-- AddForeignKey +ALTER TABLE "workspace_members" ADD CONSTRAINT "workspace_members_settingsId_fkey" FOREIGN KEY ("settingsId") REFERENCES "user_settings"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "companies" ADD CONSTRAINT "companies_workspaceMemberAccountOwnerId_fkey" FOREIGN KEY ("workspaceMemberAccountOwnerId") REFERENCES "workspace_members"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "activities" ADD CONSTRAINT "activities_workspaceMemberAuthorId_fkey" FOREIGN KEY ("workspaceMemberAuthorId") REFERENCES "workspace_members"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "activities" ADD CONSTRAINT "activities_workspaceMemberAssigneeId_fkey" FOREIGN KEY ("workspaceMemberAssigneeId") REFERENCES "workspace_members"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "comments" ADD CONSTRAINT "comments_workspaceMemberAuthorId_fkey" FOREIGN KEY ("workspaceMemberAuthorId") REFERENCES "workspace_members"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "attachments" ADD CONSTRAINT "attachments_workspaceMemberAuthorId_fkey" FOREIGN KEY ("workspaceMemberAuthorId") REFERENCES "workspace_members"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/server/src/database/schema.prisma b/server/src/database/schema.prisma index 3dadfbe31..900074b54 100644 --- a/server/src/database/schema.prisma +++ b/server/src/database/schema.prisma @@ -138,7 +138,8 @@ model UserSettings { /// @Validator.IsString() locale String - user User? + user User? + WorkspaceMember WorkspaceMember[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -210,6 +211,15 @@ model WorkspaceMember { updatedAt DateTime @updatedAt Favorite Favorite[] + authoredActivities Activity[] @relation(name: "authoredActivities") + assignedActivities Activity[] @relation(name: "assignedActivities") + authoredAttachments Attachment[] @relation(name: "authoredAttachments") + settings UserSettings? @relation(fields: [settingsId], references: [id]) + settingsId String? + + companies Company[] + comments Comment[] + @@map("workspace_members") } @@ -242,13 +252,15 @@ model Company { /// @Validator.IsOptional() employees Int? - people Person[] - accountOwner User? @relation(fields: [accountOwnerId], references: [id], onDelete: SetNull) - accountOwnerId String? + people Person[] + accountOwner User? @relation(fields: [accountOwnerId], references: [id], onDelete: SetNull) + accountOwnerId String? + workspaceMemberAccountOwner WorkspaceMember? @relation(fields: [workspaceMemberAccountOwnerId], references: [id], onDelete: SetNull) + workspaceMemberAccountOwnerId String? /// @TypeGraphQL.omit(input: true, output: true) - workspace Workspace @relation(fields: [workspaceId], references: [id]) + workspace Workspace @relation(fields: [workspaceId], references: [id]) /// @TypeGraphQL.omit(input: true, output: true) - workspaceId String + workspaceId String /// @TypeGraphQL.omit(input: true, output: true) deletedAt DateTime? @@ -358,12 +370,20 @@ model Activity { attachments Attachment[] author User @relation(fields: [authorId], references: [id], name: "authoredActivities", onDelete: Cascade) authorId String - assignee User? @relation(fields: [assigneeId], references: [id], name: "assignedActivities", onDelete: SetNull) - assigneeId String? + + workspaceMemberAuthor WorkspaceMember? @relation(fields: [workspaceMemberAuthorId], references: [id], name: "authoredActivities", onDelete: Cascade) + workspaceMemberAuthorId String? + + assignee User? @relation(fields: [assigneeId], references: [id], name: "assignedActivities", onDelete: SetNull) + assigneeId String? + + workspaceMemberAssignee WorkspaceMember? @relation(fields: [workspaceMemberAssigneeId], references: [id], name: "assignedActivities", onDelete: SetNull) + workspaceMemberAssigneeId String? + /// @TypeGraphQL.omit(input: true, output: true) - workspace Workspace @relation(fields: [workspaceId], references: [id]) + workspace Workspace @relation(fields: [workspaceId], references: [id]) /// @TypeGraphQL.omit(input: true, output: true) - workspaceId String + workspaceId String /// @TypeGraphQL.omit(input: true, output: true) deletedAt DateTime? @@ -381,8 +401,12 @@ model Comment { /// @Validator.IsString() body String - author User @relation(fields: [authorId], references: [id], onDelete: Cascade) - authorId String + author User @relation(fields: [authorId], references: [id], onDelete: Cascade) + authorId String + + workspaceMemberAuthor WorkspaceMember? @relation(fields: [workspaceMemberAuthorId], references: [id], onDelete: Cascade) + workspaceMemberAuthorId String? + activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade) activityId String? commentThreadId String? @@ -550,8 +574,12 @@ model Attachment { /// @TypeGraphQL.omit(input: true, output: true) workspaceId String - author User @relation(fields: [authorId], references: [id], name: "authoredAttachments", onDelete: Cascade) - authorId String + author User @relation(fields: [authorId], references: [id], name: "authoredAttachments", onDelete: Cascade) + authorId String + + workspaceMemberAuthor WorkspaceMember? @relation(fields: [workspaceMemberAuthorId], references: [id], name: "authoredAttachments", onDelete: Cascade) + workspaceMemberAuthorId String? + activity Activity @relation(fields: [activityId], references: [id], onDelete: Cascade) activityId String diff --git a/server/src/database/seeds/users.ts b/server/src/database/seeds/users.ts index 23e9df803..c8f7ed977 100644 --- a/server/src/database/seeds/users.ts +++ b/server/src/database/seeds/users.ts @@ -1,5 +1,13 @@ import { PrismaClient } from '@prisma/client'; export const seedUsers = async (prisma: PrismaClient) => { + await prisma.userSettings.upsert({ + where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109' }, + update: {}, + create: { + id: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109', + locale: 'en', + }, + }); await prisma.user.upsert({ where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-b76aa0bfc102' }, update: {}, @@ -11,11 +19,7 @@ export const seedUsers = async (prisma: PrismaClient) => { locale: 'en', passwordHash: '$2b$10$66d.6DuQExxnrfI9rMqOg.U1XIYpagr6Lv05uoWLYbYmtK0HDIvS6', // Applecar2025 - settings: { - create: { - locale: 'en', - }, - }, + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109', avatarUrl: null, workspaceMember: { connectOrCreate: { @@ -24,12 +28,21 @@ export const seedUsers = async (prisma: PrismaClient) => { }, create: { workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-9dcb1084c109', }, }, }, }, }); + await prisma.userSettings.upsert({ + where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215' }, + update: {}, + create: { + id: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215', + locale: 'en', + }, + }); await prisma.user.upsert({ where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-b76aa0bfa408' }, update: {}, @@ -39,21 +52,26 @@ export const seedUsers = async (prisma: PrismaClient) => { lastName: 'Ive', email: 'jony.ive@apple.dev', locale: 'en', - settings: { - create: { - locale: 'en', - }, - }, + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215', avatarUrl: null, workspaceMember: { create: { id: 'twenty-7ef9d213-1c25-4d02-bf35-6aeccf7ea419', workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-2c4a2035a215', }, }, }, }); + await prisma.userSettings.upsert({ + where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328' }, + update: {}, + create: { + id: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328', + locale: 'en', + }, + }); await prisma.user.upsert({ where: { id: 'twenty-gk256b39-3ec3-4fe3-8997-b76aa0bfa408' }, update: {}, @@ -63,21 +81,26 @@ export const seedUsers = async (prisma: PrismaClient) => { lastName: 'Schiler', email: 'phil.schiler@apple.dev', locale: 'en', - settings: { - create: { - locale: 'en', - }, - }, + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328', avatarUrl: null, workspaceMember: { create: { id: 'twenty-7ed9d213-1c25-4d02-bf35-6aeccf7ea419', workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419', + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-8e1f2097b328', }, }, }, }); + await prisma.userSettings.upsert({ + where: { id: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430' }, + update: {}, + create: { + id: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430', + locale: 'en', + }, + }); await prisma.user.upsert({ where: { id: 'twenty-dev-gk256b39-3ec3-4fe3-8997-b76aa0boa408' }, update: {}, @@ -87,15 +110,12 @@ export const seedUsers = async (prisma: PrismaClient) => { lastName: 'Bochet', email: 'charles@twenty.dev', locale: 'en', - settings: { - create: { - locale: 'en', - }, - }, + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430', workspaceMember: { create: { id: 'twenty-dev-7ed9d213-1c25-4d02-bf35-6aeccf7oa419', workspaceId: 'twenty-dev-7ed9d212-1c25-4d02-bf25-6aeccf7ea420', + settingsId: 'twenty-ge256b39-3ec3-4fe3-8997-5e2d1049c430', }, }, },