From 1590bef2272c18015c8f0e368b78997b985939d7 Mon Sep 17 00:00:00 2001 From: ayrisdev Date: Wed, 15 Apr 2026 22:37:39 +0300 Subject: [PATCH] first commit --- Dockerfile | 61 + app/accommodation/[slug]/page.tsx | 154 ++ app/accommodation/page.tsx | 90 + app/activities/page.tsx | 13 + app/beach/page.tsx | 13 + app/components/AccommodationCard.tsx | 86 + app/components/ExperienceCard.tsx | 41 + app/components/FloatingBookingBar.tsx | 49 + app/components/Footer.tsx | 124 + app/components/Navbar.tsx | 80 + app/components/ScrollVideo.tsx | 57 + app/components/SecurityBadge.tsx | 22 + app/components/SmoothScroll.tsx | 16 + app/components/SplitSection.tsx | 95 + app/components/TestimonialsSlider.tsx | 120 + app/contact/page.tsx | 30 + app/dining/page.tsx | 13 + app/gallery/page.tsx | 13 + app/globals.css | 89 +- app/layout.tsx | 39 +- app/organizations/page.tsx | 13 + app/page.tsx | 283 +- app/spa/page.tsx | 102 + docs/konaklama/odalar/aileodası/a1b (1).jpg | Bin 0 -> 70937 bytes docs/konaklama/odalar/aileodası/a1b.jpg | Bin 0 -> 70937 bytes docs/konaklama/odalar/aileodası/a2b.jpg | Bin 0 -> 74323 bytes docs/konaklama/odalar/aileodası/a3b.jpg | Bin 0 -> 81136 bytes docs/konaklama/odalar/aileodası/a4b.jpg | Bin 0 -> 75593 bytes docs/konaklama/odalar/aileodası/aileodası.md | 21 + docs/konaklama/odalar/juniorsuite/j1b.jpg | Bin 0 -> 100159 bytes docs/konaklama/odalar/juniorsuite/j2b.jpg | Bin 0 -> 100813 bytes docs/konaklama/odalar/juniorsuite/j3b.jpg | Bin 0 -> 110399 bytes docs/konaklama/odalar/juniorsuite/j4b.jpg | Bin 0 -> 121180 bytes docs/konaklama/odalar/juniorsuite/j5b.jpg | Bin 0 -> 90023 bytes docs/konaklama/odalar/juniorsuite/j6b.jpg | Bin 0 -> 71806 bytes .../odalar/juniorsuite/juniorsuite.md | 21 + docs/konaklama/odalar/kingsuite/k1b (1).jpg | Bin 0 -> 98120 bytes docs/konaklama/odalar/kingsuite/k1b.jpg | Bin 0 -> 98120 bytes docs/konaklama/odalar/kingsuite/k2b.jpg | Bin 0 -> 132243 bytes docs/konaklama/odalar/kingsuite/k3b.jpg | Bin 0 -> 97764 bytes docs/konaklama/odalar/kingsuite/k4b.jpg | Bin 0 -> 113790 bytes docs/konaklama/odalar/kingsuite/k5b.jpg | Bin 0 -> 138823 bytes docs/konaklama/odalar/kingsuite/k6b.jpg | Bin 0 -> 104569 bytes docs/konaklama/odalar/kingsuite/kingsuite.md | 21 + .../odalar/penthousesuite/1b (1).jpg | Bin 0 -> 97921 bytes docs/konaklama/odalar/penthousesuite/1b.jpg | Bin 0 -> 97921 bytes docs/konaklama/odalar/penthousesuite/2b.jpg | Bin 0 -> 75731 bytes docs/konaklama/odalar/penthousesuite/3b.jpg | Bin 0 -> 144413 bytes docs/konaklama/odalar/penthousesuite/4b.jpg | Bin 0 -> 107138 bytes docs/konaklama/odalar/penthousesuite/5b.jpg | Bin 0 -> 78833 bytes docs/konaklama/odalar/penthousesuite/6b.jpg | Bin 0 -> 107869 bytes .../odalar/penthousesuite/penthousesuite.md | 22 + docs/konaklama/odalar/standartoda/s1b (1).jpg | Bin 0 -> 87914 bytes docs/konaklama/odalar/standartoda/s1b.jpg | Bin 0 -> 87914 bytes docs/konaklama/odalar/standartoda/s2b.jpg | Bin 0 -> 91358 bytes docs/konaklama/odalar/standartoda/s3b.jpg | Bin 0 -> 78366 bytes docs/konaklama/odalar/standartoda/s4b.jpg | Bin 0 -> 118191 bytes .../odalar/standartoda/standartoda.md | 36 + docs/konaklama/rezidans/rezidans1+1/1+1.md | 18 + .../rezidans/rezidans1+1/r1b (1).jpg | Bin 0 -> 82236 bytes docs/konaklama/rezidans/rezidans1+1/r1b.jpg | Bin 0 -> 82236 bytes docs/konaklama/rezidans/rezidans1+1/r2b.jpg | Bin 0 -> 66533 bytes docs/konaklama/rezidans/rezidans1+1/r3b.jpg | Bin 0 -> 85949 bytes docs/konaklama/rezidans/rezidans1+1/r4b.jpg | Bin 0 -> 71129 bytes docs/konaklama/rezidans/rezidans1+1/r5b.jpg | Bin 0 -> 81240 bytes docs/konaklama/rezidans/rezidans1+1/r7b.jpg | Bin 0 -> 66786 bytes docs/konaklama/rezidans/rezidans2+1/2+1.md | 19 + docs/konaklama/rezidans/rezidans2+1/rr2b.jpg | Bin 0 -> 89047 bytes docs/konaklama/rezidans/rezidans2+1/rr3b.jpg | Bin 0 -> 93937 bytes docs/konaklama/rezidans/rezidans2+1/rr4b.jpg | Bin 0 -> 98760 bytes docs/konaklama/rezidans/rezidans2+1/rr5b.jpg | Bin 0 -> 113925 bytes docs/konaklama/rezidans/rezidans2+1/rr6b.jpg | Bin 0 -> 114403 bytes .../rezidans/rezidans2+1/rr7b (1).jpg | Bin 0 -> 108565 bytes docs/konaklama/rezidans/rezidans2+1/rr7b.jpg | Bin 0 -> 108565 bytes docs/prd.md | 61 + docs/restaurant/Pastane/1a.jpg | Bin 0 -> 56422 bytes docs/restaurant/Pastane/2a.jpg | Bin 0 -> 37507 bytes docs/restaurant/Pastane/3a.jpg | Bin 0 -> 46403 bytes docs/restaurant/Pastane/4a.jpg | Bin 0 -> 49569 bytes docs/restaurant/Pastane/Pastane.md | 0 docs/restaurant/anarestoran/,.jpg | Bin 0 -> 54837 bytes docs/restaurant/anarestoran/2.jpg | Bin 0 -> 71877 bytes docs/restaurant/anarestoran/3 (1).jpg | Bin 0 -> 53611 bytes docs/restaurant/anarestoran/3.jpg | Bin 0 -> 53611 bytes docs/restaurant/anarestoran/4.jpg | Bin 0 -> 56502 bytes docs/restaurant/anarestoran/anarestoran.md | 2 + docs/restaurant/poseidonalacarte/2p.jpg | Bin 0 -> 60300 bytes docs/restaurant/poseidonalacarte/3p.jpg | Bin 0 -> 44412 bytes docs/restaurant/poseidonalacarte/4p.jpg | Bin 0 -> 61320 bytes .../poseidonalacarte/poseidonalacarte.md | 2 + html/SALMAKİS BODRUM RESORT.html | 433 +++ html/SALMAKİS BODRUM RESORT_files/1.jpg | Bin 0 -> 54837 bytes html/SALMAKİS BODRUM RESORT_files/1a.jpg | Bin 0 -> 56422 bytes html/SALMAKİS BODRUM RESORT_files/1b.jpg | Bin 0 -> 46528 bytes html/SALMAKİS BODRUM RESORT_files/1d.jpg | Bin 0 -> 70136 bytes html/SALMAKİS BODRUM RESORT_files/1l.jpg | Bin 0 -> 62014 bytes html/SALMAKİS BODRUM RESORT_files/1p.jpg | Bin 0 -> 44804 bytes html/SALMAKİS BODRUM RESORT_files/2.jpg | Bin 0 -> 71877 bytes html/SALMAKİS BODRUM RESORT_files/2a.jpg | Bin 0 -> 37507 bytes html/SALMAKİS BODRUM RESORT_files/2b.jpg | Bin 0 -> 75755 bytes html/SALMAKİS BODRUM RESORT_files/2d.jpg | Bin 0 -> 70528 bytes html/SALMAKİS BODRUM RESORT_files/2l.jpg | Bin 0 -> 71936 bytes html/SALMAKİS BODRUM RESORT_files/2p.jpg | Bin 0 -> 60300 bytes html/SALMAKİS BODRUM RESORT_files/3.jpg | Bin 0 -> 53611 bytes html/SALMAKİS BODRUM RESORT_files/3a.jpg | Bin 0 -> 46403 bytes html/SALMAKİS BODRUM RESORT_files/3b.jpg | Bin 0 -> 69642 bytes html/SALMAKİS BODRUM RESORT_files/3d.jpg | Bin 0 -> 60973 bytes html/SALMAKİS BODRUM RESORT_files/3l.jpg | Bin 0 -> 52071 bytes html/SALMAKİS BODRUM RESORT_files/3p.jpg | Bin 0 -> 44412 bytes html/SALMAKİS BODRUM RESORT_files/4.jpg | Bin 0 -> 56502 bytes html/SALMAKİS BODRUM RESORT_files/4a.jpg | Bin 0 -> 49569 bytes html/SALMAKİS BODRUM RESORT_files/4d.jpg | Bin 0 -> 75427 bytes html/SALMAKİS BODRUM RESORT_files/4l.jpg | Bin 0 -> 46368 bytes html/SALMAKİS BODRUM RESORT_files/4p.jpg | Bin 0 -> 61320 bytes .../SmoothScroll.js.indir | 523 ++++ .../bootstrap-select.js.indir | 1592 +++++++++++ .../bootstrap-select.min.css | 6 + .../bootstrap.min.css | 10 + .../bootstrap.min.js.indir | 7 + .../common.js.indir | 223 ++ html/SALMAKİS BODRUM RESORT_files/css | 262 ++ .../font-awesome.min.css | 4 + .../font-lotusicon.css | 115 + .../isotope.pkgd.min.js.indir | 12 + .../jquery-1.11.0.min.js.indir | 4 + .../jquery-ui.min.css | 7 + .../jquery-ui.min.js.indir | 13 + .../jquery.appear.min.js.indir | 151 + .../jquery.countTo.js.indir | 5 + .../jquery.countdown.min.js.indir | 22 + .../jquery.magnific-popup.min.js.indir | 4 + .../jquery.parallax-1.1.3.js.indir | 69 + .../jquery.themepunch.revolution.min.js.indir | 9 + .../jquery.themepunch.tools.min.js.indir | 142 + html/SALMAKİS BODRUM RESORT_files/js | 644 +++++ .../logo-footer.png | Bin 0 -> 11089 bytes .../logo-header.png | Bin 0 -> 18869 bytes .../magnific-popup.css | 397 +++ .../owl.carousel.css | 231 ++ .../owl.carousel.js.indir | 1512 ++++++++++ .../scripts.js.indir | 992 +++++++ .../SALMAKİS BODRUM RESORT_files/settings.css | 2436 +++++++++++++++++ html/SALMAKİS BODRUM RESORT_files/style.css | 307 +++ .../tripadvisor.png | Bin 0 -> 30150 bytes .../util.js.indir | 356 +++ ...78df7c7c0f484497ecbca7046644da1771523124516 | 1 + next.config.ts | 16 +- package-lock.json | 107 + package.json | 4 + scratch/bulk_upload.js | 60 + scratch/resort_upload_report.json | 282 ++ scratch/scan_cloudinary.js | 38 + scratch/upload_report.json | 622 +++++ scratch/upload_resort_photos.js | 85 + src/data/resort.ts | 325 +++ src/lib/cloudinary.ts | 53 + 156 files changed, 13823 insertions(+), 84 deletions(-) create mode 100644 Dockerfile create mode 100644 app/accommodation/[slug]/page.tsx create mode 100644 app/accommodation/page.tsx create mode 100644 app/activities/page.tsx create mode 100644 app/beach/page.tsx create mode 100644 app/components/AccommodationCard.tsx create mode 100644 app/components/ExperienceCard.tsx create mode 100644 app/components/FloatingBookingBar.tsx create mode 100644 app/components/Footer.tsx create mode 100644 app/components/Navbar.tsx create mode 100644 app/components/ScrollVideo.tsx create mode 100644 app/components/SecurityBadge.tsx create mode 100644 app/components/SmoothScroll.tsx create mode 100644 app/components/SplitSection.tsx create mode 100644 app/components/TestimonialsSlider.tsx create mode 100644 app/contact/page.tsx create mode 100644 app/dining/page.tsx create mode 100644 app/gallery/page.tsx create mode 100644 app/organizations/page.tsx create mode 100644 app/spa/page.tsx create mode 100644 docs/konaklama/odalar/aileodası/a1b (1).jpg create mode 100644 docs/konaklama/odalar/aileodası/a1b.jpg create mode 100644 docs/konaklama/odalar/aileodası/a2b.jpg create mode 100644 docs/konaklama/odalar/aileodası/a3b.jpg create mode 100644 docs/konaklama/odalar/aileodası/a4b.jpg create mode 100644 docs/konaklama/odalar/aileodası/aileodası.md create mode 100644 docs/konaklama/odalar/juniorsuite/j1b.jpg create mode 100644 docs/konaklama/odalar/juniorsuite/j2b.jpg create mode 100644 docs/konaklama/odalar/juniorsuite/j3b.jpg create mode 100644 docs/konaklama/odalar/juniorsuite/j4b.jpg create mode 100644 docs/konaklama/odalar/juniorsuite/j5b.jpg create mode 100644 docs/konaklama/odalar/juniorsuite/j6b.jpg create mode 100644 docs/konaklama/odalar/juniorsuite/juniorsuite.md create mode 100644 docs/konaklama/odalar/kingsuite/k1b (1).jpg create mode 100644 docs/konaklama/odalar/kingsuite/k1b.jpg create mode 100644 docs/konaklama/odalar/kingsuite/k2b.jpg create mode 100644 docs/konaklama/odalar/kingsuite/k3b.jpg create mode 100644 docs/konaklama/odalar/kingsuite/k4b.jpg create mode 100644 docs/konaklama/odalar/kingsuite/k5b.jpg create mode 100644 docs/konaklama/odalar/kingsuite/k6b.jpg create mode 100644 docs/konaklama/odalar/kingsuite/kingsuite.md create mode 100644 docs/konaklama/odalar/penthousesuite/1b (1).jpg create mode 100644 docs/konaklama/odalar/penthousesuite/1b.jpg create mode 100644 docs/konaklama/odalar/penthousesuite/2b.jpg create mode 100644 docs/konaklama/odalar/penthousesuite/3b.jpg create mode 100644 docs/konaklama/odalar/penthousesuite/4b.jpg create mode 100644 docs/konaklama/odalar/penthousesuite/5b.jpg create mode 100644 docs/konaklama/odalar/penthousesuite/6b.jpg create mode 100644 docs/konaklama/odalar/penthousesuite/penthousesuite.md create mode 100644 docs/konaklama/odalar/standartoda/s1b (1).jpg create mode 100644 docs/konaklama/odalar/standartoda/s1b.jpg create mode 100644 docs/konaklama/odalar/standartoda/s2b.jpg create mode 100644 docs/konaklama/odalar/standartoda/s3b.jpg create mode 100644 docs/konaklama/odalar/standartoda/s4b.jpg create mode 100644 docs/konaklama/odalar/standartoda/standartoda.md create mode 100644 docs/konaklama/rezidans/rezidans1+1/1+1.md create mode 100644 docs/konaklama/rezidans/rezidans1+1/r1b (1).jpg create mode 100644 docs/konaklama/rezidans/rezidans1+1/r1b.jpg create mode 100644 docs/konaklama/rezidans/rezidans1+1/r2b.jpg create mode 100644 docs/konaklama/rezidans/rezidans1+1/r3b.jpg create mode 100644 docs/konaklama/rezidans/rezidans1+1/r4b.jpg create mode 100644 docs/konaklama/rezidans/rezidans1+1/r5b.jpg create mode 100644 docs/konaklama/rezidans/rezidans1+1/r7b.jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/2+1.md create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr2b.jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr3b.jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr4b.jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr5b.jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr6b.jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr7b (1).jpg create mode 100644 docs/konaklama/rezidans/rezidans2+1/rr7b.jpg create mode 100644 docs/prd.md create mode 100644 docs/restaurant/Pastane/1a.jpg create mode 100644 docs/restaurant/Pastane/2a.jpg create mode 100644 docs/restaurant/Pastane/3a.jpg create mode 100644 docs/restaurant/Pastane/4a.jpg create mode 100644 docs/restaurant/Pastane/Pastane.md create mode 100644 docs/restaurant/anarestoran/,.jpg create mode 100644 docs/restaurant/anarestoran/2.jpg create mode 100644 docs/restaurant/anarestoran/3 (1).jpg create mode 100644 docs/restaurant/anarestoran/3.jpg create mode 100644 docs/restaurant/anarestoran/4.jpg create mode 100644 docs/restaurant/anarestoran/anarestoran.md create mode 100644 docs/restaurant/poseidonalacarte/2p.jpg create mode 100644 docs/restaurant/poseidonalacarte/3p.jpg create mode 100644 docs/restaurant/poseidonalacarte/4p.jpg create mode 100644 docs/restaurant/poseidonalacarte/poseidonalacarte.md create mode 100644 html/SALMAKİS BODRUM RESORT.html create mode 100644 html/SALMAKİS BODRUM RESORT_files/1.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/1a.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/1b.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/1d.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/1l.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/1p.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/2.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/2a.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/2b.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/2d.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/2l.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/2p.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/3.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/3a.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/3b.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/3d.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/3l.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/3p.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/4.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/4a.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/4d.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/4l.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/4p.jpg create mode 100644 html/SALMAKİS BODRUM RESORT_files/SmoothScroll.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/bootstrap-select.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/bootstrap-select.min.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/bootstrap.min.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/bootstrap.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/common.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/css create mode 100644 html/SALMAKİS BODRUM RESORT_files/font-awesome.min.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/font-lotusicon.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/isotope.pkgd.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery-1.11.0.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery-ui.min.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery-ui.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.appear.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.countTo.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.countdown.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.magnific-popup.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.parallax-1.1.3.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.themepunch.revolution.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/jquery.themepunch.tools.min.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/js create mode 100644 html/SALMAKİS BODRUM RESORT_files/logo-footer.png create mode 100644 html/SALMAKİS BODRUM RESORT_files/logo-header.png create mode 100644 html/SALMAKİS BODRUM RESORT_files/magnific-popup.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/owl.carousel.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/owl.carousel.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/scripts.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/settings.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/style.css create mode 100644 html/SALMAKİS BODRUM RESORT_files/tripadvisor.png create mode 100644 html/SALMAKİS BODRUM RESORT_files/util.js.indir create mode 100644 html/SALMAKİS BODRUM RESORT_files/v8c78df7c7c0f484497ecbca7046644da1771523124516 create mode 100644 scratch/bulk_upload.js create mode 100644 scratch/resort_upload_report.json create mode 100644 scratch/scan_cloudinary.js create mode 100644 scratch/upload_report.json create mode 100644 scratch/upload_resort_photos.js create mode 100644 src/data/resort.ts create mode 100644 src/lib/cloudinary.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bc8cd98 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,61 @@ +# 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 based on the preferred package manager +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 +ARG NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME +ARG CLOUDINARY_API_KEY +ARG CLOUDINARY_API_SECRET + +ENV NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=$NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME +ENV CLOUDINARY_API_KEY=$CLOUDINARY_API_KEY +ENV CLOUDINARY_API_SECRET=$CLOUDINARY_API_SECRET +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 +# https://nextjs.org/docs/advanced-features/output-file-tracing +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 +# set hostname to localhost +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/app/accommodation/[slug]/page.tsx b/app/accommodation/[slug]/page.tsx new file mode 100644 index 0000000..b64de91 --- /dev/null +++ b/app/accommodation/[slug]/page.tsx @@ -0,0 +1,154 @@ +"use client"; + +import { motion } from "framer-motion"; +import { resortData } from "@/src/data/resort"; +import Image from "next/image"; +import Link from "next/link"; +import { notFound, useParams } from "next/navigation"; + +const getCloudinaryUrl = (publicId: string) => { + return `https://res.cloudinary.com/du7xohbct/image/upload/q_auto,f_auto,w_2000/${publicId}`; +}; + +export default function RoomDetailsPage() { + const params = useParams(); + const slug = params.slug as string; + const lang = "tr"; // Mocked locale + + const room = resortData.rooms.find((r) => r.slug === slug); + + if (!room) { + notFound(); + } + + return ( +
+ {/* Hero Header */} +
+ + {room.name[lang]} + + +
+ + {room.size} — {room.capacity} + + + {room.name[lang]} + +
+
+ + {/* Content Section */} +
+
+ + {/* Left: Description & Details */} +
+
+

ODA HAKKINDA

+

+ {room.description[lang]} +

+
+ +
+ {room.features.map((f) => { + const feat = resortData.featureIcons[f]; + return feat ? ( +
+
+ {feat.icon} +
+ + {feat.label[lang]} + +
+ ) : null; + })} +
+
+ + {/* Right: Sticky Sidebar / Call to Action */} +
+
+
+
+

KONAKLAMA TİPİ

+

{room.capacity}

+
+ +
+

ODA BÜYÜKLÜĞÜ

+

{room.size}

+
+ +
+ + ŞİMDİ REZERVASYON YAP + +

+ * En iyi fiyat garantisi ve resmi web sitesi avantajları ile yerinizi ayırtın. +

+
+
+
+
+ +
+
+ + {/* Gallery Highlight */} +
+
+
+ {room.galleryImageIds.slice(0, 3).map((id, i) => ( +
+ {`${room.name[lang]} +
+ ))} +
+ +
+ + + GERİ DÖN + + +
+
+
+
+ ); +} diff --git a/app/accommodation/page.tsx b/app/accommodation/page.tsx new file mode 100644 index 0000000..a29cdd4 --- /dev/null +++ b/app/accommodation/page.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { motion } from "framer-motion"; +import { resortData } from "@/src/data/resort"; +import { useState } from "react"; +import AccommodationCard from "../components/AccommodationCard"; + +export default function AccommodationPage() { + const lang = "tr"; + const [filter, setFilter] = useState<"all" | "rooms" | "residence">("all"); + + const filteredRooms = resortData.rooms.filter(room => { + if (filter === "all") return true; + if (filter === "rooms") return !room.slug.includes("rezidans"); + if (filter === "residence") return room.slug.includes("rezidans"); + return true; + }); + + return ( +
+
+ + {/* Header Section */} +
+
+ +
+ + KONAKLAMA DENEYİMİ + + + + + Zarafet &
Ege Serüveni +
+ + +

+ “Bardakçı Koyu'nun büyüleyici manzarasında, lüksün ve huzurun buluştuğu noktada sizi ağırlıyoruz.” +

+
+
+ +
+ {["all", "rooms", "residence"].map((id) => ( + + ))} +
+
+ + {/* Accommodation Grid - Single Column for Wide Mode */} +
+ {filteredRooms.map((room) => ( + + + + ))} +
+
+
+ ); +} diff --git a/app/activities/page.tsx b/app/activities/page.tsx new file mode 100644 index 0000000..d71e96a --- /dev/null +++ b/app/activities/page.tsx @@ -0,0 +1,13 @@ +export default function PlaceholderPage() { + return ( +
+
+

COMING SOON

+
+

+ Bu bölüm çok yakında yayına girecektir. +

+
+
+ ); +} diff --git a/app/beach/page.tsx b/app/beach/page.tsx new file mode 100644 index 0000000..4f9fd79 --- /dev/null +++ b/app/beach/page.tsx @@ -0,0 +1,13 @@ +export default function PlaceholderPage() { + return ( +
+
+

SOON

+

+ “Ege'nin turkuaz sularında eşsiz bir sahil deneyimi çok yakında.” +

+
+
+
+ ); +} diff --git a/app/components/AccommodationCard.tsx b/app/components/AccommodationCard.tsx new file mode 100644 index 0000000..71e568e --- /dev/null +++ b/app/components/AccommodationCard.tsx @@ -0,0 +1,86 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { resortData } from "@/src/data/resort"; +import { motion } from "framer-motion"; +import { useRef } from "react"; + +interface AccommodationCardProps { + room: any; + lang: "tr"; +} + +const getCloudinaryUrl = (publicId: string, width = 1200) => { + if (!publicId) return "https://images.unsplash.com/photo-1566073771259-6a8506099945?auto=format&fit=crop&q=80&w=1200"; + return `https://res.cloudinary.com/du7xohbct/image/upload/q_auto,f_auto,w_${width}/${publicId}`; +}; + +export default function AccommodationCard({ room, lang }: AccommodationCardProps) { + const allImages = [ + room.mainImageId, + ...(room.galleryImageIds || []) + ]; + + const scrollRef = useRef(null); + + return ( +
+ {/* Precision Header Section */} +
+ + {/* Left: Title & Descriptions */} +
+

+ {room.name[lang]} ({room.size}) +

+

+ {room.description[lang]} +

+
+ + {/* Right: Booking Button */} +
+ + Odayı İncele + +
+
+ + {/* DRAGGABLE SLIDER */} +
+ + {allImages.map((id, idx) => ( +
+ Gallery +
+ ))} +
+
+
+ ); +} diff --git a/app/components/ExperienceCard.tsx b/app/components/ExperienceCard.tsx new file mode 100644 index 0000000..7400e14 --- /dev/null +++ b/app/components/ExperienceCard.tsx @@ -0,0 +1,41 @@ +import { ExperienceSection } from "@/src/data/resort"; +import { getCloudinaryUrl } from "@/src/lib/cloudinary"; +import Link from "next/link"; +import Image from "next/image"; + +interface ExperienceCardProps { + experience: ExperienceSection; + lang: "tr" | "en" | "de"; +} + +export default function ExperienceCard({ experience, lang }: ExperienceCardProps) { + return ( + + {experience.title[lang]} + + {/* Dark Overlay */} +
+ + {/* Minimal Gold Border Hover */} +
+ + {lang === "tr" ? "KEŞFET" : lang === "en" ? "EXPLORE" : "ENTDECKEN"} + +
+ + {/* Content */} +
+

{experience.title[lang]}

+

{experience.subtitle[lang]}

+
+ + ); +} diff --git a/app/components/FloatingBookingBar.tsx b/app/components/FloatingBookingBar.tsx new file mode 100644 index 0000000..417f870 --- /dev/null +++ b/app/components/FloatingBookingBar.tsx @@ -0,0 +1,49 @@ +"use client"; + +import { resortData } from "@/src/data/resort"; +import Link from "next/link"; + +export default function FloatingBookingBar() { + // Normally would have state for dates, but following PRD's "minimal bar" + const lang = "tr"; // Mocked locale + + return ( +
+
+
+ {/* Check In */} +
+ + {resortData.floatingBar.checkIn[lang]} + + Select Date +
+ + {/* Check Out */} +
+ + {resortData.floatingBar.checkOut[lang]} + + Select Date +
+ + {/* Guests */} +
+ + {resortData.floatingBar.guests[lang]} + + 2 Adults +
+
+ + + {resortData.floatingBar.search[lang]} + +
+
+ ); +} diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx new file mode 100644 index 0000000..d933b9d --- /dev/null +++ b/app/components/Footer.tsx @@ -0,0 +1,124 @@ +"use client"; + +import Link from "next/link"; +import { resortData } from "@/src/data/resort"; + +export default function Footer() { + return ( +
+
+ {/* Main Footer Content */} +
+ + {/* Column 1: Brand & Newsletter */} +
+

SALMAKIS

+ +
+
+ + +
+ + +
+ +
+ {resortData.social.map((social) => ( + + + + ))} +
+
+ + {/* Column 2: Salmakis */} +
+

SALMAKIS

+
    + {["Doğada.", "Sofrada.", "Odalarda.", "Bizimle."].map((item) => ( +
  • + {item} +
  • + ))} +
+
+ + {/* Column 3: Sayfalar */} +
+

SAYFALAR

+
    + {["Konaklama", "S.S.S", "Sürdürülebilirlik", "Resim Galerisi", "Fiyat Listesi"].map((item) => ( +
  • + {item} +
  • + ))} +
+
+ + {/* Column 4: Yasal */} +
+

VERİ KORUMA

+
    + {["Künye", "Gizlilik Politikası", "KVKK", "Çerez Ayarları"].map((item) => ( +
  • + {item} +
  • + ))} +
+
+ + {/* Column 5: İletişim */} +
+

İLETİŞİM

+
    +
  • Oda Rezerve Et
  • +
  • Yol Tarifi Al
  • +
  • İletişime Geç
  • +
  • {resortData.contact.phone}
  • +
  • {resortData.contact.email}
  • +
+
+
+ + {/* Brand Logos / Partners */} +
+ {/* Placeholder Logos reflecting the style in the image */} +
BODRUM
+
EGE MUTFAĞI
+
S
+
BLUE FLAG
+
+ + {/* Bottom copyright area */} +
+
© 2026 — HER HAKKI SAKLIDIR.
+ + {/* Decorative Mountain Line (SVG) */} +
+ + + +
+ +
SALMAKIS RESORT & SPA
+
+
+
+ ); +} diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx new file mode 100644 index 0000000..312cb14 --- /dev/null +++ b/app/components/Navbar.tsx @@ -0,0 +1,80 @@ +"use client"; + +import Link from "next/link"; + +export default function Navbar() { + const lang = "tr"; // Mocked locale + + const navLinks = [ + { label: { tr: "KONAKLAMA", en: "ACCOMMODATION", de: "UNTERKUNFT" }, href: "/accommodation", side: "left" }, + { label: { tr: "YİYECEK & İÇECEK", en: "FOOD & BEVERAGE", de: "FOOD & BEVERAGE" }, href: "/dining", side: "left" }, + { label: { tr: "AKTİVİTE", en: "ACTIVITIES", de: "AKTIVITÄTEN" }, href: "/activities", side: "left" }, + { label: { tr: "ORGANİZASYON", en: "ORGANIZER", de: "ORGANISATION" }, href: "/organizations", side: "right" }, + { label: { tr: "GALERİ", en: "GALLERY", de: "GALERIE" }, href: "/gallery", side: "right" }, + { label: { tr: "SPA CENTER", en: "SPA CENTER", de: "SPA CENTER" }, href: "/spa", side: "right" }, + ]; + + return ( + + ); +} diff --git a/app/components/ScrollVideo.tsx b/app/components/ScrollVideo.tsx new file mode 100644 index 0000000..2440939 --- /dev/null +++ b/app/components/ScrollVideo.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useRef } from "react"; +import { motion, useScroll, useTransform, useSpring } from "framer-motion"; + +export default function ScrollVideo() { + const containerRef = useRef(null); + + // Use scroll progress within this section + const { scrollYProgress } = useScroll({ + target: containerRef, + offset: ["start start", "end end"] + }); + + // Smooth out the scroll value + const smoothProgress = useSpring(scrollYProgress, { + stiffness: 100, + damping: 30, + restDelta: 0.001 + }); + + // Calculate the expansion: Starts at 25% circle, grows to 150% (to fully cover screen) + const clipPathValue = useTransform(smoothProgress, [0, 1], ["circle(25% at 50% 50%)", "circle(100% at 50% 50%)"]); + + // Also fade the overlay + const overlayOpacity = useTransform(smoothProgress, [0, 0.8], [0.3, 0]); + + return ( +
+
+ + {/* YouTube Embed with Hardware Acceleration */} +
+ +
+ + {/* Subtle Overlay */} + + +
+
+ ); +} diff --git a/app/components/SecurityBadge.tsx b/app/components/SecurityBadge.tsx new file mode 100644 index 0000000..0666c7f --- /dev/null +++ b/app/components/SecurityBadge.tsx @@ -0,0 +1,22 @@ +import { resortData } from "@/src/data/resort"; + +export default function SecurityBadge({ lang }: { lang: "tr" | "en" | "de" }) { + return ( +
+
+ + + +
+

+ {resortData.securityBadge.title[lang]} +

+

+ {resortData.securityBadge.description[lang]} +

+
+ Official Website Protection • Verified Secure +
+
+ ); +} diff --git a/app/components/SmoothScroll.tsx b/app/components/SmoothScroll.tsx new file mode 100644 index 0000000..f3271a0 --- /dev/null +++ b/app/components/SmoothScroll.tsx @@ -0,0 +1,16 @@ +"use client"; + +import { ReactLenis } from "lenis/react"; +import { ReactNode } from "react"; + +export default function SmoothScroll({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/app/components/SplitSection.tsx b/app/components/SplitSection.tsx new file mode 100644 index 0000000..cf9fa81 --- /dev/null +++ b/app/components/SplitSection.tsx @@ -0,0 +1,95 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { getCloudinaryUrl } from "@/src/lib/cloudinary"; +import { motion } from "framer-motion"; + +interface SplitSectionProps { + title: string; + subtitle: string; + description: string; + mainImage: string; + secondImage: string; + href: string; + reverse?: boolean; +} + +export default function SplitSection({ + title, + subtitle, + description, + mainImage, + secondImage, + href, + reverse = false +}: SplitSectionProps) { + return ( +
+ + {/* Text Side */} + + + {subtitle} + +

+ {title} +

+

+ {description} +

+
+ + KEŞFET + + +
+
+ + {/* Image Side */} +
+ + {title} + + + {/* Floating Secondary Image with Parallax-ish Scroll Effect */} + +
+
+ ); +} diff --git a/app/components/TestimonialsSlider.tsx b/app/components/TestimonialsSlider.tsx new file mode 100644 index 0000000..da35065 --- /dev/null +++ b/app/components/TestimonialsSlider.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { motion } from "framer-motion"; +import { useRef, useState, useEffect } from "react"; + +const testimonials = [ + { + name: "Ayşe Y.", + location: "Türkiye", + text: "Bardakçı Koyu'nun büyüleyici manzarasında unutulmaz bir tatil geçirdik. Personelin ilgisi ve otelin zarafeti bizi mest etti.", + stars: 5 + }, + { + name: "John D.", + location: "United Kingdom", + text: "An absolutely stunning resort. The spa treatment was world-class, and the breakfast by the sea was the highlight of our stay.", + stars: 5 + }, + { + name: "Klaus M.", + location: "Germany", + text: "Sehr schönes Hotel mit exzellentem Service. Der Privatstrand ist kristallklar ve sehr ruhig. Wir kommen auf jeden Fall wieder!", + stars: 5 + }, + { + name: "Elena S.", + location: "Italy", + text: "Un posto magico. La colazione è fantastica e la camera con vista mare ha superato le nostre aspettative. Grazie Salmakis!", + stars: 5 + }, + { + name: "Mehmet A.", + location: "Türkiye", + text: "Gastronomi anlamında gerçekten çok başarılı. Her akşam farklı bir lezzet şöleni yaşadık. Sahili ise kelimenin tam anlamıyla kusursuz.", + stars: 5 + } +]; + +export default function TestimonialsSlider() { + const constraintsRef = useRef(null); + const [width, setWidth] = useState(0); + + useEffect(() => { + if (constraintsRef.current) { + // Calculate how much we can drag: total width - container width + setWidth(constraintsRef.current.scrollWidth - constraintsRef.current.offsetWidth); + } + }, []); + + return ( +
+
+
+
+ + MİSAFİR YORUMLARI + +

+ Misafirlerimiz
Ne Diyor? +

+
+ +
+

+ Gerçek deneyimler ve samimi sözler. Salmakis Resort & Spa'daki anıların + her türlü tanımdan daha fazlasını anlattığına inanıyoruz. +

+ +
+
+
+ + {/* Slider Container */} +
+ + {testimonials.map((item, i) => ( +
+
+
+ {[...Array(item.stars)].map((_, i) => ( + + ))} +
+
+ + {item.location} + +

+ “{item.text}” +

+
+
+ +
+ + {item.name} + +
+
+ ))} +
+
+
+ ); +} diff --git a/app/contact/page.tsx b/app/contact/page.tsx new file mode 100644 index 0000000..cb7146b --- /dev/null +++ b/app/contact/page.tsx @@ -0,0 +1,30 @@ +export default function Contact() { + return ( +
+
+
+ CONTACT +

+ Let's talk about your
next getaway. +

+
+

info@salmakis.com.tr

+

+90 252 316 65 06

+
+
+ +
+

Location

+

+ Bardakçı Koyu,
+ Bodrum, Muğla,
+ Türkiye +

+
+ [Interactive Map Placeholder] +
+
+
+
+ ); +} diff --git a/app/dining/page.tsx b/app/dining/page.tsx new file mode 100644 index 0000000..ab56a46 --- /dev/null +++ b/app/dining/page.tsx @@ -0,0 +1,13 @@ +export default function PlaceholderPage({ params }: { params: { slug: string } }) { + return ( +
+
+

SOON

+

+ “Ege'nin kalbindeki bu deneyimi çok yakında keşfedeceksiniz.” +

+
+
+
+ ); +} diff --git a/app/gallery/page.tsx b/app/gallery/page.tsx new file mode 100644 index 0000000..d71e96a --- /dev/null +++ b/app/gallery/page.tsx @@ -0,0 +1,13 @@ +export default function PlaceholderPage() { + return ( +
+
+

COMING SOON

+
+

+ Bu bölüm çok yakında yayına girecektir. +

+
+
+ ); +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..7afe756 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,81 @@ @import "tailwindcss"; -:root { - --background: #ffffff; - --foreground: #171717; +@theme { + --font-sans: var(--font-oswald), ui-sans-serif, system-ui; + --font-serif: var(--font-playfair), ui-serif, Georgia; + + --color-gold: #C5A059; + --color-gold-dark: #A67C00; + --color-bodrum-blue: #002B45; + --color-sand: #FAF9F6; + + --radius-xl: 1rem; + --radius-2xl: 1.5rem; } -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { +@layer base { :root { - --background: #0a0a0a; - --foreground: #ededed; + --background: #ffffff; + --foreground: #111111; + } + + body { + background-color: var(--background); + color: var(--foreground); + font-feature-settings: "ss01", "ss02", "cv01", "cv02"; } } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +@layer components { + .glass { + @apply bg-white/70 backdrop-blur-md border border-white/20; + } + + .glass-dark { + @apply bg-black/50 backdrop-blur-md border border-white/10; + } + + .btn-primary { + @apply px-6 py-3 bg-gold text-white rounded-full font-medium transition-all hover:bg-gold-dark hover:scale-105 active:scale-95 shadow-lg shadow-gold/10; + } + + .section-padding { + @apply py-20 px-6 md:px-12 lg:px-24; + } +} + +/* Custom Animations */ +@keyframes fade-in-up { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fade-in-up { + animation: fade-in-up 0.8s ease-out forwards; +} + +.delay-100 { animation-delay: 100ms; } +.delay-200 { animation-delay: 200ms; } +.delay-300 { animation-delay: 300ms; } + +/* Portrait Card Aspect Ratio */ +.aspect-portrait { + aspect-ratio: 3 / 4; +} + +/* Hide scrollbar for Chrome, Safari and Opera */ +.no-scrollbar::-webkit-scrollbar { + display: none; +} + +/* Hide scrollbar for IE, Edge and Firefox */ +.no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ } diff --git a/app/layout.tsx b/app/layout.tsx index 976eb90..729b47c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,25 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Oswald, Playfair_Display } from "next/font/google"; import "./globals.css"; +import Navbar from "./components/Navbar"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], +import Footer from "./components/Footer"; +import SmoothScroll from "./components/SmoothScroll"; + +const oswald = Oswald({ + variable: "--font-oswald", + subsets: ["latin", "latin-ext"], + weight: ["200", "300", "400", "500", "600", "700"], }); -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], +const playfair = Playfair_Display({ + variable: "--font-playfair", + subsets: ["latin", "latin-ext"], }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Salmakis Resort & Spa | Official Website", + description: "Experience the magic of the Aegean at Salmakis Resort & Spa Bodrum. Luxury accommodation, spa, and beach experience.", }; export default function RootLayout({ @@ -23,11 +28,17 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - {children} + + + + +
+ {children} +
+