Add account deletion functionality

chore/delete-account
Kamran Ahmed 1 year ago
parent 8a94609fb9
commit d1aa0983b6
  1. 1829
      pnpm-lock.yaml
  2. 16
      src/components/DeleteAccount/DeleteAccount.astro
  3. 54
      src/components/DeleteAccount/DeleteAccount.tsx
  4. 52
      src/components/DeleteAccount/DeleteAccountForm.tsx
  5. 21
      src/components/DeleteAccount/DeleteAccountPopup.astro
  6. 9
      src/pages/account/settings.astro

File diff suppressed because it is too large Load Diff

@ -0,0 +1,16 @@
---
import DeleteAccountPopup from "./DeleteAccountPopup.astro";
---
<DeleteAccountPopup />
<h2 class='text-xl font-bold sm:text-2xl'>Delete Account</h2>
<p class='mt-2 text-gray-400'>
Permanently remove your account from the roadmap.sh. This cannot be undone and all your progress and data will be lost.
</p>
<button
data-popup='delete-account-popup'
class="mt-4 w-full rounded-lg bg-red-600 py-2 text-base font-regular text-white outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-1"
>
Delete Account
</button>

@ -1,54 +0,0 @@
export function showDeleteAccountPopup() {
const popupEl = document.querySelector(`#delete-account-popup`);
if (!popupEl) {
return;
}
popupEl.classList.remove('hidden');
popupEl.classList.add('flex');
const focusEl = popupEl.querySelector<HTMLElement>('[autofocus]');
if (focusEl) {
focusEl.focus();
}
}
export function hideDeleteAccountPopup() {
const popupEl = document.querySelector(`#delete-account-popup`);
if (!popupEl) {
return;
}
popupEl.classList.remove('flex');
popupEl.classList.add('hidden');
const focusEl = popupEl.querySelector<HTMLElement>('[autofocus]');
if (focusEl) {
focusEl.blur();
}
}
export function DeleteAccount() {
return (
<div>
<div>
<h2 className="text-xl font-bold sm:text-2xl">
Delete Account
</h2>
<p className="mt-2 text-gray-400">
Permanently remove your account from the roadmap.sh. This action is
not reversible, so please continue with caution.
</p>
</div>
<div className="mt-4">
<button
className="inline-flex h-10 items-center justify-center rounded-md bg-red-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-red-500/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-red-200 disabled:pointer-events-none disabled:opacity-50"
onClick={showDeleteAccountPopup}
>
Delete Account
</button>
</div>
</div>
);
}

@ -1,22 +1,23 @@
import { useState } from 'preact/hooks'; import {useEffect, useState} from 'preact/hooks';
import Cookies from 'js-cookie';
import { httpDelete } from '../../lib/http'; import { httpDelete } from '../../lib/http';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; import { logout } from '../Navigation/navigation';
import { hideDeleteAccountPopup } from './DeleteAccount';
const DELETE_ACCOUNT_VERIFICATION = 'delete my account';
export function DeleteAccountForm() { export function DeleteAccountForm() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(''); const [error, setError] = useState('');
const [deleteAccount, setDeleteAccount] = useState(''); const [confirmationText, setConfirmationText] = useState('');
useEffect(() => {
setError('');
setConfirmationText('');
}, [])
const handleSubmit = async (e: Event) => { const handleSubmit = async (e: Event) => {
e.preventDefault(); e.preventDefault();
setIsLoading(true); setIsLoading(true);
setError(''); setError('');
if (deleteAccount !== DELETE_ACCOUNT_VERIFICATION) { if (confirmationText.toUpperCase() !== 'DELETE') {
setError('Verification text does not match'); setError('Verification text does not match');
setIsLoading(false); setIsLoading(false);
return; return;
@ -32,38 +33,33 @@ export function DeleteAccountForm() {
return; return;
} }
Cookies.remove(TOKEN_COOKIE_NAME); logout();
window.location.href = '/';
}; };
const handleClosePopup = () => { const handleClosePopup = () => {
setIsLoading(false); setIsLoading(false);
setError(''); setError('');
setDeleteAccount(''); setConfirmationText('');
hideDeleteAccountPopup(); const deleteAccountPopup = document.getElementById('delete-account-popup');
deleteAccountPopup?.classList.add('hidden');
deleteAccountPopup?.classList.remove('flex');
}; };
return ( return (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div> <div className="my-4">
<label htmlFor="delete-account" className="text-sm text-gray-500">
To verify, type{' '}
<span className="font-medium text-gray-600">
{DELETE_ACCOUNT_VERIFICATION}
</span>{' '}
below:
</label>
<input <input
type="text" type="text"
name="delete-account" name="delete-account"
id="delete-account" id="delete-account"
className="mt-2 block w-full rounded-lg border border-gray-300 px-3 py-2 shadow-sm outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1" className="mt-2 block w-full rounded-md border border-gray-300 py-2 px-3 outline-none placeholder:text-gray-400 focus:border-gray-400"
placeholder={'Type "delete" to confirm'}
required required
autoFocus autoFocus
value={deleteAccount} value={confirmationText}
onInput={(e) => onInput={(e) =>
setDeleteAccount((e.target as HTMLInputElement).value) setConfirmationText((e.target as HTMLInputElement).value)
} }
/> />
{error && ( {error && (
@ -71,21 +67,21 @@ export function DeleteAccountForm() {
)} )}
</div> </div>
<div className="mt-6 flex items-center justify-between gap-2"> <div className="flex items-center gap-2">
<button <button
type="button" type="button"
disabled={isLoading} disabled={isLoading}
onClick={handleClosePopup} onClick={handleClosePopup}
className="flex h-10 items-center justify-center rounded-md bg-gray-100 px-4 py-2 text-sm font-medium text-black shadow-sm hover:bg-gray-100/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-200 disabled:pointer-events-none disabled:opacity-50" className="flex-grow cursor-pointer rounded-lg bg-gray-200 py-2 text-center"
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
disabled={isLoading} disabled={isLoading || confirmationText.toUpperCase() !== 'DELETE'}
className="flex h-10 items-center justify-center rounded-md bg-red-500 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-red-500/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-red-200 disabled:pointer-events-none disabled:opacity-50" className="flex-grow cursor-pointer rounded-lg bg-red-500 py-2 text-white disabled:opacity-40"
> >
{isLoading ? 'Please wait...' : 'Delete Account'} {isLoading ? 'Please wait ..' : 'Confirm'}
</button> </button>
</div> </div>
</form> </form>

@ -3,22 +3,15 @@ import Popup from '../Popup/Popup.astro';
import { DeleteAccountForm } from './DeleteAccountForm'; import { DeleteAccountForm } from './DeleteAccountForm';
--- ---
<Popup id='delete-account-popup' title='' subtitle=''> <Popup id='delete-account-popup' title='Delete Account' subtitle=''>
<div class='-mt-2.5'> <div class='-mt-2.5'>
<h2 class='mb-3 text-2xl font-semibold leading-5 text-gray-900'> <p>
Delete Account This will permanently delete your account and all your associated data
</h2> including your progress.
<p class='text-sm text-gray-600'>
Deleting your account will remove all your data from our servers
(including your progress).
</p>
<p class='rounded-md bg-yellow-300 p-2 text-xs text-yellow-800 mt-6'>
<span class='font-medium'>Warning:</span> This action is not reversible. Please
be certain.
</p> </p>
<div class='mt-6 flex flex-col gap-1.5'> <p class="text-black font-medium -mb-2 mt-3 text-base">Please type "delete" to confirm.</p>
<DeleteAccountForm client:load />
</div> <DeleteAccountForm client:only />
</div> </div>
</Popup> </Popup>

@ -2,8 +2,7 @@
import AccountSidebar from '../../components/AccountSidebar.astro'; import AccountSidebar from '../../components/AccountSidebar.astro';
import UpdatePasswordForm from '../../components/UpdatePassword/UpdatePasswordForm'; import UpdatePasswordForm from '../../components/UpdatePassword/UpdatePasswordForm';
import AccountLayout from '../../layouts/AccountLayout.astro'; import AccountLayout from '../../layouts/AccountLayout.astro';
import { DeleteAccount } from '../../components/DeleteAccount/DeleteAccount'; import DeleteAccount from '../../components/DeleteAccount/DeleteAccount.astro';
import DeleteAccountPopup from '../../components/DeleteAccount/DeleteAccountPopup.astro';
--- ---
<AccountLayout <AccountLayout
@ -14,9 +13,7 @@ import DeleteAccountPopup from '../../components/DeleteAccount/DeleteAccountPopu
> >
<AccountSidebar activePageId='settings' activePageTitle='Settings'> <AccountSidebar activePageId='settings' activePageTitle='Settings'>
<UpdatePasswordForm client:load /> <UpdatePasswordForm client:load />
<hr class='my-10' /> <hr class='my-8' />
<DeleteAccount client:load /> <DeleteAccount />
</AccountSidebar> </AccountSidebar>
<DeleteAccountPopup />
</AccountLayout> </AccountLayout>

Loading…
Cancel
Save