diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index dbac2ed5d2..01010186ea 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -5443,7 +5443,7 @@ export type BillingSubscriptionFragmentFragment = { __typename?: 'BillingSubscri export type CurrentBillingSubscriptionFragmentFragment = { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null, views?: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } }; export type WorkspaceUrlsFragmentFragment = { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }; @@ -5462,7 +5462,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; -export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null, views?: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: string, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars?: any | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: string, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, calendarStartDay?: number | null, numberFormat?: WorkspaceMemberNumberFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, deletedWorkspaceMembers?: Array<{ __typename?: 'DeletedWorkspaceMember', id: string, avatarUrl?: string | null, userEmail: string, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', permissionFlags?: Array | null, objectsPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null, restrictedFields?: any | null }> | null, twoFactorAuthenticationMethodSummary?: Array<{ __typename?: 'TwoFactorAuthenticationMethodDTO', twoFactorAuthenticationMethodId: string, status: string, strategy: string }> | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: string, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, isTwoFactorAuthenticationEnforced: boolean, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, interval?: SubscriptionInterval | null, metadata: any, currentPeriodEnd?: string | null, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }>, billingSubscriptionItems?: Array<{ __typename?: 'BillingSubscriptionItemDTO', id: string, hasReachedCurrentPeriodCap: boolean, quantity?: number | null, stripePriceId: string, billingProduct: { __typename?: 'BillingLicensedProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } | { __typename?: 'BillingMeteredProduct', name: string, description: string, images?: Array | null, metadata: { __typename?: 'BillingProductMetadata', productKey: BillingProductKey, planKey: BillingPlanKey, priceUsageBased: BillingUsageType } } }> | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: string, status: SubscriptionStatus, metadata: any, phases: Array<{ __typename?: 'BillingSubscriptionSchedulePhase', start_date: number, end_date: number, items: Array<{ __typename?: 'BillingSubscriptionSchedulePhaseItem', price: string, quantity?: number | null }> }> }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, canAccessAllTools: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, canBeAssignedToUsers: boolean, canBeAssignedToAgents: boolean, canBeAssignedToApiKeys: boolean } | null, defaultAgent?: { __typename?: 'Agent', id: string } | null } | null, availableWorkspaces: { __typename?: 'AvailableWorkspaces', availableWorkspacesForSignIn: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }>, availableWorkspacesForSignUp: Array<{ __typename?: 'AvailableWorkspace', id: string, displayName?: string | null, loginToken?: string | null, inviteHash?: string | null, personalInviteToken?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } } }; export type ViewFieldFragmentFragment = { __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }; @@ -5649,6 +5649,11 @@ export type UpdateCoreViewSortMutationVariables = Exact<{ export type UpdateCoreViewSortMutation = { __typename?: 'Mutation', updateCoreViewSort: { __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string } }; +export type FindAllCoreViewsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: string, name: string, objectMetadataId: string, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: string | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: string | null, calendarLayout?: ViewCalendarLayout | null, viewFields: Array<{ __typename?: 'CoreViewField', id: string, fieldMetadataId: string, viewId: string, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: string, fieldMetadataId: string, operand: ViewFilterOperand, value: any, viewFilterGroupId?: string | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: string }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: string, parentViewFilterGroupId?: string | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: string }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: string, fieldMetadataId: string, direction: ViewSortDirection, viewId: string }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: string, fieldMetadataId: string, isVisible: boolean, fieldValue: string, position: number, viewId: string }> }> }; + export type FindManyCoreViewFieldsQueryVariables = Exact<{ viewId: Scalars['String']; }>; @@ -6180,96 +6185,6 @@ export const RoleFragmentFragmentDoc = gql` canBeAssignedToApiKeys } `; -export const ViewFieldFragmentFragmentDoc = gql` - fragment ViewFieldFragment on CoreViewField { - id - fieldMetadataId - viewId - isVisible - position - size - aggregateOperation - createdAt - updatedAt - deletedAt -} - `; -export const ViewFilterFragmentFragmentDoc = gql` - fragment ViewFilterFragment on CoreViewFilter { - id - fieldMetadataId - operand - value - viewFilterGroupId - positionInViewFilterGroup - subFieldName - viewId -} - `; -export const ViewFilterGroupFragmentFragmentDoc = gql` - fragment ViewFilterGroupFragment on CoreViewFilterGroup { - id - parentViewFilterGroupId - logicalOperator - positionInViewFilterGroup - viewId -} - `; -export const ViewSortFragmentFragmentDoc = gql` - fragment ViewSortFragment on CoreViewSort { - id - fieldMetadataId - direction - viewId -} - `; -export const ViewGroupFragmentFragmentDoc = gql` - fragment ViewGroupFragment on CoreViewGroup { - id - fieldMetadataId - isVisible - fieldValue - position - viewId -} - `; -export const ViewFragmentFragmentDoc = gql` - fragment ViewFragment on CoreView { - id - name - objectMetadataId - type - key - icon - position - isCompact - openRecordIn - kanbanAggregateOperation - kanbanAggregateOperationFieldMetadataId - anyFieldFilterValue - calendarFieldMetadataId - calendarLayout - viewFields { - ...ViewFieldFragment - } - viewFilters { - ...ViewFilterFragment - } - viewFilterGroups { - ...ViewFilterGroupFragment - } - viewSorts { - ...ViewSortFragment - } - viewGroups { - ...ViewGroupFragment - } -} - ${ViewFieldFragmentFragmentDoc} -${ViewFilterFragmentFragmentDoc} -${ViewFilterGroupFragmentFragmentDoc} -${ViewSortFragmentFragmentDoc} -${ViewGroupFragmentFragmentDoc}`; export const AvailableWorkspaceFragmentFragmentDoc = gql` fragment AvailableWorkspaceFragment on AvailableWorkspace { id @@ -6368,9 +6283,6 @@ export const UserQueryFragmentFragmentDoc = gql` id } isTwoFactorAuthenticationEnforced - views { - ...ViewFragment - } } availableWorkspaces { ...AvailableWorkspacesFragment @@ -6385,8 +6297,97 @@ ${WorkspaceUrlsFragmentFragmentDoc} ${CurrentBillingSubscriptionFragmentFragmentDoc} ${BillingSubscriptionFragmentFragmentDoc} ${RoleFragmentFragmentDoc} -${ViewFragmentFragmentDoc} ${AvailableWorkspacesFragmentFragmentDoc}`; +export const ViewFieldFragmentFragmentDoc = gql` + fragment ViewFieldFragment on CoreViewField { + id + fieldMetadataId + viewId + isVisible + position + size + aggregateOperation + createdAt + updatedAt + deletedAt +} + `; +export const ViewFilterFragmentFragmentDoc = gql` + fragment ViewFilterFragment on CoreViewFilter { + id + fieldMetadataId + operand + value + viewFilterGroupId + positionInViewFilterGroup + subFieldName + viewId +} + `; +export const ViewFilterGroupFragmentFragmentDoc = gql` + fragment ViewFilterGroupFragment on CoreViewFilterGroup { + id + parentViewFilterGroupId + logicalOperator + positionInViewFilterGroup + viewId +} + `; +export const ViewSortFragmentFragmentDoc = gql` + fragment ViewSortFragment on CoreViewSort { + id + fieldMetadataId + direction + viewId +} + `; +export const ViewGroupFragmentFragmentDoc = gql` + fragment ViewGroupFragment on CoreViewGroup { + id + fieldMetadataId + isVisible + fieldValue + position + viewId +} + `; +export const ViewFragmentFragmentDoc = gql` + fragment ViewFragment on CoreView { + id + name + objectMetadataId + type + key + icon + position + isCompact + openRecordIn + kanbanAggregateOperation + kanbanAggregateOperationFieldMetadataId + anyFieldFilterValue + calendarFieldMetadataId + calendarLayout + viewFields { + ...ViewFieldFragment + } + viewFilters { + ...ViewFilterFragment + } + viewFilterGroups { + ...ViewFilterGroupFragment + } + viewSorts { + ...ViewSortFragment + } + viewGroups { + ...ViewGroupFragment + } +} + ${ViewFieldFragmentFragmentDoc} +${ViewFilterFragmentFragmentDoc} +${ViewFilterGroupFragmentFragmentDoc} +${ViewSortFragmentFragmentDoc} +${ViewGroupFragmentFragmentDoc}`; export const WorkflowDiffFragmentFragmentDoc = gql` fragment WorkflowDiffFragment on WorkflowVersionStepChanges { triggerDiff @@ -12399,6 +12400,40 @@ export function useUpdateCoreViewSortMutation(baseOptions?: Apollo.MutationHookO export type UpdateCoreViewSortMutationHookResult = ReturnType; export type UpdateCoreViewSortMutationResult = Apollo.MutationResult; export type UpdateCoreViewSortMutationOptions = Apollo.BaseMutationOptions; +export const FindAllCoreViewsDocument = gql` + query FindAllCoreViews { + getCoreViews { + ...ViewFragment + } +} + ${ViewFragmentFragmentDoc}`; + +/** + * __useFindAllCoreViewsQuery__ + * + * To run a query within a React component, call `useFindAllCoreViewsQuery` and pass it any options that fit your needs. + * When your component renders, `useFindAllCoreViewsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFindAllCoreViewsQuery({ + * variables: { + * }, + * }); + */ +export function useFindAllCoreViewsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FindAllCoreViewsDocument, options); + } +export function useFindAllCoreViewsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FindAllCoreViewsDocument, options); + } +export type FindAllCoreViewsQueryHookResult = ReturnType; +export type FindAllCoreViewsLazyQueryHookResult = ReturnType; +export type FindAllCoreViewsQueryResult = Apollo.QueryResult; export const FindManyCoreViewFieldsDocument = gql` query FindManyCoreViewFields($viewId: String!) { getCoreViewFields(viewId: $viewId) { diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index f4b7e4aca6..3c5f5fd11a 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -4513,6 +4513,11 @@ export type UpdateCoreViewSortMutationVariables = Exact<{ export type UpdateCoreViewSortMutation = { __typename?: 'Mutation', updateCoreViewSort: { __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any } }; +export type FindAllCoreViewsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type FindAllCoreViewsQuery = { __typename?: 'Query', getCoreViews: Array<{ __typename?: 'CoreView', id: any, name: string, objectMetadataId: any, type: ViewType, key?: ViewKey | null, icon: string, position: number, isCompact: boolean, openRecordIn: ViewOpenRecordIn, kanbanAggregateOperation?: AggregateOperations | null, kanbanAggregateOperationFieldMetadataId?: any | null, anyFieldFilterValue?: string | null, calendarFieldMetadataId?: any | null, calendarLayout?: ViewCalendarLayout | null, viewFields: Array<{ __typename?: 'CoreViewField', id: any, fieldMetadataId: any, viewId: any, isVisible: boolean, position: number, size: number, aggregateOperation?: AggregateOperations | null, createdAt: string, updatedAt: string, deletedAt?: string | null }>, viewFilters: Array<{ __typename?: 'CoreViewFilter', id: any, fieldMetadataId: any, operand: ViewFilterOperand, value: any, viewFilterGroupId?: any | null, positionInViewFilterGroup?: number | null, subFieldName?: string | null, viewId: any }>, viewFilterGroups: Array<{ __typename?: 'CoreViewFilterGroup', id: any, parentViewFilterGroupId?: any | null, logicalOperator: ViewFilterGroupLogicalOperator, positionInViewFilterGroup?: number | null, viewId: any }>, viewSorts: Array<{ __typename?: 'CoreViewSort', id: any, fieldMetadataId: any, direction: ViewSortDirection, viewId: any }>, viewGroups: Array<{ __typename?: 'CoreViewGroup', id: any, fieldMetadataId: any, isVisible: boolean, fieldValue: string, position: number, viewId: any }> }> }; + export type FindManyCoreViewFieldsQueryVariables = Exact<{ viewId: Scalars['String']; }>; @@ -5682,6 +5687,40 @@ export function useUpdateCoreViewSortMutation(baseOptions?: Apollo.MutationHookO export type UpdateCoreViewSortMutationHookResult = ReturnType; export type UpdateCoreViewSortMutationResult = Apollo.MutationResult; export type UpdateCoreViewSortMutationOptions = Apollo.BaseMutationOptions; +export const FindAllCoreViewsDocument = gql` + query FindAllCoreViews { + getCoreViews { + ...ViewFragment + } +} + ${ViewFragmentFragmentDoc}`; + +/** + * __useFindAllCoreViewsQuery__ + * + * To run a query within a React component, call `useFindAllCoreViewsQuery` and pass it any options that fit your needs. + * When your component renders, `useFindAllCoreViewsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFindAllCoreViewsQuery({ + * variables: { + * }, + * }); + */ +export function useFindAllCoreViewsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FindAllCoreViewsDocument, options); + } +export function useFindAllCoreViewsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FindAllCoreViewsDocument, options); + } +export type FindAllCoreViewsQueryHookResult = ReturnType; +export type FindAllCoreViewsLazyQueryHookResult = ReturnType; +export type FindAllCoreViewsQueryResult = Apollo.QueryResult; export const FindManyCoreViewFieldsDocument = gql` query FindManyCoreViewFields($viewId: String!) { getCoreViewFields(viewId: $viewId) { diff --git a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx index dd08e368af..00a5a4cf8f 100644 --- a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx +++ b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx @@ -21,8 +21,8 @@ import { BaseThemeProvider } from '@/ui/theme/components/BaseThemeProvider'; import { UserThemeProviderEffect } from '@/ui/theme/components/UserThemeProviderEffect'; import { PageFavicon } from '@/ui/utilities/page-favicon/components/PageFavicon'; import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle'; +import { UserAndViewsProviderEffect } from '@/users/components/UserAndViewsProviderEffect'; import { UserProvider } from '@/users/components/UserProvider'; -import { UserProviderEffect } from '@/users/components/UserProviderEffect'; import { WorkspaceProviderEffect } from '@/workspace/components/WorkspaceProviderEffect'; import { StrictMode } from 'react'; import { Outlet, useLocation } from 'react-router-dom'; @@ -36,7 +36,7 @@ export const AppRouterProviders = () => { - + diff --git a/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx b/packages/twenty-front/src/modules/users/components/UserAndViewsProviderEffect.tsx similarity index 80% rename from packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx rename to packages/twenty-front/src/modules/users/components/UserAndViewsProviderEffect.tsx index 827b64ef4c..dcbf753d6f 100644 --- a/packages/twenty-front/src/modules/users/components/UserProviderEffect.tsx +++ b/packages/twenty-front/src/modules/users/components/UserAndViewsProviderEffect.tsx @@ -22,6 +22,7 @@ import { AppPath, type ObjectPermissions } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type WorkspaceMember, + useFindAllCoreViewsQuery, useGetCurrentUserQuery, } from '~/generated-metadata/graphql'; import { dateLocaleState } from '~/localization/states/dateLocaleState'; @@ -29,7 +30,7 @@ import { dynamicActivate } from '~/utils/i18n/dynamicActivate'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isMatchingLocation } from '~/utils/isMatchingLocation'; -export const UserProviderEffect = () => { +export const UserAndViewsProviderEffect = () => { const location = useLocation(); const [isCurrentUserLoaded, setIsCurrentUserLoaded] = useRecoilState( @@ -82,53 +83,57 @@ export const UserProviderEffect = () => { currentWorkspaceDeletedMembersState, ); - const { data: queryData, loading: queryLoading } = useGetCurrentUserQuery({ - skip: - !isLoggedIn || - isCurrentUserLoaded || - isMatchingLocation(location, AppPath.Verify) || - isMatchingLocation(location, AppPath.VerifyEmail), + const shouldSkip = + !isLoggedIn || + isCurrentUserLoaded || + isMatchingLocation(location, AppPath.Verify) || + isMatchingLocation(location, AppPath.VerifyEmail); + + const { data: userQueryData, loading: userQueryLoading } = + useGetCurrentUserQuery({ + skip: shouldSkip, + }); + + const { data: queryDataCoreViews } = useFindAllCoreViewsQuery({ + skip: shouldSkip, }); useEffect(() => { - if (!queryLoading) { + if (!userQueryLoading) { setIsCurrentUserLoaded(true); } - if (!isDefined(queryData?.currentUser)) return; + if (!isDefined(userQueryData?.currentUser)) return; - setCurrentUser(queryData.currentUser); + setCurrentUser(userQueryData.currentUser); - if (isDefined(queryData.currentUser.currentWorkspace)) { + if (isDefined(userQueryData.currentUser.currentWorkspace)) { setCurrentWorkspace({ - ...queryData.currentUser.currentWorkspace, - defaultRole: queryData.currentUser.currentWorkspace.defaultRole ?? null, + ...userQueryData.currentUser.currentWorkspace, + defaultRole: + userQueryData.currentUser.currentWorkspace.defaultRole ?? null, defaultAgent: - queryData.currentUser.currentWorkspace.defaultAgent ?? null, + userQueryData.currentUser.currentWorkspace.defaultAgent ?? null, }); } - if (isDefined(queryData.currentUser.currentUserWorkspace)) { + if (isDefined(userQueryData.currentUser.currentUserWorkspace)) { setCurrentUserWorkspace({ - ...queryData.currentUser.currentUserWorkspace, + ...userQueryData.currentUser.currentUserWorkspace, objectsPermissions: - (queryData.currentUser.currentUserWorkspace + (userQueryData.currentUser.currentUserWorkspace .objectsPermissions as Array< ObjectPermissions & { objectMetadataId: string } >) ?? [], }); } - if (isDefined(queryData.currentUser?.currentWorkspace?.views)) { - setCoreViews(queryData.currentUser.currentWorkspace.views); - } - const { workspaceMember, workspaceMembers, deletedWorkspaceMembers, availableWorkspaces, - } = queryData.currentUser; + } = userQueryData.currentUser; const affectDefaultValuesOnEmptyWorkspaceMemberFields = ( workspaceMember: WorkspaceMember, @@ -168,8 +173,8 @@ export const UserProviderEffect = () => { setAvailableWorkspaces(availableWorkspaces); } }, [ - queryLoading, - queryData?.currentUser, + userQueryLoading, + userQueryData?.currentUser, setCurrentUser, setCurrentUserWorkspace, setCurrentWorkspaceMembers, @@ -183,5 +188,11 @@ export const UserProviderEffect = () => { setCoreViews, ]); + useEffect(() => { + if (!isDefined(queryDataCoreViews?.getCoreViews)) return; + + setCoreViews(queryDataCoreViews.getCoreViews); + }, [queryDataCoreViews?.getCoreViews, setCoreViews]); + return null; }; diff --git a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts index ea645140c1..40ee486a43 100644 --- a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts +++ b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts @@ -4,14 +4,13 @@ import { } from '@/auth/graphql/fragments/authFragments'; import { OBJECT_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/objectPermissionFragment'; import { ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/roleFragment'; +import { BILLING_SUBSCRIPTION_FRAGMENT } from '@/users/graphql/fragments/billingSubscriptionsFragment'; +import { CURRENT_BILLING_SUBSCRIPTION_FRAGMENT } from '@/users/graphql/fragments/currentBillingSubscriptionFragement'; import { WORKSPACE_URLS_FRAGMENT } from '@/users/graphql/fragments/workspaceUrlsFragment'; -import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment'; import { DELETED_WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/deletedWorkspaceMemberQueryFragment'; import { PARTIAL_WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/partialWorkspaceMemberQueryFragment'; import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment'; import { gql } from '@apollo/client'; -import { CURRENT_BILLING_SUBSCRIPTION_FRAGMENT } from '@/users/graphql/fragments/currentBillingSubscriptionFragement'; -import { BILLING_SUBSCRIPTION_FRAGMENT } from '@/users/graphql/fragments/billingSubscriptionsFragment'; export const USER_QUERY_FRAGMENT = gql` fragment UserQueryFragment on User { @@ -80,9 +79,6 @@ export const USER_QUERY_FRAGMENT = gql` id } isTwoFactorAuthenticationEnforced - views { - ...ViewFragment - } } availableWorkspaces { ...AvailableWorkspacesFragment @@ -96,7 +92,6 @@ export const USER_QUERY_FRAGMENT = gql` ${OBJECT_PERMISSION_FRAGMENT} ${WORKSPACE_URLS_FRAGMENT} ${ROLE_FRAGMENT} - ${VIEW_FRAGMENT} ${AVAILABLE_WORKSPACES_FOR_AUTH_FRAGMENT} ${AVAILABLE_WORKSPACE_FOR_AUTH_FRAGMENT} ${CURRENT_BILLING_SUBSCRIPTION_FRAGMENT} diff --git a/packages/twenty-front/src/modules/users/hooks/useLoadCurrentUser.ts b/packages/twenty-front/src/modules/users/hooks/useLoadCurrentUser.ts index 105b043f7f..78f06a9909 100644 --- a/packages/twenty-front/src/modules/users/hooks/useLoadCurrentUser.ts +++ b/packages/twenty-front/src/modules/users/hooks/useLoadCurrentUser.ts @@ -14,7 +14,10 @@ import { SOURCE_LOCALE, type APP_LOCALES } from 'twenty-shared/translations'; import { type ObjectPermissions } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { type ColorScheme } from 'twenty-ui/input'; -import { useGetCurrentUserLazyQuery } from '~/generated-metadata/graphql'; +import { + useFindAllCoreViewsLazyQuery, + useGetCurrentUserLazyQuery, +} from '~/generated-metadata/graphql'; import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; import { dynamicActivate } from '~/utils/i18n/dynamicActivate'; @@ -37,12 +40,17 @@ export const useLoadCurrentUser = () => { const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace(); const [getCurrentUser] = useGetCurrentUserLazyQuery(); + const [findAllCoreViews] = useFindAllCoreViewsLazyQuery(); const loadCurrentUser = useCallback(async () => { const currentUserResult = await getCurrentUser({ fetchPolicy: 'network-only', }); + const coreViewsResult = await findAllCoreViews({ + fetchPolicy: 'network-only', + }); + if (isDefined(currentUserResult.error)) { throw new Error(currentUserResult.error.message); } @@ -102,8 +110,8 @@ export const useLoadCurrentUser = () => { }); } - if (isDefined(workspace) && isDefined(workspace.views)) { - setCoreViews(workspace.views); + if (isDefined(coreViewsResult.data?.getCoreViews)) { + setCoreViews(coreViewsResult.data.getCoreViews); } return { @@ -113,16 +121,17 @@ export const useLoadCurrentUser = () => { }; }, [ getCurrentUser, - isOnAWorkspace, - setAvailableWorkspaces, - setCoreViews, + findAllCoreViews, setCurrentUser, - setCurrentUserWorkspace, setCurrentWorkspace, - setCurrentWorkspaceMember, + isOnAWorkspace, setCurrentWorkspaceMembers, + setAvailableWorkspaces, + setCurrentUserWorkspace, + setCurrentWorkspaceMember, initializeFormatPreferences, setLastAuthenticateWorkspaceDomain, + setCoreViews, ]); return { diff --git a/packages/twenty-front/src/modules/views/graphql/queries/findAllCoreViews.ts b/packages/twenty-front/src/modules/views/graphql/queries/findAllCoreViews.ts new file mode 100644 index 0000000000..eb04d662de --- /dev/null +++ b/packages/twenty-front/src/modules/views/graphql/queries/findAllCoreViews.ts @@ -0,0 +1,11 @@ +import { VIEW_FRAGMENT } from '@/views/graphql/fragments/viewFragment'; +import { gql } from '@apollo/client'; + +export const FIND_ALL_CORE_VIEWS = gql` + ${VIEW_FRAGMENT} + query FindAllCoreViews { + getCoreViews { + ...ViewFragment + } + } +`; diff --git a/packages/twenty-front/src/modules/views/hooks/useIsViewAnyFieldFilterDifferentFromCurrentAnyFieldFilter.ts b/packages/twenty-front/src/modules/views/hooks/useIsViewAnyFieldFilterDifferentFromCurrentAnyFieldFilter.ts index 4927631cdb..d71b6c0a5c 100644 --- a/packages/twenty-front/src/modules/views/hooks/useIsViewAnyFieldFilterDifferentFromCurrentAnyFieldFilter.ts +++ b/packages/twenty-front/src/modules/views/hooks/useIsViewAnyFieldFilterDifferentFromCurrentAnyFieldFilter.ts @@ -1,7 +1,7 @@ import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; -import { isDefined } from 'twenty-shared/utils'; +import { isNonEmptyString } from '@sniptt/guards'; import { compareNonEmptyStrings } from '~/utils/compareNonEmptyStrings'; export const useIsViewAnyFieldFilterDifferentFromCurrentAnyFieldFilter = () => { @@ -12,10 +12,11 @@ export const useIsViewAnyFieldFilterDifferentFromCurrentAnyFieldFilter = () => { const viewAnyFieldFilterValue = currentView?.anyFieldFilterValue; - const viewAnyFieldFilterDifferentFromCurrentAnyFieldFilter = - anyFieldFilterValue === '' - ? isDefined(viewAnyFieldFilterValue) - : !compareNonEmptyStrings(viewAnyFieldFilterValue, anyFieldFilterValue); + const viewAnyFieldFilterDifferentFromCurrentAnyFieldFilter = isNonEmptyString( + anyFieldFilterValue, + ) + ? !compareNonEmptyStrings(viewAnyFieldFilterValue, anyFieldFilterValue) + : isNonEmptyString(viewAnyFieldFilterValue); return { viewAnyFieldFilterDifferentFromCurrentAnyFieldFilter }; }; diff --git a/packages/twenty-front/src/testing/decorators/PageDecorator.tsx b/packages/twenty-front/src/testing/decorators/PageDecorator.tsx index d57b07cc18..e36aa6659e 100644 --- a/packages/twenty-front/src/testing/decorators/PageDecorator.tsx +++ b/packages/twenty-front/src/testing/decorators/PageDecorator.tsx @@ -15,7 +15,7 @@ import { ClientConfigProviderEffect } from '@/client-config/components/ClientCon import { ApolloCoreClientMockedProvider } from '@/object-metadata/hooks/__mocks__/ApolloCoreClientMockedProvider'; import { DefaultLayout } from '@/ui/layout/page/components/DefaultLayout'; -import { UserProviderEffect } from '@/users/components/UserProviderEffect'; +import { UserAndViewsProviderEffect } from '@/users/components/UserAndViewsProviderEffect'; import { ClientConfigProvider } from '~/modules/client-config/components/ClientConfigProvider'; import { UserProvider } from '~/modules/users/components/UserProvider'; import { mockedApolloClient } from '~/testing/mockedApolloClient'; @@ -85,7 +85,7 @@ const Providers = () => { - + diff --git a/packages/twenty-server/src/engine/api/graphql/metadata.module-factory.ts b/packages/twenty-server/src/engine/api/graphql/metadata.module-factory.ts index 5b9655e0b2..0e21b85fce 100644 --- a/packages/twenty-server/src/engine/api/graphql/metadata.module-factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/metadata.module-factory.ts @@ -44,7 +44,7 @@ export const metadataModuleFactory = async ( useCachedMetadata({ cacheGetter: cacheStorageService.get.bind(cacheStorageService), cacheSetter: cacheStorageService.set.bind(cacheStorageService), - operationsToCache: ['ObjectMetadataItems'], + operationsToCache: ['ObjectMetadataItems', 'FindAllCoreViews'], }), ], path: '/metadata', diff --git a/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts b/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts index dbb95720dc..10ad62e3df 100644 --- a/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts +++ b/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts @@ -107,7 +107,7 @@ export class CacheStorageService { do { const result = await redisClient.scan(cursor, { - MATCH: scanPattern, + MATCH: `${this.namespace}:${scanPattern}`, COUNT: 100, }); diff --git a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-field.service.spec.ts b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-field.service.spec.ts index c3e83958de..1deef197e7 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-field.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-field.service.spec.ts @@ -53,6 +53,7 @@ describe('ViewFieldService', () => { provide: ViewService, useValue: { findByIdWithRelatedObjectMetadata: jest.fn(), + flushGraphQLCache: jest.fn(), }, }, ], diff --git a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter-group.service.spec.ts b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter-group.service.spec.ts index 414b186361..4232293890 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter-group.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter-group.service.spec.ts @@ -13,6 +13,7 @@ import { generateViewFilterGroupUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-filter-group.exception'; import { ViewFilterGroupService } from 'src/engine/core-modules/view/services/view-filter-group.service'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; describe('ViewFilterGroupService', () => { let viewFilterGroupService: ViewFilterGroupService; @@ -44,6 +45,12 @@ describe('ViewFilterGroupService', () => { delete: jest.fn(), }, }, + { + provide: ViewService, + useValue: { + flushGraphQLCache: jest.fn(), + }, + }, ], }).compile(); diff --git a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter.service.spec.ts b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter.service.spec.ts index 1cc6e763c1..99c2d92cf5 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-filter.service.spec.ts @@ -13,6 +13,7 @@ import { generateViewFilterUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-filter.exception'; import { ViewFilterService } from 'src/engine/core-modules/view/services/view-filter.service'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; describe('ViewFilterService', () => { let viewFilterService: ViewFilterService; @@ -35,6 +36,12 @@ describe('ViewFilterService', () => { const module: TestingModule = await Test.createTestingModule({ providers: [ ViewFilterService, + { + provide: ViewService, + useValue: { + flushGraphQLCache: jest.fn(), + }, + }, { provide: getRepositoryToken(ViewFilterEntity), useValue: { diff --git a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-group.service.spec.ts b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-group.service.spec.ts index c305abeb33..385f5c49f1 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-group.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-group.service.spec.ts @@ -12,6 +12,7 @@ import { generateViewGroupUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-group.exception'; import { ViewGroupService } from 'src/engine/core-modules/view/services/view-group.service'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; describe('ViewGroupService', () => { let viewGroupService: ViewGroupService; @@ -45,6 +46,12 @@ describe('ViewGroupService', () => { delete: jest.fn(), }, }, + { + provide: ViewService, + useValue: { + flushGraphQLCache: jest.fn(), + }, + }, ], }).compile(); diff --git a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-sort.service.spec.ts b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-sort.service.spec.ts index d60c83062e..99a6337302 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/tests/view-sort.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/tests/view-sort.service.spec.ts @@ -13,6 +13,7 @@ import { generateViewSortUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-sort.exception'; import { ViewSortService } from 'src/engine/core-modules/view/services/view-sort.service'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; describe('ViewSortService', () => { let viewSortService: ViewSortService; @@ -44,6 +45,12 @@ describe('ViewSortService', () => { delete: jest.fn(), }, }, + { + provide: ViewService, + useValue: { + flushGraphQLCache: jest.fn(), + }, + }, ], }).compile(); diff --git a/packages/twenty-server/src/engine/core-modules/view/services/tests/view.service.spec.ts b/packages/twenty-server/src/engine/core-modules/view/services/tests/view.service.spec.ts index e2114c88e2..3452756ebf 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/tests/view.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/tests/view.service.spec.ts @@ -15,6 +15,7 @@ import { generateViewUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view.exception'; import { ViewService } from 'src/engine/core-modules/view/services/view.service'; +import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; describe('ViewService', () => { let viewService: ViewService; @@ -56,6 +57,12 @@ describe('ViewService', () => { delete: jest.fn(), }, }, + { + provide: WorkspaceCacheStorageService, + useValue: { + flushGraphQLOperation: jest.fn(), + }, + }, { provide: I18nService, useValue: { diff --git a/packages/twenty-server/src/engine/core-modules/view/services/view-field.service.ts b/packages/twenty-server/src/engine/core-modules/view/services/view-field.service.ts index 8621938b20..61470c62b8 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/view-field.service.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/view-field.service.ts @@ -95,6 +95,9 @@ export class ViewFieldService { ); const savedViewField = await this.viewFieldRepository.save(viewField); + + await this.viewService.flushGraphQLCache(viewFieldData.workspaceId); + const createdViewField = await this.findById( savedViewField.id, viewFieldData.workspaceId, @@ -223,6 +226,8 @@ export class ViewFieldService { ...updateData, }); + await this.viewService.flushGraphQLCache(workspaceId); + return { ...existingViewField, ...updatedViewField }; } @@ -241,6 +246,8 @@ export class ViewFieldService { await this.viewFieldRepository.softDelete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return viewField; } @@ -259,6 +266,8 @@ export class ViewFieldService { await this.viewFieldRepository.delete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return viewField; } diff --git a/packages/twenty-server/src/engine/core-modules/view/services/view-filter-group.service.ts b/packages/twenty-server/src/engine/core-modules/view/services/view-filter-group.service.ts index f7fd2b090a..b3986fac3a 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/view-filter-group.service.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/view-filter-group.service.ts @@ -12,12 +12,14 @@ import { generateViewFilterGroupExceptionMessage, generateViewFilterGroupUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-filter-group.exception'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; @Injectable() export class ViewFilterGroupService { constructor( @InjectRepository(ViewFilterGroupEntity) private readonly viewFilterGroupRepository: Repository, + private readonly viewService: ViewService, ) {} async findByWorkspaceId( @@ -118,7 +120,14 @@ export class ViewFilterGroupService { const viewFilterGroup = this.viewFilterGroupRepository.create(viewFilterGroupData); - return this.viewFilterGroupRepository.save(viewFilterGroup); + await this.viewService.flushGraphQLCache(viewFilterGroupData.workspaceId); + + const savedViewFilterGroup = + await this.viewFilterGroupRepository.save(viewFilterGroup); + + await this.viewService.flushGraphQLCache(viewFilterGroupData.workspaceId); + + return savedViewFilterGroup; } async update( @@ -143,6 +152,8 @@ export class ViewFilterGroupService { ...updateData, }); + await this.viewService.flushGraphQLCache(workspaceId); + return { ...existingViewFilterGroup, ...updatedViewFilterGroup }; } @@ -182,6 +193,8 @@ export class ViewFilterGroupService { await this.viewFilterGroupRepository.delete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return true; } } diff --git a/packages/twenty-server/src/engine/core-modules/view/services/view-filter.service.ts b/packages/twenty-server/src/engine/core-modules/view/services/view-filter.service.ts index a045279bee..fa18a5d8e9 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/view-filter.service.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/view-filter.service.ts @@ -12,12 +12,14 @@ import { generateViewFilterExceptionMessage, generateViewFilterUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-filter.exception'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; @Injectable() export class ViewFilterService { constructor( @InjectRepository(ViewFilterEntity) private readonly viewFilterRepository: Repository, + private readonly viewService: ViewService, ) {} async findByWorkspaceId(workspaceId: string): Promise { @@ -109,7 +111,13 @@ export class ViewFilterService { const viewFilter = this.viewFilterRepository.create(viewFilterData); - return this.viewFilterRepository.save(viewFilter); + await this.viewService.flushGraphQLCache(viewFilterData.workspaceId); + + const savedViewFilter = await this.viewFilterRepository.save(viewFilter); + + await this.viewService.flushGraphQLCache(viewFilterData.workspaceId); + + return savedViewFilter; } async update( @@ -134,6 +142,8 @@ export class ViewFilterService { ...updateData, }); + await this.viewService.flushGraphQLCache(workspaceId); + return { ...existingViewFilter, ...updatedViewFilter }; } @@ -152,6 +162,8 @@ export class ViewFilterService { await this.viewFilterRepository.softDelete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return viewFilter; } @@ -170,6 +182,8 @@ export class ViewFilterService { await this.viewFilterRepository.delete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return true; } } diff --git a/packages/twenty-server/src/engine/core-modules/view/services/view-group.service.ts b/packages/twenty-server/src/engine/core-modules/view/services/view-group.service.ts index ba64d21888..4bbf71d603 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/view-group.service.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/view-group.service.ts @@ -12,12 +12,14 @@ import { generateViewGroupExceptionMessage, generateViewGroupUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-group.exception'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; @Injectable() export class ViewGroupService { constructor( @InjectRepository(ViewGroupEntity) private readonly viewGroupRepository: Repository, + private readonly viewService: ViewService, ) {} async findByWorkspaceId(workspaceId: string): Promise { @@ -109,7 +111,13 @@ export class ViewGroupService { const viewGroup = this.viewGroupRepository.create(viewGroupData); - return this.viewGroupRepository.save(viewGroup); + await this.viewService.flushGraphQLCache(viewGroupData.workspaceId); + + const savedViewGroup = await this.viewGroupRepository.save(viewGroup); + + await this.viewService.flushGraphQLCache(viewGroupData.workspaceId); + + return savedViewGroup; } async update( @@ -134,6 +142,8 @@ export class ViewGroupService { ...updateData, }); + await this.viewService.flushGraphQLCache(workspaceId); + return { ...existingViewGroup, ...updatedViewGroup }; } @@ -152,6 +162,8 @@ export class ViewGroupService { await this.viewGroupRepository.softDelete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return viewGroup; } @@ -170,6 +182,8 @@ export class ViewGroupService { await this.viewGroupRepository.delete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return true; } } diff --git a/packages/twenty-server/src/engine/core-modules/view/services/view-sort.service.ts b/packages/twenty-server/src/engine/core-modules/view/services/view-sort.service.ts index 03c868dea7..aaa019b629 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/view-sort.service.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/view-sort.service.ts @@ -12,12 +12,14 @@ import { generateViewSortExceptionMessage, generateViewSortUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view-sort.exception'; +import { ViewService } from 'src/engine/core-modules/view/services/view.service'; @Injectable() export class ViewSortService { constructor( @InjectRepository(ViewSortEntity) private readonly viewSortRepository: Repository, + private readonly viewService: ViewService, ) {} async findByWorkspaceId(workspaceId: string): Promise { @@ -105,7 +107,11 @@ export class ViewSortService { const viewSort = this.viewSortRepository.create(viewSortData); - return this.viewSortRepository.save(viewSort); + const savedViewSort = await this.viewSortRepository.save(viewSort); + + await this.viewService.flushGraphQLCache(viewSortData.workspaceId); + + return savedViewSort; } async update( @@ -130,6 +136,8 @@ export class ViewSortService { ...updateData, }); + await this.viewService.flushGraphQLCache(workspaceId); + return { ...existingViewSort, ...updatedViewSort }; } @@ -148,6 +156,8 @@ export class ViewSortService { await this.viewSortRepository.softDelete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return viewSort; } @@ -166,6 +176,8 @@ export class ViewSortService { await this.viewSortRepository.delete(id); + await this.viewService.flushGraphQLCache(workspaceId); + return true; } } diff --git a/packages/twenty-server/src/engine/core-modules/view/services/view.service.ts b/packages/twenty-server/src/engine/core-modules/view/services/view.service.ts index 9b83c4ff7b..491c6f0603 100644 --- a/packages/twenty-server/src/engine/core-modules/view/services/view.service.ts +++ b/packages/twenty-server/src/engine/core-modules/view/services/view.service.ts @@ -15,6 +15,7 @@ import { generateViewExceptionMessage, generateViewUserFriendlyExceptionMessage, } from 'src/engine/core-modules/view/exceptions/view.exception'; +import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service'; @Injectable() export class ViewService { @@ -22,6 +23,7 @@ export class ViewService { @InjectRepository(ViewEntity) private readonly viewRepository: Repository, private readonly i18nService: I18nService, + private readonly workspaceCacheStorageService: WorkspaceCacheStorageService, ) {} async findByWorkspaceId(workspaceId: string): Promise { @@ -134,7 +136,11 @@ export class ViewService { isCustom: true, }); - return this.viewRepository.save(view); + const savedView = await this.viewRepository.save(view); + + await this.flushGraphQLCache(viewData.workspaceId); + + return savedView; } async update( @@ -159,6 +165,8 @@ export class ViewService { ...updateData, }); + await this.flushGraphQLCache(workspaceId); + return { ...existingView, ...updatedView }; } @@ -177,6 +185,8 @@ export class ViewService { await this.viewRepository.softDelete(id); + await this.flushGraphQLCache(workspaceId); + return view; } @@ -194,6 +204,7 @@ export class ViewService { } await this.viewRepository.delete(id); + await this.flushGraphQLCache(workspaceId); return true; } @@ -235,4 +246,11 @@ export class ViewService { return viewName; } + + async flushGraphQLCache(workspaceId: string): Promise { + await this.workspaceCacheStorageService.flushGraphQLOperation({ + operationName: 'FindAllCoreViews', + workspaceId, + }); + } } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts index 160668f096..2cfff9b913 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/services/field-metadata-related-records.service.ts @@ -116,6 +116,7 @@ export class FieldMetadataRelatedRecordsService { await this.syncNoValueViewGroup(newFieldMetadata, view); } + await this.viewService.flushGraphQLCache(newFieldMetadata.workspaceId); } public async resetViewKanbanAggregateOperation( @@ -140,6 +141,7 @@ export class FieldMetadataRelatedRecordsService { kanbanAggregateOperation: null, }); } + await this.viewService.flushGraphQLCache(fieldMetadata.workspaceId); } public async updateRelatedViewFilters( @@ -247,6 +249,7 @@ export class FieldMetadataRelatedRecordsService { ); } } + await this.viewService.flushGraphQLCache(newFieldMetadata.workspaceId); } async syncNoValueViewGroup( @@ -274,6 +277,7 @@ export class FieldMetadataRelatedRecordsService { fieldMetadata.workspaceId, ); } + await this.viewService.flushGraphQLCache(fieldMetadata.workspaceId); } public getOptionsDifferences( diff --git a/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-related-records.service.ts b/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-related-records.service.ts index 1ae6953cd9..2d7325b98a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-related-records.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-metadata/services/object-metadata-related-records.service.ts @@ -34,6 +34,7 @@ export class ObjectMetadataRelatedRecordsService { await this.createViewFields(objectMetadata, view.id); await this.createViewWorkspaceFavorite(objectMetadata.workspaceId, view.id); + await this.viewService.flushGraphQLCache(objectMetadata.workspaceId); } public async updateLabelMetadataIdentifierInObjectViews({ diff --git a/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts b/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts index 84b2331067..e7f8391555 100644 --- a/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts +++ b/packages/twenty-server/src/engine/workspace-cache-storage/workspace-cache-storage.service.ts @@ -221,6 +221,18 @@ export class WorkspaceCacheStorageService { ); } + async flushGraphQLOperation({ + operationName, + workspaceId, + }: { + operationName: string; + workspaceId: string; + }): Promise { + await this.cacheStorageService.flushByPattern( + `${WorkspaceCacheKeys.GraphQLOperations}:${operationName}:${workspaceId}:*`, + ); + } + async flushVersionedMetadata( workspaceId: string, metadataVersion?: number,