first commit

This commit is contained in:
mstfyldz
2026-06-03 16:13:15 +03:00
parent a3b76fa65d
commit 716bad9d7c
40 changed files with 2698 additions and 135 deletions
+468
View File
@@ -0,0 +1,468 @@
"use client";
import { useState } from "react";
import { motion, type Variants } from "framer-motion";
import Link from "next/link";
import { Utensils, Umbrella, Home, Wind, Hotel, Star, MapPin, ArrowRight } from "lucide-react";
import { Button } from "@/app/components/ui/button";
import { SectionHeader } from "@/app/components/ui/section-header";
import { Marquee } from "@/app/components/ui/marquee";
import { AnimatedNumber } from "@/app/components/ui/animated-number";
import { TextReveal } from "@/app/components/ui/text-reveal";
import { ShimmerButton } from "@/app/components/ui/shimmer-button";
import { Badge } from "@/app/components/ui/badge";
const HERO_BG =
"https://images.unsplash.com/photo-1499793983690-e29da59ef1c2?auto=format&fit=crop&q=80&w=2070";
const MARQUEE_ITEMS = [
"Akyaka",
"Akçapınar",
"Datça",
"Beach Bar",
"Kite Surf",
"Restaurant",
"Bungalov",
"Hotel",
"Ege Denizi",
];
const STATS = [
{ value: 15, suffix: "+", label: "Yıl Deneyim" },
{ value: 3, suffix: "", label: "Lokasyon" },
{ value: 5000, suffix: "+", label: "Mutlu Misafir" },
{ value: 5, suffix: "", label: "Hizmet Kategorisi" },
];
const SERVICES = [
{
id: "restaurant",
title: "Restaurant",
icon: Utensils,
desc: "Ege'nin taze lezzetleri, yerel malzemeler ve şef imzalı tatlar.",
bg: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?auto=format&fit=crop&q=80&w=800",
},
{
id: "beach",
title: "Beach Bar",
icon: Umbrella,
desc: "Altın kumlar, masmavi deniz ve özenle seçilmiş içecekler.",
bg: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80&w=800",
},
{
id: "bungalow",
title: "Bungalov",
icon: Home,
desc: "Doğa ile iç içe, huzurlu ve modern konaklama deneyimi.",
bg: "https://images.unsplash.com/photo-1449844908441-8829872d2607?auto=format&fit=crop&q=80&w=800",
},
{
id: "kitesurf",
title: "Kite Surf",
icon: Wind,
desc: "Rüzgarın enerjisini hissedin; başlangıç ve ileri seviye dersler.",
bg: "https://images.unsplash.com/photo-1526685514088-290076a0d426?auto=format&fit=crop&q=80&w=800",
},
{
id: "hotel",
title: "Hotel",
icon: Hotel,
desc: "Premium konfor ve Ege'nin eşsiz manzarasıyla özel tatil.",
bg: "https://images.unsplash.com/photo-1566073771259-6a8506099945?auto=format&fit=crop&q=80&w=800",
},
];
const TESTIMONIALS = [
{
name: "Ayşe Y.",
origin: "İstanbul",
comment:
"Ege'de şimdiye kadar geçirdiğim en güzel tatildi. Hizmet kalitesi muazzamdı, her detay özenle düşünülmüş.",
rating: 5,
},
{
name: "John S.",
origin: "London",
comment:
"The kite surfing instructors were incredibly professional. The location is breathtaking — I'll be back next summer.",
rating: 5,
},
{
name: "Mehmet K.",
origin: "Ankara",
comment:
"Bungalovlar son derece huzurlu. Doğanın içindeyken şehrin konforundan hiç taviz vermiyorsunuz.",
rating: 5,
},
];
const fadeUp: Variants = {
hidden: { opacity: 0, y: 30 },
visible: (i = 0) => ({
opacity: 1,
y: 0,
transition: { duration: 0.65, delay: i * 0.12, ease: [0.16, 1, 0.3, 1] as [number, number, number, number] },
}),
};
export default function HomeClient({ lang, dict }: { lang: string; dict: any }) {
const [activeMap, setActiveMap] = useState(0);
const VENUES: { id: string; title: string; tag: string; desc: string; img: string; color: string }[] = dict.venuesList;
const LOCATIONS_MAP: { place: string; addr: string; embed: string }[] = dict.locationsMap;
return (
<div className="flex flex-col min-h-screen">
{/* ── Hero ── */}
<section className="relative h-screen flex items-center justify-center overflow-hidden moy-grain">
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat scale-105"
style={{ backgroundImage: `url(${HERO_BG})` }}
>
<div className="absolute inset-0 bg-gradient-to-b from-black/50 via-black/30 to-black/60" />
</div>
<div className="relative z-10 text-center px-4 max-w-5xl mx-auto">
{/* Eyebrow */}
<motion.p
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-[10px] uppercase tracking-[0.3em] text-[var(--color-moy-gold)] mb-8 font-medium"
>
{dict.hero.eyebrow}
</motion.p>
{/* Main heading */}
<h1 className="text-5xl md:text-7xl lg:text-8xl font-serif text-white mb-8 leading-[0.95] tracking-tight">
<TextReveal text={dict.hero.title} delay={0.2} />
</h1>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 0.9 }}
className="text-base md:text-lg text-gray-300/90 mb-12 font-light max-w-xl mx-auto leading-relaxed"
>
{dict.hero.subtitle}
</motion.p>
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, delay: 1.1 }}
className="flex flex-col sm:flex-row gap-4 justify-center"
>
<Link href={`/${lang}/iletisim`}>
<Button variant="primary" size="lg" shimmer className="group">
{dict.hero.btn1}
</Button>
</Link>
<Button variant="outline-light" size="lg" className="group" as="button">
<Link href={`/${lang}/#mekanlar`} className="flex items-center gap-2">
{dict.hero.btn2}
<ArrowRight size={14} className="group-hover:translate-x-1 transition-transform" />
</Link>
</Button>
</motion.div>
</div>
{/* Scroll indicator */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.6, duration: 0.6 }}
className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2"
>
<span className="text-[9px] uppercase tracking-[0.25em] text-white/50">{dict.hero.scroll}</span>
<motion.div
animate={{ y: [0, 8, 0] }}
transition={{ duration: 1.6, repeat: Infinity, ease: "easeInOut" }}
className="w-[1px] h-8 bg-gradient-to-b from-white/50 to-transparent"
/>
</motion.div>
</section>
{/* ── Marquee Strip ── */}
<div className="bg-[var(--color-moy-gold)] py-4 overflow-hidden">
<Marquee
items={MARQUEE_ITEMS}
separator="·"
speed="normal"
itemClassName="text-[var(--color-moy-dark)] font-semibold"
separatorClassName="text-[var(--color-moy-dark)] opacity-40"
/>
</div>
{/* ── Stats ── */}
<section className="bg-[var(--color-moy-dark)] py-20">
<div className="max-w-5xl mx-auto px-6">
<div className="grid grid-cols-2 md:grid-cols-4 gap-12 md:gap-6">
{STATS.map((s, i) => (
<motion.div
key={s.label}
custom={i}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
variants={fadeUp}
className="text-center"
>
<p className="text-5xl md:text-6xl font-serif text-[var(--color-moy-gold)] mb-2 leading-none">
<AnimatedNumber value={s.value} suffix={s.suffix} duration={1600} />
</p>
<p className="text-[10px] uppercase tracking-[0.2em] text-gray-500 mt-3">
{s.label}
</p>
</motion.div>
))}
</div>
</div>
</section>
{/* ── Services (Geçici olarak gizlendi) ── */}
{/*
<section id="hizmetler" className="py-28 bg-[var(--color-moy-light)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<SectionHeader
eyebrow="Ne Sunuyoruz"
title="Hizmetlerimiz"
subtitle="Her biri kendi hikâyesini taşıyan beş kategori — hepsi Ege'nin özüyle harmanlanmış."
/>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-5">
{SERVICES.map((service, i) => (
<motion.div
key={service.id}
custom={i}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
variants={fadeUp}
whileHover={{ y: -10 }}
className="group relative h-80 overflow-hidden cursor-pointer"
>
<div
className="absolute inset-0 bg-cover bg-center transition-transform duration-700 group-hover:scale-110"
style={{ backgroundImage: `url(${service.bg})` }}
/>
<div className="absolute inset-0 bg-gradient-to-t from-[var(--color-moy-dark)] via-[var(--color-moy-dark)]/50 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 h-[2px] bg-[var(--color-moy-gold)] scale-x-0 group-hover:scale-x-100 transition-transform duration-500 origin-left" />
<div className="absolute inset-0 p-6 flex flex-col justify-end items-center text-center">
<div className="w-14 h-14 border border-[var(--color-moy-gold)]/60 text-[var(--color-moy-gold)] flex items-center justify-center mb-4 group-hover:bg-[var(--color-moy-gold)] group-hover:text-[var(--color-moy-dark)] group-hover:border-[var(--color-moy-gold)] transition-all duration-300">
<service.icon size={24} />
</div>
<h3 className="text-xl font-serif text-white mb-2">{service.title}</h3>
<p className="text-xs text-gray-300/80 opacity-0 translate-y-3 group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-300 leading-relaxed">
{service.desc}
</p>
</div>
</motion.div>
))}
</div>
</div>
</section>
*/}
{/* ── Venues ── */}
<section id="mekanlar" className="py-28 bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<SectionHeader
eyebrow={dict.venues.eyebrow}
title={dict.venues.title}
subtitle={dict.venues.subtitle}
/>
<div className="space-y-20">
{VENUES.map((venue, i) => (
<motion.div
key={venue.id}
initial={{ opacity: 0, y: 40 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-60px" }}
transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
className={`flex flex-col ${i % 2 === 1 ? "md:flex-row-reverse" : "md:flex-row"} gap-10 items-center`}
>
{/* Image */}
<div className="w-full md:w-[55%] group relative overflow-hidden shadow-[var(--shadow-xl)]">
<img
src={venue.img}
alt={venue.title}
className="w-full h-[420px] object-cover transition-transform duration-700 group-hover:scale-105"
/>
<div className={`absolute inset-0 bg-gradient-to-t ${venue.color} to-transparent opacity-50`} />
<Badge variant="light" className="absolute bottom-6 left-6 shadow-md">
{venue.tag}
</Badge>
</div>
{/* Text */}
<div className="w-full md:w-[45%] px-2 md:px-6">
<p className="text-[10px] uppercase tracking-[0.22em] text-[var(--color-moy-gold-dark)] mb-4">
{venue.tag}
</p>
<h3 className="text-3xl md:text-4xl font-serif text-[var(--color-moy-dark)] mb-6 leading-tight">
{venue.title}
</h3>
<span className="moy-gold-line mb-6 block" />
<p className="text-gray-500 mb-8 text-base leading-relaxed">{venue.desc}</p>
<Link
href={`/${lang}/mekanlar/${venue.id}`}
className="inline-flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] font-semibold text-[var(--color-moy-dark)] border-b border-[var(--color-moy-gold)] pb-1 hover:text-[var(--color-moy-gold-dark)] transition-colors group"
>
{dict.cta.exploreBtn}
<ArrowRight size={12} className="group-hover:translate-x-1 transition-transform" />
</Link>
</div>
</motion.div>
))}
</div>
</div>
</section>
{/* ── Testimonials (Geçici olarak gizlendi) ── */}
{/*
<section className="py-28 bg-[var(--color-moy-blue)] relative overflow-hidden">
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[600px] h-[600px] rounded-full bg-[var(--color-moy-gold)]/5 blur-3xl pointer-events-none" />
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 relative">
<SectionHeader
eyebrow="Misafir Deneyimleri"
title="Misafirlerimiz Ne Diyor?"
light
/>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{TESTIMONIALS.map((t, i) => (
<motion.div
key={i}
custom={i}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
variants={fadeUp}
className="relative bg-white/6 backdrop-blur-sm border border-white/10 p-8 hover:border-[var(--color-moy-gold)]/30 transition-colors duration-300"
>
<span className="absolute top-6 right-8 text-6xl font-serif text-[var(--color-moy-gold)]/15 leading-none select-none">
"
</span>
<div className="flex gap-1 mb-6">
{[...Array(t.rating)].map((_, j) => (
<Star key={j} size={14} className="fill-[var(--color-moy-gold)] text-[var(--color-moy-gold)]" />
))}
</div>
<p className="text-gray-300/85 text-sm leading-relaxed mb-8 italic">
"{t.comment}"
</p>
<div className="flex items-center gap-3">
<div className="w-8 h-[1px] bg-[var(--color-moy-gold)]/50" />
<div>
<p className="font-serif text-white text-base">{t.name}</p>
<p className="text-[10px] uppercase tracking-[0.15em] text-gray-500 mt-0.5">{t.origin}</p>
</div>
</div>
</motion.div>
))}
</div>
</div>
</section>
*/}
{/* ── CTA Banner ── */}
<section className="relative py-32 overflow-hidden bg-[var(--color-moy-dark)]">
<div
className="absolute inset-0 bg-cover bg-center opacity-20"
style={{
backgroundImage: `url(https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=60&w=1600)`,
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-[var(--color-moy-dark)] via-transparent to-[var(--color-moy-dark)]" />
<div className="relative max-w-3xl mx-auto px-6 text-center">
<p className="text-[10px] uppercase tracking-[0.28em] text-[var(--color-moy-gold)] mb-6">
{dict.cta.eyebrow}
</p>
<h2 className="text-4xl md:text-6xl font-serif text-white mb-6 leading-tight">
{dict.cta.title1}<br />
<span className="italic text-[var(--color-moy-gold)]">{dict.cta.title2}</span>
</h2>
<p className="text-gray-400 text-base mb-12 max-w-md mx-auto leading-relaxed">
{dict.cta.subtitle}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href={`/${lang}/iletisim`}>
<ShimmerButton
shimmerColor="rgba(201,169,110,0.6)"
background="var(--color-moy-gold)"
className="text-[var(--color-moy-dark)] font-bold"
>
{dict.cta.btn1}
</ShimmerButton>
</Link>
<Link href={`/${lang}/iletisim`}>
<Button variant="outline-light" size="lg">
{dict.cta.btn2}
</Button>
</Link>
</div>
</div>
</section>
{/* ── Map / Location ── */}
<section className="py-28 bg-[var(--color-moy-light)]">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex flex-col lg:flex-row gap-16 items-start">
<div className="w-full lg:w-1/3">
<SectionHeader
eyebrow={dict.venues.eyebrow}
title={dict.venues.title}
align="left"
/>
<p className="text-gray-500 mb-10 -mt-8 leading-relaxed">
{dict.venues.subtitle}
</p>
<ul className="space-y-4">
{LOCATIONS_MAP.map((loc, idx) => (
<li
key={loc.place}
className={`flex items-start gap-3 p-4 rounded-xl cursor-pointer transition-all duration-300 ${activeMap === idx ? 'bg-white shadow-[var(--shadow-sm)] translate-x-2' : 'hover:bg-white/50'}`}
onClick={() => setActiveMap(idx)}
>
<MapPin className={`${activeMap === idx ? 'text-[var(--color-moy-gold)]' : 'text-gray-400'} mt-0.5 shrink-0 transition-colors`} size={18} />
<div>
<strong className={`block text-sm font-medium mb-0.5 transition-colors ${activeMap === idx ? 'text-[var(--color-moy-dark)]' : 'text-gray-600'}`}>
{loc.place}
</strong>
<span className="text-xs text-gray-500">{loc.addr}</span>
</div>
</li>
))}
</ul>
</div>
<div className="w-full lg:w-2/3 h-[420px] bg-white overflow-hidden shadow-[var(--shadow-lg)] relative rounded-2xl">
<iframe
src={LOCATIONS_MAP[activeMap].embed}
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen={false}
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
className="absolute inset-0"
/>
</div>
</div>
</div>
</section>
</div>
);
}
+140
View File
@@ -0,0 +1,140 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { X, ZoomIn } from "lucide-react";
import { Badge } from "@/app/components/ui/badge";
export default function GaleriClient({ lang, dict }: { lang: string; dict: any }) {
const t = dict.gallery;
const CATEGORIES = [t.all, t.venues, "Beach", "Bungalov", "Kite Surf", "Hotel"] as const;
type Category = string;
const IMAGES = [
{ src: "https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?auto=format&fit=crop&q=80&w=800", cat: t.venues, title: "Akşam Servisi" },
{ src: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80&w=800", cat: "Beach", title: "Moy Beach Alaçatı" },
{ src: "https://images.unsplash.com/photo-1449844908441-8829872d2607?auto=format&fit=crop&q=80&w=800", cat: "Bungalov", title: "Doğada Konaklama" },
{ src: "https://images.unsplash.com/photo-1526685514088-290076a0d426?auto=format&fit=crop&q=80&w=800", cat: "Kite Surf", title: "Gökova Körfezi" },
{ src: "https://images.unsplash.com/photo-1566073771259-6a8506099945?auto=format&fit=crop&q=80&w=800", cat: "Hotel", title: "Kite Beach Hotel" },
{ src: "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=800", cat: "Beach", title: "Gün Batımı" },
{ src: "https://images.unsplash.com/photo-1571003123894-1f0594d2b5d9?auto=format&fit=crop&q=80&w=800", cat: "Hotel", title: "Oda Manzarası" },
{ src: "https://images.unsplash.com/photo-1614214227038-f9d9cf98b1ec?auto=format&fit=crop&q=80&w=800", cat: "Kite Surf", title: "Sabah Seansı" },
{ src: "https://images.unsplash.com/photo-1490197415175-074fd86b1fcc?auto=format&fit=crop&q=80&w=800", cat: t.venues, title: "Taze Deniz Ürünleri" },
];
const [active, setActive] = useState<Category>(t.all);
const [lightbox, setLightbox] = useState<{src:string, cat:string, title:string} | null>(null);
const filtered = active === t.all ? IMAGES : IMAGES.filter((img) => img.cat === active);
return (
<div className="min-h-screen bg-[var(--color-moy-dark)]">
{/* Hero */}
<div className="pt-40 pb-16 text-center px-6">
<p className="text-[10px] uppercase tracking-[0.28em] text-[var(--color-moy-gold)] mb-4">{t.eyebrow}</p>
<h1 className="text-5xl md:text-6xl font-serif text-white mb-6">{t.title}</h1>
<p className="text-gray-400 text-base max-w-md mx-auto leading-relaxed">
{t.subtitle}
</p>
</div>
{/* Filter pills */}
<div className="flex flex-wrap justify-center gap-3 px-6 mb-14">
{CATEGORIES.map((cat) => (
<button
key={cat}
onClick={() => setActive(cat)}
className={[
"px-5 py-2 text-[10px] uppercase tracking-[0.16em] font-medium transition-all duration-200",
active === cat
? "bg-[var(--color-moy-gold)] text-[var(--color-moy-dark)]"
: "border border-white/15 text-gray-400 hover:border-[var(--color-moy-gold)]/50 hover:text-[var(--color-moy-gold)]",
].join(" ")}
>
{cat}
</button>
))}
</div>
{/* Grid */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 pb-28">
<motion.div
layout
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"
>
<AnimatePresence mode="popLayout">
{filtered.map((img) => (
<motion.div
key={img.src}
layout
initial={{ opacity: 0, scale: 0.94 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.94 }}
transition={{ duration: 0.35, ease: [0.16, 1, 0.3, 1] }}
className="group relative aspect-square cursor-pointer overflow-hidden"
onClick={() => setLightbox(img)}
>
<img
src={img.src}
alt={img.title}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-[var(--color-moy-dark)]/0 group-hover:bg-[var(--color-moy-dark)]/50 transition-all duration-300 flex flex-col items-center justify-center gap-3">
<ZoomIn
size={28}
className="text-white opacity-0 group-hover:opacity-100 transition-opacity duration-300 translate-y-2 group-hover:translate-y-0"
/>
<span className="text-white text-sm font-serif opacity-0 group-hover:opacity-100 transition-opacity duration-300">
{img.title}
</span>
</div>
<Badge
variant="dark"
className="absolute top-3 left-3 opacity-0 group-hover:opacity-100 transition-opacity duration-300"
>
{img.cat}
</Badge>
</motion.div>
))}
</AnimatePresence>
</motion.div>
</div>
{/* Lightbox */}
<AnimatePresence>
{lightbox && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[300] bg-black/90 flex items-center justify-center p-6"
onClick={() => setLightbox(null)}
>
<button
className="absolute top-6 right-6 text-white/60 hover:text-white transition-colors"
onClick={() => setLightbox(null)}
>
<X size={28} />
</button>
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="max-w-4xl w-full"
onClick={(e) => e.stopPropagation()}
>
<img
src={lightbox.src}
alt={lightbox.title}
className="w-full max-h-[80vh] object-contain"
/>
<p className="text-center text-white font-serif text-xl mt-4">{lightbox.title}</p>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
+18
View File
@@ -0,0 +1,18 @@
import { getDictionary, Locale } from "../../dictionaries";
import GaleriClient from "./GaleriClient";
export const metadata = {
title: "Galeri | Moy Group",
};
export default async function GaleriPage({
params,
}: {
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
const locale = lang as Locale;
const dict = await getDictionary(locale);
return <GaleriClient lang={locale} dict={dict} />;
}
+186
View File
@@ -0,0 +1,186 @@
import { MapPin, Phone, Mail } from "lucide-react";
import { Input, Textarea } from "@/app/components/ui/input";
import { Button } from "@/app/components/ui/button";
import { SectionHeader } from "@/app/components/ui/section-header";
import { Badge } from "@/app/components/ui/badge";
import { getDictionary, Locale } from "../../dictionaries";
const LOCATIONS = [
{
name: "Moy Beach",
tag: "Beach & Bar",
address: "Akyaka, Atatürk 1. Cd No:86, 48640 Ula/Muğla",
phones: ["+90 534 465 62 35"],
email: "beach@moygroup.com.tr",
variant: "gold" as const,
},
{
name: "Kite Beach Hotel",
tag: "Hotel",
address: "Akçapınar Mah. Akçapınar Sok. Kiteboard Sahili, Muğla",
phones: ["+90 533 081 61 81"],
email: "hotel@moygroup.com.tr",
variant: "blue" as const,
},
{
name: "Volkswagen Kite (Surfing)",
tag: "Kite Surf",
address: "Akçapınar kite sahili, Ula, Turkey 48640",
phones: ["+90 537 882 31 37", "+90 505 204 98 56"],
email: "kite@moygroup.com.tr",
variant: "green" as const,
},
];
export default async function IletisimPage({
params,
}: {
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
const locale = lang as Locale;
const dict = await getDictionary(locale);
const t = dict.contact;
return (
<div className="min-h-screen bg-[var(--color-moy-light)]">
{/* Hero */}
<div className="bg-[var(--color-moy-dark)] pt-40 pb-20">
<div className="max-w-3xl mx-auto px-6 text-center">
<p className="text-[10px] uppercase tracking-[0.28em] text-[var(--color-moy-gold)] mb-4">
{t.eyebrow}
</p>
<h1 className="text-5xl md:text-6xl font-serif text-white mb-6 leading-tight">
{t.title}
</h1>
<p className="text-gray-400 text-base leading-relaxed max-w-lg mx-auto">
{t.subtitle}
</p>
</div>
</div>
{/* Locations */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 -mt-10 pb-24">
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 mb-24">
{LOCATIONS.map((loc) => (
<div
key={loc.name}
className="bg-white border border-gray-100 p-8 shadow-[var(--shadow-sm)] hover:shadow-[var(--shadow-md)] transition-shadow duration-300"
>
<Badge variant={loc.variant} className="mb-5">{loc.tag}</Badge>
<h3 className="text-xl font-serif text-[var(--color-moy-dark)] mb-5">{loc.name}</h3>
<ul className="space-y-3">
<li className="flex items-start gap-3">
<MapPin size={15} className="text-[var(--color-moy-gold)] mt-0.5 shrink-0" />
<span className="text-sm text-gray-500">{loc.address}</span>
</li>
<li className="flex items-start gap-3">
<Phone size={15} className="text-[var(--color-moy-gold)] shrink-0 mt-0.5" />
<div className="flex flex-col gap-1">
{loc.phones.map((phone, i) => (
<a key={i} href={`tel:${phone.replace(/\s/g, "")}`} className="text-sm text-gray-500 hover:text-[var(--color-moy-gold)] transition-colors">
{phone}
</a>
))}
</div>
</li>
<li className="flex items-center gap-3">
<Mail size={15} className="text-[var(--color-moy-gold)] shrink-0" />
<a href={`mailto:${loc.email}`} className="text-sm text-gray-500 hover:text-[var(--color-moy-gold)] transition-colors">
{loc.email}
</a>
</li>
</ul>
</div>
))}
</div>
{/* Contact Form + Info */}
<div className="grid grid-cols-1 lg:grid-cols-5 gap-12">
{/* Form */}
<div className="lg:col-span-3 bg-white border border-gray-100 p-10 shadow-[var(--shadow-sm)]">
<SectionHeader
eyebrow={t.formEyebrow}
title={t.formTitle}
align="left"
className="mb-10"
/>
<form className="space-y-5">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
<Input label={t.name} placeholder={t.namePlaceholder} id="name" />
<Input label={t.email} type="email" placeholder={t.emailPlaceholder} id="email" />
</div>
<Input label={t.phone} type="tel" placeholder={t.phonePlaceholder} id="phone" />
<div>
<label className="text-[10px] font-medium uppercase tracking-[0.16em] text-gray-500 block mb-1.5">
{t.subject}
</label>
<select className="w-full px-4 py-3.5 bg-white border border-gray-200 text-sm text-[var(--color-moy-dark)] focus:outline-none focus:ring-2 focus:ring-[var(--color-moy-gold)]/40 focus:border-[var(--color-moy-gold)] transition-all duration-200">
{t.subjectOptions.map((opt: string, i: number) => (
<option key={i} value={i === 0 ? "" : opt}>{opt}</option>
))}
</select>
</div>
<Textarea label={t.message} placeholder={t.messagePlaceholder} id="message" rows={5} />
<Button variant="primary" size="lg" shimmer className="w-full group mt-2">
{t.send}
</Button>
</form>
</div>
{/* Sidebar info */}
<div className="lg:col-span-2 space-y-8">
<div className="bg-[var(--color-moy-dark)] p-8">
<h3 className="font-serif text-xl text-white mb-6">{t.generalContact}</h3>
<ul className="space-y-4">
<li className="flex items-center gap-3">
<Phone size={16} className="text-[var(--color-moy-gold)]" />
<div>
<p className="text-[10px] uppercase tracking-[0.15em] text-gray-500 mb-0.5">{t.phoneText}</p>
<a href="tel:+905344656235" className="text-sm text-gray-300 hover:text-[var(--color-moy-gold)] transition-colors">+90 534 465 62 35</a>
</div>
</li>
<li className="flex items-center gap-3">
<Mail size={16} className="text-[var(--color-moy-gold)]" />
<div>
<p className="text-[10px] uppercase tracking-[0.15em] text-gray-500 mb-0.5">{t.emailText}</p>
<a href="mailto:info@moygroup.com.tr" className="text-sm text-gray-300 hover:text-[var(--color-moy-gold)] transition-colors">info@moygroup.com.tr</a>
</div>
</li>
</ul>
</div>
<div className="bg-white border border-gray-100 p-8">
<h3 className="font-serif text-xl text-[var(--color-moy-dark)] mb-6">{t.hours}</h3>
<ul className="space-y-2 text-sm">
{[
[t.monFri, "09:00 23:00"],
[t.sat, "10:00 24:00"],
[t.sun, "10:00 23:00"],
].map(([day, hours]) => (
<li key={day as string} className="flex justify-between text-gray-500">
<span>{day as string}</span>
<span className="font-medium text-[var(--color-moy-dark)]">{hours as string}</span>
</li>
))}
</ul>
<p className="text-xs text-[var(--color-moy-gold-dark)] mt-4">{t.hoursNote}</p>
</div>
<div className="bg-white border border-gray-100 p-8">
<h3 className="font-serif text-xl text-[var(--color-moy-dark)] mb-4">{t.social}</h3>
<div className="flex gap-4">
<a href="#" aria-label="Instagram" className="w-10 h-10 border border-gray-200 flex items-center justify-center hover:border-[var(--color-moy-gold)] hover:text-[var(--color-moy-gold)] text-gray-400 transition-all duration-200">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="20" height="20" x="2" y="2" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"/><line x1="17.5" x2="17.51" y1="6.5" y2="6.5"/></svg>
</a>
<a href="#" aria-label="Facebook" className="w-10 h-10 border border-gray-200 flex items-center justify-center hover:border-[var(--color-moy-gold)] hover:text-[var(--color-moy-gold)] text-gray-400 transition-all duration-200">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
+58
View File
@@ -0,0 +1,58 @@
import { getDictionary, Locale } from "../../dictionaries";
export const metadata = {
title: "Kurumsal | Moy Group",
};
export default async function KurumsalPage({
params,
}: {
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
const locale = lang as Locale;
const dict = await getDictionary(locale);
const t = dict.corporate;
return (
<div className="pt-32 pb-24 min-h-screen bg-[var(--color-moy-light)]">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{/* HAKKIMIZDA */}
<section className="mb-20">
<h1 className="text-4xl md:text-5xl font-serif text-[var(--color-moy-dark)] mb-6 text-center">{t.title}</h1>
<div className="w-24 h-1 bg-[var(--color-moy-gold)] mx-auto mb-10"></div>
<div className="bg-white p-8 md:p-12 rounded-2xl shadow-sm border border-gray-100 text-gray-700 leading-relaxed space-y-6">
<p>{t.p1}</p>
<p>{t.p2}</p>
<p>{t.p3}</p>
<p>{t.p4}</p>
</div>
</section>
{/* VİZYONUMUZ & MİSYONUMUZ */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-10">
<section>
<div className="bg-[var(--color-moy-dark)] text-white p-8 md:p-10 rounded-2xl shadow-lg h-full border border-gray-800">
<h2 className="text-2xl font-serif text-[var(--color-moy-gold)] mb-6 uppercase tracking-wider">{t.vision}</h2>
<p className="text-gray-300 leading-relaxed font-light">
{t.visionText}
</p>
</div>
</section>
<section>
<div className="bg-white p-8 md:p-10 rounded-2xl shadow-sm h-full border border-gray-200">
<h2 className="text-2xl font-serif text-[var(--color-moy-gold-dark)] mb-6 uppercase tracking-wider">{t.mission}</h2>
<p className="text-gray-600 leading-relaxed">
{t.missionText}
</p>
</div>
</section>
</div>
</div>
</div>
);
}
+48
View File
@@ -0,0 +1,48 @@
import type { Metadata } from "next";
import { Cormorant_Garamond, Montserrat } from "next/font/google";
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import "../globals.css";
import { Locale, getDictionary } from "../dictionaries";
const cormorant = Cormorant_Garamond({
variable: "--font-cormorant",
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
});
const montserrat = Montserrat({
variable: "--font-montserrat",
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
});
export const metadata: Metadata = {
title: "Moy Group | Ege'nin Ruhunu Hisset",
description: "Akyaka, Akçapınar ve Datça'da restoran, beach, bungalov ve kitesurf hizmetleriyle doğa ile iç içe premium tatil deneyimi.",
};
export default async function RootLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: Promise<{ lang: string }>;
}>) {
const { lang } = await params;
const locale = lang as Locale;
const dict = await getDictionary(locale);
return (
<html
lang={locale}
className={`${cormorant.variable} ${montserrat.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
<Navbar lang={locale} dict={dict.navigation} />
<main className="flex-1">{children}</main>
<Footer lang={locale} dict={dict.footer} />
</body>
</html>
);
}
+121
View File
@@ -0,0 +1,121 @@
import { SectionHeader } from "@/app/components/ui/section-header";
import { Badge } from "@/app/components/ui/badge";
import { getDictionary, Locale } from "../../dictionaries";
export const metadata = {
title: "Dijital Menü | Moy Group",
};
export default async function MenuPage({
params,
}: {
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
const locale = lang as Locale;
const dict = await getDictionary(locale);
const t = dict.menu;
const MENU_SECTIONS = [
{
title: t.starters,
badge: "Starters",
variant: "gold" as const,
items: [
{ name: "Zeytinyağlı Enginar", desc: "Taze dereotu ve limon", price: "₺ 185" },
{ name: "Ahtapot Izgara", desc: "Kapari salatası, fesleğen yağı", price: "₺ 340" },
{ name: "Kabak Çiçeği Dolması", desc: "Lor peyniri, nane", price: "₺ 210" },
{ name: "Ezme Tabağı", desc: "Ev yapımı ekmek ile", price: "₺ 145" },
],
},
{
title: t.mains,
badge: "Mains",
variant: "blue" as const,
items: [
{ name: "Levrek Izgara", desc: "Mevsim yeşillikleri, limon yağı", price: "₺ 680" },
{ name: "Çipura Fırın", desc: "Ege otları, domates, zeytinyağı", price: "₺ 620" },
{ name: "Kuzu Pirzola", desc: "Kekik soslu, ızgara sebze", price: "₺ 750" },
{ name: "Mantar Risotto", desc: "Porcini, parmesan, taze kekik", price: "₺ 390" },
],
},
{
title: t.desserts,
badge: "Desserts",
variant: "green" as const,
items: [
{ name: "Incir Tatlısı", desc: "Kaymak, ceviz, pekmez", price: "₺ 175" },
{ name: "Dondurma Tabağı", desc: "Yerel bademli, mevsim meyve", price: "₺ 145" },
{ name: "Kemalpaşa", desc: "Sıcak servis, dövme ceviz", price: "₺ 120" },
],
},
{
title: t.drinks,
badge: "Drinks",
variant: "dark" as const,
items: [
{ name: "Datça Badem Şerbeti", desc: "Ev yapımı, taze", price: "₺ 95" },
{ name: "Limonata", desc: "Taze sıkım, nane", price: "₺ 85" },
{ name: "Lokal Şarap", desc: "Günlük seçki", price: "₺ 195 / bardak" },
{ name: "Soda & Maden Suyu", desc: "", price: "₺ 45" },
],
},
];
return (
<div className="min-h-screen bg-[var(--color-moy-light)]">
{/* Hero */}
<div className="bg-[var(--color-moy-dark)] pt-40 pb-20">
<div className="max-w-3xl mx-auto px-6 text-center">
<p className="text-[10px] uppercase tracking-[0.28em] text-[var(--color-moy-gold)] mb-4">{t.eyebrow}</p>
<h1 className="text-5xl md:text-6xl font-serif text-white mb-6">{t.title}</h1>
<p className="text-gray-400 text-base max-w-lg mx-auto leading-relaxed">
{t.subtitle}
</p>
</div>
</div>
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-24 space-y-16">
<SectionHeader
eyebrow="Moy Restaurant"
title={t.sectionTitle}
subtitle={t.sectionSubtitle}
/>
{MENU_SECTIONS.map((section) => (
<div key={section.title}>
<div className="flex items-center gap-4 mb-8">
<h2 className="text-2xl font-serif text-[var(--color-moy-dark)]">{section.title}</h2>
<Badge variant={section.variant}>{section.badge}</Badge>
</div>
<div className="divide-y divide-gray-200/60">
{section.items.map((item) => (
<div
key={item.name}
className="flex items-baseline justify-between py-5 group hover:px-3 transition-all duration-200"
>
<div>
<h3 className="font-serif text-lg text-[var(--color-moy-dark)] group-hover:text-[var(--color-moy-gold-dark)] transition-colors">
{item.name}
</h3>
{item.desc && (
<p className="text-xs text-gray-400 mt-1 tracking-wide">{item.desc}</p>
)}
</div>
<span className="text-sm font-medium text-[var(--color-moy-dark)] ml-8 shrink-0">
{item.price}
</span>
</div>
))}
</div>
</div>
))}
<p className="text-xs text-center text-gray-400 border-t border-gray-200 pt-8">
{t.note}
</p>
</div>
</div>
);
}
+13
View File
@@ -0,0 +1,13 @@
import { getDictionary, Locale } from "../dictionaries";
import HomeClient from "./HomeClient";
export default async function Page({
params,
}: {
params: Promise<{ lang: Locale }>;
}) {
const { lang } = await params;
const dict = await getDictionary(lang);
return <HomeClient lang={lang} dict={dict.home} />;
}
+106
View File
@@ -0,0 +1,106 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Plus, Minus } from "lucide-react";
import { SectionHeader } from "@/app/components/ui/section-header";
import { Badge } from "@/app/components/ui/badge";
function FaqItem({ q, a }: { q: string; a: string }) {
const [open, setOpen] = useState(false);
return (
<div className="border-b border-gray-100 last:border-0">
<button
className="w-full flex items-center justify-between py-5 text-left group"
onClick={() => setOpen((v) => !v)}
>
<span className="font-serif text-lg text-[var(--color-moy-dark)] group-hover:text-[var(--color-moy-gold-dark)] transition-colors pr-4">
{q}
</span>
<span className="shrink-0 w-7 h-7 border border-gray-200 group-hover:border-[var(--color-moy-gold)] flex items-center justify-center transition-colors">
{open
? <Minus size={14} className="text-[var(--color-moy-gold)]" />
: <Plus size={14} className="text-gray-400 group-hover:text-[var(--color-moy-gold)] transition-colors" />
}
</span>
</button>
<AnimatePresence initial={false}>
{open && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
className="overflow-hidden"
>
<p className="text-gray-500 text-sm leading-relaxed pb-5">{a}</p>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default function SSSClient({ lang, dict }: { lang: string; dict: any }) {
const t = dict.faq;
const FAQS = [
{
category: t.categories.accommodation,
variant: "blue" as const,
items: [
{ q: t.q1, a: t.a1 },
{ q: t.q2, a: t.a2 },
{ q: t.q3, a: t.a3 },
],
},
{
category: t.categories.kitesurf,
variant: "green" as const,
items: [
{ q: t.q4, a: t.a4 },
{ q: t.q5, a: t.a5 },
{ q: t.q6, a: t.a6 },
],
},
{
category: t.categories.reservation,
variant: "gold" as const,
items: [
{ q: t.q7, a: t.a7 },
{ q: t.q8, a: t.a8 },
{ q: t.q9, a: t.a9 },
],
},
];
return (
<div className="min-h-screen bg-white">
{/* Hero */}
<div className="bg-[var(--color-moy-dark)] pt-40 pb-20">
<div className="max-w-3xl mx-auto px-6 text-center">
<p className="text-[10px] uppercase tracking-[0.28em] text-[var(--color-moy-gold)] mb-4">{t.eyebrow}</p>
<h1 className="text-5xl md:text-6xl font-serif text-white mb-6">{t.title}</h1>
<p className="text-gray-400 text-base max-w-lg mx-auto leading-relaxed">
{t.subtitle}
</p>
</div>
</div>
<div className="max-w-3xl mx-auto px-4 sm:px-6 py-24 space-y-14">
{FAQS.map((group) => (
<div key={group.category}>
<div className="flex items-center gap-3 mb-6">
<Badge variant={group.variant}>{group.category}</Badge>
</div>
<div className="bg-[var(--color-moy-light)] px-6">
{group.items.map((item) => (
<FaqItem key={item.q} q={item.q} a={item.a} />
))}
</div>
</div>
))}
</div>
</div>
);
}
+18
View File
@@ -0,0 +1,18 @@
import { getDictionary, Locale } from "../../dictionaries";
import SSSClient from "./SSSClient";
export const metadata = {
title: "SSS | Moy Group",
};
export default async function SSSPage({
params,
}: {
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
const locale = lang as Locale;
const dict = await getDictionary(locale);
return <SSSClient lang={locale} dict={dict} />;
}