parent
37107c495f
commit
8a94609fb9
8 changed files with 202 additions and 21 deletions
@ -0,0 +1,54 @@ |
||||
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> |
||||
); |
||||
} |
@ -0,0 +1,93 @@ |
||||
import { useState } from 'preact/hooks'; |
||||
import Cookies from 'js-cookie'; |
||||
import { httpDelete } from '../../lib/http'; |
||||
import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; |
||||
import { hideDeleteAccountPopup } from './DeleteAccount'; |
||||
|
||||
const DELETE_ACCOUNT_VERIFICATION = 'delete my account'; |
||||
|
||||
export function DeleteAccountForm() { |
||||
const [isLoading, setIsLoading] = useState(false); |
||||
const [error, setError] = useState(''); |
||||
const [deleteAccount, setDeleteAccount] = useState(''); |
||||
|
||||
const handleSubmit = async (e: Event) => { |
||||
e.preventDefault(); |
||||
setIsLoading(true); |
||||
setError(''); |
||||
|
||||
if (deleteAccount !== DELETE_ACCOUNT_VERIFICATION) { |
||||
setError('Verification text does not match'); |
||||
setIsLoading(false); |
||||
return; |
||||
} |
||||
|
||||
const { response, error } = await httpDelete( |
||||
`${import.meta.env.PUBLIC_API_URL}/v1-delete-account` |
||||
); |
||||
|
||||
if (error || !response) { |
||||
setIsLoading(false); |
||||
setError(error?.message || 'Something went wrong'); |
||||
return; |
||||
} |
||||
|
||||
Cookies.remove(TOKEN_COOKIE_NAME); |
||||
window.location.href = '/'; |
||||
}; |
||||
|
||||
const handleClosePopup = () => { |
||||
setIsLoading(false); |
||||
setError(''); |
||||
setDeleteAccount(''); |
||||
|
||||
hideDeleteAccountPopup(); |
||||
}; |
||||
|
||||
return ( |
||||
<form onSubmit={handleSubmit}> |
||||
<div> |
||||
<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 |
||||
type="text" |
||||
name="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" |
||||
required |
||||
autoFocus |
||||
value={deleteAccount} |
||||
onInput={(e) => |
||||
setDeleteAccount((e.target as HTMLInputElement).value) |
||||
} |
||||
/> |
||||
{error && ( |
||||
<p className="mt-2 rounded-lg bg-red-100 p-2 text-red-700">{error}</p> |
||||
)} |
||||
</div> |
||||
|
||||
<div className="mt-6 flex items-center justify-between gap-2"> |
||||
<button |
||||
type="button" |
||||
disabled={isLoading} |
||||
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" |
||||
> |
||||
Cancel |
||||
</button> |
||||
<button |
||||
type="submit" |
||||
disabled={isLoading} |
||||
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" |
||||
> |
||||
{isLoading ? 'Please wait...' : 'Delete Account'} |
||||
</button> |
||||
</div> |
||||
</form> |
||||
); |
||||
} |
@ -0,0 +1,24 @@ |
||||
--- |
||||
import Popup from '../Popup/Popup.astro'; |
||||
import { DeleteAccountForm } from './DeleteAccountForm'; |
||||
--- |
||||
|
||||
<Popup id='delete-account-popup' title='' subtitle=''> |
||||
<div class='-mt-2.5'> |
||||
<h2 class='mb-3 text-2xl font-semibold leading-5 text-gray-900'> |
||||
Delete Account |
||||
</h2> |
||||
<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> |
||||
|
||||
<div class='mt-6 flex flex-col gap-1.5'> |
||||
<DeleteAccountForm client:load /> |
||||
</div> |
||||
</div> |
||||
</Popup> |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,22 @@ |
||||
--- |
||||
import AccountSidebar from '../../components/AccountSidebar.astro'; |
||||
import UpdatePasswordForm from '../../components/UpdatePassword/UpdatePasswordForm'; |
||||
import AccountLayout from '../../layouts/AccountLayout.astro'; |
||||
import { DeleteAccount } from '../../components/DeleteAccount/DeleteAccount'; |
||||
import DeleteAccountPopup from '../../components/DeleteAccount/DeleteAccountPopup.astro'; |
||||
--- |
||||
|
||||
<AccountLayout |
||||
title='Settings' |
||||
description='' |
||||
noIndex={true} |
||||
initialLoadingMessage={'Loading settings'} |
||||
> |
||||
<AccountSidebar activePageId='settings' activePageTitle='Settings'> |
||||
<UpdatePasswordForm client:load /> |
||||
<hr class='my-10' /> |
||||
<DeleteAccount client:load /> |
||||
</AccountSidebar> |
||||
|
||||
<DeleteAccountPopup /> |
||||
</AccountLayout> |
@ -1,16 +0,0 @@ |
||||
--- |
||||
import AccountSidebar from '../../components/AccountSidebar.astro'; |
||||
import UpdatePasswordForm from '../../components/UpdatePassword/UpdatePasswordForm'; |
||||
import AccountLayout from '../../layouts/AccountLayout.astro'; |
||||
--- |
||||
|
||||
<AccountLayout |
||||
title='Change Password' |
||||
description='' |
||||
noIndex={true} |
||||
initialLoadingMessage={'Loading profile'} |
||||
> |
||||
<AccountSidebar activePageId='change-password' activePageTitle='Change Password'> |
||||
<UpdatePasswordForm client:load /> |
||||
</AccountSidebar> |
||||
</AccountLayout> |
Loading…
Reference in new issue