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 ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
-
- ); -} 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 */} +
+ Moy Beach Atmosphere +
+ +
+
+
+ ); +} 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 ( +
+
+
+

{dict.contact.title}

+

{dict.contact.heading}

+
+ +
+ + {/* İletişim Bilgileri */} +
+
+
+ +
+
+

{dict.contact.address.title}

+

+ {dict.contact.address.text1}
+ {dict.contact.address.text2} +

+
+
+ +
+
+ +
+
+

{dict.contact.phone}

+

+ +90 534 465 62 35 +

+
+
+ +
+
+ +
+
+

{dict.contact.email}

+

+ info@moybeachakyaka.com +

+
+
+ +
+
+ +
+
+

{dict.contact.hours.title}

+

+ {dict.contact.hours.text} +

+
+
+
+ + {/* Google Maps Embed */} +
+