@@ -403,6 +403,19 @@ <h4>Change Password</h4>
403403 </ p >
404404 </ div >
405405 {% endif %}
406+
407+ <!-- Danger Zone Section -->
408+ < div class ="danger-zone ">
409+ < h4 > Danger Zone</ h4 >
410+ < p class ="help-text "> Permanently delete your account and all associated data. This action cannot be undone.</ p >
411+ < button type ="button " class ="btn btn-danger " onclick ="openDeleteAccountModal() ">
412+ < svg width ="16 " height ="16 " viewBox ="0 0 24 24 " fill ="none " style ="margin-right: 6px; ">
413+ < path d ="M3 6H5H21 " stroke ="currentColor " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round "/>
414+ < path d ="M8 6V4C8 3.46957 8.21071 2.96086 8.58579 2.58579C8.96086 2.21071 9.46957 2 10 2H14C14.5304 2 15.0391 2.21071 15.4142 2.58579C15.7893 2.96086 16 3.46957 16 4V6M19 6V20C19 20.5304 18.7893 21.0391 18.4142 21.4142C18.0391 21.7893 17.5304 22 17 22H7C6.46957 22 5.96086 21.7893 5.58579 21.4142C5.21071 21.0391 5 20.5304 5 20V6H19Z " stroke ="currentColor " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round "/>
415+ </ svg >
416+ Delete Account
417+ </ button >
418+ </ div >
406419 </ div >
407420 </ div >
408421 </ div >
@@ -451,6 +464,48 @@ <h2>Edit Family Member</h2>
451464 </ div >
452465</ div >
453466
467+ <!-- Delete Account Modal -->
468+ < div id ="deleteAccountModal " class ="modal ">
469+ < div class ="modal-content modal-small ">
470+ < div class ="modal-header modal-header-danger ">
471+ < h2 > Delete Account</ h2 >
472+ < span class ="close " role ="button " tabindex ="0 " aria-label ="Close modal " onclick ="closeDeleteAccountModal() "> ×</ span >
473+ </ div >
474+ < div class ="modal-body ">
475+ < div class ="delete-warning ">
476+ < svg width ="48 " height ="48 " viewBox ="0 0 24 24 " fill ="none " class ="warning-icon ">
477+ < path d ="M12 9V13M12 17H12.01M10.29 3.86L1.82 18C1.64 18.3 1.55 18.64 1.55 19C1.55 19.36 1.64 19.7 1.82 20C2 20.3 2.26 20.56 2.56 20.74C2.86 20.92 3.21 21.01 3.56 21H20.44C20.79 21.01 21.14 20.92 21.44 20.74C21.74 20.56 22 20.3 22.18 20C22.36 19.7 22.45 19.36 22.45 19C22.45 18.64 22.36 18.3 22.18 18L13.71 3.86C13.53 3.56 13.27 3.32 12.97 3.15C12.67 2.98 12.34 2.89 12 2.89C11.66 2.89 11.33 2.98 11.03 3.15C10.73 3.32 10.47 3.56 10.29 3.86Z " stroke ="currentColor " stroke-width ="2 " stroke-linecap ="round " stroke-linejoin ="round "/>
478+ </ svg >
479+ < p > < strong > This action cannot be undone.</ strong > </ p >
480+ </ div >
481+ < p > This will permanently delete:</ p >
482+ < ul class ="delete-list ">
483+ < li > Your account and profile</ li >
484+ < li > All expenses and transactions</ li >
485+ < li > All receipts and attachments</ li >
486+ < li > All plan year documents</ li >
487+ < li > HSA balance history</ li >
488+ </ ul >
489+
490+ {% if not session.auth_method or session.auth_method == 'local' %}
491+ < div class ="form-group ">
492+ < label for ="deleteAccountPassword "> Enter your password to confirm:</ label >
493+ < input type ="password " id ="deleteAccountPassword " placeholder ="Your password " required >
494+ </ div >
495+ {% else %}
496+ < div class ="form-group ">
497+ < label for ="deleteAccountConfirmation "> Type < strong > {{ session.username }}</ strong > to confirm:</ label >
498+ < input type ="text " id ="deleteAccountConfirmation " placeholder ="{{ session.username }} " required autocomplete ="off ">
499+ </ div >
500+ {% endif %}
501+ </ div >
502+ < div class ="modal-footer ">
503+ < button type ="button " class ="btn btn-secondary " onclick ="closeDeleteAccountModal() "> Cancel</ button >
504+ < button type ="button " class ="btn btn-danger " onclick ="confirmDeleteAccount() "> Delete My Account</ button >
505+ </ div >
506+ </ div >
507+ </ div >
508+
454509< script >
455510function handleFileSelect ( event ) {
456511 const file = event . target . files [ 0 ] ;
@@ -902,15 +957,83 @@ <h2>Edit Family Member</h2>
902957window . addEventListener ( 'click' , function ( event ) {
903958 const providerModal = document . getElementById ( 'editProviderModal' ) ;
904959 const familyMemberModal = document . getElementById ( 'editFamilyMemberModal' ) ;
960+ const deleteAccountModal = document . getElementById ( 'deleteAccountModal' ) ;
905961
906962 if ( event . target === providerModal ) {
907963 closeEditProviderModal ( ) ;
908964 }
909965 if ( event . target === familyMemberModal ) {
910966 closeEditFamilyMemberModal ( ) ;
911967 }
968+ if ( event . target === deleteAccountModal ) {
969+ closeDeleteAccountModal ( ) ;
970+ }
912971} ) ;
913972
973+ // Delete Account Modal Functions
974+ function openDeleteAccountModal ( ) {
975+ openModal ( document . getElementById ( 'deleteAccountModal' ) ) ;
976+ }
977+
978+ function closeDeleteAccountModal ( ) {
979+ document . getElementById ( 'deleteAccountModal' ) . style . display = 'none' ;
980+ // Clear inputs
981+ const passwordInput = document . getElementById ( 'deleteAccountPassword' ) ;
982+ const confirmInput = document . getElementById ( 'deleteAccountConfirmation' ) ;
983+ if ( passwordInput ) passwordInput . value = '' ;
984+ if ( confirmInput ) confirmInput . value = '' ;
985+ }
986+
987+ function confirmDeleteAccount ( ) {
988+ const passwordInput = document . getElementById ( 'deleteAccountPassword' ) ;
989+ const confirmInput = document . getElementById ( 'deleteAccountConfirmation' ) ;
990+
991+ let payload = { } ;
992+ if ( passwordInput ) {
993+ if ( ! passwordInput . value ) {
994+ showAlert ( 'Please enter your password' , 'error' ) ;
995+ return ;
996+ }
997+ payload . password = passwordInput . value ;
998+ } else if ( confirmInput ) {
999+ if ( ! confirmInput . value ) {
1000+ showAlert ( 'Please type your username to confirm' , 'error' ) ;
1001+ return ;
1002+ }
1003+ payload . confirmation = confirmInput . value ;
1004+ }
1005+
1006+ fetch ( '/settings/delete-account' , addCSRFToken ( {
1007+ method : 'POST' ,
1008+ headers : {
1009+ 'Content-Type' : 'application/json' ,
1010+ } ,
1011+ body : JSON . stringify ( payload )
1012+ } ) )
1013+ . then ( async response => {
1014+ const text = await response . text ( ) ;
1015+ try {
1016+ const data = JSON . parse ( text ) ;
1017+ return { ok : response . ok , data } ;
1018+ } catch ( e ) {
1019+ console . error ( 'Invalid JSON response:' , text ) ;
1020+ throw new Error ( 'Server error: ' + text . substring ( 0 , 100 ) ) ;
1021+ }
1022+ } )
1023+ . then ( result => {
1024+ if ( result . ok && result . data . success ) {
1025+ // Redirect to login page
1026+ window . location . href = '/login' ;
1027+ } else {
1028+ showAlert ( result . data . message || 'Failed to delete account' , 'error' ) ;
1029+ }
1030+ } )
1031+ . catch ( error => {
1032+ console . error ( 'Error:' , error ) ;
1033+ showAlert ( error . message || 'An error occurred. Please try again.' , 'error' ) ;
1034+ } ) ;
1035+ }
1036+
9141037// Keyboard navigation: Tab to first family member after profile button/dropdown
9151038document . addEventListener ( 'DOMContentLoaded' , function ( ) {
9161039 const profileBtn = document . getElementById ( 'profileBtn' ) ;
0 commit comments