diff --git a/ipi-survey-platform/src/pages/ChangePassword/ChangePassword.jsx b/ipi-survey-platform/src/pages/ChangePassword/ChangePassword.jsx index 2c1213f..4f7bda2 100644 --- a/ipi-survey-platform/src/pages/ChangePassword/ChangePassword.jsx +++ b/ipi-survey-platform/src/pages/ChangePassword/ChangePassword.jsx @@ -4,6 +4,52 @@ import apiClient from '@/services/api/apiClient'; const logoSrc = '/assets/images/FCSCLogo.svg'; +const ToggleableInput = ({ label, type = "password", value, onChange, show, toggleShow, placeholder }) => ( +
+ +
+ + +
+
+); + const ChangePassword = () => { const navigate = useNavigate(); const location = useLocation(); @@ -53,7 +99,14 @@ const ChangePassword = () => { if (response?.data) { setSuccess("Password changed successfully. Redirecting to login..."); - setTimeout(() => navigate("/login"), 1500); + setTimeout(() => { + navigate("/login", { + state: { + showSuccessToast: true, + successMessage: "Password reset successful. Please login with your new password." + } + }); + }, 1500); } else { setError("Something went wrong. Please try again."); } @@ -68,52 +121,6 @@ const ChangePassword = () => { } }; - const ToggleableInput = ({ label, type = "password", value, onChange, show, toggleShow, placeholder }) => ( -
- -
- - -
-
- ); - return (
@@ -195,7 +202,7 @@ const ChangePassword = () => { onClick={() => navigate(-1)} className="w-full h-10 rounded-md border border-[#92722A] text-[#92722A] hover:bg-[#92722A]/10 text-sm" > - Cancel + Back
diff --git a/ipi-survey-platform/src/pages/Login/Login.jsx b/ipi-survey-platform/src/pages/Login/Login.jsx index e25a105..d97f1d3 100644 --- a/ipi-survey-platform/src/pages/Login/Login.jsx +++ b/ipi-survey-platform/src/pages/Login/Login.jsx @@ -1,11 +1,12 @@ import React from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import { login } from '@/services/auth/authService.js'; import { RefreshCw } from "lucide-react"; const logoSrc = '/assets/images/FCSCLogo.svg'; const Login = () => { const navigate = useNavigate(); + const location = useLocation(); const [email, setEmail] = React.useState(''); const [password, setPassword] = React.useState(''); const [showPassword, setShowPassword] = React.useState(false); @@ -30,10 +31,6 @@ const Login = () => { setCaptcha(code); }, []); - React.useEffect(() => { - generateCaptcha(); - }, [generateCaptcha]); - const closeToast = React.useCallback(() => { if (toastTimeoutRef.current) { window.clearTimeout(toastTimeoutRef.current); @@ -70,6 +67,18 @@ const Login = () => { [navigate] ); + React.useEffect(() => { + generateCaptcha(); + }, [generateCaptcha]); + + React.useEffect(() => { + if (location.state?.showSuccessToast && location.state?.successMessage) { + showToast('success', location.state.successMessage); + + window.history.replaceState({}, document.title); + } + }, [location.state, showToast]); + React.useEffect( () => () => { if (toastTimeoutRef.current) { @@ -224,46 +233,46 @@ const Login = () => {
- -
- setPassword(e.target.value)} - placeholder="Enter password" - className="block w-full h-10 rounded-md border-2 border-[#92722A] focus:border-[#92722A] focus:ring-0 px-4 pr-11 text-sm bg-white" - required - /> - -
-
+ +
+ setPassword(e.target.value)} + placeholder="Enter password" + className="block w-full h-10 rounded-md border-2 border-[#92722A] focus:border-[#92722A] focus:ring-0 px-4 pr-11 text-sm bg-white" + required + /> + +
+
+ type="button" + onClick={generateCaptcha} + className="flex items-center gap-1 text-sm text-[#92722A] hover:underline" + > + + Refresh +

If you cannot read the code, click "Refresh" or type:{" "} diff --git a/ipi-survey-platform/src/pages/PublicContactForm.jsx b/ipi-survey-platform/src/pages/PublicContactForm.jsx index 26a120b..606715e 100644 --- a/ipi-survey-platform/src/pages/PublicContactForm.jsx +++ b/ipi-survey-platform/src/pages/PublicContactForm.jsx @@ -11,9 +11,6 @@ const PublicContactForm = () => { establishmentName: "", establishmentId: "", email: "", - // contactName: "", - // contactPhone: "", - // notes: "", }); const [errors, setErrors] = useState({}); @@ -28,6 +25,9 @@ const PublicContactForm = () => { const [otp, setOtp] = useState(""); const [otpMsg, setOtpMsg] = useState(""); const [otpError, setOtpError] = useState(""); + const [toast, setToast] = useState(null); + + const toastTimeoutRef = React.useRef(null); const generateCaptcha = () => { const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; @@ -42,6 +42,35 @@ const PublicContactForm = () => { generateCaptcha(); }, []); + useEffect(() => { + return () => { + if (toastTimeoutRef.current) { + window.clearTimeout(toastTimeoutRef.current); + } + }; + }, []); + + const showToast = (type, message) => { + if (!message) return; + if (toastTimeoutRef.current) { + window.clearTimeout(toastTimeoutRef.current); + toastTimeoutRef.current = null; + } + setToast({ type, message }); + toastTimeoutRef.current = window.setTimeout(() => { + setToast(null); + toastTimeoutRef.current = null; + }, 4000); + }; + + const closeToast = () => { + if (toastTimeoutRef.current) { + window.clearTimeout(toastTimeoutRef.current); + toastTimeoutRef.current = null; + } + setToast(null); + }; + const validate = (data = formData) => { const newErrors = {}; @@ -49,33 +78,12 @@ const PublicContactForm = () => { if (!data.email.trim()) newErrors.email = "Registered Email is required."; else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) newErrors.email = "Enter a valid email address."; - // if (!data.contactName.trim()) newErrors.contactName = "Contact Person Name is required."; - // if (!data.contactPhone.trim()) { - // newErrors.contactPhone = "Contact Phone is required."; - // } else if (!/^\d{10,12}$/.test(data.contactPhone)) { - // newErrors.contactPhone = "Enter a valid phone number (10–12 digits)."; - // } setErrors(newErrors); setIsValid(Object.keys(newErrors).length === 0); return newErrors; }; -// const handleNameChange = (e) => { -// const { value } = e.target; -// const filtered = value.replace(/[^A-Za-z\s]/g, ""); -// setFormData((prev) => ({ ...prev, contactName: filtered })); -// validate({ ...formData, contactName: filtered }); -// }; - -// const handlePhoneChange = (e) => { -// let { value } = e.target; -// value = value.replace(/\D/g, ""); -// if (value.length > 12) value = value.slice(0, 12); -// setFormData((prev) => ({ ...prev, contactPhone: value })); -// validate({ ...formData, contactPhone: value }); -// }; - const handleChange = (e) => { const { name, value } = e.target; const updated = { ...formData, [name]: value }; @@ -113,9 +121,12 @@ const PublicContactForm = () => { const response = await apiClient.post("/forgot-password/request-otp", payload); if (response?.data) { - navigate("/change-password", { - state: { email: formData.email.trim() }, - }); + showToast('success', 'OTP sent to your registered email successfully!'); + setTimeout(() => { + navigate("/change-password", { + state: { email: formData.email.trim() }, + }); + }, 1500); } else { setErrorMsg("Something went wrong. Please try again."); } @@ -165,7 +176,6 @@ const PublicContactForm = () => { setLoading(false); } }; - const handleResendOtp = () => { setOtpMsg("A new OTP has been sent to your registered email."); @@ -174,6 +184,29 @@ const PublicContactForm = () => { return (

+ {toast && ( +
+
+
+ {toast.message} +
+ +
+
+ )} +
@@ -210,8 +243,9 @@ const PublicContactForm = () => { error={errors.establishmentName} /> @@ -224,35 +258,6 @@ const PublicContactForm = () => { onChange={handleChange} error={errors.email} /> - {/* - -
- - -
*/}
+ type="button" + onClick={generateCaptcha} + className="flex items-center gap-1 text-sm text-[#92722A] hover:underline" + > + + Refresh +

- If you cannot read the code, click "Refresh" or type:{" "} - {captcha} -

+ If you cannot read the code, click "Refresh" or type:{" "} + {captcha} +