From 53ba97eff9c1b9db0fe1cecea2261916c01ae6d9 Mon Sep 17 00:00:00 2001 From: Poyraz Avsever Date: Mon, 17 Nov 2025 17:55:35 +0300 Subject: [PATCH 1/2] Refactor Header component for improved navigation and active section tracking --- src/components/Header.tsx | 273 ++++++++++++++++++++++++-------------- 1 file changed, 174 insertions(+), 99 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 985845ad..4959672f 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,89 +1,162 @@ -import React, { useState } from 'react'; -import { useLanguage } from '../contexts/LanguageContext'; -import { Button } from '@/components/ui/button'; -import { Menu, X, Globe } from 'lucide-react'; -import { Link, useLocation, useNavigate } from 'react-router-dom'; +import React, { useEffect, useState } from "react"; +import { useLanguage } from "../contexts/LanguageContext"; +import { Button } from "@/components/ui/button"; +import { Menu, X, Globe } from "lucide-react"; +import { Link, useLocation, useNavigate } from "react-router-dom"; const Header = () => { const { language, setLanguage, t } = useLanguage(); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [activeSection, setActiveSection] = useState("home"); const location = useLocation(); const navigate = useNavigate(); const scrollToSection = (sectionId: string) => { // Eğer ana sayfada değilsek, önce ana sayfaya git - if (location.pathname !== '/') { - navigate('/'); + if (location.pathname !== "/") { + navigate("/"); // Ana sayfa yüklendikten sonra bölüme scroll yap setTimeout(() => { const element = document.getElementById(sectionId); - element?.scrollIntoView({ behavior: 'smooth' }); + element?.scrollIntoView({ behavior: "smooth" }); + // Mark as active after navigation + setActiveSection(sectionId); }, 100); } else { // Ana sayfadaysak direkt scroll yap const element = document.getElementById(sectionId); - element?.scrollIntoView({ behavior: 'smooth' }); + element?.scrollIntoView({ behavior: "smooth" }); + setActiveSection(sectionId); } setIsMobileMenuOpen(false); }; const toggleLanguage = () => { - setLanguage(language === 'tr' ? 'en' : 'tr'); + setLanguage(language === "tr" ? "en" : "tr"); + }; + + // Observe sections on the homepage to update activeSection while scrolling + useEffect(() => { + if (location.pathname !== "/") { + // If we're on a different page, set active based on pathname for page links + if (location.pathname.startsWith("/merch")) setActiveSection("merch"); + else if (location.pathname.startsWith("/gonullu")) + setActiveSection("volunteers"); + return; + } + + const ids = ["home", "events", "contact", "patreon"]; + const elements = ids + .map((id) => document.getElementById(id)) + .filter(Boolean) as HTMLElement[]; + + if (!elements.length) return; + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && entry.intersectionRatio > 0.5) { + setActiveSection(entry.target.id); + } + }); + }, + { threshold: [0.5] } + ); + + elements.forEach((el) => observer.observe(el)); + + return () => observer.disconnect(); + }, [location.pathname]); + + // Nav items configuration (so we can map and apply consistent classes) + const navItems: Array<{ + id: string; + label: string; + type: "section" | "route"; + }> = [ + { id: "home", label: t.nav.home, type: "section" }, + { id: "events", label: t.nav.events, type: "section" }, + { id: "contact", label: t.nav.contact, type: "section" }, + { id: "/merch", label: t.nav.merch, type: "route" }, + { id: "patreon", label: t.nav.support, type: "section" }, + { id: "/gonullu", label: t.nav.volunteers, type: "route" }, + ]; + + const navItemClass = ( + id: string, + _idx: number, + type: "section" | "route" + ) => { + const isActive = + type === "route" + ? id === "/merch" + ? location.pathname.startsWith("/merch") + : location.pathname.startsWith("/gonullu") + : activeSection === id; + + // Base ensures color/transform transitions so when active toggles we get a smooth change + const base = `text-gray-700 transition-colors duration-200 ease-in-out transform`; + + // Hover should show same bg/text as active + const hover = `hover:text-unog-blue`; + + const active = isActive + ? "bg-unog-blue text-white px-3 py-1 rounded-tl-lg rounded-br-lg hover:text-white" + : ""; + + return `${base} ${hover} ${active}`; }; return ( -
+
-
- - {t.common.logo} - -
+ + {t.common.logo} + {/* Desktop Navigation */} -
@@ -120,45 +197,43 @@ const Header = () => { {/* Mobile Navigation */} {isMobileMenuOpen && ( )} From d651aa098671fc1e0198ff94062e3ef333c241af Mon Sep 17 00:00:00 2001 From: Poyraz Avsever Date: Tue, 18 Nov 2025 11:20:01 +0300 Subject: [PATCH 2/2] Refactor Footer and Header components for improved layout and navigation --- src/components/Footer.tsx | 240 ++++++++++++++++++++++++-------------- src/components/Header.tsx | 104 ++--------------- 2 files changed, 162 insertions(+), 182 deletions(-) diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 44ef6b23..8f51965c 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,6 +1,15 @@ -import React from 'react'; -import { useLanguage } from '../contexts/LanguageContext'; -import { Instagram, Linkedin, X, Mail, Heart, Youtube, Newspaper } from 'lucide-react'; +import React from "react"; +import { Link } from "react-router-dom"; +import { useLanguage } from "../contexts/LanguageContext"; +import { + Instagram, + Linkedin, + X, + Mail, + Heart, + Youtube, + Newspaper, +} from "lucide-react"; // Discord SVG Icon Component const DiscordIcon = ({ className }: { className?: string }) => ( @@ -41,119 +50,174 @@ const Footer = () => { const socialLinks = [ { - name: 'Discord', + name: "Discord", icon: DiscordIcon, - url: 'https://unog.dev/discord', - color: 'hover:text-indigo-500' + url: "https://unog.dev/discord", + color: "hover:text-indigo-500", }, { - name: 'Instagram', + name: "Instagram", icon: Instagram, - url: 'https://instagram.com/unoghub', - color: 'hover:text-pink-500' + url: "https://instagram.com/unoghub", + color: "hover:text-pink-500", }, { - name: 'LinkedIn', + name: "LinkedIn", icon: Linkedin, - url: 'https://linkedin.com/company/unoghub', - color: 'hover:text-blue-600' + url: "https://linkedin.com/company/unoghub", + color: "hover:text-blue-600", }, { - name: 'X', + name: "X", icon: X, - url: 'https://x.com/unoghub', - color: 'hover:text-blue-400' + url: "https://x.com/unoghub", + color: "hover:text-blue-400", }, { - name: 'YouTube', + name: "YouTube", icon: Youtube, - url: 'https://youtube.com/@unoghub', - color: 'hover:text-red-500' + url: "https://youtube.com/@unoghub", + color: "hover:text-red-500", }, { - name: 'Spotify', + name: "Spotify", icon: SpotifyIcon, - url: 'https://unog.dev/podcast', - color: 'hover:text-green-500' - } + url: "https://unog.dev/podcast", + color: "hover:text-green-500", + }, + ]; + + const quickLinks: Array<{ + label: string; + href: string; + type: "anchor" | "route"; + }> = [ + { label: t.nav.home, href: "#home", type: "anchor" }, + { label: t.nav.events, href: "#events", type: "anchor" }, + { label: t.nav.contact, href: "#contact", type: "anchor" }, + { label: t.nav.support, href: "#patreon", type: "anchor" }, + { label: t.nav.merch, href: "/merch", type: "route" }, + { label: t.nav.volunteers, href: "/gonullu", type: "route" }, ]; + const quickLinkClass = + "text-sm font-semibold tracking-tight text-slate-600 px-4 py-2 rounded-tl-xl rounded-br-xl border border-transparent bg-white/60 shadow-sm transition-all duration-200 hover:text-unog-blue hover:border-unog-blue/30 hover:-translate-y-0.5"; + return ( -