Repository navigation
feat(sdk): user delete blockers UI #2042
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rohanchkrabrty
merged 12 commits into
feat/project-delete-blocked-dialog
from
feat/account-delete-dialog
Nov 5, 2026
+424
−41
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
91fc3bb
feat(sdk): add the account delete flow
rohanchkrabrty 681bd00
feat(sdk): put the account delete button behind enableUserDelete
rohanchkrabrty f6f23fa
feat(sdk): check the account delete before opening the dialog
rohanchkrabrty 802efc3
chore(sdk): drop comments from the account delete changes
rohanchkrabrty 2f40d3f
refactor(sdk): give the account confirmation its own dialog content
rohanchkrabrty 0409acf
feat(sdk): let DeleteBlockedDialog take titled blocker groups
rohanchkrabrty cf351eb
feat(sdk): show account delete blockers as one list
rohanchkrabrty fb2acb0
refactor(sdk): rename onAccountDeleted to onDeleteSuccess
rohanchkrabrty 98bcbbd
chore(client-demo): turn on account delete in the profile page
rohanchkrabrty 524fa2a
refactor(sdk): drop the payload cast in DeleteUserDialog
rohanchkrabrty b7dd7cf
fix(sdk): label the account delete acknowledgement checkbox
rohanchkrabrty bbb995d
fix(sdk): show the ownership transfer last in the account dialog
rohanchkrabrty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,12 @@ | ||
| import { ProfileView } from '@raystack/frontier/client'; | ||
|
|
||
| export default function Profile() { | ||
| return <ProfileView />; | ||
| return ( | ||
| <ProfileView | ||
| enableUserDelete | ||
| onDeleteSuccess={() => { | ||
| window.location.href = '/login'; | ||
| }} | ||
| /> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| export { | ||
| DeleteBlockedContent, | ||
| DeleteBlockedDialog, | ||
| DeleteBlockedDialogRoot | ||
| } from './delete-blocked-dialog'; | ||
|
|
||
3 changes: 3 additions & 0 deletions
3
web/sdk/client/components/delete-user-dialog/delete-user-dialog.module.css
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| .content { | ||
| width: 480px !important; | ||
| } |
226 changes: 226 additions & 0 deletions
226
web/sdk/client/components/delete-user-dialog/delete-user-dialog.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,226 @@ | ||
| 'use client'; | ||
|
|
||
| import { useCallback, useEffect, useState } from 'react'; | ||
| import { useMutation } from '@connectrpc/connect-query'; | ||
| import { FrontierServiceQueries } from '@raystack/proton/frontier'; | ||
| import { | ||
| AlertDialog, | ||
| Button, | ||
| Checkbox, | ||
| Field, | ||
| Flex, | ||
| Input, | ||
| Text, | ||
| toastManager | ||
| } from '@raystack/apsara'; | ||
| import type { DeleteBlockedPayload, Slot } from '../../../shared/types'; | ||
| import { useFrontier } from '../../contexts/FrontierContext'; | ||
| import { | ||
| GENERIC_DELETE_BLOCKED_MESSAGE, | ||
| toUserDeleteBlocked | ||
| } from '../../utils/delete-blockers'; | ||
| import { DeleteBlockedContent } from '../delete-blocked-dialog'; | ||
| import { handleConnectError } from '~/utils/error'; | ||
| import styles from './delete-user-dialog.module.css'; | ||
|
|
||
| const showErrorToast = () => | ||
| toastManager.add({ | ||
| title: 'Something went wrong', | ||
| description: 'Please try again later or contact support.', | ||
| type: 'error' | ||
| }); | ||
|
|
||
| type DeleteUserPayload = | ||
| | { step: 'confirm' } | ||
| | { | ||
| step: 'blocked'; | ||
| blocked: Extract<DeleteBlockedPayload, { entity: 'user' }>; | ||
| }; | ||
|
|
||
| export interface DeleteUserDialogHandle { | ||
| open: () => void; | ||
| } | ||
|
|
||
| const deleteStarters = new WeakMap<DeleteUserDialogHandle, () => void>(); | ||
|
|
||
| const createDeleteUserDialogHandle = (): DeleteUserDialogHandle => { | ||
| const handle: DeleteUserDialogHandle = { | ||
| open: () => deleteStarters.get(handle)?.() | ||
| }; | ||
| return handle; | ||
| }; | ||
|
|
||
| export interface DeleteUserDialogProps { | ||
| trigger?: Slot<{ onClick: () => void; loading: boolean }>; | ||
| handle?: DeleteUserDialogHandle; | ||
| onDeleted: () => void; | ||
| } | ||
|
|
||
| export function DeleteUserDialog({ | ||
| trigger, | ||
| handle, | ||
| onDeleted | ||
| }: DeleteUserDialogProps) { | ||
| const [dialogHandle] = useState(() => | ||
| AlertDialog.createHandle<DeleteUserPayload>() | ||
| ); | ||
| const { mutateAsync: checkCurrentUserDelete, isPending: isChecking } = | ||
| useMutation(FrontierServiceQueries.checkCurrentUserDelete); | ||
|
|
||
| const checkAndOpen = useCallback(async () => { | ||
| try { | ||
| const check = await checkCurrentUserDelete({}); | ||
| dialogHandle.openWithPayload( | ||
|
Shreyag02 marked this conversation as resolved.
Resolved
|
||
| check.canDelete | ||
| ? { step: 'confirm' } | ||
| : { step: 'blocked', blocked: toUserDeleteBlocked(check) } | ||
| ); | ||
| return check.canDelete; | ||
|
rohanchkrabrty marked this conversation as resolved.
Resolved
|
||
| } catch { | ||
| dialogHandle.close(); | ||
| showErrorToast(); | ||
| return false; | ||
| } | ||
| }, [checkCurrentUserDelete, dialogHandle]); | ||
|
|
||
| const startDelete = useCallback(() => { | ||
| void checkAndOpen(); | ||
| }, [checkAndOpen]); | ||
|
|
||
| useEffect(() => { | ||
| if (!handle) return; | ||
| deleteStarters.set(handle, startDelete); | ||
| return () => { | ||
| deleteStarters.delete(handle); | ||
| }; | ||
| }, [handle, startDelete]); | ||
|
|
||
|
rohanchkrabrty marked this conversation as resolved.
Resolved
|
||
| async function onDeleteRefused() { | ||
| if (await checkAndOpen()) { | ||
| toastManager.add({ | ||
| title: 'Unable to delete account', | ||
| description: GENERIC_DELETE_BLOCKED_MESSAGE, | ||
| type: 'error' | ||
| }); | ||
| } | ||
|
rohanchkrabrty marked this conversation as resolved.
Resolved
|
||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| {trigger?.({ onClick: startDelete, loading: isChecking })} | ||
| <AlertDialog handle={dialogHandle}> | ||
| {({ payload }) => { | ||
| if (!payload) return null; | ||
| return payload.step === 'blocked' ? ( | ||
| <DeleteBlockedContent | ||
| payload={payload.blocked} | ||
| close={() => dialogHandle.close()} | ||
| /> | ||
| ) : ( | ||
| <AlertDialog.Content className={styles.content}> | ||
| <DeleteUserForm | ||
| onDeleted={() => { | ||
| dialogHandle.close(); | ||
| onDeleted(); | ||
| }} | ||
| onDeleteRefused={onDeleteRefused} | ||
| /> | ||
| </AlertDialog.Content> | ||
| ); | ||
| }} | ||
| </AlertDialog> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| DeleteUserDialog.createHandle = createDeleteUserDialogHandle; | ||
|
|
||
| interface DeleteUserFormProps { | ||
| onDeleted: () => void; | ||
| onDeleteRefused: () => void; | ||
| } | ||
|
|
||
| function DeleteUserForm({ onDeleted, onDeleteRefused }: DeleteUserFormProps) { | ||
| const { user } = useFrontier(); | ||
| const [email, setEmail] = useState(''); | ||
| const [isAcknowledged, setIsAcknowledged] = useState(false); | ||
| const { mutateAsync: deleteCurrentUser, isPending } = useMutation( | ||
| FrontierServiceQueries.deleteCurrentUser | ||
| ); | ||
| const isEmailConfirmed = | ||
| !!user?.email && email.trim().toLowerCase() === user.email.toLowerCase(); | ||
|
|
||
| async function onDelete() { | ||
| try { | ||
| await deleteCurrentUser({}); | ||
| toastManager.add({ title: 'Account deleted', type: 'success' }); | ||
| onDeleted(); | ||
| } catch (error) { | ||
| handleConnectError(error, { | ||
| FailedPrecondition: onDeleteRefused, | ||
| Default: showErrorToast | ||
| }); | ||
| } | ||
| } | ||
|
rohanchkrabrty marked this conversation as resolved.
Resolved
|
||
|
|
||
| return ( | ||
| <> | ||
| <AlertDialog.Header> | ||
| <AlertDialog.Title>Delete Account</AlertDialog.Title> | ||
| </AlertDialog.Header> | ||
| <AlertDialog.Body> | ||
| <Flex direction="column" gap={6}> | ||
| <Text size="small" variant="secondary"> | ||
| This action is irreversible. Deleting your account will permanently | ||
| remove your account and associated data. Do you want to continue? | ||
| </Text> | ||
| <Field label="Please type your email id."> | ||
| <Input | ||
| size="large" | ||
| type="email" | ||
| value={email} | ||
| onChange={event => setEmail(event.target.value)} | ||
| data-test-id="frontier-sdk-delete-account-email-input" | ||
| /> | ||
| </Field> | ||
| <Flex gap={3} align="start" render={<label />}> | ||
| <Checkbox | ||
|
Shreyag02 marked this conversation as resolved.
Resolved
|
||
| checked={isAcknowledged} | ||
| onCheckedChange={(checked: boolean) => setIsAcknowledged(checked)} | ||
| data-test-id="frontier-sdk-delete-account-checkbox" | ||
| /> | ||
| <Text size="small" variant="secondary"> | ||
| I acknowledge that I understand my account and associated data | ||
| will be permanently deleted and want to proceed | ||
| </Text> | ||
| </Flex> | ||
| </Flex> | ||
| </AlertDialog.Body> | ||
| <AlertDialog.Footer> | ||
| <AlertDialog.Close | ||
| render={ | ||
| <Button | ||
| variant="outline" | ||
| color="neutral" | ||
| disabled={isPending} | ||
| data-test-id="frontier-sdk-delete-account-cancel-btn" | ||
| > | ||
| Cancel | ||
| </Button> | ||
| } | ||
| /> | ||
| <Button | ||
| variant="solid" | ||
| color="danger" | ||
| onClick={onDelete} | ||
| disabled={!isEmailConfirmed || !isAcknowledged || isPending} | ||
| loading={isPending} | ||
| loaderText="Deleting..." | ||
| data-test-id="frontier-sdk-delete-account-btn" | ||
|
rohanchkrabrty marked this conversation as resolved.
Resolved
|
||
| > | ||
| Delete | ||
| </Button> | ||
| </AlertDialog.Footer> | ||
| </> | ||
| ); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export { DeleteUserDialog } from './delete-user-dialog'; | ||
| export type { | ||
| DeleteUserDialogProps, | ||
| DeleteUserDialogHandle | ||
| } from './delete-user-dialog'; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.