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 (