From b28c186d42f7a8bcb688b20781d3a40be00964d8 Mon Sep 17 00:00:00 2001 From: "Namgit config --global user.email" Date: Fri, 26 Sep 2025 01:57:46 +0530 Subject: [PATCH] Updated email validation in footer --- src/theme/Footer/Layout/enhanced-footer.css | 105 ++++++++++++++++ src/theme/Footer/Layout/index.tsx | 128 +++++++++++++++++--- 2 files changed, 218 insertions(+), 15 deletions(-) diff --git a/src/theme/Footer/Layout/enhanced-footer.css b/src/theme/Footer/Layout/enhanced-footer.css index 89635ac4..88d42044 100644 --- a/src/theme/Footer/Layout/enhanced-footer.css +++ b/src/theme/Footer/Layout/enhanced-footer.css @@ -964,6 +964,12 @@ html[data-theme='light'] .enhanced-footer { margin-bottom: 16px; } +.newsletter-input-wrapper { + position: relative; + display: flex; + align-items: center; +} + .newsletter-input { padding: 12px 16px; border: 1px solid rgba(255, 255, 255, 0.1); @@ -974,6 +980,29 @@ html[data-theme='light'] .enhanced-footer { font-weight: 400; backdrop-filter: blur(20px); transition: all 0.3s ease; + width: 100%; +} + +/* Enhanced specificity for input error state */ +.enhanced-footer .newsletter-input.error, +[data-theme='dark'] .enhanced-footer .newsletter-input.error, +[data-theme='light'] .enhanced-footer .newsletter-input.error { + border-color: #ef4444 !important; + background: rgba(239, 68, 68, 0.15) !important; + color: #fca5a5 !important; + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.3) !important; +} + +.enhanced-footer .newsletter-input.error::placeholder, +[data-theme='dark'] .enhanced-footer .newsletter-input.error::placeholder, +[data-theme='light'] .enhanced-footer .newsletter-input.error::placeholder { + color: #fca5a5 !important; + opacity: 0.8; +} + +.newsletter-input.validating { + border-color: #f59e0b !important; + box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.3) !important; } .newsletter-input:focus { @@ -987,6 +1016,82 @@ html[data-theme='light'] .enhanced-footer { color: #94a3b8; } +.validation-spinner { + position: absolute; + right: 12px; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; +} + +.spinner { + width: 16px; + height: 16px; + border: 2px solid rgba(255, 255, 255, 0.2); + border-top: 2px solid #6366f1; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Enhanced specificity for error message to override footer protection */ +.enhanced-footer .error-message, +[data-theme='dark'] .enhanced-footer .error-message, +[data-theme='light'] .enhanced-footer .error-message { + color: #ef4444 !important; + font-size: 13px !important; + font-weight: 600 !important; + margin-top: 4px !important; + padding: 8px 12px !important; + background: rgba(239, 68, 68, 0.2) !important; + border-radius: 8px !important; + border: 1px solid rgba(239, 68, 68, 0.5) !important; + animation: fadeIn 0.3s ease !important; + box-shadow: 0 4px 6px rgba(239, 68, 68, 0.1) !important; + text-align: left !important; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(-10px); } + to { opacity: 1; transform: translateY(0); } +} + +.newsletter-button { + padding: 14px 24px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); + color: white; + border: none; + border-radius: 12px; + font-size: 14px; + font-weight: 700; + cursor: pointer; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: + 0 8px 24px rgba(102, 126, 234, 0.4), + 0 0 0 1px rgba(255, 255, 255, 0.1), + inset 0 1px 0 rgba(255, 255, 255, 0.2); + position: relative; + overflow: hidden; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.newsletter-button:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none !important; + box-shadow: + 0 4px 12px rgba(102, 126, 234, 0.1), + 0 0 0 1px rgba(255, 255, 255, 0.05), + inset 0 1px 0 rgba(255, 255, 255, 0.05); +} + .newsletter-button { padding: 14px 24px; background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); diff --git a/src/theme/Footer/Layout/index.tsx b/src/theme/Footer/Layout/index.tsx index e999a4d6..caf521cd 100644 --- a/src/theme/Footer/Layout/index.tsx +++ b/src/theme/Footer/Layout/index.tsx @@ -28,6 +28,8 @@ export default function FooterLayout({ }); const [email, setEmail] = useState(''); const [isSubscribed, setIsSubscribed] = useState(false); + const [emailError, setEmailError] = useState(null); + const [isValidating, setIsValidating] = useState(false); useEffect(() => { // Simulate real-time stats updates @@ -55,15 +57,99 @@ export default function FooterLayout({ return () => clearInterval(interval); }, []); + // Advanced email validation function + const validateEmail = (email: string): string | null => { + // Check basic format + const basicRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!basicRegex.test(email)) { + return 'Please enter a valid email address.'; + } + + // Check for common disposable email patterns + const disposableDomains = [ + '10minutemail.com', 'tempmail.org', 'guerrillamail.com', + 'mailinator.com', 'throwaway.email', 'disposablemail.com', + 'sharklasers.com', 'trashmail.com', 'yopmail.com' + ]; + + const domain = email.split('@')[1].toLowerCase(); + if (disposableDomains.includes(domain)) { + return 'Disposable email addresses are not allowed.'; + } + + // Check for common free email domains with additional validation + const freeEmailDomains = [ + 'gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', + 'protonmail.com', 'icloud.com', 'aol.com', 'live.com' + ]; + + if (!freeEmailDomains.includes(domain) && domain.length < 3) { + return 'Email domain appears to be invalid.'; + } + + // Check for common patterns that indicate invalid emails + if (email.length > 254) { + return 'Email address is too long.'; + } + + if (email.startsWith('.') || email.endsWith('.')) { + return 'Email address cannot start or end with a dot.'; + } + + if (email.includes('..')) { + return 'Email address cannot contain consecutive dots.'; + } + + // Check for valid characters in local part + const localPart = email.split('@')[0]; + if (localPart.length > 64) { + return 'Email local part is too long.'; + } + + const validLocalRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+$/; + if (!validLocalRegex.test(localPart)) { + return 'Email contains invalid characters.'; + } + + return null; // Valid email + }; + + // Handle real-time validation + const handleEmailChange = (e: React.ChangeEvent) => { + const value = e.target.value; + setEmail(value); + + // Clear error if field is empty + if (value === '') { + setEmailError(null); + return; + } + + // Perform validation with a slight delay to avoid excessive checks + setIsValidating(true); + setTimeout(() => { + const error = validateEmail(value); + setEmailError(error); + setIsValidating(false); + }, 300); + }; + const handleSubscribe = (e: React.FormEvent) => { e.preventDefault(); - if (email) { - setIsSubscribed(true); - setTimeout(() => { - setIsSubscribed(false); - setEmail(''); - }, 3000); + + // Final validation before submission + const error = validateEmail(email); + if (error) { + setEmailError(error); + return; } + + setIsSubscribed(true); + setTimeout(() => { + setIsSubscribed(false); + setEmail(''); + setEmailError(null); // Clear error on successful subscription + }, 3000); }; return (