revamp template
This commit is contained in:
49
src/auth/pages/jwt/CheckEmail.tsx
Normal file
49
src/auth/pages/jwt/CheckEmail.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
|
||||
const CheckEmail = () => {
|
||||
return (
|
||||
<div className="card max-w-[440px] w-full">
|
||||
<div className="card-body p-10">
|
||||
<div className="flex justify-center py-10">
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/30.svg')}
|
||||
className="dark:hidden max-h-[130px]"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/30-dark.svg')}
|
||||
className="light:hidden max-h-[130px]"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-gray-900 text-center mb-3">Check your email</h3>
|
||||
<div className="text-2sm text-center text-gray-700 mb-7.5">
|
||||
Please click the link sent to your email
|
||||
<a href="#" className="text-2sm text-gray-900 font-medium hover:text-primary-active">
|
||||
bob@keenthemes.com
|
||||
</a>
|
||||
<br />
|
||||
to verify your account. Thank you
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center mb-5">
|
||||
<Link to="/" className="btn btn-primary flex justify-center">
|
||||
Back to Home
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<span className="text-xs text-gray-700">Didn’t receive an email?</span>
|
||||
<Link to="/auth/classic/login" className="text-xs font-medium link">
|
||||
Resend
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { CheckEmail };
|
||||
189
src/auth/pages/jwt/Login.tsx
Normal file
189
src/auth/pages/jwt/Login.tsx
Normal file
@ -0,0 +1,189 @@
|
||||
import { type MouseEvent, useState } from 'react';
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
||||
import clsx from 'clsx';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { KeenIcon } from '@/components';
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
import { useAuthContext } from '@/auth';
|
||||
import { useLayout } from '@/providers';
|
||||
import { Alert } from '@/components';
|
||||
import moment from 'moment';
|
||||
|
||||
const loginSchema = Yup.object().shape({
|
||||
username: Yup.string().required('Username is required'),
|
||||
password: Yup.string()
|
||||
.min(3, 'Minimum 3 symbols')
|
||||
.max(50, 'Maximum 50 symbols')
|
||||
.required('Password is required'),
|
||||
remember: Yup.boolean()
|
||||
});
|
||||
|
||||
const initialValues = {
|
||||
username: '',
|
||||
password: '',
|
||||
application: 'credit',
|
||||
remember: false
|
||||
};
|
||||
|
||||
const Login = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { login } = useAuthContext();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const from = location.state?.from?.pathname || '/';
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const { currentLayout } = useLayout();
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues,
|
||||
validationSchema: loginSchema,
|
||||
onSubmit: async (values, { setStatus, setSubmitting }) => {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
if (!login) {
|
||||
throw new Error('JWTProvider is required for this form.');
|
||||
}
|
||||
|
||||
await login(values.username, values.password, values.application);
|
||||
console.log(login);
|
||||
|
||||
if (values.remember) {
|
||||
localStorage.setItem('username', values.username);
|
||||
} else {
|
||||
localStorage.removeItem('username');
|
||||
}
|
||||
|
||||
navigate(from, { replace: true });
|
||||
} catch (error: any) {
|
||||
if (error.response && error.response.data) {
|
||||
setStatus(error.response.data.message);
|
||||
} else {
|
||||
setStatus('The login details are incorrect');
|
||||
}
|
||||
|
||||
setSubmitting(false);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
const togglePassword = (event: MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
setShowPassword(!showPassword);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card max-w-[390px] border-0 shadow-none w-full">
|
||||
<form className="card-body flex flex-col gap-5 p-5" onSubmit={formik.handleSubmit} noValidate>
|
||||
<div className="flex align-center justify-center text-center mb-2.5">
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/app/bri_tl_logo.png')}
|
||||
className="h-[50px] max-w-none mb-2 text-center"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex gap-3 items-center">
|
||||
<div>
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/app/brilian_apps_logo.png')}
|
||||
className="h-[40px] max-w-none"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<h3 className="font-semibold text-gray-700" style={{ fontSize: '18px' }}>
|
||||
/ Pengajuan Kredit
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
{formik.status && <Alert variant="danger">{formik.status}</Alert>}
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="form-label text-gray-900 ps-2.5">Login</label>
|
||||
<label className="input">
|
||||
<input
|
||||
placeholder="Enter username"
|
||||
autoComplete="off"
|
||||
{...formik.getFieldProps('username')}
|
||||
className={clsx('form-control', {
|
||||
'is-invalid': formik.touched.username && formik.errors.username
|
||||
})}
|
||||
/>
|
||||
</label>
|
||||
{formik.touched.username && formik.errors.username && (
|
||||
<span role="alert" className="text-danger text-xs mt-1">
|
||||
{formik.errors.username}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
<label className="form-label text-gray-900 ps-2.5">Password</label>
|
||||
</div>
|
||||
<label className="input">
|
||||
<input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
placeholder="Enter Password"
|
||||
autoComplete="off"
|
||||
{...formik.getFieldProps('password')}
|
||||
className={clsx('form-control', {
|
||||
'is-invalid': formik.touched.password && formik.errors.password
|
||||
})}
|
||||
/>
|
||||
<button className="btn btn-icon" onClick={togglePassword}>
|
||||
<KeenIcon icon="eye" className={clsx('text-gray-500', { hidden: showPassword })} />
|
||||
<KeenIcon
|
||||
icon="eye-slash"
|
||||
className={clsx('text-gray-500', { hidden: !showPassword })}
|
||||
/>
|
||||
</button>
|
||||
</label>
|
||||
{formik.touched.password && formik.errors.password && (
|
||||
<span role="alert" className="text-danger text-xs mt-1">
|
||||
{formik.errors.password}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-1">
|
||||
<label className="checkbox-group">
|
||||
<input
|
||||
className="checkbox checkbox-sm"
|
||||
type="checkbox"
|
||||
{...formik.getFieldProps('remember')}
|
||||
/>
|
||||
<span className="checkbox-label">Remember me</span>
|
||||
</label>
|
||||
<Link
|
||||
to={
|
||||
currentLayout?.name === 'auth-branded'
|
||||
? '/auth/reset-password'
|
||||
: '/auth/classic/reset-password'
|
||||
}
|
||||
className="text-2sm link shrink-0"
|
||||
>
|
||||
Forgot Password?
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary flex justify-center grow"
|
||||
disabled={loading || formik.isSubmitting}
|
||||
>
|
||||
{loading ? 'Please wait...' : 'Sign In'}
|
||||
</button>
|
||||
<div>
|
||||
<p className="text-2sm" style={{ fontSize: '12px', letterSpacing: 0.25 }}>
|
||||
Copyright {moment().year()} © Bank Rakyat Indonesia Timor Leste.
All rights
|
||||
reserved.
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { Login };
|
||||
76
src/auth/pages/jwt/TwoFactorAuth.tsx
Normal file
76
src/auth/pages/jwt/TwoFactorAuth.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
import { KeenIcon } from '@/components';
|
||||
|
||||
const TwoFactorAuth = () => {
|
||||
const [codeInputs, setCodeInputs] = useState(Array(6).fill(''));
|
||||
|
||||
const handleInputChange = (index: number, value: string) => {
|
||||
if (value.length > 1) return;
|
||||
const updatedInputs = [...codeInputs];
|
||||
updatedInputs[index] = value;
|
||||
setCodeInputs(updatedInputs);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card max-w-[380px] w-full">
|
||||
<form className="card-body flex flex-col gap-5 p-10">
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/34.svg')}
|
||||
className="dark:hidden h-20 mb-2"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/34-dark.svg')}
|
||||
className="light:hidden h-20 mb-2"
|
||||
alt=""
|
||||
/>
|
||||
|
||||
<div className="text-center mb-2">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-5">Verify your phone</h3>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-2sm text-gray-700 mb-1.5">
|
||||
Enter the verification code we sent to
|
||||
</span>
|
||||
<a href="#" className="text-sm font-medium text-gray-900">
|
||||
****** 7859
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-2.5">
|
||||
{codeInputs.map((value, index) => (
|
||||
<input
|
||||
key={index}
|
||||
type="text"
|
||||
maxLength={1}
|
||||
className="input focus:border-primary-clarity focus:ring focus:ring-primary-clarity size-10 shrink-0 px-0 text-center"
|
||||
value={value}
|
||||
onChange={(e) => handleInputChange(index, e.target.value)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center mb-2">
|
||||
<span className="text-xs text-gray-700 me-1.5">Didn’t receive a code? (37s)</span>
|
||||
<Link to="/auth/classic/login" className="text-xs link">
|
||||
Resend
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<button className="btn btn-primary flex justify-center grow">Continue</button>
|
||||
|
||||
<Link
|
||||
to="/auth/login"
|
||||
className="flex items-center justify-center text-sm gap-2 text-gray-700 hover:text-primary"
|
||||
>
|
||||
<KeenIcon icon="black-left" />
|
||||
Back to Login
|
||||
</Link>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { TwoFactorAuth };
|
||||
4
src/auth/pages/jwt/index.ts
Normal file
4
src/auth/pages/jwt/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from './reset-password';
|
||||
export * from './Login';
|
||||
export * from './CheckEmail';
|
||||
export * from './TwoFactorAuth';
|
||||
134
src/auth/pages/jwt/reset-password/ResetPassword.tsx
Normal file
134
src/auth/pages/jwt/reset-password/ResetPassword.tsx
Normal file
@ -0,0 +1,134 @@
|
||||
import clsx from 'clsx';
|
||||
import { useFormik } from 'formik';
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import * as Yup from 'yup';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useAuthContext } from '@/auth/useAuthContext';
|
||||
import { Alert, KeenIcon } from '@/components';
|
||||
import { useLayout } from '@/providers';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
const initialValues = {
|
||||
email: ''
|
||||
};
|
||||
|
||||
const forgotPasswordSchema = Yup.object().shape({
|
||||
email: Yup.string()
|
||||
.email('Wrong email format')
|
||||
.min(3, 'Minimum 3 symbols')
|
||||
.max(50, 'Maximum 50 symbols')
|
||||
.required('Email is required')
|
||||
});
|
||||
|
||||
const ResetPassword = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [hasErrors, setHasErrors] = useState<boolean | undefined>(undefined);
|
||||
const { requestPasswordResetLink } = useAuthContext();
|
||||
const { currentLayout } = useLayout();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues,
|
||||
validationSchema: forgotPasswordSchema,
|
||||
onSubmit: async (values, { setStatus, setSubmitting }) => {
|
||||
setLoading(true);
|
||||
setHasErrors(undefined);
|
||||
try {
|
||||
if (!requestPasswordResetLink) {
|
||||
throw new Error('JWTProvider is required for this form.');
|
||||
}
|
||||
await requestPasswordResetLink(values.email);
|
||||
setHasErrors(false);
|
||||
setLoading(false);
|
||||
const params = new URLSearchParams();
|
||||
params.append('email', values.email);
|
||||
navigate({
|
||||
pathname:
|
||||
currentLayout?.name === 'auth-branded'
|
||||
? '/auth/reset-password/check-email'
|
||||
: '/auth/classic/reset-password/check-email',
|
||||
search: params.toString()
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError && error.response) {
|
||||
setStatus(error.response.data.message);
|
||||
} else {
|
||||
setStatus('Password reset failed. Please try again.');
|
||||
}
|
||||
setHasErrors(true);
|
||||
setLoading(false);
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
return (
|
||||
<div className="card max-w-[370px] w-full">
|
||||
<form
|
||||
className="card-body flex flex-col gap-5 p-10"
|
||||
noValidate
|
||||
onSubmit={formik.handleSubmit}
|
||||
>
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-semibold text-gray-900">Your Email</h3>
|
||||
<span className="text-2sm text-gray-600 font-medium">
|
||||
Enter your email to reset password
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{hasErrors && <Alert variant="danger">{formik.status}</Alert>}
|
||||
|
||||
{hasErrors === false && (
|
||||
<Alert variant="success">
|
||||
Password reset link sent. Please check your email to proceed
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="form-label text-gray-900">Email</label>
|
||||
<label className="input">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="email@email.com"
|
||||
autoComplete="off"
|
||||
{...formik.getFieldProps('email')}
|
||||
className={clsx(
|
||||
'form-control bg-transparent',
|
||||
{ 'is-invalid': formik.touched.email && formik.errors.email },
|
||||
{
|
||||
'is-valid': formik.touched.email && !formik.errors.email
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
{formik.touched.email && formik.errors.email && (
|
||||
<span role="alert" className="text-danger text-xs mt-1">
|
||||
{formik.errors.email}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-5 items-stretch">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary flex justify-center grow"
|
||||
disabled={loading || formik.isSubmitting}
|
||||
>
|
||||
{loading ? 'Please wait...' : 'Continue'}
|
||||
</button>
|
||||
|
||||
<Link
|
||||
to={currentLayout?.name === 'auth-branded' ? '/auth/login' : '/auth/classic/login'}
|
||||
className="flex items-center justify-center text-sm gap-2 text-gray-700 hover:text-primary"
|
||||
>
|
||||
<KeenIcon icon="black-left" />
|
||||
Back to Login
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ResetPassword };
|
||||
173
src/auth/pages/jwt/reset-password/ResetPasswordChange.tsx
Normal file
173
src/auth/pages/jwt/reset-password/ResetPasswordChange.tsx
Normal file
@ -0,0 +1,173 @@
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { Alert, KeenIcon } from '@/components';
|
||||
import { useAuthContext } from '@/auth';
|
||||
import { useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useLayout } from '@/providers';
|
||||
import { AxiosError } from 'axios';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
const passwordSchema = Yup.object().shape({
|
||||
newPassword: Yup.string()
|
||||
.min(6, 'Password must be at least 6 characters')
|
||||
.required('New password is required'),
|
||||
confirmPassword: Yup.string()
|
||||
.oneOf([Yup.ref('newPassword')], 'Passwords must match')
|
||||
.required('Please confirm your new password')
|
||||
});
|
||||
|
||||
const ResetPasswordChange = () => {
|
||||
const { currentLayout } = useLayout();
|
||||
const { changePassword } = useAuthContext();
|
||||
const navigate = useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [hasErrors, setHasErrors] = useState<boolean | undefined>(undefined);
|
||||
const [showNewPassword, setShowNewPassword] = useState(false);
|
||||
const [showNewPasswordConfirmation, setShowNewPasswordConfirmation] = useState(false);
|
||||
const { id } = useParams();
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
},
|
||||
validationSchema: passwordSchema,
|
||||
onSubmit: async (values, { setStatus, setSubmitting }) => {
|
||||
setLoading(true);
|
||||
setHasErrors(undefined);
|
||||
|
||||
const token = id;
|
||||
console.log(token);
|
||||
|
||||
if (!token) {
|
||||
setHasErrors(true);
|
||||
setStatus('Token and email properties are required');
|
||||
setLoading(false);
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await changePassword(token, values.newPassword, values.confirmPassword);
|
||||
setHasErrors(false);
|
||||
navigate(
|
||||
currentLayout?.name === 'auth-branded'
|
||||
? '/auth/reset-password/changed'
|
||||
: '/auth/classic/reset-password/changed'
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError && error.response) {
|
||||
setStatus(error.response.data.message);
|
||||
} else {
|
||||
setStatus('Password reset failed. Please try again.');
|
||||
}
|
||||
setHasErrors(true);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card max-w-[370px] w-full">
|
||||
<form
|
||||
className="card-body flex flex-col gap-5 p-10"
|
||||
onSubmit={formik.handleSubmit}
|
||||
noValidate
|
||||
>
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-medium text-gray-900">Reset Password</h3>
|
||||
<span className="text-2sm text-gray-700">Enter your new password</span>
|
||||
</div>
|
||||
|
||||
{hasErrors && <Alert variant="danger">{formik.status}</Alert>}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="form-label text-gray-900">New Password</label>
|
||||
<label className="input">
|
||||
<input
|
||||
type={showNewPassword ? 'text' : 'password'}
|
||||
placeholder="Enter a new password"
|
||||
autoComplete="off"
|
||||
{...formik.getFieldProps('newPassword')}
|
||||
className={clsx(
|
||||
'form-control bg-transparent',
|
||||
{ 'is-invalid': formik.touched.newPassword && formik.errors.newPassword },
|
||||
{ 'is-valid': formik.touched.newPassword && !formik.errors.newPassword }
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
className="btn btn-icon"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setShowNewPassword(!showNewPassword);
|
||||
}}
|
||||
>
|
||||
<KeenIcon icon="eye" className={clsx('text-gray-500', { hidden: showNewPassword })} />
|
||||
<KeenIcon
|
||||
icon="eye-slash"
|
||||
className={clsx('text-gray-500', { hidden: !showNewPassword })}
|
||||
/>
|
||||
</button>
|
||||
</label>
|
||||
{formik.touched.newPassword && formik.errors.newPassword && (
|
||||
<span role="alert" className="text-danger text-xs mt-1">
|
||||
{formik.errors.newPassword}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="form-label font-normal text-gray-900">Confirm New Password</label>
|
||||
<label className="input">
|
||||
<input
|
||||
type={showNewPasswordConfirmation ? 'text' : 'password'}
|
||||
placeholder="Re-enter a new Password"
|
||||
autoComplete="off"
|
||||
{...formik.getFieldProps('confirmPassword')}
|
||||
className={clsx(
|
||||
'form-control bg-transparent',
|
||||
{ 'is-invalid': formik.touched.confirmPassword && formik.errors.confirmPassword },
|
||||
{ 'is-valid': formik.touched.confirmPassword && !formik.errors.confirmPassword }
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
className="btn btn-icon"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setShowNewPasswordConfirmation(!showNewPasswordConfirmation);
|
||||
}}
|
||||
>
|
||||
<KeenIcon
|
||||
icon="eye"
|
||||
className={clsx('text-gray-500', { hidden: showNewPasswordConfirmation })}
|
||||
/>
|
||||
<KeenIcon
|
||||
icon="eye-slash"
|
||||
className={clsx('text-gray-500', { hidden: !showNewPasswordConfirmation })}
|
||||
/>
|
||||
</button>
|
||||
</label>
|
||||
{formik.touched.confirmPassword && formik.errors.confirmPassword && (
|
||||
<span role="alert" className="text-danger text-xs mt-1">
|
||||
{formik.errors.confirmPassword}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary flex justify-center grow"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Please wait...' : 'Submit'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ResetPasswordChange };
|
||||
46
src/auth/pages/jwt/reset-password/ResetPasswordChanged.tsx
Normal file
46
src/auth/pages/jwt/reset-password/ResetPasswordChanged.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
import { useLayout } from '@/providers';
|
||||
|
||||
const ResetPasswordChanged = () => {
|
||||
const { currentLayout } = useLayout();
|
||||
|
||||
return (
|
||||
<div className="card max-w-[440px] w-full">
|
||||
<div className="card-body p-10">
|
||||
<div className="flex justify-center mb-5">
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/32.svg')}
|
||||
className="dark:hidden max-h-[180px]"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/32-dark.svg')}
|
||||
className="light:hidden max-h-[180px]"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-gray-900 text-center mb-4">
|
||||
Your password is changed
|
||||
</h3>
|
||||
<div className="text-2sm text-center text-gray-700 mb-7.5">
|
||||
Your password has been successfully updated.
|
||||
<br />
|
||||
Your account's security is our priority.
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Link
|
||||
to={currentLayout?.name === 'auth-branded' ? '/auth/login' : '/auth/classic/login'}
|
||||
className="btn btn-primary"
|
||||
>
|
||||
Sign in
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ResetPasswordChanged };
|
||||
@ -0,0 +1,72 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { toAbsoluteUrl } from '@/utils';
|
||||
import { useLayout } from '@/providers';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const ResetPasswordCheckEmail = () => {
|
||||
const { currentLayout } = useLayout();
|
||||
const [email, setEmail] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setEmail(new URLSearchParams(window.location.search).get('email'));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="card max-w-[440px] w-full">
|
||||
<div className="card-body p-10">
|
||||
<div className="flex justify-center py-10">
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/30.svg')}
|
||||
className="dark:hidden max-h-[130px]"
|
||||
alt=""
|
||||
/>
|
||||
<img
|
||||
src={toAbsoluteUrl('/media/illustrations/30-dark.svg')}
|
||||
className="light:hidden max-h-[130px]"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg font-medium text-gray-900 text-center mb-3">Check your email</h3>
|
||||
<div className="text-2sm text-center text-gray-700 mb-7.5">
|
||||
Please click the link sent to your email{' '}
|
||||
<a href="#" className="text-2sm text-gray-800 font-medium hover:text-primary-active">
|
||||
{email}
|
||||
</a>
|
||||
<br />
|
||||
to reset your password. Thank you
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center mb-5">
|
||||
<Link
|
||||
to={
|
||||
currentLayout?.name === 'auth-branded'
|
||||
? '/auth/reset-password/changed'
|
||||
: '/auth/classic/reset-password/changed'
|
||||
}
|
||||
className="btn btn-primary flex justify-center"
|
||||
>
|
||||
Skip for now
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<span className="text-xs text-gray-600">Didn’t receive an email?</span>
|
||||
<Link
|
||||
to={
|
||||
currentLayout?.name === 'auth-branded'
|
||||
? '/auth/reset-password/enter-email'
|
||||
: '/auth/classic/reset-password/enter-email'
|
||||
}
|
||||
className="text-xs font-medium link"
|
||||
>
|
||||
Resend
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ResetPasswordCheckEmail };
|
||||
@ -0,0 +1,45 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { KeenIcon } from '@/components';
|
||||
import { useLayout } from '@/providers';
|
||||
|
||||
const ResetPasswordEnterEmail = () => {
|
||||
const { currentLayout } = useLayout();
|
||||
const [searchInput, setSearchInput] = useState('');
|
||||
|
||||
return (
|
||||
<div className="card max-w-[370px] w-full">
|
||||
<form className="card-body flex flex-col gap-5 p-10">
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-medium text-gray-900">Your Email</h3>
|
||||
<span className="text-2sm text-gray-700">Enter your email to reset password</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="form-label font-normal text-gray-900">Email</label>
|
||||
<input
|
||||
className="input"
|
||||
type="text"
|
||||
placeholder="email@email.com"
|
||||
value={searchInput}
|
||||
onChange={(e) => setSearchInput(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
to={
|
||||
currentLayout?.name === 'auth-branded'
|
||||
? '/auth/reset-password/check-email'
|
||||
: '/auth/classic/reset-password/check-email'
|
||||
}
|
||||
className="btn btn-primary flex justify-center grow"
|
||||
>
|
||||
Continue
|
||||
<KeenIcon icon="black-right" />
|
||||
</Link>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { ResetPasswordEnterEmail };
|
||||
5
src/auth/pages/jwt/reset-password/index.ts
Normal file
5
src/auth/pages/jwt/reset-password/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from './ResetPassword';
|
||||
export * from './ResetPasswordChange';
|
||||
export * from './ResetPasswordChanged';
|
||||
export * from './ResetPasswordCheckEmail';
|
||||
export * from './ResetPasswordEnterEmail';
|
||||
Reference in New Issue
Block a user