diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..94946cd
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,53 @@
+# 1. Base image
+FROM node:20-alpine AS base
+
+# 2. Dependencies
+FROM base AS deps
+RUN apk add --no-cache libc6-compat
+WORKDIR /app
+
+# Install dependencies
+COPY package.json package-lock.json* ./
+RUN npm ci --legacy-peer-deps
+
+
+# 3. Builder
+FROM base AS builder
+WORKDIR /app
+COPY --from=deps /app/node_modules ./node_modules
+COPY . .
+
+# Environment variables must be present at build time for Next.js
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN npm run build
+
+# 4. Runner
+FROM base AS runner
+WORKDIR /app
+
+ENV NODE_ENV=production
+ENV NEXT_TELEMETRY_DISABLED=1
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
+
+COPY --from=builder /app/public ./public
+
+# Set the correct permission for prerender cache
+RUN mkdir .next
+RUN chown nextjs:nodejs .next
+
+# Automatically leverage output traces to reduce image size
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+
+USER nextjs
+
+EXPOSE 3000
+
+ENV PORT=3000
+ENV HOSTNAME="0.0.0.0"
+
+# Start the server
+CMD ["node", "server.js"]
diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx
new file mode 100644
index 0000000..eb5af55
--- /dev/null
+++ b/app/[lang]/layout.tsx
@@ -0,0 +1,39 @@
+import type { Metadata } from "next";
+import { Playfair_Display, Lato } from "next/font/google";
+import "../globals.css";
+
+const playfair = Playfair_Display({
+ variable: "--font-playfair",
+ subsets: ["latin"],
+});
+
+const lato = Lato({
+ variable: "--font-lato",
+ subsets: ["latin"],
+ weight: ["300", "400", "700", "900"],
+});
+
+export const metadata: Metadata = {
+ title: "Moy Beach Akyaka | Lüks & Huzur",
+ description: "Akyaka'nın kalbinde doğayla iç içe lüks bir beach, restoran ve otel deneyimi.",
+};
+
+export async function generateStaticParams() {
+ return [{ lang: 'tr' }, { lang: 'en' }];
+}
+
+export default function RootLayout({
+ children,
+ params,
+}: Readonly<{
+ children: React.ReactNode;
+ params: { lang: string };
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/app/[lang]/page.tsx b/app/[lang]/page.tsx
new file mode 100644
index 0000000..1445f59
--- /dev/null
+++ b/app/[lang]/page.tsx
@@ -0,0 +1,26 @@
+import Header from "@/components/Header";
+import Hero from "@/components/Hero";
+import About from "@/components/About";
+import Services from "@/components/Services";
+import Gallery from "@/components/Gallery";
+import InstagramFeed from "@/components/InstagramFeed";
+import Contact from "@/components/Contact";
+import Footer from "@/components/Footer";
+import { getDictionary } from "../dictionaries";
+
+export default async function Home({ params: { lang } }: { params: { lang: string } }) {
+ const dict = await getDictionary(lang);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/dictionaries.ts b/app/dictionaries.ts
new file mode 100644
index 0000000..581199b
--- /dev/null
+++ b/app/dictionaries.ts
@@ -0,0 +1,13 @@
+import 'server-only';
+
+const dictionaries: Record Promise> = {
+ tr: () => import('../dictionaries/tr.json').then((module) => module.default),
+ en: () => import('../dictionaries/en.json').then((module) => module.default),
+};
+
+export const getDictionary = async (locale: string) => {
+ if (typeof dictionaries[locale] === 'function') {
+ return dictionaries[locale]();
+ }
+ return dictionaries['tr'](); // fallback
+};
diff --git a/app/favicon.ico b/app/favicon.ico
index 718d6fe..380789b 100644
Binary files a/app/favicon.ico and b/app/favicon.ico differ
diff --git a/app/globals.css b/app/globals.css
index a2dc41e..d255e89 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,26 +1,66 @@
@import "tailwindcss";
-:root {
- --background: #ffffff;
- --foreground: #171717;
-}
-
@theme inline {
- --color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
+ --color-sand: #E3D9C6;
+ --color-sand-light: #F4EFEB;
+ --color-sand-dark: #C2B49A;
+
+ --color-sea-blue: #2A4B7C;
+ --color-sea-light: #4A6B9C;
+ --color-sea-dark: #1A2B4C;
+
+ --color-coral: #FF6B6B;
+ --color-coral-light: #FF8B8B;
+ --color-coral-dark: #D94A4A;
+
+ --color-brand-white: #FFFFFF;
+ --color-brand-black: #171717;
+
+ --font-serif: var(--font-playfair);
+ --font-sans: var(--font-lato);
}
-@media (prefers-color-scheme: dark) {
- :root {
- --background: #0a0a0a;
- --foreground: #ededed;
- }
+:root {
+ --background: #FFFFFF;
+ --foreground: #171717;
}
body {
background: var(--background);
color: var(--foreground);
- font-family: Arial, Helvetica, sans-serif;
+ font-family: var(--font-sans), sans-serif;
+ overflow-x: hidden;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ font-family: var(--font-serif), serif;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+/* Shimmer animation for CTA buttons */
+@keyframes shimmer {
+ 0% { transform: translateX(-150%); }
+ 100% { transform: translateX(150%); }
+}
+
+.btn-shimmer {
+ position: relative;
+ overflow: hidden;
+}
+
+.btn-shimmer::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 90deg,
+ transparent 0%,
+ rgba(255, 255, 255, 0.22) 50%,
+ transparent 100%
+ );
+ transform: translateX(-150%);
+ animation: shimmer 2.8s ease-in-out infinite;
}
diff --git a/app/layout.tsx b/app/layout.tsx
deleted file mode 100644
index 976eb90..0000000
--- a/app/layout.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
-import "./globals.css";
-
-const geistSans = Geist({
- variable: "--font-geist-sans",
- subsets: ["latin"],
-});
-
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
- subsets: ["latin"],
-});
-
-export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
- {children}
-
- );
-}
diff --git a/app/page.tsx b/app/page.tsx
deleted file mode 100644
index 3f36f7c..0000000
--- a/app/page.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import Image from "next/image";
-
-export default function Home() {
- return (
-
-
-
-
-
- To get started, edit the page.tsx file.
-
-
- Looking for a starting point or more instructions? Head over to{" "}
-
- Templates
- {" "}
- or the{" "}
-
- Learning
- {" "}
- center.
-
-
-
-
-
- );
-}
diff --git a/components/About.tsx b/components/About.tsx
new file mode 100644
index 0000000..cb9b6d1
--- /dev/null
+++ b/components/About.tsx
@@ -0,0 +1,78 @@
+"use client";
+
+import { motion } from "framer-motion";
+import Image from "next/image";
+
+export default function About({ dict }: { dict: any }) {
+ const stats = [
+ { value: "5+", label: dict.about.stats.years },
+ { value: "10K+", label: dict.about.stats.guests },
+ { value: "4.8★", label: dict.about.stats.rating },
+ ];
+
+ return (
+
+
+
+
+ {/* Text side */}
+
+ {/* Label with decorative lines */}
+
+
+
{dict.about.title}
+
+
+
+
+
+
+ {dict.about.p1}
+
+
+ {dict.about.p2}
+
+
+ {/* Stats row */}
+
+ {stats.map((stat) => (
+
+
{stat.value}
+
{stat.label}
+
+ ))}
+
+
+
+ {/* Image side */}
+
+ {/* Shadow shape behind */}
+
+ {/* Image clipped to shape */}
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/Contact.tsx b/components/Contact.tsx
new file mode 100644
index 0000000..bb3ebf6
--- /dev/null
+++ b/components/Contact.tsx
@@ -0,0 +1,86 @@
+"use client";
+
+import { MapPin, Phone, Mail, Clock } from "lucide-react";
+
+export default function Contact({ dict }: { dict: any }) {
+ return (
+
+ );
+}
diff --git a/components/Footer.tsx b/components/Footer.tsx
new file mode 100644
index 0000000..a4ac606
--- /dev/null
+++ b/components/Footer.tsx
@@ -0,0 +1,163 @@
+"use client";
+
+const socials = [
+ {
+ label: "Instagram",
+ href: "https://instagram.com/moybeachakyaka",
+ icon: (
+
+ ),
+ },
+ {
+ label: "Facebook",
+ href: "https://facebook.com/moybeachakyaka",
+ icon: (
+
+ ),
+ },
+ {
+ label: "YouTube",
+ href: "https://youtube.com/@moybeach",
+ icon: (
+
+ ),
+ },
+];
+
+export default function Footer({ dict }: { dict: any }) {
+ const links = [
+ { label: dict.nav.about, href: "#about" },
+ { label: dict.nav.services, href: "#services" },
+ { label: dict.nav.gallery, href: "#gallery" },
+ { label: dict.nav.contact, href: "#contact" },
+ ];
+
+ return (
+
+ );
+}
diff --git a/components/Gallery.tsx b/components/Gallery.tsx
new file mode 100644
index 0000000..0d3218c
--- /dev/null
+++ b/components/Gallery.tsx
@@ -0,0 +1,136 @@
+"use client";
+
+import { useState } from "react";
+import { motion, AnimatePresence } from "framer-motion";
+import { X, ZoomIn } from "lucide-react";
+import Image from "next/image";
+
+const photos = [
+ { id: 1, categoryKey: "beach", url: "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&q=80&w=1200" },
+ { id: 2, categoryKey: "rooms", url: "https://images.unsplash.com/photo-1611892440504-42a792e24d32?auto=format&fit=crop&q=80&w=1200" },
+ { id: 3, categoryKey: "restaurant", url: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?auto=format&fit=crop&q=80&w=1200" },
+ { id: 4, categoryKey: "beach", url: "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=1200" },
+ { id: 5, categoryKey: "events", url: "https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&q=80&w=1200" },
+ { id: 6, categoryKey: "restaurant", url: "https://images.unsplash.com/photo-1555396273-367ea4eb4db5?auto=format&fit=crop&q=80&w=1200" },
+];
+
+export default function Gallery({ dict }: { dict: any }) {
+ const categories = [
+ { key: "all", label: dict.gallery.categories.all },
+ { key: "beach", label: dict.gallery.categories.beach },
+ { key: "restaurant", label: dict.gallery.categories.restaurant },
+ { key: "rooms", label: dict.gallery.categories.rooms },
+ { key: "events", label: dict.gallery.categories.events }
+ ];
+
+ const [activeCategoryKey, setActiveCategoryKey] = useState("all");
+ const [selectedImage, setSelectedImage] = useState(null);
+
+ const filteredPhotos =
+ activeCategoryKey === "all"
+ ? photos
+ : photos.filter((p) => p.categoryKey === activeCategoryKey);
+
+ return (
+
+
+
+
+
+
+
{dict.gallery.title}
+
+
+
{dict.gallery.heading}
+
+ {/* Category filters */}
+
+ {categories.map((cat) => (
+
+ ))}
+
+
+
+
+
+ {filteredPhotos.map((photo) => (
+ setSelectedImage(photo.url)}
+ >
+
+ {/* Hover overlay */}
+
+
+
+ {dict.gallery.categories[photo.categoryKey]}
+
+
+
+ ))}
+
+
+
+
+ {/* Lightbox */}
+
+ {selectedImage && (
+ setSelectedImage(null)}
+ >
+
+ e.stopPropagation()}
+ >
+
+
+
+ )}
+
+
+ );
+}
diff --git a/components/Header.tsx b/components/Header.tsx
new file mode 100644
index 0000000..3a5a9d6
--- /dev/null
+++ b/components/Header.tsx
@@ -0,0 +1,94 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Menu, X } from "lucide-react";
+
+export default function Header({ dict, lang }: { dict: any, lang: string }) {
+ const [isScrolled, setIsScrolled] = useState(false);
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
+
+ useEffect(() => {
+ const handleScroll = () => {
+ setIsScrolled(window.scrollY > 50);
+ };
+ window.addEventListener("scroll", handleScroll);
+ return () => window.removeEventListener("scroll", handleScroll);
+ }, []);
+
+ const navLinks = [
+ { name: dict.nav.about, href: "#about" },
+ { name: dict.nav.services, href: "#services" },
+ { name: dict.nav.gallery, href: "#gallery" },
+ { name: dict.nav.contact, href: "#contact" },
+ ];
+
+ return (
+
+ );
+}
diff --git a/components/Hero.tsx b/components/Hero.tsx
new file mode 100644
index 0000000..a313ab0
--- /dev/null
+++ b/components/Hero.tsx
@@ -0,0 +1,141 @@
+"use client";
+
+import { motion } from "framer-motion";
+import { ChevronDown } from "lucide-react";
+import Image from "next/image";
+
+export default function Hero({ dict }: { dict: any }) {
+ const words = ["MOY", "BEACH"];
+
+ return (
+
+
+ {/* Background Image */}
+
+
+
+
+ {/* Multi-layer gradient for depth */}
+
+
+
+ {/* Main Content */}
+
+
+ {/* Location badge */}
+
+ Akyaka · Muğla · Türkiye
+
+
+ {/* Decorative line */}
+
+
+ {/* Main Title — word-by-word reveal */}
+
+ {words.map((word, i) => (
+
+
+ {word}
+
+ {i === 0 && (
+
+ )}
+
+ ))}
+
+
+ {/* Subtitle */}
+
+ {dict.hero.subtitle}
+
+
+ {/* CTA Buttons */}
+
+
+ {dict.hero.explore}
+
+
+
+
+ {/* Scroll Indicator — direct child of section, correctly positioned */}
+
+ Kaydır
+
+
+
+
+
+ {/* Wave divider into About section */}
+
+
+ );
+}
diff --git a/components/InstagramFeed.tsx b/components/InstagramFeed.tsx
new file mode 100644
index 0000000..70c6d16
--- /dev/null
+++ b/components/InstagramFeed.tsx
@@ -0,0 +1,88 @@
+"use client";
+
+const reels = [
+ { id: 1, url: "https://www.instagram.com/reel/C97iqFqoSyl/embed" },
+ { id: 2, url: "https://www.instagram.com/reel/C-slzOcIQB3/embed" },
+ { id: 3, url: "https://www.instagram.com/reel/DLM8ng6MOKf/embed" },
+ { id: 4, url: "https://www.instagram.com/reel/DX-BDErM5qd/embed" },
+];
+
+function InstagramIcon() {
+ return (
+
+ );
+}
+
+export default function InstagramFeed({ dict }: { dict: any }) {
+ return (
+
+
+
+ {/* Header */}
+
+
+
+
+
+ {dict.social.title}
+
+
+
+
+
{dict.social.heading}
+
+
+
+ @moy_akyaka
+
+
+
+ {/* Reels grid */}
+
+ {reels.map((reel) => (
+
+ {/* Thin coral top border accent */}
+
+
+
+
+ ))}
+
+
+ {/* CTA */}
+
+
+
+
+ );
+}
diff --git a/components/Reservation.tsx b/components/Reservation.tsx
new file mode 100644
index 0000000..2a050c1
--- /dev/null
+++ b/components/Reservation.tsx
@@ -0,0 +1,108 @@
+"use client";
+
+import { motion } from "framer-motion";
+import { Calendar, Users, Coffee } from "lucide-react";
+
+export default function Reservation() {
+ return (
+
+ {/* Decorative background blobs */}
+
+
+
+
+
+ {/* Inner glow */}
+
+
+
+
+
+
Hemen Yerinizi Ayırtın
+
+
+
Rezervasyon Talebi
+
+ Unutulmaz bir Ege deneyimi için hemen formumuzu doldurun, ekibimiz en kısa sürede sizinle iletişime geçsin.
+
+
+
+
e.preventDefault()}
+ >
+
+
+ {/* Date */}
+
+
+
+
+
+ {/* Guests */}
+
+
+
+
+
+ {/* Service type */}
+
+
+
+
+
+
+
+
+
+
+
+ {/* Shimmer submit button */}
+
+
+
+ Bu form ön rezervasyon talebidir. Kesin onay için ekibimiz sizinle iletişime geçecektir.
+
+
+
+
+
+ );
+}
diff --git a/components/Services.tsx b/components/Services.tsx
new file mode 100644
index 0000000..baf5b43
--- /dev/null
+++ b/components/Services.tsx
@@ -0,0 +1,91 @@
+"use client";
+
+import { motion } from "framer-motion";
+import { Waves, UtensilsCrossed, Hotel, CalendarHeart } from "lucide-react";
+import Image from "next/image";
+
+export default function Services({ dict }: { dict: any }) {
+ const services = [
+ {
+ title: dict.services.items.beach.title,
+ description: dict.services.items.beach.description,
+ icon: Waves,
+ image: "https://images.unsplash.com/photo-1519046904884-53103b34b206?auto=format&fit=crop&q=80&w=800",
+ },
+ {
+ title: dict.services.items.restaurant.title,
+ description: dict.services.items.restaurant.description,
+ icon: UtensilsCrossed,
+ image: "https://images.unsplash.com/photo-1544148103-0773bf10d330?auto=format&fit=crop&q=80&w=800",
+ },
+ {
+ title: dict.services.items.hotel.title,
+ description: dict.services.items.hotel.description,
+ icon: Hotel,
+ image: "https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?auto=format&fit=crop&q=80&w=800",
+ },
+ {
+ title: dict.services.items.events.title,
+ description: dict.services.items.events.description,
+ icon: CalendarHeart,
+ image: "https://images.unsplash.com/photo-1511285560929-80b456fea0bc?auto=format&fit=crop&q=80&w=800",
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+
{dict.services.title}
+
+
+
{dict.services.heading}
+
+
+
+ {services.map((service, index) => (
+
+ {/* Image card with gradient border on hover */}
+
+ {/* Border beam — visible on hover */}
+
+
+
+
+
+
+
+ {/* Icon badge */}
+
+
+
+
+
+
+
+ {service.title}
+
+ {service.description}
+
+ ))}
+
+
+
+ );
+}
diff --git a/components/VideoTestimonials.tsx b/components/VideoTestimonials.tsx
new file mode 100644
index 0000000..d5a4095
--- /dev/null
+++ b/components/VideoTestimonials.tsx
@@ -0,0 +1,122 @@
+"use client";
+
+import { motion } from "framer-motion";
+import { Star, Quote, Play } from "lucide-react";
+import Image from "next/image";
+
+const testimonials = [
+ {
+ name: "Ayşe Y.",
+ platform: "Google Yorumu",
+ text: "Ege'de gördüğüm en kaliteli mekanlardan biri. Yemekler harika, denizi zaten muhteşem. Özellikle kokteyllerini denemelisiniz.",
+ rating: 5,
+ },
+ {
+ name: "David M.",
+ platform: "TripAdvisor",
+ text: "Mükemmel bir deneyimdi. Ailece geldik ve çocuklar da biz de çok eğlendik. Personel çok ilgili ve güler yüzlüydü.",
+ rating: 5,
+ },
+ {
+ name: "Caner K.",
+ platform: "Google Yorumu",
+ text: "Harika bir atmosfer. Akşam yemeği için tercih ettik, gün batımı manzarası eşliğinde unutulmaz bir anı oldu.",
+ rating: 4,
+ },
+];
+
+export default function VideoTestimonials() {
+ return (
+
+
+
+ {/* Video / Image Panel */}
+
+
+
+
+ {/* Play button overlay */}
+
+
+
+
+
Moy Beach'i Keşfedin
+
+
+
+ {/* Testimonials Panel */}
+
+
+
+
Referanslar
+
+
Misafirlerimiz
Ne Diyor?
+
+
+ {testimonials.map((testimonial, index) => (
+
+
+
+ {/* Stars */}
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+
+
+ "{testimonial.text}"
+
+
+
+
+ {testimonial.name.charAt(0)}
+
+
+
{testimonial.name}
+
{testimonial.platform}
+
+
+
+ ))}
+
+
+ {/* Aggregate rating */}
+
+
+ 4.8
+ / 5
+
+
+ Google & TripAdvisor değerlendirmelerine göre
+
+ (150+ Yorum)
+
+
+
+
+
+ );
+}
diff --git a/dictionaries/en.json b/dictionaries/en.json
new file mode 100644
index 0000000..9cbe39f
--- /dev/null
+++ b/dictionaries/en.json
@@ -0,0 +1,83 @@
+{
+ "nav": {
+ "about": "About",
+ "services": "Services",
+ "gallery": "Gallery",
+ "contact": "Contact"
+ },
+ "hero": {
+ "subtitle": "An Escape in the Most Beautiful Corner of the Aegean",
+ "explore": "Explore"
+ },
+ "about": {
+ "title": "About Us",
+ "heading": "Where Nature
Meets Luxury",
+ "p1": "Moy Beach Akyaka is designed to offer a nature-friendly and luxurious experience in the fascinating atmosphere of the Aegean. Brought to life with Moy Group quality, this special venue promises its guests peace, flavor, and entertainment beyond an ordinary beach.",
+ "p2": "While cooling off in the crystal-clear waters during the day, you can embark on a gastronomic journey in the evenings with special delicacies prepared by our chefs.",
+ "stats": {
+ "years": "Years of Experience",
+ "guests": "Happy Guests",
+ "rating": "Average Rating"
+ }
+ },
+ "services": {
+ "title": "Privileges",
+ "heading": "Our Services",
+ "items": {
+ "beach": {
+ "title": "Beach Club",
+ "description": "Enjoy the sun and the sea all day long on our private sunbeds and cabanas."
+ },
+ "restaurant": {
+ "title": "Restaurant & Bar",
+ "description": "Meet our selected delicacies from Mediterranean cuisine and signature cocktails."
+ },
+ "hotel": {
+ "title": "Boutique Hotel",
+ "description": "Wake up in our rooms carefully designed for your comfort."
+ },
+ "events": {
+ "title": "Special Events",
+ "description": "Catch the rhythm with unforgettable parties and live DJ performances."
+ }
+ }
+ },
+ "gallery": {
+ "title": "Atmosphere",
+ "heading": "Gallery",
+ "categories": {
+ "all": "All",
+ "beach": "Beach",
+ "restaurant": "Restaurant",
+ "rooms": "Rooms",
+ "events": "Events"
+ }
+ },
+ "social": {
+ "title": "Social Media",
+ "heading": "We are on Instagram",
+ "button": "View All Reels"
+ },
+ "contact": {
+ "title": "Contact",
+ "heading": "Get in Touch",
+ "address": {
+ "title": "Address",
+ "text1": "Akyaka, Ataturk 1. Cd No:86",
+ "text2": "48640 Ula/Mugla"
+ },
+ "phone": "Phone",
+ "email": "Email",
+ "hours": {
+ "title": "Working Hours",
+ "text": "Everyday: 09:00 - 02:00"
+ }
+ },
+ "footer": {
+ "desc": "A unique escape where the cool waters of the Aegean meet luxury. With the assurance of Moy Group.",
+ "pages": "Pages",
+ "contact": "Contact",
+ "rights": "Moy Beach Akyaka. All rights reserved.",
+ "created_by": "Created by"
+ }
+}
diff --git a/dictionaries/tr.json b/dictionaries/tr.json
new file mode 100644
index 0000000..f581551
--- /dev/null
+++ b/dictionaries/tr.json
@@ -0,0 +1,83 @@
+{
+ "nav": {
+ "about": "Hakkımızda",
+ "services": "Hizmetler",
+ "gallery": "Galeri",
+ "contact": "İletişim"
+ },
+ "hero": {
+ "subtitle": "Ege'nin En Güzel Köşesinde Bir Kaçış",
+ "explore": "Keşfet"
+ },
+ "about": {
+ "title": "Hakkımızda",
+ "heading": "Doğanın Lüksle
Buluştuğu Nokta",
+ "p1": "Moy Beach Akyaka, Ege'nin büyüleyici atmosferinde, doğaya saygılı ve lüks bir deneyim sunmak için tasarlandı. Moy Group kalitesiyle hayata geçen bu özel mekan, sıradan bir plajın ötesinde, misafirlerine huzur, lezzet ve eğlenceyi bir arada vadediyor.",
+ "p2": "Gündüzleri kristal berraklığındaki sularda serinlerken, akşamları şeflerimizin hazırladığı özel lezzetlerle gastronomi yolculuğuna çıkabilirsiniz.",
+ "stats": {
+ "years": "Yıl Deneyim",
+ "guests": "Mutlu Misafir",
+ "rating": "Ortalama Puan"
+ }
+ },
+ "services": {
+ "title": "Ayrıcalıklar",
+ "heading": "Hizmetlerimiz",
+ "items": {
+ "beach": {
+ "title": "Beach Club",
+ "description": "Özel şezlong ve localarımızda gün boyu güneşin ve denizin tadını çıkarın."
+ },
+ "restaurant": {
+ "title": "Restoran & Bar",
+ "description": "Akdeniz mutfağından seçme lezzetler ve imza kokteyllerimizle tanışın."
+ },
+ "hotel": {
+ "title": "Boutique Otel",
+ "description": "Konforunuz için özenle tasarlanmış odalarımızda uyanın."
+ },
+ "events": {
+ "title": "Özel Etkinlikler",
+ "description": "Unutulmaz partiler ve canlı DJ performanslarıyla ritmi yakalayın."
+ }
+ }
+ },
+ "gallery": {
+ "title": "Atmosfer",
+ "heading": "Galeri",
+ "categories": {
+ "all": "Tümü",
+ "beach": "Plaj",
+ "restaurant": "Restoran",
+ "rooms": "Odalar",
+ "events": "Etkinlikler"
+ }
+ },
+ "social": {
+ "title": "Sosyal Medya",
+ "heading": "Instagram'da Biz",
+ "button": "Tüm Reels'ları Gör"
+ },
+ "contact": {
+ "title": "İletişim",
+ "heading": "Bize Ulaşın",
+ "address": {
+ "title": "Adres",
+ "text1": "Akyaka, Atatürk 1. Cd No:86",
+ "text2": "48640 Ula/Muğla"
+ },
+ "phone": "Telefon",
+ "email": "E-Posta",
+ "hours": {
+ "title": "Çalışma Saatleri",
+ "text": "Her Gün: 09:00 - 02:00"
+ }
+ },
+ "footer": {
+ "desc": "Ege'nin serin sularıyla lüksün buluştuğu eşsiz bir kaçış. Moy Group güvencesiyle.",
+ "pages": "Sayfalar",
+ "contact": "İletişim",
+ "rights": "Moy Beach Akyaka. Tüm hakları saklıdır.",
+ "created_by": "Created by"
+ }
+}
diff --git a/docs/prd.md b/docs/prd.md
new file mode 100644
index 0000000..d95083d
--- /dev/null
+++ b/docs/prd.md
@@ -0,0 +1,120 @@
+# 🌊 Moy Beach Akyaka — Web Sitesi Yenileme Prompt'u
+
+## Mevcut Site Analizi
+
+**URL:** https://www.moybeachakyaka.com/
+**Konsept:** Beach & Hotel — Akyaka, Muğla
+**Mevcut Durum:**
+- Tek sayfalık (one-page) bir yapı
+- Sadece 1 navigasyon öğesi: "KURUMSAL" (dış bağlantı)
+- Hero bölümü: tam ekran fotoğraf + "MOY BEACH" yazısı
+- İçerik: yalnızca fotoğraf galerisi (6 görsel) + YouTube video + Google Harita
+- Footer: Eagle-Themes tasarımı, minimal bilgi
+- Metin içeriği neredeyse yok; SEO değeri çok düşük
+
+---
+
+## Yenileme Prompt'u
+
+Sen deneyimli bir web tasarımcısı ve UX stratejistisisin.
+**Moy Beach Akyaka** için mevcut web sitesini, lüks bir Ege sahil deneyimini yansıtacak şekilde baştan tasarla.
+
+### 🎯 Hedef Kitle
+- Yerli ve yabancı turistler (yaz sezonu odaklı)
+- Rezervasyon yapmak isteyen bireyler ve aileler
+- Düğün, özel etkinlik ve kurumsal organizasyon arayan gruplar
+
+---
+
+### 🗂️ Sayfa Yapısı ve İçerik
+
+#### 1. Header / Navigasyon
+- Sticky (sayfaya yapışık) navbar
+- Logo sol tarafta, menü sağ tarafta
+- Menü öğeleri: **Hakkımızda | Hizmetler | Galeri | Etkinlikler | İletişim | Rezervasyon**
+- Dil seçeneği: TR / EN
+- "Rezervasyon Yap" butonu — belirgin CTA (örnek: altın sarısı/turuncu)
+
+#### 2. Hero Bölümü
+- Tam ekran video veya yüksek çözünürlüklü slider (mevcut fotoğraflar kullanılabilir)
+- Üzerine yerleştirilmiş kısa slogan: *"Ege'nin En Güzel Köşesinde Bir Kaçış"*
+- Alt kısımda kaydırma yönlendirici ok animasyonu
+
+#### 3. Hakkımızda
+- Moy Beach'in hikayesi, felsefesi ve Moy Group bağlantısı
+- Öne çıkan özellikler: konum, atmosfer, deneyim kalitesi
+- Kısa metin + yan yana görsel
+
+#### 4. Hizmetler / Deneyimler
+- Beach Club (şezlong, şemsiye, bar)
+- Restoran & Yemek (menü bağlantısı)
+- Otel / Konaklama seçenekleri
+- Özel etkinlikler & organizasyon
+- Her hizmet için ikon, başlık ve kısa açıklama kartı
+
+#### 5. Galeri
+- Masonry veya grid düzeninde filtrelenebilir fotoğraf galerisi
+- Kategoriler: Plaj | Restoran | Odalar | Etkinlikler
+- Lightbox ile tam ekran görüntüleme
+
+#### 6. Video Bölümü
+- Mevcut YouTube videosu tam ekran arka plan veya embed olarak
+- Otomatik oynatma (sessiz), kullanıcı sesi açabilir
+
+#### 7. Yorumlar / Referanslar
+- Google veya TripAdvisor yorumlarından seçmeler (şu an 3.3 ⭐ — iyileştirme notu ekle)
+- Misafir fotoğrafları ile birlikte alıntı kutuları
+
+#### 8. Konum & İletişim
+- Mevcut Google Harita embed'i korunabilir
+- Adres: Akyaka, Atatürk 1. Cd No:88, 48640 Ula/Muğla
+- Telefon, e-posta, sosyal medya ikonları
+- İletişim formu (ad, e-posta, mesaj, tarih seçici)
+
+#### 9. Rezervasyon Bölümü
+- Tarih seçimi (giriş/çıkış)
+- Kişi sayısı
+- Hizmet tipi seçimi
+- "Rezervasyon Talebi Gönder" CTA butonu
+
+#### 10. Footer
+- Logo + kısa açıklama
+- Hızlı bağlantılar
+- Sosyal medya (Instagram, Facebook, YouTube)
+- KVKK / Gizlilik Politikası bağlantıları
+- © 2026 Moy Beach Akyaka
+
+---
+
+### 🎨 Tasarım Rehberi
+
+| Özellik | Öneri |
+|---|---|
+| **Renk Paleti** | Kum beji, deniz mavisi, mercan/turuncu vurgu, beyaz |
+| **Tipografi** | Serif başlık (örn. Playfair Display) + sans-serif gövde (örn. Lato) |
+| **Duygu** | Lüks ama samimi, doğal, rahatlatıcı |
+| **Animasyonlar** | Scroll-triggered fade-in, parallax efekti hero'da |
+| **Görseller** | Mevcut profesyonel fotoğraflar korunmalı, yeni çekimler eklenebilir |
+
+---
+
+### ⚙️ Teknik Gereksinimler
+
+- **Responsive** tasarım: mobil öncelikli (mobile-first)
+- **Sayfa hızı:** Core Web Vitals optimizasyonu (LCP < 2.5s)
+- **SEO:** Meta etiketleri, schema.org yapılandırması (Hotel, Restaurant, LocalBusiness)
+- **CMS:** WordPress veya Webflow (içerik kolayca güncellenebilmeli)
+- **SSL** zorunlu
+- **Google Analytics 4** entegrasyonu
+- **WhatsApp** hızlı iletişim butonu (sağ alt köşe sabit)
+
+---
+
+### 🚀 Öncelik Sırası (MVP)
+
+1. Hero + Navigasyon
+2. Hizmetler kartları
+3. Galeri
+4. İletişim & Harita
+5. Rezervasyon formu
+6. Çok dilli destek (TR/EN)
\ No newline at end of file
diff --git a/middleware.ts b/middleware.ts
new file mode 100644
index 0000000..656f134
--- /dev/null
+++ b/middleware.ts
@@ -0,0 +1,27 @@
+import { NextResponse } from "next/server";
+import type { NextRequest } from "next/server";
+
+const locales = ["tr", "en"];
+const defaultLocale = "tr";
+
+export function middleware(request: NextRequest) {
+ // Check if there is any supported locale in the pathname
+ const { pathname } = request.nextUrl;
+
+ const pathnameHasLocale = locales.some(
+ (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
+ );
+
+ if (pathnameHasLocale) return;
+
+ // If no locale found, redirect to default locale
+ request.nextUrl.pathname = `/${defaultLocale}${pathname}`;
+ return NextResponse.redirect(request.nextUrl);
+}
+
+export const config = {
+ matcher: [
+ // Skip all internal paths (_next) and static files
+ '/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
+ ],
+};
diff --git a/package-lock.json b/package-lock.json
index b1c6609..e6efec1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,8 @@
"name": "moybeach",
"version": "0.1.0",
"dependencies": {
+ "framer-motion": "^12.40.0",
+ "lucide-react": "^1.17.0",
"next": "16.2.7",
"react": "19.2.4",
"react-dom": "19.2.4"
@@ -3762,6 +3764,33 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.40.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.40.0.tgz",
+ "integrity": "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.40.0",
+ "motion-utils": "^12.39.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -5032,6 +5061,15 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/lucide-react": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.17.0.tgz",
+ "integrity": "sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -5099,6 +5137,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/motion-dom": {
+ "version": "12.40.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.40.0.tgz",
+ "integrity": "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.39.0"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.39.0",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
+ "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
+ "license": "MIT"
+ },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
diff --git a/package.json b/package.json
index 8dbf6eb..152cb0b 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,8 @@
"lint": "eslint"
},
"dependencies": {
+ "framer-motion": "^12.40.0",
+ "lucide-react": "^1.17.0",
"next": "16.2.7",
"react": "19.2.4",
"react-dom": "19.2.4"
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..a40828f
Binary files /dev/null and b/public/logo.png differ
diff --git a/public/moy-beach.jpg b/public/moy-beach.jpg
new file mode 100644
index 0000000..21cdbf1
Binary files /dev/null and b/public/moy-beach.jpg differ