diff --git a/app/[locale]/dairelerimiz/[slug]/page.tsx b/app/[locale]/dairelerimiz/[slug]/page.tsx
new file mode 100644
index 0000000..b71fd0c
--- /dev/null
+++ b/app/[locale]/dairelerimiz/[slug]/page.tsx
@@ -0,0 +1,181 @@
+import { getTranslations, setRequestLocale } from 'next-intl/server'
+import { Link } from '@/i18n/routing'
+import Image from 'next/image'
+import { notFound } from 'next/navigation'
+import { CheckCircle2, Maximize, Users, BedDouble, ArrowRight } from 'lucide-react'
+
+// Bu sayfa parametre olarak dinamik slug alacak
+export default async function RoomDetailPage({ params }: { params: Promise<{ locale: string, slug: string }> }) {
+ const { locale, slug } = await params
+ setRequestLocale(locale)
+
+ const tRooms = await getTranslations('rooms')
+
+ const MOCK_ROOMS = [
+ {
+ slug: '1-0-daire',
+ name: tRooms('room_list.1_0_daire.name'),
+ description: tRooms('room_list.1_0_daire.desc'),
+ capacity: 2,
+ size: '25m²',
+ beds: '1',
+ image: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80',
+ gallery: [
+ 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80',
+ 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?auto=format&fit=crop&q=80',
+ 'https://images.unsplash.com/photo-1582719508461-905c673771fd?auto=format&fit=crop&q=80'
+ ],
+ amenities: ['Mini Mutfak', 'Klima', 'Balkon', 'WiFi', 'TV', 'Saç Kurutma Makinesi']
+ },
+ {
+ slug: '1-1-daire-a',
+ name: tRooms('room_list.1_1_daire_a.name'),
+ description: tRooms('room_list.1_1_daire_a.desc'),
+ capacity: 2,
+ size: '40m²',
+ beds: '1 + Sofa',
+ image: 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80',
+ gallery: [
+ 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80',
+ 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?auto=format&fit=crop&q=80',
+ 'https://images.unsplash.com/photo-1582719508461-905c673771fd?auto=format&fit=crop&q=80'
+ ],
+ amenities: ['Ayrı Mutfak', 'Klima', 'Balkon', 'WiFi', 'Oturma Alanı', 'Çamaşır Makinesi']
+ },
+ {
+ slug: '1-1-daire-b',
+ name: tRooms('room_list.1_1_daire_b.name'),
+ description: tRooms('room_list.1_1_daire_b.desc'),
+ capacity: 4,
+ size: '50m²',
+ beds: '2',
+ image: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?auto=format&fit=crop&q=80',
+ gallery: [
+ 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80',
+ 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80',
+ 'https://images.unsplash.com/photo-1582719508461-905c673771fd?auto=format&fit=crop&q=80'
+ ],
+ amenities: ['2 Oda', 'Ayrı Mutfak', 'Klima', 'Balkon', 'WiFi', 'Geniş Oturma Alanı']
+ }
+ ];
+
+ const room = MOCK_ROOMS.find(r => r.slug === slug);
+
+ if (!room) {
+ notFound();
+ }
+
+ return (
+
+ {/* Hero Section */}
+
+
+ {/* Main Content */}
+
+
+
+ {/* Left Column - Details */}
+
+
+ {/* Badges */}
+
+
+
+ Maks {room.capacity} Kişi
+
+
+
+ {room.size}
+
+
+
+ {room.beds} Yatak
+
+
+
+ {/* Description */}
+
+
+ {room.description}
+
+
+
+ {/* Amenities */}
+
+
+ {tRooms('details.features')}
+
+
+ {room.amenities.map((amenity, index) => (
+
+
+ {amenity}
+
+ ))}
+
+
+
+ {/* Mini Gallery */}
+
+
+ {tRooms('details.gallery')}
+
+
+ {room.gallery.map((img, i) => (
+
+
+
+ ))}
+
+
+
+
+
+ {/* Right Column - Sticky Sidebar */}
+
+
+
+ Bilgi Alın
+
+
+ Tarihlerinize uygun fiyat ve müsaitlik durumunu öğrenmek için bizimle iletişime geçin.
+
+
+
+ {tRooms('details.book_now_cta')}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/[locale]/dairelerimiz/page.tsx b/app/[locale]/dairelerimiz/page.tsx
new file mode 100644
index 0000000..4956b64
--- /dev/null
+++ b/app/[locale]/dairelerimiz/page.tsx
@@ -0,0 +1,107 @@
+import { getTranslations, setRequestLocale } from 'next-intl/server'
+import { Link } from '@/i18n/routing'
+import Image from 'next/image'
+import { Users, Square, Info } from 'lucide-react'
+
+export default async function RoomsPage({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params
+ setRequestLocale(locale)
+
+ const tNav = await getTranslations('nav')
+ const tRooms = await getTranslations('rooms')
+
+ const MOCK_ROOMS = [
+ {
+ slug: '1-0-daire',
+ name: tRooms('room_list.1_0_daire.name'),
+ description: tRooms('room_list.1_0_daire.desc'),
+ capacity: 2,
+ size: '25m²',
+ image: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80',
+ amenities: ['Mini Mutfak', 'Klima', 'Balkon', 'WiFi']
+ },
+ {
+ slug: '1-1-daire-a',
+ name: tRooms('room_list.1_1_daire_a.name'),
+ description: tRooms('room_list.1_1_daire_a.desc'),
+ capacity: 2,
+ size: '40m²',
+ image: 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80',
+ amenities: ['Ayrı Mutfak', 'Klima', 'Balkon', 'WiFi', 'Oturma Alanı']
+ },
+ {
+ slug: '1-1-daire-b',
+ name: tRooms('room_list.1_1_daire_b.name'),
+ description: tRooms('room_list.1_1_daire_b.desc'),
+ capacity: 4,
+ size: '50m²',
+ image: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?auto=format&fit=crop&q=80',
+ amenities: ['2 Oda', 'Ayrı Mutfak', 'Klima', 'Balkon', 'WiFi']
+ }
+ ];
+
+ return (
+
+
+
+
{tRooms('title')}
+
+ {tRooms('description')}
+
+
+
+
+ {MOCK_ROOMS.map((room) => (
+
+
+
+
+
+
+
{room.name}
+
+
+
+ {room.description}
+
+
+
+
+
+ {room.capacity} {tRooms('capacity')}
+
+
+
+ {room.size}
+
+
+
+ {room.amenities.length} {tRooms('amenities')}
+
+
+
+
+
+ {tRooms('ask_price')} {tRooms('per_night')}
+
+
+ {tRooms('details_btn')}
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/app/[locale]/galeri/page.tsx b/app/[locale]/galeri/page.tsx
new file mode 100644
index 0000000..64486c8
--- /dev/null
+++ b/app/[locale]/galeri/page.tsx
@@ -0,0 +1,182 @@
+'use client'
+
+import { useState } from 'react'
+import { motion, AnimatePresence } from 'framer-motion'
+import Image from 'next/image'
+import BougainvilleaMotif from '@/components/BougainvilleaMotif'
+import { useTranslations } from 'next-intl'
+import { X } from 'lucide-react'
+
+export default function GalleryPage() {
+ const t = useTranslations('gallery')
+ const [activeFilter, setActiveFilter] = useState('all')
+ const [selectedImage, setSelectedImage] = useState(null)
+
+ const filters = [
+ { id: 'all', label: t('filters.all') },
+ { id: 'rooms', label: t('filters.rooms') },
+ { id: 'exterior', label: t('filters.exterior') },
+ { id: 'view', label: t('filters.view') },
+ { id: 'balcony', label: t('filters.balcony') },
+ ]
+
+ const galleryItems = [
+ {
+ id: 1,
+ category: 'rooms',
+ title: t('items.1'),
+ src: 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80',
+ colSpan: 'col-span-1',
+ },
+ {
+ id: 2,
+ category: 'exterior',
+ title: t('items.2'),
+ src: 'https://images.unsplash.com/photo-1596394516093-501ba68a0ba6?auto=format&fit=crop&q=80',
+ colSpan: 'col-span-1 md:col-span-2 lg:col-span-1',
+ },
+ {
+ id: 3,
+ category: 'view',
+ title: t('items.3'),
+ src: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?auto=format&fit=crop&q=80',
+ colSpan: 'col-span-1',
+ },
+ {
+ id: 4,
+ category: 'balcony',
+ title: t('items.4'),
+ src: 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80',
+ colSpan: 'col-span-1 md:col-span-2',
+ },
+ {
+ id: 5,
+ category: 'rooms',
+ title: t('items.5'),
+ src: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80',
+ colSpan: 'col-span-1',
+ },
+ {
+ id: 6,
+ category: 'view',
+ title: t('items.6'),
+ src: 'https://images.unsplash.com/photo-1582719508461-905c673771fd?auto=format&fit=crop&q=80',
+ colSpan: 'col-span-1 md:col-span-2 lg:col-span-1',
+ },
+ ]
+
+ const filteredItems = galleryItems.filter(item =>
+ activeFilter === 'all' || item.category === activeFilter
+ )
+
+ return (
+
+ {/* Header Section */}
+
+
+
+
+
+ {t('title')}
+
+
+ {t('description')}
+
+
+
+ {/* Filter Bar */}
+
+ {filters.map((filter) => (
+
+ ))}
+
+
+ {/* Masonry-like Grid */}
+
+
+ {filteredItems.map((item) => (
+ setSelectedImage(item.src)}
+ tabIndex={0}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ setSelectedImage(item.src);
+ }
+ }}
+ >
+
+
+
+
+ {item.title}
+
+
+
+
+ ))}
+
+
+
+ {/* Lightbox Overlay */}
+
+ {selectedImage && (
+ setSelectedImage(null)}
+ >
+
+ e.stopPropagation()}
+ >
+
+
+
+ )}
+
+
+ )
+}
diff --git a/app/[locale]/iletisim/page.tsx b/app/[locale]/iletisim/page.tsx
new file mode 100644
index 0000000..33eceee
--- /dev/null
+++ b/app/[locale]/iletisim/page.tsx
@@ -0,0 +1,196 @@
+'use client'
+
+import { motion } from 'framer-motion'
+import Image from 'next/image'
+import BougainvilleaMotif from '@/components/BougainvilleaMotif'
+import { MapPin, Waves, ShoppingBasket, Coffee, PersonStanding, Phone, Mail } from 'lucide-react'
+import { useTranslations } from 'next-intl'
+
+const EASE: [number, number, number, number] = [0.22, 1, 0.36, 1]
+
+export default function ContactPage() {
+ const t = useTranslations('contact')
+
+ return (
+
+ {/* Hero Section */}
+
+
+
+
+
+ {t('title')}
+
+
+ {t('description')}
+
+
+
+ {/* Location Grid */}
+
+
+
+ {/* Map Container */}
+
+
+
+ {t('connect.map_placeholder')}
+
+
+ {/* Mock Map Interaction Layer */}
+
+
+
+
+
+
{t('connect.address_title')}
+
{t('connect.address_value')}
+
+
+
+
+ {/* Highlights List */}
+
+
+
{t('walking_distance')}
+
+
+
+
+ {[
+ { icon: Waves, title: t('locations.beach.title'), desc: t('locations.beach.desc') },
+ { icon: ShoppingBasket, title: t('locations.market.title'), desc: t('locations.market.desc') },
+ { icon: Coffee, title: t('locations.cafe.title'), desc: t('locations.cafe.desc') },
+ { icon: PersonStanding, title: t('locations.hiking.title'), desc: t('locations.hiking.desc') },
+ ].map((item, idx) => (
+ -
+
+
+
{item.title}
+
{item.desc}
+
+
+ ))}
+
+
+
+
+
+
+ {/* Contact Section */}
+
+
+
+ {/* Contact Form Card */}
+
+ {t('form.title')}
+
+
+
+ {/* Contact Details & Visual */}
+
+
+
{t('connect.title')}
+
+ {t('connect.description')}
+
+
+
+
+
+
+
+
{t('connect.phone')}
+
+90 530 510 98 42
+
+
+
+
+
+
+
+
+
{t('connect.email')}
+
iletisim@sitarapart.com
+
+
+
+
+ {/* Decorative Image */}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx
index e64a5e9..a775aa5 100644
--- a/app/[locale]/layout.tsx
+++ b/app/[locale]/layout.tsx
@@ -1,24 +1,28 @@
import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
+import { Literata, Nunito_Sans } from "next/font/google";
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { routing } from '@/i18n/routing';
+import Header from '@/components/Header';
+import Footer from '@/components/Footer';
import "../globals.css";
-const geistSans = Geist({
- variable: "--font-geist-sans",
+const nunito_sans = Nunito_Sans({
+ variable: "--font-nunito-sans",
subsets: ["latin"],
+ weight: ["400", "600", "700"],
});
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
+const literata = Literata({
+ variable: "--font-literata",
subsets: ["latin"],
+ weight: ["400", "500", "600", "700"],
});
export const metadata: Metadata = {
- title: "Boilerplate App",
- description: "Next.js boilerplate with next-intl and NextAuth",
+ title: "Sitar Apart & Otel",
+ description: "Akyaka'nın Huzuru Sizinle!",
};
export function generateStaticParams() {
@@ -44,11 +48,15 @@ export default async function RootLayout({
return (
-
+
- {children}
+
+
+ {children}
+
+
diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx
index e86607f..ba40208 100644
--- a/app/[locale]/page.tsx
+++ b/app/[locale]/page.tsx
@@ -1,48 +1,195 @@
-import { getTranslations, setRequestLocale } from 'next-intl/server'
-import Link from 'next/link'
+'use client'
-export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) {
- const { locale } = await params
- setRequestLocale(locale)
-
- const t = await getTranslations('hero')
- const nav = await getTranslations('nav')
- const footer = await getTranslations('footer')
+import { useTranslations } from 'next-intl'
+import { Link } from '@/i18n/routing'
+import { motion, useReducedMotion } from 'framer-motion'
+import { MapPin, Wifi, UtensilsCrossed, Waves, Car } from 'lucide-react'
+import Image from 'next/image'
+import BougainvilleaMotif from '@/components/BougainvilleaMotif'
+
+const EASE: [number, number, number, number] = [0.22, 1, 0.36, 1]
+
+export default function HomePage() {
+ const tHero = useTranslations('hero')
+ const tFeatures = useTranslations('features')
+ const tRooms = useTranslations('home_rooms')
+ const prefersReduced = useReducedMotion()
+
+ const heroContainer = prefersReduced ? undefined : {
+ hidden: {},
+ show: { transition: { staggerChildren: 0.12 } }
+ }
+
+ const heroItem = prefersReduced ? undefined : {
+ hidden: { opacity: 0, y: 40, filter: 'blur(8px)' },
+ show: {
+ opacity: 1,
+ y: 0,
+ filter: 'blur(0px)',
+ transition: { duration: 0.8, ease: EASE }
+ }
+ }
return (
-
-
- Boilerplate
-
+
+ {/* Hero Section */}
+
+
+
+
+
+ {tHero('title')}
+
+
+
+ {tHero('description')}
+
+
+
+
+ {tHero('cta')}
+
+
+
+
+
-
-
- {t('title')}
-
-
- This is a dummy landing page to demonstrate the localization setup using next-intl.
-
-
-
-
-
- TR
- EN
-
+ {/* Amenities Strip */}
+
+
+ {[
+ { icon: Waves, label: tFeatures('pool') },
+ { icon: Wifi, label: tFeatures('wifi') },
+ { icon: UtensilsCrossed, label: tFeatures('kitchen') },
+ { icon: Car, label: tFeatures('parking') }
+ ].map((amenity, idx) => (
+
+
+ {amenity.label}
+
+ ))}
-
+
-
+ {/* Apartments Section */}
+
+
+
+
+
+
+ {tRooms('title')}
+
+
+ {tRooms('subtitle')}
+
+
+
+
+ {[
+ {
+ id: '1-0-daire',
+ title: tRooms('room_1.title'),
+ desc: tRooms('room_1.desc'),
+ img: 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80'
+ },
+ {
+ id: '1-1-daire-a',
+ title: tRooms('room_2.title'),
+ desc: tRooms('room_2.desc'),
+ img: 'https://images.unsplash.com/photo-1522708323590-d24dbb6b0267?auto=format&fit=crop&q=80'
+ },
+ {
+ id: '1-1-daire-b',
+ title: tRooms('room_3.title'),
+ desc: tRooms('room_3.desc'),
+ img: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?auto=format&fit=crop&q=80'
+ }
+ ].map((room, idx) => (
+
+
+
+
+
+
{room.title}
+
+ {room.desc}
+
+
+ Detayları İncele
→
+
+
+
+ ))}
+
+
+
+
+ Tüm Daireleri Gör
+
+
+
)
}
diff --git a/app/globals.css b/app/globals.css
index c56032b..28e2243 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -6,115 +6,142 @@
@theme inline {
--color-background: var(--background);
- --color-foreground: var(--foreground);
- --font-sans: var(--font-sans);
+ --color-foreground: var(--on-background);
+ --font-sans: var(--font-nunito-sans);
+ --font-serif: var(--font-literata);
--font-mono: var(--font-geist-mono);
- --font-heading: var(--font-sans);
- --color-sidebar-ring: var(--sidebar-ring);
- --color-sidebar-border: var(--sidebar-border);
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
- --color-sidebar-accent: var(--sidebar-accent);
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
- --color-sidebar-primary: var(--sidebar-primary);
- --color-sidebar-foreground: var(--sidebar-foreground);
- --color-sidebar: var(--sidebar);
- --color-chart-5: var(--chart-5);
- --color-chart-4: var(--chart-4);
- --color-chart-3: var(--chart-3);
- --color-chart-2: var(--chart-2);
- --color-chart-1: var(--chart-1);
- --color-ring: var(--ring);
- --color-input: var(--input);
- --color-border: var(--border);
- --color-destructive: var(--destructive);
- --color-accent-foreground: var(--accent-foreground);
- --color-accent: var(--accent);
- --color-muted-foreground: var(--muted-foreground);
- --color-muted: var(--muted);
- --color-secondary-foreground: var(--secondary-foreground);
- --color-secondary: var(--secondary);
- --color-primary-foreground: var(--primary-foreground);
+ --font-heading: var(--font-literata);
+
+ --color-surface: var(--surface);
+ --color-surface-dim: var(--surface-dim);
+ --color-surface-bright: var(--surface-bright);
+ --color-surface-container-lowest: var(--surface-container-lowest);
+ --color-surface-container-low: var(--surface-container-low);
+ --color-surface-container: var(--surface-container);
+ --color-surface-container-high: var(--surface-container-high);
+ --color-surface-container-highest: var(--surface-container-highest);
+ --color-on-surface: var(--on-surface);
+ --color-on-surface-variant: var(--on-surface-variant);
+ --color-inverse-surface: var(--inverse-surface);
+ --color-inverse-on-surface: var(--inverse-on-surface);
+
+ --color-outline: var(--outline);
+ --color-outline-variant: var(--outline-variant);
+
--color-primary: var(--primary);
- --color-popover-foreground: var(--popover-foreground);
- --color-popover: var(--popover);
- --color-card-foreground: var(--card-foreground);
- --color-card: var(--card);
- --radius-sm: calc(var(--radius) * 0.6);
- --radius-md: calc(var(--radius) * 0.8);
- --radius-lg: var(--radius);
- --radius-xl: calc(var(--radius) * 1.4);
- --radius-2xl: calc(var(--radius) * 1.8);
- --radius-3xl: calc(var(--radius) * 2.2);
- --radius-4xl: calc(var(--radius) * 2.6);
+ --color-on-primary: var(--on-primary);
+ --color-primary-container: var(--primary-container);
+ --color-on-primary-container: var(--on-primary-container);
+ --color-inverse-primary: var(--inverse-primary);
+
+ --color-secondary: var(--secondary);
+ --color-on-secondary: var(--on-secondary);
+ --color-secondary-container: var(--secondary-container);
+ --color-on-secondary-container: var(--on-secondary-container);
+
+ --color-tertiary: var(--tertiary);
+ --color-on-tertiary: var(--on-tertiary);
+ --color-tertiary-container: var(--tertiary-container);
+ --color-on-tertiary-container: var(--on-tertiary-container);
+
+ --color-error: var(--error);
+ --color-on-error: var(--on-error);
+ --color-error-container: var(--error-container);
+ --color-on-error-container: var(--on-error-container);
+
+ --color-accent-pink: var(--accent-pink);
+
+ /* shadcn mappings */
+ --color-border: var(--outline-variant);
+ --color-input: var(--outline-variant);
+ --color-ring: var(--primary);
+ --color-muted: var(--surface-container-highest);
+ --color-muted-foreground: var(--on-surface-variant);
+ --color-accent: var(--tertiary);
+ --color-accent-foreground: var(--on-tertiary);
+ --color-card: var(--surface);
+ --color-card-foreground: var(--on-surface);
+ --color-popover: var(--surface);
+ --color-popover-foreground: var(--on-surface);
+ --color-destructive: var(--error);
+ --color-destructive-foreground: var(--on-error);
+
+ --radius-sm: 0.25rem;
+ --radius-md: 0.5rem;
+ --radius-lg: 1rem;
+ --radius-xl: 1.5rem;
+ --radius-2xl: 2rem;
+ --radius-full: 9999px;
+
+ --spacing-unit: 8px;
+ --spacing-container-max: 1200px;
+ --spacing-gutter: 24px;
+ --spacing-margin-mobile: 20px;
+ --spacing-section-gap: 80px;
}
:root {
- --background: oklch(1 0 0);
- --foreground: oklch(0.145 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.145 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.145 0 0);
- --primary: oklch(0.205 0 0);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.97 0 0);
- --secondary-foreground: oklch(0.205 0 0);
- --muted: oklch(0.97 0 0);
- --muted-foreground: oklch(0.556 0 0);
- --accent: oklch(0.97 0 0);
- --accent-foreground: oklch(0.205 0 0);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.922 0 0);
- --input: oklch(0.922 0 0);
- --ring: oklch(0.708 0 0);
- --chart-1: oklch(0.87 0 0);
- --chart-2: oklch(0.556 0 0);
- --chart-3: oklch(0.439 0 0);
- --chart-4: oklch(0.371 0 0);
- --chart-5: oklch(0.269 0 0);
- --radius: 0.625rem;
- --sidebar: oklch(0.985 0 0);
- --sidebar-foreground: oklch(0.145 0 0);
- --sidebar-primary: oklch(0.205 0 0);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.97 0 0);
- --sidebar-accent-foreground: oklch(0.205 0 0);
- --sidebar-border: oklch(0.922 0 0);
- --sidebar-ring: oklch(0.708 0 0);
+ /* Aegean Coastal Sanctuary Palette */
+ --surface: #f6fafb;
+ --surface-dim: #d7dbdc;
+ --surface-bright: #f6fafb;
+ --surface-container-lowest: #ffffff;
+ --surface-container-low: #f1f4f5;
+ --surface-container: #ebeef0;
+ --surface-container-high: #e5e9ea;
+ --surface-container-highest: #dfe3e4;
+ --on-surface: #181c1d;
+ --on-surface-variant: #3e494b;
+ --inverse-surface: #2d3132;
+ --inverse-on-surface: #eef1f2;
+ --outline: #6e797c;
+ --outline-variant: #bdc8cb;
+ --surface-tint: #006877;
+ --primary: #006574;
+ --on-primary: #ffffff;
+ --primary-container: #088092;
+ --on-primary-container: #f8fdff;
+ --inverse-primary: #78d4e7;
+ --secondary: #705a4c;
+ --on-secondary: #ffffff;
+ --secondary-container: #f8dac8;
+ --on-secondary-container: #755e50;
+ --tertiary: #8a4c17;
+ --on-tertiary: #ffffff;
+ --tertiary-container: #a7642e;
+ --on-tertiary-container: #fffbff;
+ --error: #ba1a1a;
+ --on-error: #ffffff;
+ --error-container: #ffdad6;
+ --on-error-container: #93000a;
+ --background: #f6fafb;
+ --on-background: #181c1d;
+
+ --accent-pink: #d46a8c;
+
+ /* Fluid Typography from Design System */
+ --text-hero: clamp(3rem, 8vw, 8rem);
+ --text-h1: 48px;
+ --text-h2: 32px;
+ --text-h3: 24px;
+ --text-body-lg: 18px;
+ --text-body-md: 16px;
+ --text-label-lg: 14px;
}
+/* For simplicity, map dark mode to slightly adjusted values if needed, or leave as is if dark mode isn't explicitly defined in design.md */
.dark {
- --background: oklch(0.145 0 0);
- --foreground: oklch(0.985 0 0);
- --card: oklch(0.205 0 0);
- --card-foreground: oklch(0.985 0 0);
- --popover: oklch(0.205 0 0);
- --popover-foreground: oklch(0.985 0 0);
- --primary: oklch(0.922 0 0);
- --primary-foreground: oklch(0.205 0 0);
- --secondary: oklch(0.269 0 0);
- --secondary-foreground: oklch(0.985 0 0);
- --muted: oklch(0.269 0 0);
- --muted-foreground: oklch(0.708 0 0);
- --accent: oklch(0.269 0 0);
- --accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.556 0 0);
- --chart-1: oklch(0.87 0 0);
- --chart-2: oklch(0.556 0 0);
- --chart-3: oklch(0.439 0 0);
- --chart-4: oklch(0.371 0 0);
- --chart-5: oklch(0.269 0 0);
- --sidebar: oklch(0.205 0 0);
- --sidebar-foreground: oklch(0.985 0 0);
- --sidebar-primary: oklch(0.488 0.243 264.376);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.269 0 0);
- --sidebar-accent-foreground: oklch(0.985 0 0);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.556 0 0);
+ --background: #181c1d;
+ --on-background: #f6fafb;
+ --surface: #181c1d;
+ --on-surface: #e5e9ea;
+ --primary: #78d4e7;
+ --on-primary: #00363f;
+ --secondary: #dec1af;
+ --on-secondary: #3e2d22;
+ --tertiary: #ffb782;
+ --on-tertiary: #4c2700;
+ --outline-variant: #3e494b;
}
@layer base {
@@ -124,7 +151,7 @@
body {
@apply bg-background text-foreground;
}
- html {
- @apply font-sans;
- }
+ h1 { font-size: var(--text-h1); line-height: 1.2; font-weight: 600; }
+ h2 { font-size: var(--text-h2); line-height: 1.3; font-weight: 600; }
+ h3 { font-size: var(--text-h3); line-height: 1.4; font-weight: 500; }
}
\ No newline at end of file
diff --git a/components/BougainvilleaMotif.tsx b/components/BougainvilleaMotif.tsx
new file mode 100644
index 0000000..49f04ab
--- /dev/null
+++ b/components/BougainvilleaMotif.tsx
@@ -0,0 +1,14 @@
+export default function BougainvilleaMotif({ className = "" }: { className?: string }) {
+ return (
+
+ )
+}
diff --git a/components/Footer.tsx b/components/Footer.tsx
new file mode 100644
index 0000000..b7040d7
--- /dev/null
+++ b/components/Footer.tsx
@@ -0,0 +1,39 @@
+import { useTranslations } from 'next-intl';
+import { Link } from '@/i18n/routing';
+
+export default function Footer() {
+ const t = useTranslations('footer');
+
+ return (
+
+ );
+}
diff --git a/components/Header.tsx b/components/Header.tsx
new file mode 100644
index 0000000..4ec3466
--- /dev/null
+++ b/components/Header.tsx
@@ -0,0 +1,172 @@
+'use client'
+
+import { motion } from 'framer-motion'
+import { useState, useEffect } from 'react'
+import { Link, usePathname } from '@/i18n/routing'
+import { useTranslations } from 'next-intl'
+import LocaleSwitcher from './LocaleSwitcher'
+import { cn } from '@/lib/utils'
+import { Menu, X } from 'lucide-react'
+
+export default function Header() {
+ const [scrolled, setScrolled] = useState(false)
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
+ const t = useTranslations('nav')
+ const pathname = usePathname()
+
+ const isHome = pathname === '/'
+ // If we are not on the home page, the header should always look "scrolled"
+ // so the text is visible against the light background.
+ const isScrolledOrInner = scrolled || !isHome
+
+ useEffect(() => {
+ const handler = () => setScrolled(window.scrollY > 50)
+ window.addEventListener('scroll', handler, { passive: true })
+ // Initialize
+ handler()
+ return () => window.removeEventListener('scroll', handler)
+ }, [])
+
+ return (
+
+
+
+ {/* Logo */}
+
+
+ Sitar Apart
+
+
+
+ {/* Desktop Navigation */}
+
+
+ {/* Desktop Actions */}
+
+
+
+ {t('book_now')}
+
+
+
+ {/* Mobile Menu Button */}
+
+
+
+
+ {/* Mobile Menu */}
+ {mobileMenuOpen && (
+
+
+
+ )}
+
+ )
+}
diff --git a/components/LocaleSwitcher.tsx b/components/LocaleSwitcher.tsx
new file mode 100644
index 0000000..504208c
--- /dev/null
+++ b/components/LocaleSwitcher.tsx
@@ -0,0 +1,39 @@
+'use client'
+
+import { useLocale } from 'next-intl'
+import { useRouter, usePathname } from '@/i18n/routing'
+import { ChangeEvent } from 'react'
+import { cn } from '@/lib/utils'
+import { Globe } from 'lucide-react'
+
+export default function LocaleSwitcher({ scrolled }: { scrolled: boolean }) {
+ const locale = useLocale()
+ const router = useRouter()
+ const pathname = usePathname()
+
+ const onSelectChange = (e: ChangeEvent) => {
+ const nextLocale = e.target.value
+ router.replace({ pathname }, { locale: nextLocale as any })
+ }
+
+ return (
+
+
+
+
+ )
+}
diff --git a/docs/design.md b/docs/design.md
new file mode 100644
index 0000000..c6c3a4f
--- /dev/null
+++ b/docs/design.md
@@ -0,0 +1,69 @@
+# Design Brief — Sitar Apart & Otel
+
+`/design-demo` skill'ine doğrudan girdi olarak verilebilir.
+
+---
+
+📐 **Estetik Yön: Warm Coastal** (Warm/Organic + kıyı tonu karışımı — lüks otel değil, samimi butik apart)
+
+- **Neden:** Broşürdeki gerçek görseller (ahşap balkonlar, yeşillik, deniz-dağ manzarası, havlu üzerine serpiştirilmiş buğunvil çiçekleri) samimi, ev sıcaklığında bir apart havası veriyor. "Luxury/Refined" bu işletme için fazla iddialı — 1+0/1+1 self-catering daireler satıyoruz, 5 yıldızlı otel değil.
+- **Karakter:** Yuvarlatılmış köşeler, toprak + turkuaz tonları, bol doğal ışık hissi, gerçekçi/samimi fotoğraf ağırlıklı
+
+🎨 **Palet (OKLCH):**
+```css
+:root {
+ --background: oklch(0.97 0.012 80); /* kirli beyaz/krem */
+ --foreground: oklch(0.16 0.01 40); /* koyu ahşap-kahve */
+ --primary: oklch(0.62 0.09 200); /* turkuaz — broşür logosu/bantları */
+ --primary-foreground: oklch(0.98 0.01 80);
+ --secondary: oklch(0.40 0.05 40); /* koyu ahşap balkon tonu */
+ --accent: oklch(0.68 0.14 15); /* buğunvil pembe-mor — vurgu/CTA'da nokta atışı kullan */
+ --muted: oklch(0.92 0.01 80);
+ --muted-foreground: oklch(0.48 0.01 60);
+ --border: oklch(0.87 0.01 80);
+ --radius: 0.75rem; /* organik, yuvarlak */
+}
+.dark {
+ --background: oklch(0.14 0.01 40);
+ --foreground: oklch(0.95 0.01 80);
+ --primary: oklch(0.68 0.10 200);
+ --border: oklch(0.24 0.01 40);
+}
+```
+
+🔤 **Font:**
+- Başlık: **Lora** (700) — sıcak serif, otel siteleri için klişe olmayan seçim
+- Body: **Nunito** (400/600) — yuvarlak, samimi sans
+
+**Tipografi tonu:** Büyük başlıklarda serif ağırlık, gövde metinde yumuşak sans. Aşırı ince/keskin geometrik font yok — sıcaklık hissi korunmalı.
+
+---
+
+## Unforgettable Element (Farklılaştırıcı Detay)
+
+**Buğunvil çiçeği motifi** — broşürdeki gerçek fotoğraflarda havlu/yatak üzerine serpiştirilmiş çiçekler işletmenin kendine has, samimi bir dokunuşu. Bunu jenerik "gradient blob" yerine tekrar eden görsel imza olarak kullan:
+- Hero'da başlık etrafında ince, elle çizilmiş çiçek/dal SVG detayı (Magic UI değil, custom)
+- Section divider'larda minimal çiçek ikonu
+- Daire kartlarında hover'da hafif yaprak/çiçek parçacık animasyonu (abartısız, `prefers-reduced-motion` ile kapanır)
+
+Bu, "jenerik AI otel sitesi" hissinden kaçınmak için PRD'deki gerçek marka detayına dayanıyor — uydurma değil.
+
+---
+
+## Bölüm Bazlı Yönlendirme
+
+- **Hero:** Broşürdeki balkon/manzara fotoğrafı (veya benzeri) full-bleed, hafif koyu overlay, ortalanmış serif başlık + "Akyaka'nın Huzuru Sizinle" alt başlık + rezervasyon/iletişim CTA. Transparent navbar, scroll'da frosted glass.
+- **Daire kartları:** 3/2 oranlı büyük görsel, shadcn Carousel, `whileHover scale-[1.02]`, minimal metin (ad + kapasite + "Detaylar" linki)
+- **Olanaklar (Amenity) bandı:** Ana sayfada ikon + kısa etiket şeklinde yatay şerit (Havuz, Wifi, Özel Mutfak, Balkon)
+- **Konum/Galeri:** Editorial düzen, bol whitespace, masonry veya kategori tab'lı galeri
+- **Footer:** Koyu ahşap-kahve arka plan (`--secondary` bazlı), serif logo, adres + telefon + WhatsApp butonu
+
+## Anti-Klişe Kontrol
+- `bg-gradient-to-r from-blue-500 to-purple-600` yok — palet OKLCH sabitlerinden
+- Sistem fontu yok — `next/font` ile Lora + Nunito
+- Her section `py-20 text-center` tekrarı yok — ritim değişken
+- Stok "genel otel" görseli yerine broşürdeki gerçek fotoğraflar önceliklendirilir
+
+---
+
+**Sonraki adım:** Bu brief + `docs/prd.md` ile `/design-demo` çalıştırılabilir.
\ No newline at end of file
diff --git a/docs/prd.md b/docs/prd.md
new file mode 100644
index 0000000..d26cd0a
--- /dev/null
+++ b/docs/prd.md
@@ -0,0 +1,187 @@
+# PRD — Sitar Apart & Otel Web Sitesi
+
+## 1. Proje Özeti
+
+**İşletme:** Sitar Apart & Otel
+**Konum:** Akyaka, İnişdibi Cd. No:46, Ula/Muğla
+**Telefon:** 0 530 510 98 42
+**Mevcut domain:** www.akyakastarapart.com
+**Slogan:** "Akyaka'nın Huzuru Sizinle!"
+
+Akyaka'da, denize 10 dakika yürüme mesafesinde, merkezi konumda (market, pazaryeri, kafelere yakın) faaliyet gösteren self-catering apart otel. Kaynak içerik: işletmenin mevcut tanıtım broşüründen (sitar apart.pdf) çıkarıldı.
+
+**Site tipi:** Otel / Apart Otel (self-catering)
+
+## 2. Marka Bilgileri
+
+- **Logo:** Ev/dalga siluetli çizgi logo, turkuaz (#2FB6C4 civarı) renkte, "SİTAR APART & OTEL" yazısı
+- **Ana renk:** Turkuaz / camgöbeği (broşürde üst-alt bantlarda kullanılan turkuaz)
+- **Yardımcı renkler:** Kirli beyaz / krem arka plan, koyu kahve (ahşap balkon detayları)
+- **Ton:** Sıcak, sade, huzurlu — butik apart otel havası (5 yıldızlı lüks otel değil)
+
+## 3. Site Haritası (Sayfa Envanteri)
+
+| Route | Açıklama |
+|---|---|
+| `/` | Ana Sayfa — hero, kısa tanıtım, olanaklar özeti, öne çıkan daireler, CTA (rezervasyon/iletişim) |
+| `/dairelerimiz` | Tüm daire tiplerinin listesi (1+0, 1+1 x2 varyant) |
+| `/dairelerimiz/[slug]` | Daire detay sayfası — galeri, açıklama, olanaklar, kapasite, rezervasyon formu |
+| `/galeri` | Fotoğraf galerisi (kategorili: daireler, dış cephe, manzara, balkon) |
+| `/konum` | Akyaka & ulaşım bilgisi, harita gömme, çevredeki market/pazaryeri/kafeler |
+| `/iletisim` | İletişim formu, adres, telefon, WhatsApp linki, harita |
+| `/admin/*` | Yönetim paneli (bkz. Bölüm 8) |
+
+Not: Bu işletme restoran/spa gibi ek hizmetler sunmuyor (self-catering apart), bu yüzden "Hizmetlerimiz" sayfası yerine olanaklar (amenities) ana sayfada ve daire detayında gösterilir.
+
+## 4. Dil Seçenekleri
+
+`tr`, `en`, `de` (Akyaka bölgesi yoğun Alman turist trafiği alıyor, DE desteği önemli)
+
+→ next-intl locales: `['tr', 'en', 'de']`
+→ `messages/tr.json`, `en.json`, `de.json`
+
+## 5. Servisler / Olanaklar (Amenity)
+
+Broşürden çıkarılan olanaklar:
+
+- Yüzme Havuzu (Pool)
+- Wifi
+- Özel Mutfak Alanı (Private Kitchen Area)
+- Balkon (fotoğraflarda görünüyor, tüm dairelerde var)
+- Klima (Akyaka yaz sıcaklarında standart, eklenmesi önerilir — teyit gerekir)
+
+→ Prisma: `model Amenity { slug, labelTr, labelEn, labelDe, icon }`
+→ Room modeli üzerinde `amenities String[]` veya `RoomAmenity` join tablosu
+
+## 6. Daire Tipleri (Room)
+
+Broşürden 3 daire konfigürasyonu çıkarıldı:
+
+| Slug | Ad (TR) | Açıklama | Kapasite (tahmini) |
+|---|---|---|---|
+| `1-0-daire` | 1+0 Dairemiz | Stüdyo tip, tek yatak, mini mutfak, banyo | 1-2 kişi |
+| `1-1-daire-a` | 1+1 Dairemiz | Yatak odası + ayrı mutfak köşesi, balkon | 2 kişi |
+| `1-1-daire-b` | 1+1 Dairemiz (2 Oda) | 2 kişilik ve 1 kişilik olmak üzere 2 oda, balkon | 3-4 kişi |
+
+Ortak özellikler (broşürden): Denize 10 dk yürüme mesafesi, merkezi konum, market/pazaryeri/kafelere yakınlık.
+
+→ Prisma:
+```prisma
+enum RoomType {
+ STUDIO_1_0
+ SUITE_1_1
+}
+
+model Room {
+ id String @id @default(cuid())
+ slug String @unique
+ type RoomType
+ nameTr String
+ nameEn String
+ nameDe String
+ descriptionTr String
+ descriptionEn String
+ descriptionDe String
+ capacity Int
+ price Int? // gecelik fiyat (varsa; broşürde fiyat yok, admin'den girilecek)
+ imageUrl String?
+ images String[]
+ amenities String[]
+ available Boolean @default(true)
+ featured Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+}
+```
+
+**Not:** Broşürde gecelik fiyat bilgisi yok. İşletme sahibinden fiyat teyidi alınmalı; alınana kadar mock data'da placeholder fiyat kullanılacak ve "Fiyat için iletişime geçin" ibaresi eklenmesi önerilir.
+
+## 7. Rezervasyon Formu Alanları
+
+Bu bir self-catering apart, online ödeme/anlık onay yok — **rezervasyon talebi** (inquiry) formu olarak kurgulanmalı, otomatik onay değil.
+
+Alanlar: `checkIn`, `checkOut`, `adults`, `children`, `roomId` (daire tipi seçimi), `fullName`, `email`, `phone`, `message`
+
+→ Prisma:
+```prisma
+enum ReservationStatus {
+ PENDING
+ CONFIRMED
+ CANCELLED
+}
+
+model Reservation {
+ id String @id @default(cuid())
+ roomId String
+ room Room @relation(fields: [roomId], references: [id])
+ checkIn DateTime
+ checkOut DateTime
+ adults Int
+ children Int @default(0)
+ fullName String
+ email String
+ phone String
+ message String?
+ status ReservationStatus @default(PENDING)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+}
+```
+→ API: `POST /api/reservations` (public), `GET /api/admin/reservations` (liste)
+→ Zod: `ReservationSchema`
+
+## 8. İletişim Formu Alanları
+
+`fullName`, `email`, `phone`, `message`
+
+→ Prisma: `model ContactMessage { fullName, email, phone, message, read Boolean @default(false), ... }`
+→ API: `POST /api/contact` (public), `GET /api/admin/messages`
+
+## 9. Admin / Yönetim İhtiyaçları
+
+Standart admin paneli (her projede sabit) + bu proje için:
+
+- `/admin/rooms` — daire tipleri CRUD (ad, açıklama TR/EN/DE, kapasite, fiyat, olanaklar, görseller)
+- `/admin/reservations` — rezervasyon talepleri listesi, durum güncelleme (PENDING/CONFIRMED/CANCELLED)
+- `/admin/gallery` — galeri görselleri CRUD, kategori bazlı
+- `/admin/messages` — iletişim mesajları listesi, okundu işaretleme
+- `/admin/amenities` — olanaklar listesi (opsiyonel, sabit liste de olabilir)
+- Dashboard: toplam daire sayısı, bekleyen rezervasyon sayısı, okunmamış mesaj sayısı
+
+## 10. Sosyal Medya / 3. Parti Entegrasyonlar
+
+- **WhatsApp click-to-chat:** `0 530 510 98 42` numarasına yönlenen buton (header + iletişim sayfası)
+- **Google Maps embed:** Akyaka, İnişdibi Cd. No:46, Ula/Muğla konumu
+- Instagram/Facebook hesabı broşürde belirtilmemiş — işletme sahibinden teyit alınmalı, varsa footer'a eklenir
+
+→ Env: `NEXT_PUBLIC_WHATSAPP_NUMBER`, `NEXT_PUBLIC_MAPS_EMBED_URL`
+
+## 11. Teknik Notlar
+
+- Mevcut domain `www.akyakastarapart.com` — yeni site bu domain üzerine yayınlanacak (Coolify deploy sürecinde teyit edilmeli)
+- Broşürdeki fotoğraflar (3 sayfa, düşük-orta çözünürlük) başlangıç mock görseli olarak kullanılabilir; ancak production için işletmeden yüksek çözünürlüklü orijinal fotoğraf talep edilmesi önerilir
+- Fiyat bilgisi eksik — admin panelinden sonradan girilecek şekilde `price` alanı nullable
+- Tek işletme, çoklu şube yok — `Location`/`Branch` modeline gerek yok
+
+## 12. Rebranding / Öncelik Sırası (Todo)
+
+1. Ana Sayfa + Daireler listesi + Daire detay (temel akış)
+2. Rezervasyon talep formu + admin onay ekranı
+3. Galeri sayfası
+4. İletişim sayfası + WhatsApp/Harita entegrasyonu
+5. Admin panel (rooms, reservations, gallery, messages)
+6. TR/EN/DE içerik doldurma
+7. Openinary görsel yükleme entegrasyonu
+8. Coolify deploy (bkz. `/coolify-deploy` skill)
+
+---
+
+## PRD'den Çıkarılanlar (Özet)
+
+- **Modeller:** Room, Reservation, Amenity, Gallery, ContactMessage
+- **Lokaller:** tr, en, de
+- **Admin sayfaları:** rooms, reservations, gallery, messages, amenities
+- **Özel route'lar:** `/api/reservations` (public POST), `/api/contact` (public POST)
+- **Eksik / teyit gereken bilgiler:** gecelik fiyatlar, sosyal medya hesapları, klima olup olmadığı, yüksek çözünürlüklü görseller
\ No newline at end of file
diff --git a/docs/stitch_akyaka_golden_hour_retreat/aegean_coastal_sanctuary/DESIGN.md b/docs/stitch_akyaka_golden_hour_retreat/aegean_coastal_sanctuary/DESIGN.md
new file mode 100644
index 0000000..f65d6a4
--- /dev/null
+++ b/docs/stitch_akyaka_golden_hour_retreat/aegean_coastal_sanctuary/DESIGN.md
@@ -0,0 +1,175 @@
+---
+name: Aegean Coastal Sanctuary
+colors:
+ surface: '#f6fafb'
+ surface-dim: '#d7dbdc'
+ surface-bright: '#f6fafb'
+ surface-container-lowest: '#ffffff'
+ surface-container-low: '#f1f4f5'
+ surface-container: '#ebeef0'
+ surface-container-high: '#e5e9ea'
+ surface-container-highest: '#dfe3e4'
+ on-surface: '#181c1d'
+ on-surface-variant: '#3e494b'
+ inverse-surface: '#2d3132'
+ inverse-on-surface: '#eef1f2'
+ outline: '#6e797c'
+ outline-variant: '#bdc8cb'
+ surface-tint: '#006877'
+ primary: '#006574'
+ on-primary: '#ffffff'
+ primary-container: '#088092'
+ on-primary-container: '#f8fdff'
+ inverse-primary: '#78d4e7'
+ secondary: '#705a4c'
+ on-secondary: '#ffffff'
+ secondary-container: '#f8dac8'
+ on-secondary-container: '#755e50'
+ tertiary: '#8a4c17'
+ on-tertiary: '#ffffff'
+ tertiary-container: '#a7642e'
+ on-tertiary-container: '#fffbff'
+ error: '#ba1a1a'
+ on-error: '#ffffff'
+ error-container: '#ffdad6'
+ on-error-container: '#93000a'
+ primary-fixed: '#a3eeff'
+ primary-fixed-dim: '#78d4e7'
+ on-primary-fixed: '#001f25'
+ on-primary-fixed-variant: '#004e5a'
+ secondary-fixed: '#fbddca'
+ secondary-fixed-dim: '#dec1af'
+ on-secondary-fixed: '#28180d'
+ on-secondary-fixed-variant: '#574335'
+ tertiary-fixed: '#ffdcc5'
+ tertiary-fixed-dim: '#ffb782'
+ on-tertiary-fixed: '#301400'
+ on-tertiary-fixed-variant: '#703802'
+ background: '#f6fafb'
+ on-background: '#181c1d'
+ surface-variant: '#dfe3e4'
+typography:
+ headline-xl:
+ fontFamily: Literata
+ fontSize: 48px
+ fontWeight: '600'
+ lineHeight: '1.2'
+ headline-lg:
+ fontFamily: Literata
+ fontSize: 32px
+ fontWeight: '600'
+ lineHeight: '1.3'
+ headline-md:
+ fontFamily: Literata
+ fontSize: 24px
+ fontWeight: '500'
+ lineHeight: '1.4'
+ body-lg:
+ fontFamily: Nunito Sans
+ fontSize: 18px
+ fontWeight: '400'
+ lineHeight: '1.6'
+ body-md:
+ fontFamily: Nunito Sans
+ fontSize: 16px
+ fontWeight: '400'
+ lineHeight: '1.6'
+ label-lg:
+ fontFamily: Nunito Sans
+ fontSize: 14px
+ fontWeight: '700'
+ lineHeight: '1.2'
+ letterSpacing: 0.05em
+ headline-lg-mobile:
+ fontFamily: Literata
+ fontSize: 28px
+ fontWeight: '600'
+ lineHeight: '1.3'
+rounded:
+ sm: 0.25rem
+ DEFAULT: 0.5rem
+ md: 0.75rem
+ lg: 1rem
+ xl: 1.5rem
+ full: 9999px
+spacing:
+ unit: 8px
+ container-max: 1200px
+ gutter: 24px
+ margin-mobile: 20px
+ section-gap: 80px
+---
+
+## Brand & Style
+
+This design system embodies the "Warm Coastal" aesthetic, specifically tailored for the slow-living atmosphere of Akyaka. It departs from sterile corporate luxury in favor of an **Authentic & Boutique** personality. The visual language is tactile and inviting, evoking the feeling of sun-drenched stone walls, turquoise waters, and the iconic bougainvillea of the Turkish coast.
+
+The style leverages **soft minimalism** paired with **organic accents**. Large, evocative photography of the Aegean landscape is grounded by generous whitespace and soft, rounded geometry. A signature line-art motif of a bougainvillea branch is used sparingly to anchor section headers, adding a human, illustrative touch to the digital interface.
+
+## Colors
+
+The palette is derived directly from the Akyaka landscape:
+- **Primary (Turquoise):** Represents the Azmak River and the Aegean sea. Used for primary branding and navigational elements.
+- **Secondary (Dark Wood):** Inspired by the traditional Ula-style architecture and woodwork. Used for deep contrast, footers, and high-priority text.
+- **Accent (Bougainvillea Pink):** Reserved specifically for call-to-action buttons and highlights to ensure they pop against the neutral base.
+- **Background (Cream):** An off-white, paper-like base that feels warmer and more organic than pure white.
+
+## Typography
+
+The typographic hierarchy balances a literary, authoritative serif with a friendly, accessible sans-serif.
+
+- **Headlines:** Use **Literata** (or Lora) to convey a sense of history and boutique quality. Larger headlines should use tighter tracking, while smaller subheads benefit from more breathing room.
+- **Body & UI:** Use **Nunito Sans**. Its rounded terminals mirror the "soft" shape language of the UI and ensure high readability for room descriptions and booking details.
+- **Accents:** Use all-caps labels with slight letter spacing for categories or "small print" to maintain a clean, organized appearance.
+
+## Layout & Spacing
+
+This design system uses a **Fluid-Fixed hybrid grid**. On desktop, content is contained within a 1200px max-width container to maintain focus, while background elements can bleed to the edges.
+
+- **Rhythm:** A base 8px unit dictates all spacing.
+- **Sectioning:** Large gaps (80px-120px) are used between major content blocks to evoke a sense of calm and "unhurried" browsing.
+- **Mobile:** Margins shrink to 20px, and vertical spacing is compressed slightly to 64px between sections to maintain momentum.
+
+## Elevation & Depth
+
+Visual hierarchy is established through **Tonal Layers** and **Ambient Shadows**.
+
+- **Surfaces:** Cards and floating elements sit on the Cream background with extremely soft, diffused shadows (Blur: 32px, Opacity: 4%, Color: Secondary Brown).
+- **Glassmorphism:** The primary navigation bar uses a frosted glass effect (Backdrop Blur: 12px) with a semi-transparent cream tint (85% opacity). This allows the colors of the hero imagery to bleed through subtly.
+- **Depth Levels:**
+ - Level 0: Background (#F7F4EF).
+ - Level 1: Flat cards with a 1px #E5E0D5 border.
+ - Level 2: Raised elements (interactive cards) with the ambient shadow.
+
+## Shapes
+
+The shape language is consistently "Soft-Organic."
+
+- **Corner Radii:** Standard components (buttons, input fields) use 12px. Larger components (cards, image containers) use 24px.
+- **Images:** Always utilize the `rounded-xl` (24px) radius to soften the edges of the photography, making the imagery feel more like "windows" into the hotel.
+- **Icons:** Use rounded icon sets (e.g., Feather or Lucide) with a 2px stroke weight to match the body typography weight.
+
+## Components
+
+### Navbar
+The navbar is a fixed-top element with a frosted glass background. The logo (Sitar) is placed on the left, with primary navigation links in the center (Secondary Brown) and a "Book Now" CTA in the Accent Pink on the right.
+
+### Buttons
+- **Primary:** Filled Accent Pink (#D46A8C) with Cream text. 24px rounded corners. Minimum height 48px.
+- **Secondary:** Outlined Primary Teal (#3A9CAE) with 1.5px border.
+- **Text:** Underlined Serif (Literata) for a sophisticated "Read More" feel.
+
+### Cards
+Room and amenity cards use a vertical stack:
+1. **Image:** 3:2 aspect ratio with a 24px corner radius.
+2. **Content:** Cream background, 12px padding.
+3. **Typography:** Headline-MD for titles, Body-MD for descriptions.
+
+### Input Fields
+Soft cream background (#F2EEE5) with no border until focused. On focus, a 1.5px Primary Teal border appears. Label text sits above the field in Nunito Sans Bold (12px).
+
+### Footer
+A high-contrast block using the Dark Wood Brown (#3D2B1F). Text is rendered in Cream (#F7F4EF) at 80% opacity for secondary info, and 100% for links and titles.
+
+### Motifs
+Small SVG line-art bougainvillea branches should be placed at a -5 degree angle above H2 section headers, slightly overlapping the text to create a hand-crafted, scrapbook feel.
\ No newline at end of file
diff --git a/docs/stitch_akyaka_golden_hour_retreat/apartment_details_sitar_apart_otel/code.html b/docs/stitch_akyaka_golden_hour_retreat/apartment_details_sitar_apart_otel/code.html
new file mode 100644
index 0000000..d1b0c5f
--- /dev/null
+++ b/docs/stitch_akyaka_golden_hour_retreat/apartment_details_sitar_apart_otel/code.html
@@ -0,0 +1,349 @@
+
+
+
+
+
+Sitar Apart & Otel - 1+1 Premium Apartment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1+1 Premium Apartment
+
+
+
+
+
+
+
+
+
+
+
+
+groups
+Up to 4 Guests
+
+
+square_foot
+55 m²
+
+
+bed
+1 Bedroom
+
+
+A Sanctuary of Slow Living
+
+ Our 1+1 Premium Apartment is designed for those who seek the authentic Akyaka experience. Combining traditional Aegean architecture with soft modern minimalism, this space offers a private balcony overlooking the forest-meets-sea horizon. The interiors are crafted with natural materials—exposed stone, cedarwood, and linen—to create a tactile, grounding atmosphere.
+
+
+ Whether you are waking up to the scent of salt air or spending an afternoon reading under the bougainvillea, every detail in this apartment invites you to slow down and embrace the rhythm of the Turkish coast.
+
+
+
+
+Apartment Amenities
+
+
+wifi
+High-speed Wi-Fi
+
+
+ac_unit
+Air Conditioning
+
+
+kitchen
+Fully Equipped Kitchen
+
+
+local_laundry_service
+Washing Machine
+
+
+coffee_maker
+Espresso Machine
+
+
+balcony
+Private Balcony
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/stitch_akyaka_golden_hour_retreat/apartment_details_sitar_apart_otel/screen.png b/docs/stitch_akyaka_golden_hour_retreat/apartment_details_sitar_apart_otel/screen.png
new file mode 100644
index 0000000..9de8bed
Binary files /dev/null and b/docs/stitch_akyaka_golden_hour_retreat/apartment_details_sitar_apart_otel/screen.png differ
diff --git a/docs/stitch_akyaka_golden_hour_retreat/gallery_sitar_apart_otel/code.html b/docs/stitch_akyaka_golden_hour_retreat/gallery_sitar_apart_otel/code.html
new file mode 100644
index 0000000..47cae9b
--- /dev/null
+++ b/docs/stitch_akyaka_golden_hour_retreat/gallery_sitar_apart_otel/code.html
@@ -0,0 +1,339 @@
+
+
+
+
+
+Gallery | Sitar Apart & Otel Akyaka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+Boutique Interior
+
+
+
+
+
+
+

+
+The Architecture
+
+
+
+
+
+
+

+
+Coastal Vistas
+
+
+
+
+
+
+

+
+Morning Coffee Spot
+
+
+
+
+
+
+

+
+Organic Details
+
+
+
+
+
+
+

+
+Village Panorama
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/stitch_akyaka_golden_hour_retreat/gallery_sitar_apart_otel/screen.png b/docs/stitch_akyaka_golden_hour_retreat/gallery_sitar_apart_otel/screen.png
new file mode 100644
index 0000000..689c460
Binary files /dev/null and b/docs/stitch_akyaka_golden_hour_retreat/gallery_sitar_apart_otel/screen.png differ
diff --git a/docs/stitch_akyaka_golden_hour_retreat/location_contact_sitar_apart_otel/code.html b/docs/stitch_akyaka_golden_hour_retreat/location_contact_sitar_apart_otel/code.html
new file mode 100644
index 0000000..bc92586
--- /dev/null
+++ b/docs/stitch_akyaka_golden_hour_retreat/location_contact_sitar_apart_otel/code.html
@@ -0,0 +1,347 @@
+
+
+
+
+
+Location & Contact | Sitar Apart & Otel Akyaka
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+eco
+
+
Find Us in Akyaka
+
+
+ Nestled in the heart of Akyaka's slow-living paradise, Sitar offers the perfect balance between serene seclusion and coastal proximity.
+
+
+
+
+
+
+
+
+
+
+
+location_on
+
+
+
Our Address
+
Karanfil Sokak No: 12, Akyaka
+
+
+
+
+
+
+
+-
+waves
+
+
Akyaka Blue Flag Beach
+
10 minute gentle stroll through pine trees.
+
+
+-
+shopping_basket
+
+
Local Organic Market
+
5 minutes to fresh citrus and village honey.
+
+
+-
+coffee
+
+
Azmak Riverside Cafes
+
8 minutes to the iconic crystal-clear springs.
+
+
+-
+directions_run
+
+
Forest Hiking Trails
+
Trailhead begins just 200m behind the otel.
+
+
+
+
+
+
+
+
+
+
+
+
+
Send us a message
+
+
+
+
+
+
Let's Connect
+
+ Whether you're planning your first visit to Akyaka or returning for another slow-living escape, our team is here to assist with every detail.
+
+
+
+
+
+call
+
+
+
Call Us
+
+90 252 243 00 00
+
+
+
+
+mail
+
+
+
Email
+
hello@sitarakyaka.com
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/stitch_akyaka_golden_hour_retreat/location_contact_sitar_apart_otel/screen.png b/docs/stitch_akyaka_golden_hour_retreat/location_contact_sitar_apart_otel/screen.png
new file mode 100644
index 0000000..3bd155d
Binary files /dev/null and b/docs/stitch_akyaka_golden_hour_retreat/location_contact_sitar_apart_otel/screen.png differ
diff --git a/docs/stitch_akyaka_golden_hour_retreat/sitar_apart_otel_homepage/code.html b/docs/stitch_akyaka_golden_hour_retreat/sitar_apart_otel_homepage/code.html
new file mode 100644
index 0000000..d640ef6
--- /dev/null
+++ b/docs/stitch_akyaka_golden_hour_retreat/sitar_apart_otel_homepage/code.html
@@ -0,0 +1,315 @@
+
+
+
+
+
+Sitar Apart & Otel | Akyaka'nın Huzuru Sizinle
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+pool
+
+
Outdoor Pool
+
+
+
+
+flatware
+
+
Private Kitchen
+
+
+
+balcony
+
+
Private Balcony
+
+
+
+
+
+
+
+
+spa
+
+
Konaklama Seçeneklerimiz
+
Modern konforun geleneksel dokunuşlarla buluştuğu, her ihtiyaca uygun geniş apart dairelerimiz.
+
+
+
+
+
+
+
+

+
+
+
+favorite
+
Doğanın Kalbinde Bir Tatil
+
+
+ Azmak Çayı'nın serinliği, Akyaka'nın eşsiz mimarisi ve Sitar Apart'ın samimi misafirperverliği ile unutulmaz bir tatile davetlisiniz.
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/stitch_akyaka_golden_hour_retreat/sitar_apart_otel_homepage/screen.png b/docs/stitch_akyaka_golden_hour_retreat/sitar_apart_otel_homepage/screen.png
new file mode 100644
index 0000000..b9f2b92
Binary files /dev/null and b/docs/stitch_akyaka_golden_hour_retreat/sitar_apart_otel_homepage/screen.png differ
diff --git a/lib/openinary-loader.ts b/lib/openinary-loader.ts
new file mode 100644
index 0000000..ad51f52
--- /dev/null
+++ b/lib/openinary-loader.ts
@@ -0,0 +1,39 @@
+'use client'
+
+export default function openinaryLoader({ src, width, quality }: { src: string, width: number, quality?: number }) {
+ // Handle already absolute openinary URLs
+ let path = src;
+ if (src.startsWith('https://media.ayris.tech/t/')) {
+ // format: https://media.ayris.tech/t/w_800,h_800/moyqr/img.jpg
+ const parts = src.split('/');
+ path = parts.slice(5).join('/');
+ } else if (src.startsWith('https://media.ayris.tech/upload/')) {
+ // format: https://media.ayris.tech/upload/moyqr/img.jpg
+ const parts = src.split('/');
+ path = parts.slice(4).join('/');
+ } else if (src.includes('res.cloudinary.com')) {
+ // Correctly apply width & quality for unmigrated Cloudinary URLs
+ // e.g. https://res.cloudinary.com/domain/image/upload/v1234/path.jpg
+ // becomes: https://res.cloudinary.com/domain/image/upload/w_800,f_webp,q_75/v1234/path.jpg
+ const parts = src.split('/upload/');
+ if (parts.length === 2) {
+ return `${parts[0]}/upload/w_${width},f_webp,q_${quality || 75}/${parts[1]}`;
+ }
+ return src;
+ } else if (src.startsWith('http')) {
+ // For other external URLs, we append the width as a query parameter to satisfy Next.js.
+ const url = new URL(src);
+ url.searchParams.set('w', width.toString());
+ if (quality) {
+ url.searchParams.set('q', quality.toString());
+ }
+ return url.toString();
+ }
+
+ // Clean up any leading slash
+ if (path.startsWith('/')) {
+ path = path.substring(1);
+ }
+
+ return `https://media.ayris.tech/t/w_${width},f_webp,q_${quality || 75}/${path}`
+}
diff --git a/lib/openinary-url.ts b/lib/openinary-url.ts
new file mode 100644
index 0000000..d8eb533
--- /dev/null
+++ b/lib/openinary-url.ts
@@ -0,0 +1,5 @@
+const BASE = process.env.NEXT_PUBLIC_OPENINARY_URL;
+
+export function optimizedImage(path: string, params: string) {
+ return `${BASE}/t/${params}/${path}`;
+}
diff --git a/lib/openinary.ts b/lib/openinary.ts
new file mode 100644
index 0000000..a38b38b
--- /dev/null
+++ b/lib/openinary.ts
@@ -0,0 +1,19 @@
+export async function uploadToOpeninary(file: File, folder: string) {
+ const formData = new FormData();
+ formData.append("files", file);
+ formData.append("folder", folder);
+
+ const res = await fetch(`${process.env.OPENINARY_API_URL}/api/upload`, {
+ method: "POST",
+ headers: { Authorization: `Bearer ${process.env.OPENINARY_API_KEY}` },
+ body: formData,
+ });
+
+ if (!res.ok) {
+ const errorText = await res.text();
+ console.error("Openinary upload error:", errorText);
+ throw new Error("Upload başarısız: " + errorText);
+ }
+ const data = await res.json();
+ return data.files[0]; // { path, url, size, ... }
+}
diff --git a/messages/de.json b/messages/de.json
new file mode 100644
index 0000000..aaa37f8
--- /dev/null
+++ b/messages/de.json
@@ -0,0 +1,141 @@
+{
+ "nav": {
+ "home": "Startseite",
+ "rooms": "Wohnungen",
+ "gallery": "Galerie",
+ "location": "Standort",
+ "contact": "Kontakt",
+ "book_now": "Buchen"
+ },
+ "hero": {
+ "title": "Die Ruhe von Akyaka ist mit Ihnen",
+ "subtitle": "Geräumige Apartments, die modernen Komfort mit traditionellen Akzenten verbinden.",
+ "cta": "Wohnungen entdecken",
+ "description": "Erleben Sie authentisches Slow Living im Herzen von Akyaka. Eingebettet zwischen den Bergen und der türkisfarbenen Ägäis."
+ },
+ "features": {
+ "pool": "Freibad",
+ "wifi": "Kostenloses WLAN",
+ "kitchen": "Eigene Küche",
+ "parking": "Parkplatz"
+ },
+ "home_rooms": {
+ "title": "Unsere Unterkünfte",
+ "subtitle": "Geräumige Apartments, die modernen Komfort mit traditionellen Akzenten verbinden.",
+ "room_1": {
+ "title": "Studio-Apartment",
+ "desc": "Komfortables Studio für 1-2 Personen mit Einzelbett, Miniküche und Bad."
+ },
+ "room_2": {
+ "title": "1-Zimmer-Apartment",
+ "desc": "Geräumiges Apartment für 2 Personen mit Schlafzimmer, Wohnzimmer mit Küchenzeile und Balkon."
+ },
+ "room_3": {
+ "title": "2-Zimmer-Apartment",
+ "desc": "Apartment mit 2 separaten Zimmern und Balkon. Bietet Platz für 3-4 Personen."
+ }
+ },
+ "rooms": {
+ "title": "Unsere Wohnungen",
+ "description": "Wir bieten im Sitar Apart & Hotel verschiedene Apartment-Optionen für Ihre Bedürfnisse an. Nur 10 Gehminuten vom Meer entfernt.",
+ "capacity": "Gäste",
+ "amenities": "Ausstattung",
+ "ask_price": "Preis auf Anfrage",
+ "per_night": "/ Nacht",
+ "details_btn": "Details ansehen",
+ "room_list": {
+ "1_0_daire": {
+ "name": "Studio-Apartment",
+ "desc": "Komfortables Studio für 1-2 Personen mit Einzelbett, Miniküche und Bad."
+ },
+ "1_1_daire_a": {
+ "name": "1-Zimmer-Apartment",
+ "desc": "Geräumiges Apartment für 2 Personen mit Schlafzimmer, Wohnzimmer mit Küchenzeile und Balkon."
+ },
+ "1_1_daire_b": {
+ "name": "2-Zimmer-Apartment",
+ "desc": "Apartment mit 2 separaten Zimmern und Balkon. Bietet Platz für 3-4 Personen."
+ }
+ },
+ "details": {
+ "features": "Ausstattung",
+ "gallery": "Galerie",
+ "book_now_cta": "Kontakt aufnehmen",
+ "not_found": "Wohnung nicht gefunden."
+ }
+ },
+ "gallery": {
+ "title": "Eingefangenes Slow Living",
+ "description": "Entdecken Sie die ruhige Atmosphäre von Akyaka durch unsere Linse. Jeder Winkel ist auf authentischen Komfort ausgelegt.",
+ "filters": {
+ "all": "Alle",
+ "rooms": "Zimmer",
+ "exterior": "Außenansicht",
+ "view": "Aussicht",
+ "balcony": "Balkon"
+ },
+ "items": {
+ "1": "Boutique-Interieur",
+ "2": "Die Architektur",
+ "3": "Küstenpanorama",
+ "4": "Morgenkaffee",
+ "5": "Organische Details",
+ "6": "Dorfpanorama"
+ }
+ },
+ "contact": {
+ "title": "Finden Sie uns in Akyaka",
+ "description": "Das Sitar liegt im Herzen von Akyaka und bietet die perfekte Balance zwischen ruhiger Abgeschiedenheit und Küstennähe.",
+ "walking_distance": "Gehweite",
+ "locations": {
+ "beach": {
+ "title": "Akyaka Strand",
+ "desc": "10 Minuten sanfter Spaziergang durch Kiefernwälder."
+ },
+ "market": {
+ "title": "Lokaler Bio-Markt",
+ "desc": "5 Minuten zu frischen Zitrusfrüchten und Dorfohonig."
+ },
+ "cafe": {
+ "title": "Azmak Fluss-Cafés",
+ "desc": "8 Minuten zu den kristallklaren Quellen."
+ },
+ "hiking": {
+ "title": "Wanderwege",
+ "desc": "Der Wanderweg beginnt nur 200 m hinter dem Hotel."
+ }
+ },
+ "form": {
+ "title": "Senden Sie uns eine Nachricht",
+ "name": "Vollständiger Name",
+ "name_placeholder": "Max Mustermann",
+ "email": "E-Mail-Adresse",
+ "email_placeholder": "max@beispiel.de",
+ "subject": "Betreff",
+ "subject_placeholder": "Buchungsanfrage",
+ "message": "Nachricht",
+ "message_placeholder": "Wie können wir helfen?",
+ "submit": "Nachricht Senden"
+ },
+ "connect": {
+ "title": "Kontaktieren Sie uns",
+ "description": "Unser Team hilft Ihnen bei jedem Detail Ihrer Reise nach Akyaka.",
+ "phone": "Telefon",
+ "email": "E-Mail",
+ "address_title": "Unsere Adresse",
+ "address_value": "Karanfil Sokak No: 12, Akyaka",
+ "map_placeholder": "Interaktive Karte"
+ }
+ },
+ "footer": {
+ "about": "Die Ruhe von Akyaka ist mit Ihnen! Ein ruhiger Urlaub in zentraler Lage, 10 Gehminuten vom Meer entfernt.",
+ "contact": "Kontakt",
+ "address_1": "İnişdibi Cd. No:46",
+ "address_2": "Akyaka, Ula / Muğla",
+ "quick_links": "Schnelle Links",
+ "quick_links_rooms": "Wohnungen",
+ "quick_links_gallery": "Galerie",
+ "quick_links_contact": "Kontakt",
+ "rights": "© 2024 Sitar Apart & Hotel Akyaka. Alle Rechte vorbehalten."
+ }
+}
diff --git a/messages/en.json b/messages/en.json
index f793be6..5686c4c 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -1,14 +1,141 @@
{
"nav": {
"home": "Home",
- "about": "About Us",
- "contact": "Contact"
+ "rooms": "Apartments",
+ "gallery": "Gallery",
+ "location": "Location",
+ "contact": "Contact",
+ "book_now": "Book Now"
},
"hero": {
- "title": "Welcome",
- "cta": "Learn More"
+ "title": "Peace of Akyaka is With You",
+ "subtitle": "Spacious apartments combining modern comfort with traditional touches, suitable for every need.",
+ "cta": "Explore Our Apartments",
+ "description": "Experience authentic slow living in the heart of Akyaka. Nestled between the mountains and the turquoise Aegean, discover the art of unhurried travel."
+ },
+ "features": {
+ "pool": "Outdoor Pool",
+ "wifi": "Free WiFi",
+ "kitchen": "Private Kitchen",
+ "parking": "Parking"
+ },
+ "home_rooms": {
+ "title": "Accommodation Options",
+ "subtitle": "Spacious apartments combining modern comfort with traditional touches, suitable for every need.",
+ "room_1": {
+ "title": "Studio Apartment",
+ "desc": "Comfortable studio for 1-2 people with a single bed, mini kitchen, and bathroom."
+ },
+ "room_2": {
+ "title": "1 Bedroom Apartment",
+ "desc": "Spacious apartment for 2 with a bedroom, living room with kitchenette, and balcony."
+ },
+ "room_3": {
+ "title": "2 Bedroom Apartment",
+ "desc": "Apartment with 2 separate rooms (one double, one single) and balcony. Accommodates 3-4 people."
+ }
+ },
+ "rooms": {
+ "title": "Our Apartments",
+ "description": "We offer different apartment options to suit your needs at Sitar Apart & Hotel. Check out our apartments for a comfortable stay just 10 minutes walk from the sea.",
+ "capacity": "Guests",
+ "amenities": "Amenities",
+ "ask_price": "Ask for Price",
+ "per_night": "/ night",
+ "details_btn": "View Details",
+ "room_list": {
+ "1_0_daire": {
+ "name": "Studio Apartment",
+ "desc": "Comfortable studio for 1-2 people with a single bed, mini kitchen, and bathroom."
+ },
+ "1_1_daire_a": {
+ "name": "1 Bedroom Apartment",
+ "desc": "Spacious apartment for 2 with a bedroom, living room with kitchenette, and balcony."
+ },
+ "1_1_daire_b": {
+ "name": "2 Bedroom Apartment",
+ "desc": "Apartment with 2 separate rooms (one double, one single) and balcony. Accommodates 3-4 people."
+ }
+ },
+ "details": {
+ "features": "Apartment Features",
+ "gallery": "Gallery",
+ "book_now_cta": "Contact to Book",
+ "not_found": "Apartment not found."
+ }
+ },
+ "gallery": {
+ "title": "Slow Living Captured",
+ "description": "Explore the tranquil atmosphere of Akyaka through our lens. From sun-drenched balconies to our boutique interiors, every corner of Sitar is designed for authentic comfort.",
+ "filters": {
+ "all": "All",
+ "rooms": "Rooms",
+ "exterior": "Exterior",
+ "view": "View",
+ "balcony": "Balcony"
+ },
+ "items": {
+ "1": "Boutique Interior",
+ "2": "The Architecture",
+ "3": "Coastal Vistas",
+ "4": "Morning Coffee Spot",
+ "5": "Organic Details",
+ "6": "Village Panorama"
+ }
+ },
+ "contact": {
+ "title": "Find Us in Akyaka",
+ "description": "Nestled in the heart of Akyaka's slow-living paradise, Sitar offers the perfect balance between serene seclusion and coastal proximity.",
+ "walking_distance": "Walking Distance",
+ "locations": {
+ "beach": {
+ "title": "Akyaka Blue Flag Beach",
+ "desc": "10 minute gentle stroll through pine trees."
+ },
+ "market": {
+ "title": "Local Organic Market",
+ "desc": "5 minutes to fresh citrus and village honey."
+ },
+ "cafe": {
+ "title": "Azmak Riverside Cafes",
+ "desc": "8 minutes to the iconic crystal-clear springs."
+ },
+ "hiking": {
+ "title": "Forest Hiking Trails",
+ "desc": "Trailhead begins just 200m behind the otel."
+ }
+ },
+ "form": {
+ "title": "Send us a message",
+ "name": "Full Name",
+ "name_placeholder": "John Doe",
+ "email": "Email Address",
+ "email_placeholder": "john@example.com",
+ "subject": "Subject",
+ "subject_placeholder": "Booking Inquiry",
+ "message": "Message",
+ "message_placeholder": "Tell us how we can help...",
+ "submit": "Send Message"
+ },
+ "connect": {
+ "title": "Let's Connect",
+ "description": "Whether you're planning your first visit to Akyaka or returning for another slow-living escape, our team is here to assist with every detail.",
+ "phone": "Phone",
+ "email": "Email",
+ "address_title": "Our Address",
+ "address_value": "Karanfil Sokak No: 12, Akyaka",
+ "map_placeholder": "Interactive Map Placeholder"
+ }
},
"footer": {
- "rights": "All rights reserved."
+ "about": "Peace of Akyaka is With You! A peaceful holiday in a central location, 10 minutes walking distance to the sea.",
+ "contact": "Contact",
+ "address_1": "İnişdibi Cd. No:46",
+ "address_2": "Akyaka, Ula / Muğla, Turkey",
+ "quick_links": "Quick Links",
+ "quick_links_rooms": "Apartments",
+ "quick_links_gallery": "Gallery",
+ "quick_links_contact": "Contact",
+ "rights": "© 2024 Sitar Apart & Hotel Akyaka. All rights reserved."
}
}
diff --git a/messages/tr.json b/messages/tr.json
index 1994c6c..2f3dc46 100644
--- a/messages/tr.json
+++ b/messages/tr.json
@@ -1,14 +1,141 @@
{
"nav": {
"home": "Ana Sayfa",
- "about": "Hakkımızda",
- "contact": "İletişim"
+ "rooms": "Dairelerimiz",
+ "gallery": "Galeri",
+ "location": "Konum",
+ "contact": "İletişim",
+ "book_now": "Rezervasyon"
},
"hero": {
- "title": "Hoş Geldiniz",
- "cta": "Daha Fazla Bilgi"
+ "title": "Akyaka'nın Huzuru Sizinle",
+ "subtitle": "Modern konforun geleneksel dokunuşlarla buluştuğu, her ihtiyaca uygun geniş apart dairelerimiz.",
+ "cta": "Dairelerimizi İnceleyin",
+ "description": "Akyaka'nın kalbinde otantik ve yavaş bir yaşam deneyimi. Dağlar ile turkuaz Ege denizi arasına gizlenmiş, telaşsız seyahat etme sanatını keşfedin."
+ },
+ "features": {
+ "pool": "Açık Havuz",
+ "wifi": "Ücretsiz WiFi",
+ "kitchen": "Özel Mutfak",
+ "parking": "Otopark"
+ },
+ "home_rooms": {
+ "title": "Konaklama Seçeneklerimiz",
+ "subtitle": "Modern konforun geleneksel dokunuşlarla buluştuğu, her ihtiyaca uygun geniş apart dairelerimiz.",
+ "room_1": {
+ "title": "1+0 Dairemiz",
+ "desc": "Stüdyo tip, tek yataklı, mini mutfak ve banyo içeren konforlu 1-2 kişilik dairelerimiz."
+ },
+ "room_2": {
+ "title": "1+1 Dairemiz",
+ "desc": "Yatak odası ve ayrı mutfak köşeli salon, balkonlu, 2 kişilik ferah dairelerimiz."
+ },
+ "room_3": {
+ "title": "1+1 Dairemiz (2 Oda)",
+ "desc": "2 kişilik ve 1 kişilik olmak üzere 2 ayrı oda içeren balkonlu dairelerimiz. 3-4 kişi konaklayabilir."
+ }
+ },
+ "rooms": {
+ "title": "Dairelerimiz",
+ "description": "Sitar Apart & Otel'de ihtiyacınıza uygun farklı daire seçenekleri sunuyoruz. Denize 10 dakika yürüme mesafesinde konforlu bir konaklama için dairelerimizi inceleyin.",
+ "capacity": "Kişilik",
+ "amenities": "Olanak",
+ "ask_price": "Fiyat sorunuz",
+ "per_night": "/ gece",
+ "details_btn": "Detaylı İncele",
+ "room_list": {
+ "1_0_daire": {
+ "name": "1+0 Dairemiz",
+ "desc": "Stüdyo tip, tek yataklı, mini mutfak ve banyo içeren 1-2 kişilik dairelerimiz."
+ },
+ "1_1_daire_a": {
+ "name": "1+1 Dairemiz",
+ "desc": "Yatak odası ve ayrı mutfak köşeli salon, balkonlu, 2 kişilik ferah dairelerimiz."
+ },
+ "1_1_daire_b": {
+ "name": "1+1 Dairemiz (2 Oda)",
+ "desc": "2 kişilik ve 1 kişilik olmak üzere 2 ayrı oda içeren balkonlu dairelerimiz. 3-4 kişi konaklayabilir."
+ }
+ },
+ "details": {
+ "features": "Daire Özellikleri",
+ "gallery": "Daireden Kareler",
+ "book_now_cta": "Hemen İletişime Geçin",
+ "not_found": "Daire bulunamadı."
+ }
+ },
+ "gallery": {
+ "title": "Yavaş Yaşamın Kareleri",
+ "description": "Akyaka'nın huzurlu atmosferini objektifimizden keşfedin. Güneş alan balkonlardan butik iç mekanlarımıza kadar Sitar'ın her köşesi otantik bir konfor için tasarlandı.",
+ "filters": {
+ "all": "Tümü",
+ "rooms": "Odalar",
+ "exterior": "Dış Mekan",
+ "view": "Manzara",
+ "balcony": "Balkon"
+ },
+ "items": {
+ "1": "Butik İç Mekan",
+ "2": "Mimari",
+ "3": "Sahil Manzarası",
+ "4": "Sabah Kahvesi Köşesi",
+ "5": "Organik Detaylar",
+ "6": "Köy Panoraması"
+ }
+ },
+ "contact": {
+ "title": "Akyaka'da Bizi Bulun",
+ "description": "Akyaka'nın yavaş yaşam cennetinin kalbinde yer alan Sitar, sakin bir inziva ile sahile yakınlık arasında mükemmel bir denge sunuyor.",
+ "walking_distance": "Yürüme Mesafesi",
+ "locations": {
+ "beach": {
+ "title": "Akyaka Mavi Bayraklı Plaj",
+ "desc": "Çam ağaçları arasından 10 dakikalık keyifli bir yürüyüş."
+ },
+ "market": {
+ "title": "Yerel Organik Pazar",
+ "desc": "Taze narenciye ve köy balına 5 dakika uzaklıkta."
+ },
+ "cafe": {
+ "title": "Azmak Kenarı Kafeleri",
+ "desc": "İkonik, kristal berraklığındaki su kaynaklarına 8 dakika."
+ },
+ "hiking": {
+ "title": "Orman Yürüyüş Parkurları",
+ "desc": "Parkur başlangıcı otelin sadece 200m arkasında."
+ }
+ },
+ "form": {
+ "title": "Bize Mesaj Gönderin",
+ "name": "Ad Soyad",
+ "name_placeholder": "Adınız Soyadınız",
+ "email": "E-posta Adresi",
+ "email_placeholder": "ornek@mail.com",
+ "subject": "Konu",
+ "subject_placeholder": "Rezervasyon Talebi",
+ "message": "Mesajınız",
+ "message_placeholder": "Size nasıl yardımcı olabiliriz?",
+ "submit": "Mesaj Gönder"
+ },
+ "connect": {
+ "title": "İletişime Geçin",
+ "description": "İster Akyaka'ya ilk ziyaretinizi planlıyor olun, isterseniz yeni bir yavaş yaşam kaçamağı için geri dönüyor olun, ekibimiz her detayda size yardımcı olmak için burada.",
+ "phone": "Telefon",
+ "email": "E-posta",
+ "address_title": "Adresimiz",
+ "address_value": "Karanfil Sokak No: 12, Akyaka",
+ "map_placeholder": "İnteraktif Harita"
+ }
},
"footer": {
- "rights": "Tüm hakları saklıdır."
+ "about": "Akyaka'nın Huzuru Sizinle! Denize 10 dakika yürüme mesafesinde, merkezi konumda huzurlu bir tatil.",
+ "contact": "İletişim",
+ "address_1": "İnişdibi Cd. No:46",
+ "address_2": "Akyaka, Ula / Muğla",
+ "quick_links": "Hızlı Bağlantılar",
+ "quick_links_rooms": "Dairelerimiz",
+ "quick_links_gallery": "Galeri",
+ "quick_links_contact": "İletişim",
+ "rights": "© 2024 Sitar Apart & Otel Akyaka. Tüm hakları saklıdır."
}
}
diff --git a/package-lock.json b/package-lock.json
index b36e112..8ebed4d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "fethiye-holiday",
+ "name": "starapart",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "fethiye-holiday",
+ "name": "starapart",
"version": "0.1.0",
"dependencies": {
"@base-ui/react": "^1.5.0",
@@ -14,7 +14,8 @@
"cloudinary": "^2.10.0",
"clsx": "^2.1.1",
"developer-icons": "^7.0.1",
- "framer-motion": "^12.40.0",
+ "embla-carousel-react": "^8.6.0",
+ "framer-motion": "^12.42.2",
"lucide-react": "^1.18.0",
"next": "16.2.9",
"next-auth": "^5.0.0-beta.31",
@@ -26,6 +27,7 @@
"tw-animate-css": "^1.4.0"
},
"devDependencies": {
+ "@prisma/config": "^7.8.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
@@ -1198,9 +1200,6 @@
"cpu": [
"arm"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1217,9 +1216,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1236,9 +1232,6 @@
"cpu": [
"ppc64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1255,9 +1248,6 @@
"cpu": [
"riscv64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1274,9 +1264,6 @@
"cpu": [
"s390x"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1293,9 +1280,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1312,9 +1296,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1331,9 +1312,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1350,9 +1328,6 @@
"cpu": [
"arm"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1375,9 +1350,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1400,9 +1372,6 @@
"cpu": [
"ppc64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1425,9 +1394,6 @@
"cpu": [
"riscv64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1450,9 +1416,6 @@
"cpu": [
"s390x"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1475,9 +1438,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1500,9 +1460,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1525,9 +1482,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1807,9 +1761,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1826,9 +1777,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1845,9 +1793,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -1864,9 +1809,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2123,9 +2065,6 @@
"cpu": [
"arm"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2146,9 +2085,6 @@
"cpu": [
"arm"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2169,9 +2105,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2192,9 +2125,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2215,9 +2145,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2238,9 +2165,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -2780,9 +2704,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
@@ -2799,9 +2720,6 @@
"cpu": [
"arm64"
],
- "libc": [
- "musl"
- ],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
@@ -2818,9 +2736,6 @@
"cpu": [
"ppc64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
@@ -2837,9 +2752,6 @@
"cpu": [
"s390x"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
@@ -2856,9 +2768,6 @@
"cpu": [
"x64"
],
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
@@ -2875,9 +2784,6 @@
"cpu": [
"x64"
],
- "libc": [
- "musl"
- ],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
@@ -3092,9 +2998,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3112,9 +3015,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3132,9 +3032,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3152,9 +3049,6 @@
"x64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3373,7 +3267,7 @@
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
@@ -3786,9 +3680,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3803,9 +3694,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3820,9 +3708,6 @@
"loong64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3837,9 +3722,6 @@
"loong64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3854,9 +3736,6 @@
"ppc64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3871,9 +3750,6 @@
"riscv64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3888,9 +3764,6 @@
"riscv64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3905,9 +3778,6 @@
"s390x"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3922,9 +3792,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -3939,9 +3806,6 @@
"x64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -5291,6 +5155,34 @@
"integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==",
"license": "ISC"
},
+ "node_modules/embla-carousel": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
+ "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
+ "license": "MIT"
+ },
+ "node_modules/embla-carousel-react": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz",
+ "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==",
+ "license": "MIT",
+ "dependencies": {
+ "embla-carousel": "8.6.0",
+ "embla-carousel-reactive-utils": "8.6.0"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/embla-carousel-reactive-utils": {
+ "version": "8.6.0",
+ "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz",
+ "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "embla-carousel": "8.6.0"
+ }
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -6386,12 +6278,12 @@
}
},
"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==",
+ "version": "12.42.2",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz",
+ "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==",
"license": "MIT",
"dependencies": {
- "motion-dom": "^12.40.0",
+ "motion-dom": "^12.42.2",
"motion-utils": "^12.39.0",
"tslib": "^2.4.0"
},
@@ -7933,9 +7825,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7957,9 +7846,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7981,9 +7867,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8005,9 +7888,6 @@
"x64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -8329,9 +8209,9 @@
}
},
"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==",
+ "version": "12.42.2",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz",
+ "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.39.0"
@@ -8590,6 +8470,17 @@
}
}
},
+ "node_modules/next-intl/node_modules/@swc/helpers": {
+ "version": "0.5.23",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
+ "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
diff --git a/package.json b/package.json
index 2e615fe..27b2547 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "fethiye-holiday",
+ "name": "starapart",
"version": "0.1.0",
"private": true,
"scripts": {
@@ -15,7 +15,8 @@
"cloudinary": "^2.10.0",
"clsx": "^2.1.1",
"developer-icons": "^7.0.1",
- "framer-motion": "^12.40.0",
+ "embla-carousel-react": "^8.6.0",
+ "framer-motion": "^12.42.2",
"lucide-react": "^1.18.0",
"next": "16.2.9",
"next-auth": "^5.0.0-beta.31",
@@ -27,6 +28,7 @@
"tw-animate-css": "^1.4.0"
},
"devDependencies": {
+ "@prisma/config": "^7.8.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
diff --git a/prisma.config.ts b/prisma.config.ts
new file mode 100644
index 0000000..5f884cb
--- /dev/null
+++ b/prisma.config.ts
@@ -0,0 +1,4 @@
+import { defineConfig } from '@prisma/config'
+
+export default defineConfig({
+})
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index b3a3f9d..72edd7f 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -4,7 +4,6 @@ generator client {
datasource db {
provider = "postgresql"
- url = env("DATABASE_URL")
}
enum Role {
@@ -13,31 +12,31 @@ enum Role {
}
model User {
- id String @id @default(cuid())
- name String?
- email String @unique
- password String?
- role Role @default(USER)
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
- deletedAt DateTime?
- accounts Account[]
- sessions Session[]
+ id String @id @default(cuid())
+ name String?
+ email String @unique
+ password String?
+ role Role @default(USER)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+ accounts Account[]
+ sessions Session[]
}
model Account {
- id String @id @default(cuid())
- userId String
- type String
- provider String
- providerAccountId String
- refresh_token String? @db.Text
- access_token String? @db.Text
- expires_at Int?
- token_type String?
- scope String?
- id_token String? @db.Text
- session_state String?
+ id String @id @default(cuid())
+ userId String
+ type String
+ provider String
+ providerAccountId String
+ refresh_token String? @db.Text
+ access_token String? @db.Text
+ expires_at Int?
+ token_type String?
+ scope String?
+ id_token String? @db.Text
+ session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -59,3 +58,75 @@ model VerificationToken {
@@unique([identifier, token])
}
+
+enum RoomType {
+ STUDIO_1_0
+ SUITE_1_1
+}
+
+model Room {
+ id String @id @default(cuid())
+ slug String @unique
+ type RoomType
+ nameTr String
+ nameEn String
+ nameDe String
+ descriptionTr String
+ descriptionEn String
+ descriptionDe String
+ capacity Int
+ price Int?
+ imageUrl String?
+ images String[]
+ amenities String[]
+ available Boolean @default(true)
+ featured Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+ reservations Reservation[]
+}
+
+enum ReservationStatus {
+ PENDING
+ CONFIRMED
+ CANCELLED
+}
+
+model Reservation {
+ id String @id @default(cuid())
+ roomId String
+ room Room @relation(fields: [roomId], references: [id])
+ checkIn DateTime
+ checkOut DateTime
+ adults Int
+ children Int @default(0)
+ fullName String
+ email String
+ phone String
+ message String?
+ status ReservationStatus @default(PENDING)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+}
+
+model ContactMessage {
+ id String @id @default(cuid())
+ fullName String
+ email String
+ phone String
+ message String @db.Text
+ read Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+}
+
+model Gallery {
+ id String @id @default(cuid())
+ url String
+ category String?
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}