first commit

This commit is contained in:
2026-06-16 19:17:37 +03:00
parent 2d149f1178
commit 6fa7cc6630
25 changed files with 1378 additions and 167 deletions
+108
View File
@@ -0,0 +1,108 @@
"use client";
import { useTranslations } from "next-intl";
import { motion } from "framer-motion";
import { mockData } from "@/lib/mock-data";
import Link from "next/link";
import Image from "next/image";
export function Hero() {
const t = useTranslations("hero");
return (
<>
<section
className="relative h-[921px] min-h-[600px] w-full overflow-hidden"
aria-label="Hero Section"
>
{/* Background Image - Using Next.js Image for optimization */}
<div className="absolute inset-0 z-0">
<motion.div
initial={{ scale: 1.05 }}
animate={{ scale: 1 }}
transition={{ duration: 15, ease: "easeOut" }}
style={{ willChange: "transform" }}
className="relative w-full h-full motion-reduce:!transform-none"
>
<Image
src={mockData.heroSlides[1]}
alt="Kordon Apart - Fethiye Marina manzarası"
fill
className="object-cover object-[center_top]"
priority
sizes="100vw"
/>
</motion.div>
{/* Gradient Overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-[#002045]/80 via-[#002045]/50 to-[#002045]/90" />
</div>
{/* Hero Content */}
<div className="relative z-10 h-full max-w-7xl mx-auto px-4 md:px-12 flex flex-col justify-center items-start pt-20">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
className="max-w-2xl motion-reduce:!transform-none motion-reduce:!opacity-100"
>
<span className="font-label-sm text-[12px] text-white/80 uppercase tracking-widest mb-4 block font-bold">
{t("subtitle")}
</span>
<h1 className="font-heading text-5xl md:text-6xl lg:text-7xl text-white mb-6 leading-tight font-extrabold tracking-tight">
{t("title")}
</h1>
<p className="font-body-lg text-lg text-white/90 mb-8 max-w-lg leading-relaxed" style={{ maxWidth: "65ch" }}>
{t("desc")}
</p>
<div className="flex flex-wrap gap-4">
<Link
href="#"
className="bg-[#CA8A04] text-white px-8 py-4 rounded-lg font-label-sm uppercase tracking-widest text-[12px] hover:bg-[#CA8A04]/90 transition-colors duration-200 shadow-lg cursor-pointer focus:ring-2 focus:ring-[#CA8A04]/50 focus:outline-none min-h-[48px] flex items-center"
role="button"
>
{t("explore")}
</Link>
<Link
href="#"
className="bg-white/10 backdrop-blur-md border border-white/20 text-white px-8 py-4 rounded-lg font-label-sm uppercase tracking-widest text-[12px] hover:bg-white/20 transition-colors duration-200 cursor-pointer focus:ring-2 focus:ring-white/50 focus:outline-none min-h-[48px] flex items-center"
role="button"
>
{t("virtualTour")}
</Link>
</div>
</motion.div>
</div>
</section>
{/* Search Bar Utility (Floating over Hero bottom) */}
<div className="relative z-20 max-w-5xl mx-auto px-4 -mt-24 hidden md:block">
<motion.div
initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.5 }}
className="bg-white p-8 rounded-xl shadow-2xl shadow-black/10 grid grid-cols-4 gap-6 items-end border border-gray-100 motion-reduce:!transform-none motion-reduce:!opacity-100"
>
<div className="space-y-2">
<label htmlFor="check-in" className="block font-label-sm text-[12px] text-gray-500 uppercase tracking-widest">{t("checkIn")}</label>
<input id="check-in" className="w-full bg-transparent border-0 border-b border-gray-200 py-2 focus:ring-0 focus:border-gray-900 transition-colors duration-200 text-gray-900 cursor-pointer min-h-[44px]" type="date" />
</div>
<div className="space-y-2">
<label htmlFor="check-out" className="block font-label-sm text-[12px] text-gray-500 uppercase tracking-widest">{t("checkOut")}</label>
<input id="check-out" className="w-full bg-transparent border-0 border-b border-gray-200 py-2 focus:ring-0 focus:border-gray-900 transition-colors duration-200 text-gray-900 cursor-pointer min-h-[44px]" type="date" />
</div>
<div className="space-y-2">
<label htmlFor="guests" className="block font-label-sm text-[12px] text-gray-500 uppercase tracking-widest">{t("guests")}</label>
<select id="guests" className="w-full bg-transparent border-0 border-b border-gray-200 py-2 focus:ring-0 focus:border-gray-900 transition-colors duration-200 text-gray-900 cursor-pointer min-h-[44px]">
<option value="1">{t("adult1")}</option>
<option value="2">{t("adult2")}</option>
<option value="3+">{t("adult3")}</option>
</select>
</div>
<button className="bg-[#CA8A04] text-white min-h-[48px] rounded-lg font-label-sm uppercase tracking-widest text-[12px] hover:bg-[#CA8A04]/90 transition-colors duration-200 font-bold cursor-pointer shadow-lg focus:ring-2 focus:ring-[#CA8A04]/50 focus:outline-none">
{t("checkAvailability")}
</button>
</motion.div>
</div>
</>
);
}
+51
View File
@@ -0,0 +1,51 @@
"use client";
import { useTranslations } from "next-intl";
import { motion } from "framer-motion";
export function Newsletter() {
const t = useTranslations("newsletter");
return (
<section className="py-24 bg-[#002045] text-white overflow-hidden relative" aria-label={t("title")}>
{/* Decorative Background Elements */}
<div className="absolute top-0 left-0 w-full h-full overflow-hidden z-0" aria-hidden="true">
<div className="absolute -top-[20%] -left-[10%] w-[50%] h-[150%] bg-white/5 blur-3xl transform rotate-12 rounded-full" />
<div className="absolute -bottom-[20%] -right-[10%] w-[50%] h-[150%] bg-[#CA8A04]/10 blur-3xl transform -rotate-12 rounded-full" />
</div>
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 0.6 }}
className="max-w-7xl mx-auto px-4 md:px-12 relative z-10 text-center motion-reduce:!transform-none motion-reduce:!opacity-100"
>
<h2 className="font-heading text-4xl md:text-5xl text-white mb-6 font-bold tracking-tight">
{t("title")}
</h2>
<p className="font-body-lg text-lg text-white/70 mb-10 max-w-xl mx-auto" style={{ maxWidth: "65ch" }}>
{t("desc")}
</p>
<form className="max-w-md mx-auto flex flex-col md:flex-row gap-4" onSubmit={(e) => e.preventDefault()}>
<label htmlFor="newsletter-email" className="sr-only">{t("label")}</label>
<input
id="newsletter-email"
className="flex-1 bg-white/10 border border-white/20 text-white placeholder-white/40 rounded-lg px-6 py-4 focus:ring-2 focus:ring-[#CA8A04]/50 focus:border-[#CA8A04] outline-none transition-colors duration-200 min-h-[48px]"
placeholder={t("placeholder")}
type="email"
required
autoComplete="email"
/>
<button
type="submit"
className="bg-[#CA8A04] text-white px-8 py-4 rounded-lg font-label-sm text-[12px] uppercase tracking-widest font-bold hover:bg-[#CA8A04]/90 transition-colors duration-200 shadow-lg cursor-pointer focus:ring-2 focus:ring-[#CA8A04]/50 focus:outline-none min-h-[48px]"
>
{t("subscribe")}
</button>
</form>
</motion.div>
</section>
);
}
+116
View File
@@ -0,0 +1,116 @@
"use client";
import { useTranslations } from "next-intl";
import { mockData } from "@/lib/mock-data";
import Link from "next/link";
import Image from "next/image";
import { motion } from "framer-motion";
import { ArrowRight } from "lucide-react";
export function RoomList() {
const t = useTranslations("accommodations");
// We take the first 3 rooms for the bento grid
const featuredRoom = mockData.accommodations[0];
const sideRooms = mockData.accommodations.slice(1, 3);
return (
<section className="py-24 max-w-7xl mx-auto px-4 md:px-12" aria-label="Odalarımız">
<div className="flex justify-between items-end mb-12">
<motion.div
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
className="space-y-2 motion-reduce:!transform-none motion-reduce:!opacity-100"
>
<h2 className="font-heading text-3xl md:text-4xl font-bold text-primary dark:text-primary-fixed-dim">{t("title")}</h2>
<p className="font-body-md text-on-surface-variant dark:text-outline text-lg" style={{ maxWidth: "65ch" }}>{t("desc")}</p>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
className="motion-reduce:!transform-none motion-reduce:!opacity-100"
>
<Link href="/odalar" className="font-label-sm text-[12px] uppercase tracking-widest text-primary dark:text-primary-fixed-dim flex items-center gap-2 group font-bold cursor-pointer focus:ring-2 focus:ring-primary/30 focus:outline-none rounded-md px-2 py-1 min-h-[44px]">
{t("viewAll")}
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform duration-200" />
</Link>
</motion.div>
</div>
{/* Bento-style Grid */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6 }}
className="grid grid-cols-1 md:grid-cols-12 gap-6 h-auto md:h-[600px] motion-reduce:!transform-none motion-reduce:!opacity-100"
>
<Link
href={`/odalar/${featuredRoom.slug}`}
className="md:col-span-8 group relative overflow-hidden rounded-xl cursor-pointer focus:ring-2 focus:ring-primary/50 focus:outline-none"
aria-label={`${featuredRoom.name} - ${featuredRoom.type}`}
>
<div className="relative w-full h-full min-h-[300px]">
<Image
src={featuredRoom.image}
alt={`${featuredRoom.name} - ${featuredRoom.type} oda görseli`}
fill
className="object-cover transition-transform duration-500 group-hover:scale-105"
sizes="(max-width: 768px) 100vw, 66vw"
loading="lazy"
/>
</div>
<div className="absolute inset-0 bg-gradient-to-t from-[#002045]/90 via-[#002045]/20 to-transparent opacity-80" />
<div className="absolute bottom-0 left-0 p-8 text-white w-full">
<div className="flex gap-2 mb-3">
<span className="bg-[#CA8A04] text-white px-3 py-1 rounded-full font-label-sm text-[10px] uppercase font-bold tracking-wider">
{t("bestseller")}
</span>
</div>
<h3 className="font-heading text-3xl md:text-4xl font-bold mb-2">{featuredRoom.name}</h3>
<p className="font-body-md text-white/80 text-lg">
{featuredRoom.type} {t("bedrooms", { count: featuredRoom.bedrooms })} {t("persons", { count: featuredRoom.capacity })}
</p>
</div>
</Link>
{/* Side Stacked Items */}
<div className="md:col-span-4 grid grid-rows-2 gap-6">
{sideRooms.map((room) => (
<Link
href={`/odalar/${room.slug}`}
key={room.id}
className="group relative overflow-hidden rounded-xl cursor-pointer focus:ring-2 focus:ring-primary/50 focus:outline-none"
aria-label={`${room.name} - ${room.type}`}
>
<div className="relative w-full h-full min-h-[150px]">
<Image
src={room.image}
alt={`${room.name} - ${room.type} oda görseli`}
fill
className="object-cover transition-transform duration-500 group-hover:scale-105"
sizes="(max-width: 768px) 100vw, 33vw"
loading="lazy"
/>
</div>
<div className="absolute inset-0 bg-gradient-to-t from-[#002045]/80 to-transparent opacity-90" />
<div className="absolute bottom-0 left-0 p-6 text-white w-full">
<h4 className="font-label-sm text-[12px] uppercase tracking-wider font-bold text-[#CA8A04] mb-1">{room.type}</h4>
<h3 className="font-heading text-xl font-semibold mb-1">{room.name}</h3>
<p className="font-body-md text-white/80 text-sm">
{t("persons", { count: room.capacity })}
</p>
</div>
</Link>
))}
</div>
</motion.div>
</section>
);
}
+107
View File
@@ -0,0 +1,107 @@
"use client";
import { useTranslations } from "next-intl";
import { motion } from "framer-motion";
import { Waves, UtensilsCrossed, ConciergeBell } from "lucide-react";
export function Services() {
const t = useTranslations("services");
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.15,
}
}
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } }
};
const services = [
{
icon: Waves,
title: t("service1_title"),
desc: t("service1_desc"),
},
{
icon: UtensilsCrossed,
title: t("service2_title"),
desc: t("service2_desc"),
},
{
icon: ConciergeBell,
title: t("service3_title"),
desc: t("service3_desc"),
},
];
return (
<section className="bg-surface-container-low dark:bg-inverse-surface/50 py-24" aria-label="Hizmetlerimiz">
<div className="max-w-7xl mx-auto px-4 md:px-12">
<div className="text-center max-w-2xl mx-auto mb-16">
<motion.span
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="font-label-sm text-[12px] text-[#CA8A04] dark:text-[#F8BC4B] uppercase tracking-widest mb-4 block font-bold motion-reduce:!transform-none motion-reduce:!opacity-100"
>
{t("subtitle")}
</motion.span>
<motion.h2
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.1 }}
className="font-heading text-4xl text-primary dark:text-primary-fixed-dim mb-4 font-bold motion-reduce:!transform-none motion-reduce:!opacity-100"
>
{t("title")}
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: 0.2 }}
className="font-body-md text-on-surface-variant dark:text-outline text-lg motion-reduce:!transform-none motion-reduce:!opacity-100"
style={{ maxWidth: "65ch", margin: "0 auto" }}
>
{t("desc")}
</motion.p>
</div>
<motion.div
variants={container}
initial="hidden"
whileInView="show"
viewport={{ once: true, margin: "-50px" }}
className="grid grid-cols-1 md:grid-cols-3 gap-12"
role="list"
>
{services.map((service) => (
<motion.div
variants={item}
key={service.title}
className="text-center space-y-6 px-4 motion-reduce:!transform-none motion-reduce:!opacity-100"
role="listitem"
>
<div className="w-20 h-20 bg-white dark:bg-primary-container rounded-full flex items-center justify-center mx-auto shadow-md text-primary dark:text-primary-fixed-dim" aria-hidden="true">
<service.icon className="w-8 h-8" strokeWidth={1.5} />
</div>
<div>
<h3 className="font-heading text-2xl text-primary dark:text-primary-fixed-dim mb-3 font-semibold">{service.title}</h3>
<p className="font-body-md text-on-surface-variant dark:text-outline leading-relaxed" style={{ maxWidth: "45ch", margin: "0 auto" }}>
{service.desc}
</p>
</div>
</motion.div>
))}
</motion.div>
</div>
</section>
);
}