mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-01-17 14:18:17 +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",
|
"image-size": "^1.1.1",
|
||||||
"jose": "^5.2.2",
|
"jose": "^5.2.2",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"lucide-react": "^0.334.0",
|
"lucide-react": "^0.358.0",
|
||||||
"nanoid": "^5.0.5",
|
"nanoid": "^5.0.5",
|
||||||
"nanostores": "^0.9.5",
|
"nanostores": "^0.9.5",
|
||||||
"node-html-parser": "^6.1.12",
|
"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
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^0.334.0
|
specifier: ^0.358.0
|
||||||
version: 0.334.0(react@18.2.0)
|
version: 0.358.0(react@18.2.0)
|
||||||
nanoid:
|
nanoid:
|
||||||
specifier: ^5.0.5
|
specifier: ^5.0.5
|
||||||
version: 5.0.5
|
version: 5.0.5
|
||||||
@ -4236,8 +4236,8 @@ packages:
|
|||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/lucide-react@0.334.0(react@18.2.0):
|
/lucide-react@0.358.0(react@18.2.0):
|
||||||
resolution: {integrity: sha512-y0Rv/Xx6qAq4FutZ3L/efl3O9vl6NC/1p0YOg6mBfRbQ4k1JCE2rz0rnV7WC8Moxq1RY99vLATvjcqUegGJTvA==}
|
resolution: {integrity: sha512-rBSptRjZTMBm24zsFhR6pK/NgbT18JegZGKcH4+1H3+UigMSRpeoWLtR/fAwMYwYnlJOZB+y8WpeHne9D6X6Kg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
react: ^16.5.1 || ^17.0.0 || ^18.0.0
|
||||||
dependencies:
|
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 { type FormEvent, useState } from 'react';
|
||||||
import { httpGet, httpPost } from '../../lib/http';
|
import { httpPost } from '../../lib/http';
|
||||||
import { pageProgressMessage } from '../../stores/page';
|
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 [currentPassword, setCurrentPassword] = useState('');
|
||||||
const [newPassword, setNewPassword] = useState('');
|
const [newPassword, setNewPassword] = useState('');
|
||||||
const [newPasswordConfirmation, setNewPasswordConfirmation] = useState('');
|
const [newPasswordConfirmation, setNewPasswordConfirmation] = useState('');
|
||||||
|
|
||||||
const [error, setError] = useState('');
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [success, setSuccess] = useState('');
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
|
|
||||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError('');
|
|
||||||
setSuccess('');
|
|
||||||
|
|
||||||
if (newPassword !== newPasswordConfirmation) {
|
if (newPassword !== newPasswordConfirmation) {
|
||||||
setError('Passwords do not match');
|
toast.error('Passwords do not match');
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -32,50 +34,26 @@ export default function UpdatePasswordForm() {
|
|||||||
oldPassword: authProvider === 'email' ? currentPassword : 'social-auth',
|
oldPassword: authProvider === 'email' ? currentPassword : 'social-auth',
|
||||||
password: newPassword,
|
password: newPassword,
|
||||||
confirmPassword: newPasswordConfirmation,
|
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) {
|
if (error || !response) {
|
||||||
|
toast.error(error?.message || 'Something went wrong');
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setError(error?.message || 'Something went wrong');
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { authProvider } = response;
|
setCurrentPassword('');
|
||||||
setAuthProvider(authProvider);
|
setNewPassword('');
|
||||||
|
setNewPasswordConfirmation('');
|
||||||
|
toast.success('Password updated successfully');
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
loadProfile().finally(() => {
|
|
||||||
pageProgressMessage.set('');
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="mb-8 hidden md:block">
|
<div className="mb-8 hidden md:block">
|
||||||
@ -98,7 +76,7 @@ export default function UpdatePasswordForm() {
|
|||||||
type="password"
|
type="password"
|
||||||
name="current-password"
|
name="current-password"
|
||||||
id="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"
|
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
|
required
|
||||||
minLength={6}
|
minLength={6}
|
||||||
@ -122,7 +100,7 @@ export default function UpdatePasswordForm() {
|
|||||||
type="password"
|
type="password"
|
||||||
name="new-password"
|
name="new-password"
|
||||||
id="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"
|
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
|
required
|
||||||
minLength={6}
|
minLength={6}
|
||||||
@ -145,7 +123,7 @@ export default function UpdatePasswordForm() {
|
|||||||
name="new-password-confirmation"
|
name="new-password-confirmation"
|
||||||
id="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"
|
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
|
required
|
||||||
minLength={6}
|
minLength={6}
|
||||||
placeholder="Confirm New Password"
|
placeholder="Confirm New Password"
|
||||||
@ -156,19 +134,11 @@ export default function UpdatePasswordForm() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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
|
<button
|
||||||
type="submit"
|
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"
|
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'}
|
{isLoading ? 'Please wait...' : 'Update Password'}
|
||||||
|
@ -30,7 +30,7 @@ export function UpdateProfileForm() {
|
|||||||
linkedin: linkedin || undefined,
|
linkedin: linkedin || undefined,
|
||||||
twitter: twitter || undefined,
|
twitter: twitter || undefined,
|
||||||
website: website || undefined,
|
website: website || undefined,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error || !response) {
|
if (error || !response) {
|
||||||
@ -45,11 +45,10 @@ export function UpdateProfileForm() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadProfile = async () => {
|
const loadProfile = async () => {
|
||||||
// Set the loading state
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const { error, response } = await httpGet(
|
const { error, response } = await httpGet(
|
||||||
`${import.meta.env.PUBLIC_API_URL}/v1-me`
|
`${import.meta.env.PUBLIC_API_URL}/v1-me`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error || !response) {
|
if (error || !response) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
import AccountSidebar from '../../components/AccountSidebar.astro';
|
import AccountSidebar from '../../components/AccountSidebar.astro';
|
||||||
import UpdatePasswordForm from '../../components/UpdatePassword/UpdatePasswordForm';
|
|
||||||
import AccountLayout from '../../layouts/AccountLayout.astro';
|
import AccountLayout from '../../layouts/AccountLayout.astro';
|
||||||
import DeleteAccount from '../../components/DeleteAccount/DeleteAccount.astro';
|
import DeleteAccount from '../../components/DeleteAccount/DeleteAccount.astro';
|
||||||
|
import { ProfileSettingsPage } from '../../components/ProfileSettings/ProfileSettingsPage';
|
||||||
---
|
---
|
||||||
|
|
||||||
<AccountLayout
|
<AccountLayout
|
||||||
@ -12,7 +12,7 @@ import DeleteAccount from '../../components/DeleteAccount/DeleteAccount.astro';
|
|||||||
initialLoadingMessage={'Loading settings'}
|
initialLoadingMessage={'Loading settings'}
|
||||||
>
|
>
|
||||||
<AccountSidebar activePageId='settings' activePageTitle='Settings'>
|
<AccountSidebar activePageId='settings' activePageTitle='Settings'>
|
||||||
<UpdatePasswordForm client:only="react" />
|
<ProfileSettingsPage client:load />
|
||||||
<hr class='my-8' />
|
<hr class='my-8' />
|
||||||
<DeleteAccount />
|
<DeleteAccount />
|
||||||
</AccountSidebar>
|
</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