Keep synced messages and events when removing a workspace member (#21443)
## Context Removing a workspace member deletes their connected accounts, which cascades into deleting every message and calendar event those accounts synced. For a CRM, losing the email history of departed teammates is a big deal. ## What this does Connected accounts are now kept and reassigned instead of deleted when a member is removed: - Ownership moves to the acting user (whoever removed the member). When members remove themselves (leave workspace, account deletion), it falls back to the oldest admin. - OAuth tokens are revoked, credentials wiped, message/calendar channels get `isSyncEnabled = false`, and the account is stamped with a new `archivedAt` column (fast instance command included). - Synced messages, threads and calendar events stay in the workspace. Channel visibility settings keep applying as before, since channels and associations survive. - The reassigned account appears in the new owner's Settings → Accounts, where it can still be deleted (with its data) like any other account. The transfer happens synchronously during removal, while the member's userWorkspace row still exists. This also removes `DeleteWorkspaceMemberConnectedAccountsCleanupJob` and its listener: the async job had to reconstruct the account-owner link from rows the removal flow had just deleted, which was race-prone (see 2181fb541e). Archived accounts are excluded from the workflow send-email default account resolution, and both removal confirmation modals now mention what happens to synced data. --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This commit is contained in:
+14
@@ -4,6 +4,7 @@ import { SyncStatus } from '@/settings/accounts/constants/SyncStatus';
|
||||
import { computeSyncStatus } from '@/settings/accounts/utils/computeSyncStatus';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Status } from 'twenty-ui-deprecated/display';
|
||||
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
|
||||
|
||||
@@ -21,8 +22,21 @@ export const SettingsAccountsConnectedAccountsRowRightContainer = ({
|
||||
const messageChannel = account.messageChannels[0];
|
||||
const calendarChannel = account.calendarChannels[0];
|
||||
|
||||
const isArchived = isDefined(account.archivedAt);
|
||||
|
||||
const status = computeSyncStatus(messageChannel, calendarChannel);
|
||||
|
||||
// Archived accounts are frozen (owner left the workspace): their synced data
|
||||
// is kept but sync is disabled, so the live sync status is no longer relevant.
|
||||
if (isArchived) {
|
||||
return (
|
||||
<StyledRowRightContainer>
|
||||
<Status color="gray" text={t`Archived`} weight="medium" />
|
||||
<SettingsAccountsRowDropdownMenu account={account} />
|
||||
</StyledRowRightContainer>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledRowRightContainer>
|
||||
{status === SyncStatus.FAILED && (
|
||||
|
||||
+1
@@ -7,6 +7,7 @@ export const GET_MY_CONNECTED_ACCOUNTS = gql`
|
||||
handle
|
||||
provider
|
||||
authFailedAt
|
||||
archivedAt
|
||||
scopes
|
||||
handleAliases
|
||||
lastSignedInAt
|
||||
|
||||
@@ -101,7 +101,7 @@ export const DeleteAccount = () => {
|
||||
title={t`Leave workspace`}
|
||||
subtitle={
|
||||
<>
|
||||
{t`This action cannot be undone. This will permanently remove your membership from this workspace.`}
|
||||
{t`This action cannot be undone. Your membership will be removed; synced emails and calendars stay with the workspace.`}
|
||||
<br />
|
||||
{t`Please type in your email to confirm.`}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user