diff --git a/src/components/Footer.css b/src/components/Footer.css index 18f052a..8607ba0 100644 --- a/src/components/Footer.css +++ b/src/components/Footer.css @@ -48,6 +48,101 @@ font-size: 0.95rem; } +/* Newsletter Section */ +.newsletter-section { + margin-bottom: 1.5rem; + padding: 1rem; + background: rgba(160, 160, 255, 0.05); + border-radius: 8px; + border: 1px solid rgba(160, 160, 255, 0.1); +} + +.newsletter-title { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 1rem; + font-weight: 600; + color: rgb(160, 160, 255); + margin-bottom: 0.5rem; +} + +.newsletter-icon { + font-size: 0.9rem; +} + +.newsletter-description { + color: #9ca3af; + font-size: 0.85rem; + line-height: 1.4; + margin-bottom: 1rem; +} + +.newsletter-form { + width: 100%; +} + +.newsletter-input-group { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +.newsletter-input { + flex: 1; + min-width: 200px; + padding: 0.75rem; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(160, 160, 255, 0.2); + border-radius: 6px; + color: #f9fafb; + font-size: 0.9rem; + font-family: 'Google Code', monospace; + transition: all 0.3s ease; +} + +.newsletter-input::placeholder { + color: #9ca3af; +} + +.newsletter-input:focus { + outline: none; + border-color: rgb(160, 160, 255); + background: rgba(255, 255, 255, 0.15); + box-shadow: 0 0 0 3px rgba(160, 160, 255, 0.1); +} + +.newsletter-input:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.newsletter-button { + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgb(160, 160, 255) 0%, rgb(120, 120, 255) 100%); + color: rgb(20, 20, 48); + border: none; + border-radius: 6px; + font-size: 0.9rem; + font-weight: 600; + font-family: 'Google Code', monospace; + cursor: pointer; + transition: all 0.3s ease; + white-space: nowrap; +} + +.newsletter-button:hover:not(:disabled) { + background: linear-gradient(135deg, rgb(140, 140, 255) 0%, rgb(100, 100, 255) 100%); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(160, 160, 255, 0.3); +} + +.newsletter-button:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} + .footer-social { display: flex; gap: 1rem; @@ -231,4 +326,22 @@ font-size: 0.75rem; padding: 0.2rem 0.6rem; } + + /* Newsletter responsive */ + .newsletter-section { + padding: 0.75rem; + } + + .newsletter-input-group { + flex-direction: column; + gap: 0.75rem; + } + + .newsletter-input { + min-width: auto; + } + + .newsletter-button { + width: 100%; + } } diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index ecf308e..473c0bc 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,4 +1,5 @@ import { Link } from "react-router-dom"; +import { useState } from "react"; import { FaGithub, FaReact, @@ -9,10 +10,81 @@ import { FaHeart, FaCode, FaLeaf, + FaEnvelope, } from "react-icons/fa"; +import Toast from "./ui/Toast"; import "./Footer.css"; const Footer = () => { + const [email, setEmail] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + const [toast, setToast] = useState({ + isVisible: false, + message: "", + type: "info" + }); + + const validateEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + + const handleNewsletterSubmit = async (e) => { + e.preventDefault(); + + if (!email.trim()) { + setToast({ + isVisible: true, + message: "❌ Please enter your email address.", + type: "error" + }); + return; + } + + if (!validateEmail(email)) { + setToast({ + isVisible: true, + message: "❌ Please enter a valid email address.", + type: "error" + }); + return; + } + + setIsSubmitting(true); + + try { + // Simulate API call - replace with actual backend integration + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Here you would typically make an API call to your backend + // const response = await fetch('/api/newsletter/subscribe', { + // method: 'POST', + // headers: { 'Content-Type': 'application/json' }, + // body: JSON.stringify({ email }) + // }); + + setToast({ + isVisible: true, + message: "✅ You have successfully subscribed to our newsletter!", + type: "success" + }); + + setEmail(""); // Clear the form + } catch (error) { + setToast({ + isVisible: true, + message: "❌ Something went wrong. Please try again later.", + type: "error" + }); + } finally { + setIsSubmitting(false); + } + }; + + const closeToast = () => { + setToast(prev => ({ ...prev, isVisible: false })); + }; + return ( ); }; diff --git a/src/components/ui/Toast.css b/src/components/ui/Toast.css new file mode 100644 index 0000000..842c589 --- /dev/null +++ b/src/components/ui/Toast.css @@ -0,0 +1,100 @@ +/* Toast Notifications */ +.toast { + position: fixed; + top: 20px; + right: 20px; + z-index: 1000; + min-width: 300px; + max-width: 400px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + animation: slideIn 0.3s ease-out; + font-family: 'Google Code', monospace; +} + +.toast-content { + display: flex; + align-items: center; + padding: 1rem; + gap: 0.75rem; +} + +.toast-icon { + font-size: 1.2rem; + flex-shrink: 0; +} + +.toast-message { + flex: 1; + font-size: 0.9rem; + font-weight: 500; +} + +.toast-close { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: background-color 0.2s ease; +} + +.toast-close:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +/* Toast Types */ +.toast-success { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + color: white; + border-left: 4px solid #047857; +} + +.toast-error { + background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); + color: white; + border-left: 4px solid #b91c1c; +} + +.toast-info { + background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); + color: white; + border-left: 4px solid #1d4ed8; +} + +/* Animation */ +@keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +/* Responsive Design */ +@media (max-width: 480px) { + .toast { + top: 10px; + right: 10px; + left: 10px; + min-width: auto; + max-width: none; + } + + .toast-content { + padding: 0.75rem; + } + + .toast-message { + font-size: 0.85rem; + } +} diff --git a/src/components/ui/Toast.jsx b/src/components/ui/Toast.jsx new file mode 100644 index 0000000..0db6ca2 --- /dev/null +++ b/src/components/ui/Toast.jsx @@ -0,0 +1,45 @@ +import { useState, useEffect } from "react"; +import "./Toast.css"; + +const Toast = ({ message, type, isVisible, onClose, duration = 3000 }) => { + useEffect(() => { + if (isVisible) { + const timer = setTimeout(() => { + onClose(); + }, duration); + + return () => clearTimeout(timer); + } + }, [isVisible, duration, onClose]); + + if (!isVisible) return null; + + const getIcon = () => { + switch (type) { + case "success": + return "✅"; + case "error": + return "❌"; + default: + return "â„šī¸"; + } + }; + + return ( +
+
+ {getIcon()} + {message} + +
+
+ ); +}; + +export default Toast;