first commit
This commit is contained in:
+260
-54
@@ -1,65 +1,271 @@
|
||||
import Image from "next/image";
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { menuData, MenuItem as MenuItemType } from "@/data/menu";
|
||||
import { CategoryNav } from "@/components/CategoryNav";
|
||||
import { MenuItem } from "@/components/MenuItem";
|
||||
|
||||
export const CATEGORY_ICONS: Record<string, string> = {
|
||||
"kahvaltiliklar": "🌅",
|
||||
"kaseler": "🥗",
|
||||
"burger-ve-sandvic": "🍔",
|
||||
"pizzalar": "🍕",
|
||||
"makarnalar": "🍝",
|
||||
"baslangiclar": "🧆",
|
||||
"salatalar": "🥬",
|
||||
"ana-yemekler": "🍽️",
|
||||
"tatlilar": "🍮",
|
||||
"i̇mza-kokteyller": "🍹",
|
||||
"klasik-kokteyller": "🍸",
|
||||
"biralar": "🍺",
|
||||
"shotlar": "🥃",
|
||||
"sise-ve-kadeh-alkollu": "🥂",
|
||||
"saraplar": "🍷",
|
||||
"cerezler": "🥜",
|
||||
"alkolsuz-i̇cecekler": "🧃",
|
||||
"sicak-kahveler": "☕",
|
||||
"soguk-kahveler": "🧊",
|
||||
"ekstralar": "✨",
|
||||
};
|
||||
|
||||
export default function MenuPage() {
|
||||
const [activeCategoryId, setActiveCategoryId] = useState<string>(
|
||||
menuData[0]?.id || ""
|
||||
);
|
||||
const [selectedItem, setSelectedItem] = useState<MenuItemType | null>(null);
|
||||
const sectionRefs = useRef<(HTMLElement | null)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const hit = entries.find((e) => e.isIntersecting);
|
||||
if (hit) setActiveCategoryId(hit.target.id);
|
||||
},
|
||||
{ root: null, rootMargin: "-100px 0px -62% 0px", threshold: 0 }
|
||||
);
|
||||
|
||||
sectionRefs.current.forEach((ref) => ref && observer.observe(ref));
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedItem) {
|
||||
document.body.style.overflow = "hidden";
|
||||
} else {
|
||||
document.body.style.overflow = "unset";
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = "unset";
|
||||
};
|
||||
}, [selectedItem]);
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
||||
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={100}
|
||||
height={20}
|
||||
priority
|
||||
<div className="min-h-screen" style={{ background: "var(--cream)" }}>
|
||||
|
||||
{/* ── Hero ─────────────────────────────────────── */}
|
||||
<header className="relative overflow-hidden bg-stone-900">
|
||||
{/* Background Image */}
|
||||
<div
|
||||
className="absolute inset-0 z-0 opacity-80"
|
||||
style={{
|
||||
backgroundImage: "url('/header-bg.jpg')",
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center"
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
||||
To get started, edit the page.tsx file.
|
||||
</h1>
|
||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
||||
Looking for a starting point or more instructions? Head over to{" "}
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
|
||||
{/* Dark overlay to make text readable */}
|
||||
<div className="absolute inset-0 z-0 bg-gradient-to-b from-black/60 via-black/40 to-black/80" />
|
||||
|
||||
{/* hero content */}
|
||||
<div className="relative z-10 flex flex-col items-center text-center px-6 pt-16 pb-20">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 24 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.9, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<p
|
||||
className="text-[10px] tracking-[0.38em] uppercase mb-8 font-sans font-medium"
|
||||
style={{ color: "rgba(255,220,160,1)" }}
|
||||
>
|
||||
Templates
|
||||
</a>{" "}
|
||||
or the{" "}
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
Akyaka · Muğla
|
||||
</p>
|
||||
|
||||
<h1
|
||||
className="font-display text-[82px] font-light leading-none tracking-tight"
|
||||
style={{ color: "#FFFAF2" }}
|
||||
>
|
||||
Learning
|
||||
</a>{" "}
|
||||
center.
|
||||
Moy
|
||||
</h1>
|
||||
|
||||
<h2
|
||||
className="font-display text-[28px] font-light tracking-[0.22em] uppercase"
|
||||
style={{ color: "rgba(255,200,120,0.85)", marginTop: "-2px" }}
|
||||
>
|
||||
Beach
|
||||
</h2>
|
||||
|
||||
<div
|
||||
className="flex items-center gap-3 my-5"
|
||||
style={{ color: "rgba(255,190,100,0.35)" }}
|
||||
>
|
||||
<div className="h-px w-14" style={{ background: "currentColor" }} />
|
||||
<span className="text-base">✦</span>
|
||||
<div className="h-px w-14" style={{ background: "currentColor" }} />
|
||||
</div>
|
||||
|
||||
<p
|
||||
className="text-xs font-sans tracking-wide"
|
||||
style={{ color: "rgba(255,218,168,0.5)" }}
|
||||
>
|
||||
Akyaka'nın en keyifli menüsü
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* wave transition */}
|
||||
<div className="absolute bottom-0 left-0 right-0 translate-y-px">
|
||||
<svg
|
||||
viewBox="0 0 1440 52"
|
||||
preserveAspectRatio="none"
|
||||
className="w-full h-[52px]"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0 52L80 44C160 36 320 20 480 18C640 16 800 28 960 32C1120 36 1280 32 1360 30L1440 28V52H1360C1280 52 1120 52 960 52C800 52 640 52 480 52C320 52 160 52 80 52H0Z"
|
||||
fill="#FAF8F3"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* ── Category Nav ─────────────────────────────── */}
|
||||
<CategoryNav
|
||||
categories={menuData}
|
||||
activeCategoryId={activeCategoryId}
|
||||
icons={CATEGORY_ICONS}
|
||||
/>
|
||||
|
||||
{/* ── Menu Sections ────────────────────────────── */}
|
||||
<main className="pb-28">
|
||||
{menuData.map((category, index) => (
|
||||
<motion.section
|
||||
key={category.id}
|
||||
id={category.id}
|
||||
ref={(el) => { sectionRefs.current[index] = el; }}
|
||||
className="pt-10 px-4 max-w-lg mx-auto scroll-mt-[62px]"
|
||||
initial={{ opacity: 0, y: 14 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
|
||||
>
|
||||
{/* section header */}
|
||||
<div
|
||||
className="flex items-center gap-2.5 mb-4 pb-3 border-b"
|
||||
style={{ borderColor: "var(--sand-border)" }}
|
||||
>
|
||||
<span className="text-xl leading-none select-none">
|
||||
{CATEGORY_ICONS[category.id] ?? "🍽️"}
|
||||
</span>
|
||||
<h2
|
||||
className="font-display text-[22px] leading-tight font-semibold italic"
|
||||
style={{ color: "var(--stone-ink)" }}
|
||||
>
|
||||
{category.title}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{category.items.map((item, idx) => (
|
||||
<MenuItem key={idx} item={item} onClick={() => setSelectedItem(item)} />
|
||||
))}
|
||||
</div>
|
||||
</motion.section>
|
||||
))}
|
||||
</main>
|
||||
|
||||
{/* ── Footer ───────────────────────────────────── */}
|
||||
<footer
|
||||
className="py-12 text-center"
|
||||
style={{ background: "var(--hero-from)" }}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span
|
||||
className="font-display text-3xl font-light italic"
|
||||
style={{ color: "rgba(255,220,160,0.85)" }}
|
||||
>
|
||||
Moy Beach
|
||||
</span>
|
||||
<p
|
||||
className="text-[10px] font-sans tracking-[0.3em] uppercase mt-1"
|
||||
style={{ color: "rgba(255,200,130,0.35)" }}
|
||||
>
|
||||
Akyaka, Muğla
|
||||
</p>
|
||||
<div
|
||||
className="flex items-center gap-3 my-4"
|
||||
style={{ color: "rgba(160,100,50,0.4)" }}
|
||||
>
|
||||
<div className="h-px w-10" style={{ background: "currentColor" }} />
|
||||
<span className="text-xs">✦</span>
|
||||
<div className="h-px w-10" style={{ background: "currentColor" }} />
|
||||
</div>
|
||||
<p
|
||||
className="text-[11px] font-sans"
|
||||
style={{ color: "rgba(120,90,60,0.5)" }}
|
||||
>
|
||||
© 2026 Moy Beach. Tüm hakları saklıdır.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
||||
<a
|
||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
</footer>
|
||||
|
||||
{/* ── Modal ────────────────────────────────────── */}
|
||||
<AnimatePresence>
|
||||
{selectedItem && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm"
|
||||
onClick={() => setSelectedItem(null)}
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Deploy Now
|
||||
</a>
|
||||
<a
|
||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<motion.div
|
||||
initial={{ y: 50, opacity: 0, scale: 0.95 }}
|
||||
animate={{ y: 0, opacity: 1, scale: 1 }}
|
||||
exit={{ y: 20, opacity: 0, scale: 0.95 }}
|
||||
className="bg-white rounded-2xl overflow-hidden w-full max-w-sm shadow-xl"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{selectedItem.image && (
|
||||
<div className="w-full h-56 relative">
|
||||
<img src={selectedItem.image} alt={selectedItem.name} className="w-full h-full object-cover" />
|
||||
<button
|
||||
className="absolute top-3 right-3 bg-black/50 text-white w-8 h-8 rounded-full flex items-center justify-center backdrop-blur-md transition-colors hover:bg-black/70"
|
||||
onClick={() => setSelectedItem(null)}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-5">
|
||||
<div className="flex justify-between items-start gap-4 mb-2">
|
||||
<h3 className="text-xl font-semibold font-sans" style={{ color: "var(--stone-ink)" }}>
|
||||
{selectedItem.name}
|
||||
</h3>
|
||||
<span className="shrink-0 text-lg font-semibold font-sans tabular-nums" style={{ color: "var(--amber)" }}>
|
||||
{selectedItem.price}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-[14px] leading-relaxed font-sans" style={{ color: "var(--stone-muted)" }}>
|
||||
{selectedItem.description}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user