diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 05e8a92..985ea9b 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,11 +1,11 @@ -import { useState, useEffect, useCallback } from "react"; -import { - Github, - Info, - ShieldCheck, - ScrollText, - Star, - UsersIcon, +import { useState, useEffect, useCallback, useRef } from "react"; +import { + Github, + Info, + ShieldCheck, + ScrollText, + Star, + UsersIcon, ArrowUpRight, MapPin, Heart, @@ -13,7 +13,14 @@ import { Zap, Mail, Phone, - MessageSquare + MessageSquare, + Send, + CheckCircle, + ExternalLink, + Rss, + Twitter, + Linkedin, + Youtube, } from "lucide-react"; import { Link } from "react-router-dom"; import logoF from "../assets/logo.png"; @@ -24,6 +31,12 @@ const Footer = () => { const [feedbackText, setFeedbackText] = useState(""); const [message, setMessage] = useState(""); const [isVisible, setIsVisible] = useState(false); + const [email, setEmail] = useState(""); + const [isSubscribed, setIsSubscribed] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + const [hoveredItem, setHoveredItem] = useState(null); + const canvasRef = useRef(null); + const footerRef = useRef(null); // Performance optimization: Memoize social links const socialLinks = [ @@ -32,6 +45,29 @@ const Footer = () => { href: "https://github.com/HarshS16/Civix", icon: Github, description: "View source code", + color: + "hover:bg-gray-900 dark:hover:bg-gray-100 hover:text-white dark:hover:text-gray-900", + }, + { + name: "Twitter", + href: "https://twitter.com/civix", + icon: Twitter, + description: "Follow us on Twitter", + color: "hover:bg-blue-400 hover:text-white", + }, + { + name: "LinkedIn", + href: "https://linkedin.com/company/civix", + icon: Linkedin, + description: "Connect on LinkedIn", + color: "hover:bg-blue-600 hover:text-white", + }, + { + name: "YouTube", + href: "https://youtube.com/c/civix", + icon: Youtube, + description: "Watch our videos", + color: "hover:bg-red-600 hover:text-white", }, ]; @@ -40,34 +76,113 @@ const Footer = () => { title: "Civix", icon: Sparkles, links: [ - { name: "About", href: "/about", icon: Info, description: "Learn about our mission" }, - { name: "Features", href: "/#features", icon: Star, description: "Explore platform features" }, - { name: "Feedback", href: "/feedback", icon: MessageSquare, description: "Share your feedback" }, + { + name: "About", + href: "/about", + icon: Info, + description: "Learn about our mission", + }, + { + name: "Features", + href: "/#features", + icon: Star, + description: "Explore platform features", + }, + { + name: "Feedback", + href: "/feedback", + icon: MessageSquare, + description: "Share your feedback", + }, + ], + }, + { + title: "Resources", + icon: Rss, + links: [ + { + name: "Blog", + href: "/blog", + icon: ScrollText, + description: "Read our latest articles", + }, + { + name: "Documentation", + href: "/docs", + icon: ScrollText, + description: "Developer documentation", + }, + { + name: "Tutorials", + href: "/tutorials", + icon: UsersIcon, + description: "Learn how to use Civix", + }, ], }, { title: "Legal", icon: ShieldCheck, links: [ - { name: "Privacy", href: "/privacy", icon: ShieldCheck, description: "Privacy policy" }, - { name: "Terms", href: "/terms", icon: ScrollText, description: "Terms of service" }, - { name: "Contributors", href: "/contributors", icon: UsersIcon, description: "Meet our team" }, + { + name: "Privacy", + href: "/privacy", + icon: ShieldCheck, + description: "Privacy policy", + }, + { + name: "Terms", + href: "/terms", + icon: ScrollText, + description: "Terms of service", + }, + { + name: "Contributors", + href: "/contributors", + icon: UsersIcon, + description: "Meet our team", + }, ], }, ]; const emojis = [ - { emoji: "😡", label: "Very Dissatisfied", color: "hover:bg-red-100 dark:hover:bg-red-900/20" }, - { emoji: "😕", label: "Dissatisfied", color: "hover:bg-orange-100 dark:hover:bg-orange-900/20" }, - { emoji: "😐", label: "Neutral", color: "hover:bg-yellow-100 dark:hover:bg-yellow-900/20" }, - { emoji: "🙂", label: "Satisfied", color: "hover:bg-green-100 dark:hover:bg-green-900/20" }, - { emoji: "😍", label: "Very Satisfied", color: "hover:bg-purple-100 dark:hover:bg-purple-900/20" }, + { + emoji: "😡", + label: "Very Dissatisfied", + color: "hover:bg-red-100 dark:hover:bg-red-900/20", + bg: "bg-red-500/10", + }, + { + emoji: "😕", + label: "Dissatisfied", + color: "hover:bg-orange-100 dark:hover:bg-orange-900/20", + bg: "bg-orange-500/10", + }, + { + emoji: "😐", + label: "Neutral", + color: "hover:bg-yellow-100 dark:hover:bg-yellow-900/20", + bg: "bg-yellow-500/10", + }, + { + emoji: "🙂", + label: "Satisfied", + color: "hover:bg-green-100 dark:hover:bg-green-900/20", + bg: "bg-green-500/10", + }, + { + emoji: "😍", + label: "Very Satisfied", + color: "hover:bg-purple-100 dark:hover:bg-purple-900/20", + bg: "bg-purple-500/10", + }, ]; // Performance optimization: Debounced feedback submission const handleSubmitFeedback = useCallback(() => { if (!selectedRating && !feedbackText.trim()) return; - + setMessage("Thanks for your feedback! 💚"); setFeedbackText(""); setSelectedRating(null); @@ -75,6 +190,113 @@ const Footer = () => { setTimeout(() => setMessage(""), 3000); }, [selectedRating, feedbackText]); + // Handle newsletter subscription + const handleNewsletterSubmit = useCallback( + (e) => { + e.preventDefault(); + if (!email.trim()) return; + + setIsSubmitting(true); + + // Simulate API call + setTimeout(() => { + setIsSubmitting(false); + setIsSubscribed(true); + setEmail(""); + + // Show success message + setMessage("Successfully subscribed to our newsletter! 🎉"); + + // Reset subscription status after some time + setTimeout(() => { + setIsSubscribed(false); + }, 3000); + + // Hide the message after 3 seconds + setTimeout(() => setMessage(""), 3000); + }, 1000); + }, + [email] + ); + + // Simple particle animation for background + useEffect(() => { + if (!canvasRef.current) return; + + const canvas = canvasRef.current; + const ctx = canvas.getContext("2d"); + let animationFrameId; + + const setCanvasSize = () => { + if (footerRef.current) { + canvas.width = footerRef.current.offsetWidth; + canvas.height = footerRef.current.offsetHeight; + } + }; + + setCanvasSize(); + window.addEventListener("resize", setCanvasSize); + + const particles = []; + const particleCount = window.innerWidth < 768 ? 20 : 40; + + for (let i = 0; i < particleCount; i++) { + particles.push({ + x: Math.random() * canvas.width, + y: Math.random() * canvas.height, + radius: Math.random() * 2 + 0.5, + speed: Math.random() * 0.5 + 0.2, + opacity: Math.random() * 0.5 + 0.1, + direction: Math.random() * Math.PI * 2, + }); + } + + const draw = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Draw gradient background + const gradient = ctx.createLinearGradient( + 0, + 0, + canvas.width, + canvas.height + ); + gradient.addColorStop(0, "rgba(167, 243, 208, 0.03)"); + gradient.addColorStop(0.5, "rgba(110, 231, 183, 0.02)"); + gradient.addColorStop(1, "rgba(52, 211, 153, 0.03)"); + + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Draw particles + particles.forEach((particle) => { + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); + ctx.fillStyle = `rgba(74, 222, 128, ${particle.opacity})`; + ctx.fill(); + + // Move particles + particle.x += Math.cos(particle.direction) * particle.speed; + particle.y += Math.sin(particle.direction) * particle.speed; + + // Wrap around edges + if (particle.x < 0) particle.x = canvas.width; + if (particle.x > canvas.width) particle.x = 0; + if (particle.y < 0) particle.y = canvas.height; + if (particle.y > canvas.height) particle.y = 0; + }); + + animationFrameId = requestAnimationFrame(draw); + }; + + draw(); + + return () => { + cancelAnimationFrame(animationFrameId); + window.removeEventListener("resize", setCanvasSize); + }; + }, []); + // Intersection Observer for animations useEffect(() => { const observer = new IntersectionObserver( @@ -86,63 +308,79 @@ const Footer = () => { { threshold: 0.1 } ); - const footer = document.querySelector('#footer'); - if (footer) { - observer.observe(footer); + if (footerRef.current) { + observer.observe(footerRef.current); } return () => observer.disconnect(); }, []); return ( - ); }; diff --git a/yarn.lock b/yarn.lock index 04495a4..edfe470 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9641,8 +9641,7 @@ react-toastify@^11.0.5: dependencies: clsx "^2.1.1" -react@*, "react@^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^18 || ^19", "react@^18.0 || ^19", react@^18.0.0, "react@^18.0.0 || ^19.0.0", "react@^18.0.0 || ^19.0.0 || ^19.0.0-0", react@^18.3.1, "react@>= 16", "react@>= 16.8.0", react@>=16, react@>=16.3, react@>=18: - +react@*, "react@^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^18 || ^19", "react@^18.0 || ^19", react@^18.0.0, "react@^18.0.0 || ^19.0.0", "react@^18.0.0 || ^19.0.0 || ^19.0.0-0", react@^18.3.1, "react@>= 16", "react@>= 16.8.0", react@>=16, react@>=16.3, react@>=18: version "18.3.1" resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==