Files
kovakyazilim/components/mobile-menu.tsx
T
egecankomur 2e001680bf feat: Çözümler bölümü + mobil menü; admin parser düzeltmeleri
- Çözümler: solutions tablosu, /cozumler liste + detay sayfası, anasayfa
  bölümü, tam admin CRUD (/admin/cozumler), header & footer linkleri,
  projelerde solution_slug ilişkisi, services-grid genelleştirildi
- Mobil menü (hamburger drawer) eklendi — header artık < lg'de gezilebilir
- Site ayarları parser: textarea CRLF (\r\n) normalizasyonu — neden biz,
  süreç adımları, değerler ve SSS blokları artık doğru parçalanıyor
- homepage_faq + garanti (title/description/items) saveSiteSettings'e
  bağlandı (daha önce hiç kaydedilmiyordu)
2026-06-02 18:21:58 +03:00

169 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { Menu, X, ChevronDown, Phone, ArrowRight } from "lucide-react";
type NavService = { slug: string; title: string };
const LINKS = [
{ href: "/", label: "Anasayfa" },
{ href: "/cozumler", label: "Çözümler" },
{ href: "/projeler", label: "Projeler" },
{ href: "/blog", label: "Blog" },
{ href: "/hakkimizda", label: "Hakkımızda" },
{ href: "/iletisim", label: "İletişim" },
];
export function MobileMenu({
services,
phone,
phoneRaw,
}: {
services: NavService[];
phone: string;
phoneRaw: string;
}) {
const [open, setOpen] = useState(false);
const [servicesOpen, setServicesOpen] = useState(false);
const pathname = usePathname();
// Rota değişince menüyü kapat
useEffect(() => {
setOpen(false);
}, [pathname]);
// Açıkken arka plan kaydırmasını kilitle
useEffect(() => {
document.body.style.overflow = open ? "hidden" : "";
return () => {
document.body.style.overflow = "";
};
}, [open]);
return (
<div className="lg:hidden">
<button
type="button"
onClick={() => setOpen(true)}
aria-label="Menüyü aç"
aria-expanded={open}
className="inline-flex size-9 items-center justify-center rounded-lg text-gray-700 transition-colors hover:bg-gray-100 hover:text-[var(--navy)]"
>
<Menu className="size-5" />
</button>
{/* Overlay + drawer */}
<div
className={`fixed inset-0 z-[60] lg:hidden ${
open ? "" : "pointer-events-none"
}`}
aria-hidden={!open}
>
{/* Backdrop */}
<div
onClick={() => setOpen(false)}
className={`absolute inset-0 bg-black/40 backdrop-blur-sm transition-opacity duration-300 ${
open ? "opacity-100" : "opacity-0"
}`}
/>
{/* Panel */}
<div
className={`absolute right-0 top-0 flex h-full w-[86%] max-w-sm flex-col bg-white shadow-2xl transition-transform duration-300 ease-out ${
open ? "translate-x-0" : "translate-x-full"
}`}
>
{/* Üst bar */}
<div className="flex h-14 items-center justify-between border-b border-gray-100 px-5">
<span className="text-sm font-semibold tracking-tight text-[var(--navy)]">
Menü
</span>
<button
type="button"
onClick={() => setOpen(false)}
aria-label="Menüyü kapat"
className="inline-flex size-9 items-center justify-center rounded-lg text-gray-600 transition-colors hover:bg-gray-100 hover:text-[var(--navy)]"
>
<X className="size-5" />
</button>
</div>
{/* Linkler */}
<nav className="flex-1 overflow-y-auto px-3 py-4">
<Link
href="/"
className="block rounded-xl px-4 py-3 text-base font-medium text-gray-800 transition-colors hover:bg-blue-50 hover:text-[var(--navy)]"
>
Anasayfa
</Link>
{/* Hizmetler — açılır */}
<button
type="button"
onClick={() => setServicesOpen((v) => !v)}
aria-expanded={servicesOpen}
className="flex w-full items-center justify-between rounded-xl px-4 py-3 text-base font-medium text-gray-800 transition-colors hover:bg-blue-50 hover:text-[var(--navy)]"
>
Hizmetler
<ChevronDown
className={`size-4 transition-transform duration-200 ${
servicesOpen ? "rotate-180" : ""
}`}
/>
</button>
{servicesOpen && (
<div className="mb-1 ml-3 border-l border-gray-100 pl-3">
{services.map((s) => (
<Link
key={s.slug}
href={`/hizmetler/${s.slug}`}
className="block rounded-lg px-3 py-2 text-sm text-gray-600 transition-colors hover:bg-blue-50 hover:text-[var(--navy)]"
>
{s.title}
</Link>
))}
<Link
href="/hizmetler"
className="block rounded-lg px-3 py-2 text-sm font-semibold text-[var(--sky-600)] hover:text-[var(--navy)]"
>
Tüm hizmetleri gör
</Link>
</div>
)}
{LINKS.filter((l) => l.href !== "/").map((l) => (
<Link
key={l.href}
href={l.href}
className="block rounded-xl px-4 py-3 text-base font-medium text-gray-800 transition-colors hover:bg-blue-50 hover:text-[var(--navy)]"
>
{l.label}
</Link>
))}
</nav>
{/* Alt CTA */}
<div className="space-y-2 border-t border-gray-100 p-4">
<a
href={`tel:${phoneRaw}`}
className="flex items-center justify-center gap-2 rounded-xl border border-gray-200 px-4 py-3 text-sm font-semibold text-[var(--navy)] transition-colors hover:border-[var(--navy)]"
>
<Phone className="size-4" />
{phone}
</a>
<Link
href="/iletisim"
className="flex items-center justify-center gap-2 rounded-xl bg-[var(--navy)] px-4 py-3 text-sm font-semibold text-white transition-colors hover:bg-[var(--navy-700)]"
>
Ücretsiz Teklif
<ArrowRight className="size-4" />
</Link>
</div>
</div>
</div>
</div>
);
}