removed captacha
This commit is contained in:
parent
9745092a33
commit
de3a72ca35
@ -1782,7 +1782,7 @@ const CompanyProfile = () => {
|
|||||||
try {
|
try {
|
||||||
const countResponse = await fetchEstablishments({ params: countParams, signal: controller.signal });
|
const countResponse = await fetchEstablishments({ params: countParams, signal: controller.signal });
|
||||||
const countPayload = countResponse?.data ?? countResponse;
|
const countPayload = countResponse?.data ?? countResponse;
|
||||||
const totalRecords = countPayload?.pagination?.total_records || 0;
|
const totalRecords = countResponse?.pagination?.total_records || 0;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
search: debouncedSearch || undefined,
|
search: debouncedSearch || undefined,
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { login } from '@/services/auth/authService.js';
|
import { login } from '@/services/auth/authService.js';
|
||||||
import { RefreshCw, AlertCircle, Clock } from "lucide-react";
|
import { AlertCircle, Clock } from "lucide-react";
|
||||||
|
|
||||||
const logoSrc = '/assets/images/FCSCLogo.svg';
|
const logoSrc = '/assets/images/FCSCLogo.svg';
|
||||||
|
|
||||||
const MAX_FAILED_ATTEMPTS = 3;
|
const MAX_FAILED_ATTEMPTS = 3;
|
||||||
const SHOW_CAPTCHA_AFTER = 0;
|
|
||||||
const LOCK_DURATION_MINUTES = 2;
|
const LOCK_DURATION_MINUTES = 2;
|
||||||
|
|
||||||
const Login = () => {
|
const Login = () => {
|
||||||
@ -23,10 +22,6 @@ const Login = () => {
|
|||||||
const [emailError, setEmailError] = React.useState('');
|
const [emailError, setEmailError] = React.useState('');
|
||||||
const [passwordError, setPasswordError] = React.useState('');
|
const [passwordError, setPasswordError] = React.useState('');
|
||||||
|
|
||||||
const [captcha, setCaptcha] = React.useState('');
|
|
||||||
const [userCaptcha, setUserCaptcha] = React.useState('');
|
|
||||||
const [captchaError, setCaptchaError] = React.useState('');
|
|
||||||
|
|
||||||
const [isLocked, setIsLocked] = React.useState(false);
|
const [isLocked, setIsLocked] = React.useState(false);
|
||||||
const [lockExpiry, setLockExpiry] = React.useState(null);
|
const [lockExpiry, setLockExpiry] = React.useState(null);
|
||||||
const [failedAttempts, setFailedAttempts] = React.useState(0);
|
const [failedAttempts, setFailedAttempts] = React.useState(0);
|
||||||
@ -36,15 +31,6 @@ const Login = () => {
|
|||||||
const navigateTimeoutRef = React.useRef(null);
|
const navigateTimeoutRef = React.useRef(null);
|
||||||
const countdownIntervalRef = React.useRef(null);
|
const countdownIntervalRef = React.useRef(null);
|
||||||
|
|
||||||
const generateCaptcha = React.useCallback(() => {
|
|
||||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
|
||||||
let code = '';
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
code += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
setCaptcha(code);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const closeToast = React.useCallback(() => {
|
const closeToast = React.useCallback(() => {
|
||||||
if (toastTimeoutRef.current) {
|
if (toastTimeoutRef.current) {
|
||||||
clearTimeout(toastTimeoutRef.current);
|
clearTimeout(toastTimeoutRef.current);
|
||||||
@ -106,8 +92,9 @@ const Login = () => {
|
|||||||
countdownIntervalRef.current = setInterval(updateTimer, 1000);
|
countdownIntervalRef.current = setInterval(updateTimer, 1000);
|
||||||
}, [showToast]);
|
}, [showToast]);
|
||||||
|
|
||||||
|
// Check lock status when email changes
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
generateCaptcha();
|
if (!email) return;
|
||||||
|
|
||||||
// Get the stored attempts and lock status for the current email
|
// Get the stored attempts and lock status for the current email
|
||||||
const storedAttempts = JSON.parse(localStorage.getItem('login_attempts') || '{}');
|
const storedAttempts = JSON.parse(localStorage.getItem('login_attempts') || '{}');
|
||||||
@ -128,7 +115,7 @@ const Login = () => {
|
|||||||
} else {
|
} else {
|
||||||
setIsLocked(false);
|
setIsLocked(false);
|
||||||
}
|
}
|
||||||
}, [generateCaptcha, startCountdown, email]);
|
}, [email, startCountdown]);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
@ -139,24 +126,12 @@ const Login = () => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCaptchaChange = (e) => {
|
|
||||||
setUserCaptcha(e.target.value.toUpperCase());
|
|
||||||
if (captchaError) setCaptchaError('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshCaptcha = () => {
|
|
||||||
generateCaptcha();
|
|
||||||
setUserCaptcha('');
|
|
||||||
setCaptchaError('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
const validateForm = () => {
|
const validateForm = () => {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
setEmailError('');
|
setEmailError('');
|
||||||
setPasswordError('');
|
setPasswordError('');
|
||||||
setCaptchaError('');
|
|
||||||
|
|
||||||
const trimmedEmail = email.trim();
|
const trimmedEmail = email.trim();
|
||||||
const trimmedPassword = password.trim();
|
const trimmedPassword = password.trim();
|
||||||
@ -174,14 +149,6 @@ const Login = () => {
|
|||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userCaptcha.trim()) {
|
|
||||||
setCaptchaError('Please enter the CAPTCHA.');
|
|
||||||
valid = false;
|
|
||||||
} else if (userCaptcha !== captcha) {
|
|
||||||
setCaptchaError('Invalid CAPTCHA. Try again or refresh for a new code.');
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -291,8 +258,6 @@ const Login = () => {
|
|||||||
localStorage.setItem('login_attempts', JSON.stringify(updatedAttempts));
|
localStorage.setItem('login_attempts', JSON.stringify(updatedAttempts));
|
||||||
setFailedAttempts(attempts);
|
setFailedAttempts(attempts);
|
||||||
|
|
||||||
refreshCaptcha();
|
|
||||||
|
|
||||||
if (attempts >= MAX_FAILED_ATTEMPTS) {
|
if (attempts >= MAX_FAILED_ATTEMPTS) {
|
||||||
const lockUntil = Date.now() + LOCK_DURATION_MINUTES * 60 * 1000;
|
const lockUntil = Date.now() + LOCK_DURATION_MINUTES * 60 * 1000;
|
||||||
setIsLocked(true);
|
setIsLocked(true);
|
||||||
@ -457,55 +422,6 @@ const Login = () => {
|
|||||||
{passwordError && <p className="text-xs text-red-600 mt-1">{passwordError}</p>}
|
{passwordError && <p className="text-xs text-red-600 mt-1">{passwordError}</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4">
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
CAPTCHA Verification <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="flex justify-center items-center font-mono text-lg tracking-widest bg-gray-100 border-2 border-[#92722A] rounded-md px-4 py-2 select-none h-10">
|
|
||||||
{captcha}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={refreshCaptcha}
|
|
||||||
disabled={isLocked}
|
|
||||||
className={`flex items-center gap-1 ${
|
|
||||||
isLocked
|
|
||||||
? 'text-gray-400 cursor-not-allowed'
|
|
||||||
: 'text-[#92722A] hover:text-[#7b5c1f]'
|
|
||||||
}`}
|
|
||||||
title="Refresh CAPTCHA"
|
|
||||||
>
|
|
||||||
<RefreshCw className="h-4 w-4" />
|
|
||||||
<span className="text-sm leading-none">Refresh</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
|
||||||
Enter the characters shown above. Can't read it? Click refresh for a new code.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="captcha"
|
|
||||||
value={userCaptcha}
|
|
||||||
onChange={handleCaptchaChange}
|
|
||||||
placeholder="Type the characters"
|
|
||||||
disabled={isLocked}
|
|
||||||
className={`mt-2 block w-full h-10 rounded-md border-2 px-4 text-sm focus:ring-0 ${
|
|
||||||
captchaError
|
|
||||||
? 'border-red-500 bg-red-50'
|
|
||||||
: isLocked
|
|
||||||
? 'border-gray-300 bg-gray-100 cursor-not-allowed'
|
|
||||||
: 'border-[#92722A] bg-white'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
{captchaError && <p className="text-xs text-red-600 mt-1">{captchaError}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<label className="inline-flex items-center gap-2 cursor-pointer">
|
<label className="inline-flex items-center gap-2 cursor-pointer">
|
||||||
<input
|
<input
|
||||||
@ -532,8 +448,7 @@ const Login = () => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
disabled={
|
disabled={
|
||||||
loading ||
|
loading ||
|
||||||
isLocked ||
|
isLocked
|
||||||
!userCaptcha.trim()
|
|
||||||
}
|
}
|
||||||
className={`inline-flex items-center justify-center w-full h-12 rounded-lg text-white font-medium ${
|
className={`inline-flex items-center justify-center w-full h-12 rounded-lg text-white font-medium ${
|
||||||
isLocked
|
isLocked
|
||||||
@ -557,3 +472,4 @@ const Login = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default Login;
|
export default Login;
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import apiClient from '@/services/api/apiClient';
|
import apiClient from '@/services/api/apiClient';
|
||||||
import { RefreshCw } from "lucide-react";
|
|
||||||
const logoSrc = '/assets/images/FCSCLogo.svg';
|
const logoSrc = '/assets/images/FCSCLogo.svg';
|
||||||
|
|
||||||
const PublicContactForm = () => {
|
const PublicContactForm = () => {
|
||||||
@ -16,9 +16,6 @@ const PublicContactForm = () => {
|
|||||||
|
|
||||||
const [errors, setErrors] = useState({});
|
const [errors, setErrors] = useState({});
|
||||||
const [isValid, setIsValid] = useState(false);
|
const [isValid, setIsValid] = useState(false);
|
||||||
const [captcha, setCaptcha] = useState("");
|
|
||||||
const [userCaptcha, setUserCaptcha] = useState("");
|
|
||||||
const [captchaError, setCaptchaError] = useState("");
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [success, setSuccess] = useState("");
|
const [success, setSuccess] = useState("");
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
const [errorMsg, setErrorMsg] = useState("");
|
||||||
@ -30,19 +27,6 @@ const PublicContactForm = () => {
|
|||||||
|
|
||||||
const toastTimeoutRef = React.useRef(null);
|
const toastTimeoutRef = React.useRef(null);
|
||||||
|
|
||||||
const generateCaptcha = () => {
|
|
||||||
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
||||||
let code = "";
|
|
||||||
for (let i = 0; i < 6; i++) {
|
|
||||||
code += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
setCaptcha(code);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
generateCaptcha();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (toastTimeoutRef.current) {
|
if (toastTimeoutRef.current) {
|
||||||
@ -98,11 +82,6 @@ const PublicContactForm = () => {
|
|||||||
validate(updated);
|
validate(updated);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCaptchaChange = (e) => {
|
|
||||||
setUserCaptcha(e.target.value.toUpperCase());
|
|
||||||
setCaptchaError("");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setErrorMsg("");
|
setErrorMsg("");
|
||||||
@ -111,12 +90,6 @@ const PublicContactForm = () => {
|
|||||||
const validationErrors = validate();
|
const validationErrors = validate();
|
||||||
if (Object.keys(validationErrors).length > 0) return;
|
if (Object.keys(validationErrors).length > 0) return;
|
||||||
|
|
||||||
if (userCaptcha !== captcha) {
|
|
||||||
setCaptchaError("Incorrect CAPTCHA. Please try again.");
|
|
||||||
generateCaptcha();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
@ -335,48 +308,11 @@ const PublicContactForm = () => {
|
|||||||
error={errors.email}
|
error={errors.email}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mt-4">
|
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
||||||
CAPTCHA Verification <span className="text-red-500">*</span>
|
|
||||||
</label>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
className="flex justify-center items-center font-mono text-lg tracking-widest bg-gray-100 border-2 border-[#92722A] rounded-md px-4 py-2 select-none"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
{captcha}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={generateCaptcha}
|
|
||||||
className="flex items-center gap-1 text-sm text-[#92722A] hover:underline"
|
|
||||||
>
|
|
||||||
<RefreshCw size={14} className="text-[#92722A]" />
|
|
||||||
Refresh
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
|
||||||
Enter the characters in the image. Can't read it? Refresh for a new code.
|
|
||||||
<span className="font-mono"></span>
|
|
||||||
</p>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="captcha"
|
|
||||||
value={userCaptcha}
|
|
||||||
onChange={handleCaptchaChange}
|
|
||||||
placeholder="Type the characters"
|
|
||||||
className={`mt-2 block w-full h-10 rounded-md border-2 ${
|
|
||||||
captchaError ? "border-red-500" : "border-[#92722A]"
|
|
||||||
} focus:border-[#92722A] focus:ring-0 px-4 text-sm bg-white`}
|
|
||||||
/>
|
|
||||||
{captchaError && <p className="text-xs text-red-600 mt-1">{captchaError}</p>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!isValid || loading || !userCaptcha}
|
disabled={!isValid || loading}
|
||||||
className={`inline-flex items-center justify-center w-full h-12 rounded-lg text-white ${
|
className={`inline-flex items-center justify-center w-full h-12 rounded-lg text-white ${
|
||||||
!isValid || loading || !userCaptcha
|
!isValid || loading
|
||||||
? "bg-[#bfa06a] cursor-not-allowed"
|
? "bg-[#bfa06a] cursor-not-allowed"
|
||||||
: "bg-[#92722A] hover:bg-[#7b5c1f]"
|
: "bg-[#92722A] hover:bg-[#7b5c1f]"
|
||||||
} transition-colors`}
|
} transition-colors`}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user