mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-01-16 21:58:30 +01:00
feat: ability to update email (#5370)
* chore: update email * wip: verify email endpoint * wip: implement success screen * wip: social warning * Update form for email update * Update email form UI --------- Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
This commit is contained in:
parent
4e96a58e54
commit
730af9b973
@ -41,7 +41,7 @@
|
||||
"image-size": "^1.1.1",
|
||||
"jose": "^5.2.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.334.0",
|
||||
"lucide-react": "^0.358.0",
|
||||
"nanoid": "^5.0.5",
|
||||
"nanostores": "^0.9.5",
|
||||
"node-html-parser": "^6.1.12",
|
||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -60,8 +60,8 @@ dependencies:
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
lucide-react:
|
||||
specifier: ^0.334.0
|
||||
version: 0.334.0(react@18.2.0)
|
||||
specifier: ^0.358.0
|
||||
version: 0.358.0(react@18.2.0)
|
||||
nanoid:
|
||||
specifier: ^5.0.5
|
||||
version: 5.0.5
|
||||
@ -4236,8 +4236,8 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/lucide-react@0.334.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-y0Rv/Xx6qAq4FutZ3L/efl3O9vl6NC/1p0YOg6mBfRbQ4k1JCE2rz0rnV7WC8Moxq1RY99vLATvjcqUegGJTvA==}
|
||||
/lucide-react@0.358.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-rBSptRjZTMBm24zsFhR6pK/NgbT18JegZGKcH4+1H3+UigMSRpeoWLtR/fAwMYwYnlJOZB+y8WpeHne9D6X6Kg==}
|
||||
peerDependencies:
|
||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
|
82
src/components/AuthenticationFlow/TriggerVerifyEmail.tsx
Normal file
82
src/components/AuthenticationFlow/TriggerVerifyEmail.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { httpPatch } from '../../lib/http';
|
||||
import { setAuthToken } from '../../lib/jwt';
|
||||
import { Spinner } from '../ReactIcons/Spinner';
|
||||
import { ErrorIcon2 } from '../ReactIcons/ErrorIcon2';
|
||||
import { getUrlParams } from '../../lib/browser';
|
||||
import { CheckIcon } from '../ReactIcons/CheckIcon';
|
||||
|
||||
export function TriggerVerifyEmail() {
|
||||
const { code } = getUrlParams() as { code: string };
|
||||
|
||||
// const [isLoading, setIsLoading] = useState(true);
|
||||
const [status, setStatus] = useState<'loading' | 'error' | 'success'>(
|
||||
'loading',
|
||||
);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const triggerVerify = (code: string) => {
|
||||
setStatus('loading');
|
||||
|
||||
httpPatch<{ token: string }>(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-verify-new-email/${code}`,
|
||||
{},
|
||||
)
|
||||
.then(({ response, error }) => {
|
||||
if (!response?.token) {
|
||||
setError(error?.message || 'Something went wrong. Please try again.');
|
||||
setStatus('error');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setAuthToken(response.token);
|
||||
setStatus('success');
|
||||
})
|
||||
.catch((err) => {
|
||||
setStatus('error');
|
||||
setError('Something went wrong. Please try again.');
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!code) {
|
||||
setStatus('error');
|
||||
setError('Something went wrong. Please try again later.');
|
||||
return;
|
||||
}
|
||||
|
||||
triggerVerify(code);
|
||||
}, [code]);
|
||||
|
||||
const isLoading = status === 'loading';
|
||||
if (status === 'success') {
|
||||
return (
|
||||
<div className="mx-auto flex max-w-md flex-col items-center pt-0 sm:pt-12">
|
||||
<CheckIcon additionalClasses={'h-16 w-16 opacity-100'} />
|
||||
<h2 className="mb-1 mt-4 text-center text-xl font-semibold sm:mb-3 sm:mt-4 sm:text-2xl">
|
||||
Email Update Successful
|
||||
</h2>
|
||||
<p className="text-sm sm:text-base">
|
||||
Your email has been changed successfully. Happy learning!
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex max-w-md flex-col items-center pt-0 sm:pt-12">
|
||||
<div className="mx-auto max-w-md text-center">
|
||||
{isLoading && <Spinner className="mx-auto h-16 w-16" />}
|
||||
{error && <ErrorIcon2 className="mx-auto h-16 w-16" />}
|
||||
<h2 className="mb-1 mt-4 text-center text-xl font-semibold sm:mb-3 sm:mt-4 sm:text-2xl">
|
||||
Verifying your new Email
|
||||
</h2>
|
||||
<div className="text-sm sm:text-base">
|
||||
{isLoading && <p>Please wait while we verify your new Email..</p>}
|
||||
{error && <p className="text-red-700">{error}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
57
src/components/ProfileSettings/ProfileSettingsPage.tsx
Normal file
57
src/components/ProfileSettings/ProfileSettingsPage.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { UpdateEmailForm } from '../UpdateEmail/UpdateEmailForm';
|
||||
import UpdatePasswordForm from '../UpdatePassword/UpdatePasswordForm';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import { httpGet } from '../../lib/http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
|
||||
export function ProfileSettingsPage() {
|
||||
const toast = useToast();
|
||||
|
||||
const [authProvider, setAuthProvider] = useState('');
|
||||
const [currentEmail, setCurrentEmail] = useState('');
|
||||
const [newEmail, setNewEmail] = useState('');
|
||||
|
||||
const loadProfile = async () => {
|
||||
const { error, response } = await httpGet(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-me`,
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { authProvider, email, newEmail } = response;
|
||||
setAuthProvider(authProvider);
|
||||
setCurrentEmail(email);
|
||||
setNewEmail(newEmail || '');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadProfile().finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<UpdatePasswordForm authProvider={authProvider} />
|
||||
<hr className="my-8" />
|
||||
<UpdateEmailForm
|
||||
authProvider={authProvider}
|
||||
currentEmail={currentEmail}
|
||||
newEmail={newEmail}
|
||||
key={newEmail}
|
||||
onSendVerificationCode={(newEmail) => {
|
||||
setNewEmail(newEmail);
|
||||
loadProfile().finally(() => {});
|
||||
}}
|
||||
onVerificationCancel={() => {
|
||||
loadProfile().finally(() => {});
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
245
src/components/UpdateEmail/UpdateEmailForm.tsx
Normal file
245
src/components/UpdateEmail/UpdateEmailForm.tsx
Normal file
@ -0,0 +1,245 @@
|
||||
import { type FormEvent, useState } from 'react';
|
||||
import { httpPatch } from '../../lib/http';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { ArrowUpRight, X } from 'lucide-react';
|
||||
|
||||
type UpdateEmailFormProps = {
|
||||
authProvider: string;
|
||||
currentEmail: string;
|
||||
newEmail?: string;
|
||||
onSendVerificationCode?: (newEmail: string) => void;
|
||||
onVerificationCancel?: () => void;
|
||||
};
|
||||
|
||||
export function UpdateEmailForm(props: UpdateEmailFormProps) {
|
||||
const {
|
||||
authProvider,
|
||||
currentEmail,
|
||||
newEmail: defaultNewEmail = '',
|
||||
onSendVerificationCode,
|
||||
onVerificationCancel,
|
||||
} = props;
|
||||
const toast = useToast();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isSubmitted, setIsSubmitted] = useState(defaultNewEmail !== '');
|
||||
const [newEmail, setNewEmail] = useState(defaultNewEmail);
|
||||
const [isResendDone, setIsResendDone] = useState(false);
|
||||
|
||||
const handleSentVerificationCode = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (!newEmail || !newEmail.includes('@') || isSubmitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
pageProgressMessage.set('Sending verification code');
|
||||
const { response, error } = await httpPatch(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-update-user-email`,
|
||||
{ email: newEmail },
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
pageProgressMessage.set('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pageProgressMessage.set('');
|
||||
setIsLoading(false);
|
||||
setIsSubmitted(true);
|
||||
onSendVerificationCode?.(newEmail);
|
||||
};
|
||||
|
||||
const handleResendVerificationCode = async () => {
|
||||
if (isResendDone) {
|
||||
toast.error('You have already resent the verification code');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
pageProgressMessage.set('Resending verification code');
|
||||
const { response, error } = await httpPatch(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-resend-email-verification-code`,
|
||||
{ email: newEmail },
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
pageProgressMessage.set('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success('Verification code has been resent');
|
||||
pageProgressMessage.set('');
|
||||
setIsResendDone(true);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const handleCancelEmailVerification = async () => {
|
||||
setIsLoading(true);
|
||||
pageProgressMessage.set('Cancelling email verification');
|
||||
const { response, error } = await httpPatch(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-cancel-email-verification`,
|
||||
{},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
pageProgressMessage.set('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pageProgressMessage.set('');
|
||||
onVerificationCancel?.();
|
||||
setIsSubmitted(false);
|
||||
setNewEmail('');
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
if (authProvider && authProvider !== 'email') {
|
||||
return (
|
||||
<div className="block">
|
||||
<h2 className="text-xl font-bold sm:text-2xl">Update Email</h2>
|
||||
<p className="mt-2 text-gray-400">
|
||||
You have used {authProvider} when signing up. Please set your password
|
||||
first.
|
||||
</p>
|
||||
|
||||
<div className="mt-4 flex w-full flex-col">
|
||||
<label
|
||||
htmlFor="current-email"
|
||||
className="text-sm leading-none text-slate-500"
|
||||
>
|
||||
Current Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="current-email"
|
||||
id="current-email"
|
||||
autoComplete="current-email"
|
||||
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
|
||||
disabled
|
||||
value={currentEmail}
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-3 rounded-lg border border-red-600 px-2 py-1 text-red-600">
|
||||
Please set your password first to update your email.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-8 block">
|
||||
<h2 className="text-xl font-bold sm:text-2xl">Update Email</h2>
|
||||
<p className="mt-2 text-gray-400">
|
||||
Use the form below to update your email.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSentVerificationCode} className="space-y-4">
|
||||
<div className="flex w-full flex-col">
|
||||
<label
|
||||
htmlFor="current-email"
|
||||
className="text-sm leading-none text-slate-500"
|
||||
>
|
||||
Current Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="current-email"
|
||||
id="current-email"
|
||||
autoComplete="current-email"
|
||||
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
|
||||
disabled
|
||||
value={currentEmail}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={cn('flex w-full flex-col', {
|
||||
'rounded-lg border border-green-500 p-3': isSubmitted,
|
||||
})}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<label
|
||||
htmlFor="new-email"
|
||||
className="text-sm leading-none text-slate-500"
|
||||
>
|
||||
New Email
|
||||
</label>
|
||||
|
||||
{isSubmitted && (
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleResendVerificationCode}
|
||||
disabled={isLoading || isResendDone}
|
||||
className="flex items-center gap-1 text-sm font-medium leading-none text-green-600 transition-colors hover:text-green-700"
|
||||
>
|
||||
<span className="hidden sm:block">
|
||||
Resend Verification Link
|
||||
</span>
|
||||
<span className="sm:hidden">Resend Code</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
name="new-email"
|
||||
id="new-email"
|
||||
autoComplete={'new-email'}
|
||||
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
|
||||
placeholder="Enter new email"
|
||||
value={newEmail}
|
||||
onChange={(e) => setNewEmail(e.target.value)}
|
||||
disabled={isSubmitted}
|
||||
/>
|
||||
{!isSubmitted && (
|
||||
<button
|
||||
type="submit"
|
||||
disabled={
|
||||
isLoading || !newEmail || !newEmail.includes('@') || isSubmitted
|
||||
}
|
||||
className="mt-3 inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
|
||||
>
|
||||
{isLoading ? 'Please wait...' : 'Send Verification Link'}
|
||||
</button>
|
||||
)}
|
||||
{isSubmitted && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCancelEmailVerification}
|
||||
disabled={isLoading}
|
||||
className="font-regular mt-4 w-full rounded-lg border border-red-600 py-2 text-sm text-red-600 outline-none transition-colors hover:bg-red-500 hover:text-white focus:ring-2 focus:ring-red-500 focus:ring-offset-1"
|
||||
>
|
||||
Cancel Update
|
||||
</button>
|
||||
<div className="mt-3 flex items-center gap-2 rounded-lg bg-green-100 p-4">
|
||||
<span className="text-sm text-green-800">
|
||||
A verification link has been sent to your{' '}
|
||||
<span>new email address</span>. Please follow the instructions
|
||||
in email to verify and update your email.
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,26 +1,28 @@
|
||||
import { type FormEvent, useEffect, useState } from 'react';
|
||||
import { httpGet, httpPost } from '../../lib/http';
|
||||
import { pageProgressMessage } from '../../stores/page';
|
||||
import { type FormEvent, useState } from 'react';
|
||||
import { httpPost } from '../../lib/http';
|
||||
import { useToast } from '../../hooks/use-toast';
|
||||
|
||||
type UpdatePasswordFormProps = {
|
||||
authProvider: string;
|
||||
};
|
||||
|
||||
export default function UpdatePasswordForm(props: UpdatePasswordFormProps) {
|
||||
const { authProvider } = props;
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
export default function UpdatePasswordForm() {
|
||||
const [authProvider, setAuthProvider] = useState('');
|
||||
const [currentPassword, setCurrentPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [newPasswordConfirmation, setNewPasswordConfirmation] = useState('');
|
||||
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState('');
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
setError('');
|
||||
setSuccess('');
|
||||
|
||||
if (newPassword !== newPasswordConfirmation) {
|
||||
setError('Passwords do not match');
|
||||
toast.error('Passwords do not match');
|
||||
setIsLoading(false);
|
||||
|
||||
return;
|
||||
@ -32,50 +34,26 @@ export default function UpdatePasswordForm() {
|
||||
oldPassword: authProvider === 'email' ? currentPassword : 'social-auth',
|
||||
password: newPassword,
|
||||
confirmPassword: newPasswordConfirmation,
|
||||
}
|
||||
);
|
||||
|
||||
if (error) {
|
||||
setError(error.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setError('');
|
||||
setCurrentPassword('');
|
||||
setNewPassword('');
|
||||
setNewPasswordConfirmation('');
|
||||
setSuccess('Password updated successfully');
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const loadProfile = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
const { error, response } = await httpGet(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-me`
|
||||
},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
toast.error(error?.message || 'Something went wrong');
|
||||
setIsLoading(false);
|
||||
setError(error?.message || 'Something went wrong');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { authProvider } = response;
|
||||
setAuthProvider(authProvider);
|
||||
|
||||
setCurrentPassword('');
|
||||
setNewPassword('');
|
||||
setNewPasswordConfirmation('');
|
||||
toast.success('Password updated successfully');
|
||||
setIsLoading(false);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadProfile().finally(() => {
|
||||
pageProgressMessage.set('');
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-8 hidden md:block">
|
||||
@ -98,7 +76,7 @@ export default function UpdatePasswordForm() {
|
||||
type="password"
|
||||
name="current-password"
|
||||
id="current-password"
|
||||
autoComplete={"current-password"}
|
||||
autoComplete={'current-password'}
|
||||
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 disabled:bg-gray-100"
|
||||
required
|
||||
minLength={6}
|
||||
@ -122,7 +100,7 @@ export default function UpdatePasswordForm() {
|
||||
type="password"
|
||||
name="new-password"
|
||||
id="new-password"
|
||||
autoComplete={"new-password"}
|
||||
autoComplete={'new-password'}
|
||||
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
|
||||
minLength={6}
|
||||
@ -145,7 +123,7 @@ export default function UpdatePasswordForm() {
|
||||
name="new-password-confirmation"
|
||||
id="new-password-confirmation"
|
||||
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"
|
||||
autoComplete={"new-password"}
|
||||
autoComplete={'new-password'}
|
||||
required
|
||||
minLength={6}
|
||||
placeholder="Confirm New Password"
|
||||
@ -156,19 +134,11 @@ export default function UpdatePasswordForm() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="mt-2 rounded-lg bg-red-100 p-2 text-red-700">{error}</p>
|
||||
)}
|
||||
|
||||
{success && (
|
||||
<p className="mt-2 rounded-lg bg-green-100 p-2 text-green-700">
|
||||
{success}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
disabled={
|
||||
isLoading || !newPassword || newPassword !== newPasswordConfirmation
|
||||
}
|
||||
className="inline-flex w-full items-center justify-center rounded-lg bg-black p-2 py-3 text-sm font-medium text-white outline-none focus:ring-2 focus:ring-black focus:ring-offset-1 disabled:bg-gray-400"
|
||||
>
|
||||
{isLoading ? 'Please wait...' : 'Update Password'}
|
||||
|
@ -30,7 +30,7 @@ export function UpdateProfileForm() {
|
||||
linkedin: linkedin || undefined,
|
||||
twitter: twitter || undefined,
|
||||
website: website || undefined,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
@ -45,11 +45,10 @@ export function UpdateProfileForm() {
|
||||
};
|
||||
|
||||
const loadProfile = async () => {
|
||||
// Set the loading state
|
||||
setIsLoading(true);
|
||||
|
||||
const { error, response } = await httpGet(
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-me`
|
||||
`${import.meta.env.PUBLIC_API_URL}/v1-me`,
|
||||
);
|
||||
|
||||
if (error || !response) {
|
||||
|
@ -1,8 +1,8 @@
|
||||
---
|
||||
import AccountSidebar from '../../components/AccountSidebar.astro';
|
||||
import UpdatePasswordForm from '../../components/UpdatePassword/UpdatePasswordForm';
|
||||
import AccountLayout from '../../layouts/AccountLayout.astro';
|
||||
import DeleteAccount from '../../components/DeleteAccount/DeleteAccount.astro';
|
||||
import { ProfileSettingsPage } from '../../components/ProfileSettings/ProfileSettingsPage';
|
||||
---
|
||||
|
||||
<AccountLayout
|
||||
@ -12,7 +12,7 @@ import DeleteAccount from '../../components/DeleteAccount/DeleteAccount.astro';
|
||||
initialLoadingMessage={'Loading settings'}
|
||||
>
|
||||
<AccountSidebar activePageId='settings' activePageTitle='Settings'>
|
||||
<UpdatePasswordForm client:only="react" />
|
||||
<ProfileSettingsPage client:load />
|
||||
<hr class='my-8' />
|
||||
<DeleteAccount />
|
||||
</AccountSidebar>
|
||||
|
10
src/pages/verify-email.astro
Normal file
10
src/pages/verify-email.astro
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
import AccountLayout from '../layouts/AccountLayout.astro';
|
||||
import { TriggerVerifyEmail } from '../components/AuthenticationFlow/TriggerVerifyEmail';
|
||||
---
|
||||
|
||||
<AccountLayout title='Verify email' noIndex={true}>
|
||||
<div class='container py-16'>
|
||||
<TriggerVerifyEmail client:load />
|
||||
</div>
|
||||
</AccountLayout>
|
Loading…
x
Reference in New Issue
Block a user