diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 34a1d1c515d..11ee366f987 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1813,6 +1813,7 @@ export type Mutation = { deleteSSOIdentityProvider: DeleteSsoOutput; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; deleteUser: User; + deleteUserFromWorkspace: UserWorkspace; deleteWebhook: Scalars['Boolean']; deleteWorkflowVersionEdge: WorkflowVersionStepChanges; deleteWorkflowVersionStep: WorkflowVersionStepChanges; @@ -2295,6 +2296,11 @@ export type MutationDeleteTwoFactorAuthenticationMethodArgs = { }; +export type MutationDeleteUserFromWorkspaceArgs = { + workspaceMemberIdToDelete: Scalars['String']; +}; + + export type MutationDeleteWebhookArgs = { input: DeleteWebhookInput; }; @@ -5917,6 +5923,13 @@ export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; } export type DeleteUserAccountMutation = { __typename?: 'Mutation', deleteUser: { __typename?: 'User', id: string } }; +export type DeleteUserWorkspaceMutationVariables = Exact<{ + workspaceMemberIdToDelete: Scalars['String']; +}>; + + +export type DeleteUserWorkspaceMutation = { __typename?: 'Mutation', deleteUserFromWorkspace: { __typename?: 'UserWorkspace', id: string } }; + export type UploadProfilePictureMutationVariables = Exact<{ file: Scalars['Upload']; }>; @@ -12389,6 +12402,39 @@ export function useDeleteUserAccountMutation(baseOptions?: Apollo.MutationHookOp export type DeleteUserAccountMutationHookResult = ReturnType; export type DeleteUserAccountMutationResult = Apollo.MutationResult; export type DeleteUserAccountMutationOptions = Apollo.BaseMutationOptions; +export const DeleteUserWorkspaceDocument = gql` + mutation DeleteUserWorkspace($workspaceMemberIdToDelete: String!) { + deleteUserFromWorkspace(workspaceMemberIdToDelete: $workspaceMemberIdToDelete) { + id + } +} + `; +export type DeleteUserWorkspaceMutationFn = Apollo.MutationFunction; + +/** + * __useDeleteUserWorkspaceMutation__ + * + * To run a mutation, you first call `useDeleteUserWorkspaceMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteUserWorkspaceMutation` 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 [deleteUserWorkspaceMutation, { data, loading, error }] = useDeleteUserWorkspaceMutation({ + * variables: { + * workspaceMemberIdToDelete: // value for 'workspaceMemberIdToDelete' + * }, + * }); + */ +export function useDeleteUserWorkspaceMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(DeleteUserWorkspaceDocument, options); + } +export type DeleteUserWorkspaceMutationHookResult = ReturnType; +export type DeleteUserWorkspaceMutationResult = Apollo.MutationResult; +export type DeleteUserWorkspaceMutationOptions = Apollo.BaseMutationOptions; export const UploadProfilePictureDocument = gql` mutation UploadProfilePicture($file: Upload!) { uploadProfilePicture(file: $file) { diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index dea848eb6df..d13f9251df9 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -1768,6 +1768,7 @@ export type Mutation = { deleteSSOIdentityProvider: DeleteSsoOutput; deleteTwoFactorAuthenticationMethod: DeleteTwoFactorAuthenticationMethodOutput; deleteUser: User; + deleteUserFromWorkspace: UserWorkspace; deleteWebhook: Scalars['Boolean']; deleteWorkflowVersionEdge: WorkflowVersionStepChanges; deleteWorkflowVersionStep: WorkflowVersionStepChanges; @@ -2226,6 +2227,11 @@ export type MutationDeleteTwoFactorAuthenticationMethodArgs = { }; +export type MutationDeleteUserFromWorkspaceArgs = { + workspaceMemberIdToDelete: Scalars['String']; +}; + + export type MutationDeleteWebhookArgs = { input: DeleteWebhookInput; }; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts index 8967982e9c0..16bdb20b740 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts @@ -85,7 +85,7 @@ export const useFindManyRecords = ({ const hasReadPermission = objectPermissions.canReadObjectRecords; - const { data, loading, error, fetchMore } = + const { data, loading, error, fetchMore, refetch } = useQuery(findManyRecordsQuery, { skip: skip || !objectMetadataItem || !hasReadPermission, variables: { @@ -126,5 +126,6 @@ export const useFindManyRecords = ({ queryIdentifier, hasNextPage, pageInfo, + refetch, }; }; diff --git a/packages/twenty-front/src/modules/settings/profile/components/DeleteAccount.tsx b/packages/twenty-front/src/modules/settings/profile/components/DeleteAccount.tsx index 4bb942f524a..06503647a12 100644 --- a/packages/twenty-front/src/modules/settings/profile/components/DeleteAccount.tsx +++ b/packages/twenty-front/src/modules/settings/profile/components/DeleteAccount.tsx @@ -1,43 +1,111 @@ import { useRecoilValue } from 'recoil'; import { useAuth } from '@/auth/hooks/useAuth'; +import { availableWorkspacesState } from '@/auth/states/availableWorkspacesState'; import { currentUserState } from '@/auth/states/currentUserState'; +import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; +import { countAvailableWorkspaces } from '@/auth/utils/availableWorkspacesUtils'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; +import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; +import { isDefined } from 'twenty-shared/utils'; import { H2Title } from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; -import { useDeleteUserAccountMutation } from '~/generated-metadata/graphql'; +import { + useDeleteUserAccountMutation, + useDeleteUserWorkspaceMutation, +} from '~/generated-metadata/graphql'; const DELETE_ACCOUNT_MODAL_ID = 'delete-account-modal'; +const LEAVE_WORKSPACE_MODAL_ID = 'leave-workspace-modal'; + +const StyledDiv = styled.div` + margin-bottom: ${({ theme }) => theme.spacing(2)}; +`; + export const DeleteAccount = () => { const { t } = useLingui(); const { openModal } = useModal(); + const { enqueueErrorSnackBar } = useSnackBar(); const [deleteUserAccount] = useDeleteUserAccountMutation(); + const [deleteUserFromWorkspace] = useDeleteUserWorkspaceMutation(); const currentUser = useRecoilValue(currentUserState); const userEmail = currentUser?.email; + const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); + const currentWorkspaceMemberId = currentWorkspaceMember?.id; const { signOut } = useAuth(); + const availableWorkspaces = useRecoilValue(availableWorkspacesState); + const availableWorkspacesCount = + countAvailableWorkspaces(availableWorkspaces); + + const userHasMultipleWorkspaces = availableWorkspacesCount > 1; const deleteAccount = async () => { await deleteUserAccount(); await signOut(); }; + const leaveWorkspace = async () => { + if (!isDefined(currentWorkspaceMemberId)) { + enqueueErrorSnackBar({ + message: t`Current workspace member not found.`, + }); + return; + } + + await deleteUserFromWorkspace?.({ + variables: { + workspaceMemberIdToDelete: currentWorkspaceMemberId, + }, + }); + await signOut(); + }; + return ( <> + {userHasMultipleWorkspaces && ( + +