@@ -28,6 +28,7 @@ import { useProfilePictureUpload } from '@/app/workspace/[workspaceId]/settings/
2828import { useBrandConfig } from '@/ee/whitelabeling'
2929import { useGeneralSettings , useUpdateGeneralSetting } from '@/hooks/queries/general-settings'
3030import {
31+ useDeleteAccount ,
3132 useResetPassword ,
3233 useUpdateUserProfile ,
3334 useUserProfile ,
@@ -79,6 +80,10 @@ export function General() {
7980 const [ showResetPasswordModal , setShowResetPasswordModal ] = useState ( false )
8081 const resetPassword = useResetPassword ( )
8182
83+ const [ showDeleteAccountModal , setShowDeleteAccountModal ] = useState ( false )
84+ const [ deleteConfirmText , setDeleteConfirmText ] = useState ( '' )
85+ const deleteAccount = useDeleteAccount ( )
86+
8287 const [ uploadError , setUploadError ] = useState < string | null > ( null )
8388
8489 const snapToGridValue = settings ?. snapToGridSize ?? 0
@@ -166,6 +171,23 @@ export function General() {
166171 }
167172 }
168173
174+ const handleDeleteAccountConfirm = async ( ) => {
175+ deleteAccount . mutate ( undefined , {
176+ onSuccess : async ( ) => {
177+ try {
178+ await Promise . all ( [ signOut ( ) , clearUserData ( ) ] )
179+ router . push ( '/login' )
180+ } catch ( error ) {
181+ logger . error ( 'Error during account cleanup' , { error } )
182+ router . push ( '/login' )
183+ }
184+ } ,
185+ onError : ( error ) => {
186+ logger . error ( 'Error deleting account:' , error )
187+ } ,
188+ } )
189+ }
190+
169191 const handleResetPasswordConfirm = async ( ) => {
170192 if ( ! profile ?. email ) return
171193
@@ -467,6 +489,20 @@ export function General() {
467489 time.
468490 </ p >
469491
492+ { isHosted && ! isAuthDisabled && (
493+ < div className = 'flex items-center justify-between border-t pt-4' >
494+ < div >
495+ < Label > Delete account</ Label >
496+ < p className = 'text-[var(--text-muted)] text-small' >
497+ Permanently delete your account and all associated data.
498+ </ p >
499+ </ div >
500+ < Button onClick = { ( ) => setShowDeleteAccountModal ( true ) } variant = 'active' >
501+ Delete account
502+ </ Button >
503+ </ div >
504+ ) }
505+
470506 { isTrainingEnabled && (
471507 < div className = 'flex items-center justify-between' >
472508 < Label htmlFor = 'training-controls' > Training controls</ Label >
@@ -500,6 +536,68 @@ export function General() {
500536 ) }
501537 </ div >
502538
539+ { /* Delete Account Confirmation Modal */ }
540+ < Modal
541+ open = { showDeleteAccountModal }
542+ onOpenChange = { ( open ) => {
543+ setShowDeleteAccountModal ( open )
544+ if ( ! open ) {
545+ setDeleteConfirmText ( '' )
546+ deleteAccount . reset ( )
547+ }
548+ } }
549+ >
550+ < ModalContent size = 'sm' >
551+ < ModalHeader > Delete Account</ ModalHeader >
552+ < ModalBody >
553+ < p className = 'text-[var(--text-secondary)]' >
554+ This will permanently delete your account and all associated data, including
555+ workspaces, workflows, API keys, and execution history.{ ' ' }
556+ < span className = 'text-[var(--text-error)]' > This action cannot be undone.</ span >
557+ </ p >
558+ < div className = 'mt-3' >
559+ < label
560+ htmlFor = 'delete-account-confirm'
561+ className = 'mb-1.5 block text-[var(--text-secondary)] text-sm'
562+ >
563+ Type{ ' ' }
564+ < span className = 'font-medium text-[var(--text-primary)]' > delete my account</ span > to
565+ confirm
566+ </ label >
567+ < input
568+ id = 'delete-account-confirm'
569+ type = 'text'
570+ value = { deleteConfirmText }
571+ onChange = { ( e ) => setDeleteConfirmText ( e . target . value ) }
572+ className = 'w-full rounded-md border border-[var(--border)] bg-transparent px-3 py-2 text-[var(--text-primary)] text-sm placeholder:text-[var(--text-tertiary)] focus:border-[var(--border-1)] focus:outline-none'
573+ placeholder = 'delete my account'
574+ disabled = { deleteAccount . isPending }
575+ />
576+ </ div >
577+ { deleteAccount . error && (
578+ < p className = 'mt-2 text-[var(--text-error)] text-small' >
579+ { deleteAccount . error . message }
580+ </ p >
581+ ) }
582+ </ ModalBody >
583+ < ModalFooter >
584+ < Button
585+ onClick = { ( ) => setShowDeleteAccountModal ( false ) }
586+ disabled = { deleteAccount . isPending }
587+ >
588+ Cancel
589+ </ Button >
590+ < Button
591+ variant = 'destructive'
592+ onClick = { handleDeleteAccountConfirm }
593+ disabled = { deleteAccount . isPending || deleteConfirmText !== 'delete my account' }
594+ >
595+ { deleteAccount . isPending ? 'Deleting...' : 'Delete Account' }
596+ </ Button >
597+ </ ModalFooter >
598+ </ ModalContent >
599+ </ Modal >
600+
503601 { /* Password Reset Confirmation Modal */ }
504602 < Modal open = { showResetPasswordModal } onOpenChange = { setShowResetPasswordModal } >
505603 < ModalContent size = 'sm' >
0 commit comments