parent
24c262282e
commit
c06c236da5
23 changed files with 158 additions and 242 deletions
@ -0,0 +1,85 @@ |
|||||||
|
--- |
||||||
|
import Icon from './AstroIcon.astro'; |
||||||
|
|
||||||
|
const { activePageId, activePageTitle } = Astro.props; |
||||||
|
|
||||||
|
export interface Props { |
||||||
|
activePageId: string; |
||||||
|
activePageTitle: string; |
||||||
|
} |
||||||
|
--- |
||||||
|
|
||||||
|
<div class='relative block mb-5 md:hidden p-4 border-b shadow-inner'> |
||||||
|
<button |
||||||
|
class='flex h-10 w-full items-center justify-between rounded-md border bg-white px-2 text-center font-medium text-gray-900' |
||||||
|
id='settings-menu' |
||||||
|
> |
||||||
|
{activePageTitle} |
||||||
|
<Icon icon='dropdown' /> |
||||||
|
</button> |
||||||
|
<ul |
||||||
|
id='settings-menu-dropdown' |
||||||
|
class='absolute mt-1 hidden left-0 right-0 space-y-1.5 bg-white p-2 shadow-lg z-10' |
||||||
|
> |
||||||
|
<li> |
||||||
|
<a |
||||||
|
href='/account/update-profile' |
||||||
|
class=`block w-full rounded px-2 py-1.5 font-medium text-slate-900 hover:bg-slate-200 ${activePageId === 'profile' ? 'bg-slate-100' : ''}` |
||||||
|
>Profile</a |
||||||
|
> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<a |
||||||
|
href='/account/update-password' |
||||||
|
class=`block w-full rounded px-2 py-1.5 font-medium text-slate-900 hover:bg-slate-200 ${activePageId === 'change-password' ? 'bg-slate-100' : ''}` |
||||||
|
>Change password</a |
||||||
|
> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class='container flex min-h-screen items-stretch'> |
||||||
|
<!-- Start Desktop Sidebar --> |
||||||
|
<aside class='hidden w-56 border-r border-slate-200 py-10 pr-5 md:block'> |
||||||
|
<nav> |
||||||
|
<ul class='space-y-1'> |
||||||
|
<li> |
||||||
|
<a |
||||||
|
href='/account/update-profile' |
||||||
|
class=`block w-full rounded px-2 py-1.5 font-regular text-slate-900 hover:bg-slate-100 ${activePageId === 'profile' ? 'bg-slate-100' : ''}` |
||||||
|
> |
||||||
|
Profile |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
<li> |
||||||
|
<a |
||||||
|
href='/account/update-password' |
||||||
|
class=`block w-full rounded px-2 py-1.5 font-regular text-slate-900 hover:bg-slate-100 ${activePageId === 'change-password' ? 'bg-slate-100' : ''}` |
||||||
|
> |
||||||
|
Security |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</nav> |
||||||
|
</aside> |
||||||
|
<!-- /End Desktop Sidebar --> |
||||||
|
|
||||||
|
<div class='grow px-0 py-0 md:px-10 md:py-10'> |
||||||
|
<slot /> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
const menuButton = document.getElementById('settings-menu'); |
||||||
|
const menuDropdown = document.getElementById('settings-menu-dropdown'); |
||||||
|
|
||||||
|
menuButton?.addEventListener('click', () => { |
||||||
|
menuDropdown?.classList.toggle('hidden'); |
||||||
|
}); |
||||||
|
|
||||||
|
document.addEventListener('click', (e) => { |
||||||
|
if (!menuButton?.contains(e.target as Node)) { |
||||||
|
menuDropdown?.classList.add('hidden'); |
||||||
|
} |
||||||
|
}); |
||||||
|
</script> |
@ -1,5 +0,0 @@ |
|||||||
--- |
|
||||||
--- |
|
||||||
|
|
||||||
<div class='recaptcha-field mb-2'></div> |
|
||||||
<input type='hidden' name='g-recaptcha-response' class='recaptcha-response' /> |
|
@ -1,36 +0,0 @@ |
|||||||
--- |
|
||||||
|
|
||||||
--- |
|
||||||
|
|
||||||
<script src='./captcha.js'></script> |
|
||||||
|
|
||||||
<script is:inline> |
|
||||||
window.onCaptchaLoad = function () { |
|
||||||
if (!window.grecaptcha) { |
|
||||||
console.warn('window.grecaptcha is not defined'); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
const recaptchaFields = document.querySelectorAll('.recaptcha-field'); |
|
||||||
|
|
||||||
// render recaptcha on fields |
|
||||||
recaptchaFields.forEach((field) => { |
|
||||||
// If captcha already rendered for this field |
|
||||||
if (field.hasAttribute('data-recaptcha-id')) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
const renderedId = window.grecaptcha.render(field, { |
|
||||||
sitekey: '6Ldn2YsjAAAAABlUxNxukAuDAUIuZIhO0hRVxzJW', |
|
||||||
}); |
|
||||||
|
|
||||||
field.setAttribute('data-recaptcha-id', renderedId); |
|
||||||
}); |
|
||||||
}; |
|
||||||
</script> |
|
||||||
|
|
||||||
<script |
|
||||||
src='https://www.google.com/recaptcha/api.js?onload=onCaptchaLoad&render=explicit' |
|
||||||
async |
|
||||||
defer |
|
||||||
></script> |
|
@ -1,49 +0,0 @@ |
|||||||
class Captcha { |
|
||||||
constructor() { |
|
||||||
this.onDOMLoaded = this.onDOMLoaded.bind(this); |
|
||||||
this.bindValidation = this.bindValidation.bind(this); |
|
||||||
this.validateCaptchaBeforeSubmit = |
|
||||||
this.validateCaptchaBeforeSubmit.bind(this); |
|
||||||
} |
|
||||||
|
|
||||||
validateCaptchaBeforeSubmit(e) { |
|
||||||
const target = e.target; |
|
||||||
const captchaField = target.querySelector('.recaptcha-field'); |
|
||||||
|
|
||||||
if (captchaField) { |
|
||||||
const captchaId = captchaField.dataset.recaptchaId; |
|
||||||
const captchaResponse = window.grecaptcha.getResponse(captchaId); |
|
||||||
|
|
||||||
// If valid captcha is not present, prevent form submission
|
|
||||||
if (!captchaResponse) { |
|
||||||
e.preventDefault(); |
|
||||||
alert('Please verify that you are human first'); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
target.querySelector('.recaptcha-response').value = captchaResponse; |
|
||||||
} |
|
||||||
|
|
||||||
target.closest('.popup').classList.add('hidden'); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bindValidation() { |
|
||||||
const forms = document.querySelectorAll('[captcha-form]'); |
|
||||||
|
|
||||||
forms.forEach((form) => { |
|
||||||
form.addEventListener('submit', this.validateCaptchaBeforeSubmit); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
onDOMLoaded() { |
|
||||||
this.bindValidation(); |
|
||||||
} |
|
||||||
|
|
||||||
init() { |
|
||||||
window.addEventListener('DOMContentLoaded', this.onDOMLoaded); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const captcha = new Captcha(); |
|
||||||
captcha.init(); |
|
@ -1,81 +0,0 @@ |
|||||||
--- |
|
||||||
import Icon from '../AstroIcon.astro'; |
|
||||||
const { pageUrl, name } = Astro.props; |
|
||||||
|
|
||||||
export interface Props { |
|
||||||
pageUrl: string; |
|
||||||
name: string; |
|
||||||
} |
|
||||||
--- |
|
||||||
|
|
||||||
<div |
|
||||||
class='container flex min-h-[calc(100vh-37px-70px)] items-stretch sm:min-h-[calc(100vh-37px-96px)]' |
|
||||||
> |
|
||||||
<aside class='hidden w-56 border-r border-slate-200 py-10 pr-5 md:block'> |
|
||||||
<nav> |
|
||||||
<ul class='space-y-1'> |
|
||||||
<li> |
|
||||||
<a |
|
||||||
href='/settings/update-profile' |
|
||||||
class=`block w-full rounded px-2 py-1.5 font-regular text-slate-900 hover:bg-slate-100 ${pageUrl === 'profile' ? 'bg-slate-100' : ''}` |
|
||||||
>Profile</a |
|
||||||
> |
|
||||||
</li> |
|
||||||
<li> |
|
||||||
<a |
|
||||||
href='/settings/update-password' |
|
||||||
class=`block w-full rounded px-2 py-1.5 font-regular text-slate-900 hover:bg-slate-100 ${pageUrl === 'change-password' ? 'bg-slate-100' : ''}` |
|
||||||
>Security</a |
|
||||||
> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
</nav> |
|
||||||
</aside> |
|
||||||
<div class='grow py-10 pl-0 md:p-10 md:pr-0'> |
|
||||||
<div class='relative mb-5 md:hidden'> |
|
||||||
<button |
|
||||||
class='flex h-10 w-full items-center justify-between rounded-md bg-slate-800 px-2 text-center font-medium text-slate-100' |
|
||||||
id='settings-menu' |
|
||||||
> |
|
||||||
{name} |
|
||||||
<Icon icon='dropdown' /> |
|
||||||
</button> |
|
||||||
<ul |
|
||||||
id='settings-menu-dropdown' |
|
||||||
class='absolute mt-1 hidden w-full space-y-1.5 rounded-md bg-white p-2 shadow-lg' |
|
||||||
> |
|
||||||
<li> |
|
||||||
<a |
|
||||||
href='/settings/update-profile' |
|
||||||
class=`block w-full rounded px-2 py-1.5 font-medium text-slate-900 hover:bg-slate-200 ${pageUrl === 'profile' ? 'bg-slate-100' : ''}` |
|
||||||
>Profile</a |
|
||||||
> |
|
||||||
</li> |
|
||||||
<li> |
|
||||||
<a |
|
||||||
href='/settings/update-password' |
|
||||||
class=`block w-full rounded px-2 py-1.5 font-medium text-slate-900 hover:bg-slate-200 ${pageUrl === 'change-password' ? 'bg-slate-100' : ''}` |
|
||||||
>Change password</a |
|
||||||
> |
|
||||||
</li> |
|
||||||
</ul> |
|
||||||
</div> |
|
||||||
|
|
||||||
<slot /> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<script> |
|
||||||
const menuButton = document.getElementById('settings-menu'); |
|
||||||
const menuDropdown = document.getElementById('settings-menu-dropdown'); |
|
||||||
|
|
||||||
menuButton?.addEventListener('click', () => { |
|
||||||
menuDropdown?.classList.toggle('hidden'); |
|
||||||
}); |
|
||||||
|
|
||||||
document.addEventListener('click', (e) => { |
|
||||||
if (!menuButton?.contains(e.target as Node)) { |
|
||||||
menuDropdown?.classList.add('hidden'); |
|
||||||
} |
|
||||||
}); |
|
||||||
</script> |
|
@ -0,0 +1,16 @@ |
|||||||
|
--- |
||||||
|
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> |
@ -0,0 +1,15 @@ |
|||||||
|
--- |
||||||
|
import AccountSidebar from '../../components/AccountSidebar.astro'; |
||||||
|
import { UpdateProfileForm } from '../../components/UpdateProfile/UpdateProfileForm'; |
||||||
|
import AccountLayout from '../../layouts/AccountLayout.astro'; |
||||||
|
--- |
||||||
|
|
||||||
|
<AccountLayout |
||||||
|
title='Update Profile' |
||||||
|
noIndex={true} |
||||||
|
initialLoadingMessage={'Loading profile'} |
||||||
|
> |
||||||
|
<AccountSidebar activePageId='profile' activePageTitle='Profile'> |
||||||
|
<UpdateProfileForm client:load /> |
||||||
|
</AccountSidebar> |
||||||
|
</AccountLayout> |
@ -1,16 +0,0 @@ |
|||||||
--- |
|
||||||
import SettingSidebar from '../../components/Setting/SettingSidebar.astro'; |
|
||||||
import UpdatePasswordForm from '../../components/Setting/UpdatePasswordForm'; |
|
||||||
import SettingLayout from '../../layouts/SettingLayout.astro'; |
|
||||||
--- |
|
||||||
|
|
||||||
<SettingLayout |
|
||||||
title='Change Password' |
|
||||||
description='' |
|
||||||
noIndex={true} |
|
||||||
initialLoadingMessage={'Loading profile'} |
|
||||||
> |
|
||||||
<SettingSidebar pageUrl='change-password' name='Change Password'> |
|
||||||
<UpdatePasswordForm client:load /> |
|
||||||
</SettingSidebar> |
|
||||||
</SettingLayout> |
|
@ -1,15 +0,0 @@ |
|||||||
--- |
|
||||||
import SettingSidebar from '../../components/Setting/SettingSidebar.astro'; |
|
||||||
import { UpdateProfileForm } from '../../components/Setting/UpdateProfileForm'; |
|
||||||
import SettingLayout from '../../layouts/SettingLayout.astro'; |
|
||||||
--- |
|
||||||
|
|
||||||
<SettingLayout |
|
||||||
title='Update Profile' |
|
||||||
noIndex={true} |
|
||||||
initialLoadingMessage={'Loading profile'} |
|
||||||
> |
|
||||||
<SettingSidebar pageUrl='profile' name='Profile'> |
|
||||||
<UpdateProfileForm client:load /> |
|
||||||
</SettingSidebar> |
|
||||||
</SettingLayout> |
|
@ -1,10 +1,10 @@ |
|||||||
--- |
--- |
||||||
import { VerificationEmailMessage } from '../components/AuthenticationFlow/VerificationEmailMessage'; |
import { VerificationEmailMessage } from '../components/AuthenticationFlow/VerificationEmailMessage'; |
||||||
import SettingLayout from '../layouts/SettingLayout.astro'; |
import AccountLayout from '../layouts/AccountLayout.astro'; |
||||||
--- |
--- |
||||||
|
|
||||||
<SettingLayout title='Verify Email' noIndex={true}> |
<AccountLayout title='Verify Email' noIndex={true}> |
||||||
<section class='container py-8 sm:py-20'> |
<section class='container py-8 sm:py-20'> |
||||||
<VerificationEmailMessage client:load /> |
<VerificationEmailMessage client:load /> |
||||||
</section> |
</section> |
||||||
</SettingLayout> |
</AccountLayout> |
||||||
|
@ -1,10 +1,10 @@ |
|||||||
--- |
--- |
||||||
import { TriggerVerifyAccount } from '../components/AuthenticationFlow/TriggerVerifyAccount'; |
import { TriggerVerifyAccount } from '../components/AuthenticationFlow/TriggerVerifyAccount'; |
||||||
import SettingLayout from '../layouts/SettingLayout.astro'; |
import AccountLayout from '../layouts/AccountLayout.astro'; |
||||||
--- |
--- |
||||||
|
|
||||||
<SettingLayout title='Verify account' noIndex={true}> |
<AccountLayout title='Verify account' noIndex={true}> |
||||||
<div class='container py-16'> |
<div class='container py-16'> |
||||||
<TriggerVerifyAccount client:load /> |
<TriggerVerifyAccount client:load /> |
||||||
</div> |
</div> |
||||||
</SettingLayout> |
</AccountLayout> |
||||||
|
Loading…
Reference in new issue