Initial commit: Salmakis Yachting Portal with Cloudinary & i18n
This commit is contained in:
481
app/[locale]/fleet/[slug]/page.tsx
Normal file
481
app/[locale]/fleet/[slug]/page.tsx
Normal file
@@ -0,0 +1,481 @@
|
||||
'use client';
|
||||
|
||||
import { yachts } from "../../../data/yachts";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { motion } from "framer-motion";
|
||||
import { use, useState, useCallback, useEffect } from "react";
|
||||
import { AnimatePresence } from "framer-motion";
|
||||
import { CldImage } from "next-cloudinary";
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ slug: string }>;
|
||||
}
|
||||
|
||||
export default function YachtPage({ params }: PageProps) {
|
||||
const { slug } = use(params);
|
||||
const yacht = yachts.find((y) => y.slug === slug);
|
||||
const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);
|
||||
const t = useTranslations('FleetDetail');
|
||||
const locale = useLocale();
|
||||
|
||||
const openLightbox = (index: number) => setLightboxIndex(index);
|
||||
const closeLightbox = () => setLightboxIndex(null);
|
||||
|
||||
const goNext = useCallback(() => {
|
||||
if (lightboxIndex !== null && yacht) {
|
||||
setLightboxIndex((lightboxIndex + 1) % yacht.gallery.length);
|
||||
}
|
||||
}, [lightboxIndex, yacht]);
|
||||
|
||||
const goPrev = useCallback(() => {
|
||||
if (lightboxIndex !== null && yacht) {
|
||||
setLightboxIndex((lightboxIndex - 1 + yacht.gallery.length) % yacht.gallery.length);
|
||||
}
|
||||
}, [lightboxIndex, yacht]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKey = (e: KeyboardEvent) => {
|
||||
if (lightboxIndex === null) return;
|
||||
if (e.key === 'Escape') closeLightbox();
|
||||
if (e.key === 'ArrowRight') goNext();
|
||||
if (e.key === 'ArrowLeft') goPrev();
|
||||
};
|
||||
window.addEventListener('keydown', handleKey);
|
||||
return () => window.removeEventListener('keydown', handleKey);
|
||||
}, [lightboxIndex, goNext, goPrev]);
|
||||
|
||||
if (!yacht) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const fadeInUp = {
|
||||
hidden: { opacity: 0, y: 40 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.8, ease: "easeOut" } }
|
||||
};
|
||||
|
||||
const staggerContainer = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: { opacity: 1, transition: { staggerChildren: 0.15 } }
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-surface">
|
||||
{/* Editorial Hero Section */}
|
||||
<section className="relative h-[90vh] md:h-screen w-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ scale: 1.1, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ duration: 1.5, ease: "easeOut" }}
|
||||
className="absolute inset-0 z-0"
|
||||
>
|
||||
<CldImage
|
||||
src={yacht.heroImage}
|
||||
alt={yacht.name}
|
||||
fill
|
||||
crop="fill"
|
||||
gravity="auto"
|
||||
className="object-cover grayscale-[10%]"
|
||||
priority
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-primary/90 via-primary/20 to-transparent z-10 flex flex-col items-center justify-center text-center px-6">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
variants={staggerContainer}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<motion.span variants={fadeInUp} className="font-label text-[10px] md:text-xs tracking-[0.5em] text-secondary uppercase mb-6">
|
||||
The Fleet Collection
|
||||
</motion.span>
|
||||
<motion.h1 variants={fadeInUp} className="text-white font-headline text-5xl md:text-8xl lg:text-9xl mb-4 uppercase tracking-tighter">
|
||||
{yacht.name}
|
||||
</motion.h1>
|
||||
<motion.p variants={fadeInUp} className="text-secondary font-headline text-lg md:text-2xl italic tracking-wide">
|
||||
{yacht.tagline}
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 1, duration: 1 }}
|
||||
className="absolute bottom-12 left-1/2 -translate-x-1/2 flex flex-col items-center"
|
||||
>
|
||||
<span className="text-white/40 text-[9px] md:text-[10px] tracking-[0.5em] uppercase mb-4">Explore Specifications</span>
|
||||
<div className="w-px h-12 md:h-16 bg-gradient-to-b from-secondary to-transparent"></div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Technical Atelier Bar */}
|
||||
<div className="bg-primary py-12 md:py-16 text-white border-y border-white/5 relative z-20">
|
||||
<div className="w-full flex justify-center">
|
||||
<div className="w-full max-w-5xl mx-auto grid grid-cols-2 md:grid-cols-5 px-6 gap-y-12 md:gap-y-0 text-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="material-symbols-outlined text-secondary text-2xl md:text-3xl mb-4">straighten</span>
|
||||
<span className="text-secondary font-label text-[9px] tracking-[0.4em] mb-2 uppercase">{t('length')}</span>
|
||||
<span className="text-xl md:text-2xl font-headline tracking-widest">{yacht.length}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:border-l md:border-white/10">
|
||||
<span className="material-symbols-outlined text-secondary text-2xl md:text-3xl mb-4">groups</span>
|
||||
<span className="text-secondary font-label text-[9px] tracking-[0.4em] mb-2 uppercase">{t('guests')}</span>
|
||||
<span className="text-xl md:text-2xl font-headline tracking-widest">{yacht.guests}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:border-l md:border-white/10">
|
||||
<span className="material-symbols-outlined text-secondary text-2xl md:text-3xl mb-4">bed</span>
|
||||
<span className="text-secondary font-label text-[9px] tracking-[0.4em] mb-2 uppercase">{t('cabins')}</span>
|
||||
<span className="text-xl md:text-2xl font-headline tracking-widest">{yacht.cabins}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:border-l md:border-white/10">
|
||||
<span className="material-symbols-outlined text-secondary text-2xl md:text-3xl mb-4">support_agent</span>
|
||||
<span className="text-secondary font-label text-[9px] tracking-[0.4em] mb-2 uppercase">{t('crew')}</span>
|
||||
<span className="text-xl md:text-2xl font-headline tracking-widest">{yacht.crew}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center md:border-l md:border-white/10 col-span-2 md:col-span-1">
|
||||
<span className="material-symbols-outlined text-secondary text-2xl md:text-3xl mb-4">speed</span>
|
||||
<span className="text-secondary font-label text-[9px] tracking-[0.4em] mb-2 uppercase">{t('speed')}</span>
|
||||
<span className="text-xl md:text-2xl font-headline tracking-widest">{yacht.speed}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Editorial Content Section */}
|
||||
<section className="pt-24 md:pt-24 pb-32 md:pb-48 px-6 md:px-12 overflow-hidden flex flex-col items-center justify-center w-full">
|
||||
|
||||
{/* Intro Block - Full Width Centered */}
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={staggerContainer}
|
||||
className="max-w-4xl mx-auto text-center mb-32 md:mb-48"
|
||||
>
|
||||
<motion.span variants={fadeInUp} className="font-label text-xs tracking-[0.4em] text-secondary uppercase mb-6 block">
|
||||
{t('design_engineering')}
|
||||
</motion.span>
|
||||
<motion.h2 variants={fadeInUp} className="text-primary font-headline text-5xl md:text-8xl mb-8 leading-[1.05] tracking-tight">
|
||||
{t('sanctuary_title1')} <br />
|
||||
<span className="text-secondary italic">{t('sanctuary_title2')}</span>
|
||||
</motion.h2>
|
||||
<motion.div variants={fadeInUp} className="h-px w-16 bg-secondary mx-auto mb-12"></motion.div>
|
||||
|
||||
<motion.div variants={fadeInUp} className="flex justify-center gap-12 mb-12 font-label text-xs text-on-surface-variant uppercase tracking-widest">
|
||||
<div className="text-center">
|
||||
<span className="block text-[9px] text-outline mb-1">{t('builder')}</span>
|
||||
<span className="font-medium text-primary">{yacht.builder}</span>
|
||||
</div>
|
||||
<div className="w-px h-8 bg-outline-variant/20"></div>
|
||||
<div className="text-center">
|
||||
<span className="block text-[9px] text-outline mb-1">{t('year_refit')}</span>
|
||||
<span className="font-medium text-primary">{yacht.year} {yacht.refitYear && `(${yacht.refitYear})`}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.p variants={fadeInUp} className="font-body text-on-surface-variant font-light text-lg md:text-xl leading-[2] max-w-2xl mx-auto mb-16">
|
||||
{locale === 'tr' && yacht.description_tr ? yacht.description_tr : yacht.description}
|
||||
</motion.p>
|
||||
|
||||
<motion.div variants={fadeInUp}>
|
||||
<Link href="/contact" className="inline-block px-16 py-5 bg-secondary text-white font-headline tracking-[0.3em] uppercase text-xs hover:bg-primary transition-colors duration-500">
|
||||
{t('inquire_btn')}
|
||||
</Link>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
{/* Specifications Grid - Modern Cards */}
|
||||
<div className="max-w-[1440px] mx-auto space-y-32">
|
||||
|
||||
{/* 1. Construction & Design */}
|
||||
{(yacht.construction || yacht.furniture) && (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={staggerContainer}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<motion.div variants={fadeInUp} className="flex flex-col items-center text-center mb-16">
|
||||
<span className="font-headline text-7xl md:text-9xl text-outline-variant/8 font-bold leading-none mb-4">01</span>
|
||||
<h3 className="font-headline text-xl md:text-2xl tracking-[0.3em] text-primary uppercase">{t('design_construction').replace(/0\d\s\/\s/, '')}</h3>
|
||||
<div className="h-px w-12 bg-secondary mt-4"></div>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 w-full">
|
||||
{yacht.construction && Object.entries(yacht.construction).map(([key, value]) => (
|
||||
<motion.div
|
||||
key={key}
|
||||
variants={fadeInUp}
|
||||
className="bg-white p-8 border border-outline-variant/8 hover:border-secondary/30 hover:shadow-lg transition-all duration-500 group text-center"
|
||||
>
|
||||
<span className="font-label text-[9px] tracking-[0.3em] text-secondary uppercase block mb-3">
|
||||
{key.replace(/([A-Z])/g, ' $1').trim()}
|
||||
</span>
|
||||
<span className="font-headline text-base md:text-lg text-primary tracking-tight block">
|
||||
{value}
|
||||
</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* 2. Equipment on Board */}
|
||||
{yacht.equipment && yacht.equipment.length > 0 && (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={staggerContainer}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<motion.div variants={fadeInUp} className="flex flex-col items-center text-center mb-16">
|
||||
<span className="font-headline text-7xl md:text-9xl text-outline-variant/8 font-bold leading-none mb-4">02</span>
|
||||
<h3 className="font-headline text-xl md:text-2xl tracking-[0.3em] text-primary uppercase">{t('tech_equipment').replace(/0\d\s\/\s/, '')}</h3>
|
||||
<div className="h-px w-12 bg-secondary mt-4"></div>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 w-full">
|
||||
{yacht.equipment.map((item, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
variants={fadeInUp}
|
||||
className="flex items-center gap-5 px-6 py-5 bg-white border border-outline-variant/8 hover:border-secondary/20 hover:shadow-md transition-all duration-500 group"
|
||||
>
|
||||
<div className="w-8 h-8 flex items-center justify-center bg-surface border border-outline-variant/10 group-hover:bg-secondary/10 group-hover:border-secondary/20 transition-all duration-500 shrink-0">
|
||||
<span className="material-symbols-outlined text-secondary text-sm">check</span>
|
||||
</div>
|
||||
<span className="font-body text-sm text-primary/80 tracking-wide leading-snug">{item}</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* 3. Tenders & Toys */}
|
||||
{yacht.watersports && yacht.watersports.length > 0 && (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={staggerContainer}
|
||||
className="flex flex-col items-center"
|
||||
>
|
||||
<motion.div variants={fadeInUp} className="flex flex-col items-center text-center mb-16">
|
||||
<span className="font-headline text-7xl md:text-9xl text-outline-variant/8 font-bold leading-none mb-4">03</span>
|
||||
<h3 className="font-headline text-xl md:text-2xl tracking-[0.3em] text-primary uppercase">{t('water_toys').replace(/0\d\s\/\s/, '')}</h3>
|
||||
<div className="h-px w-12 bg-secondary mt-4"></div>
|
||||
</motion.div>
|
||||
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
{yacht.watersports.map((item, idx) => (
|
||||
<motion.div
|
||||
key={idx}
|
||||
variants={fadeInUp}
|
||||
className="px-8 py-5 bg-white border border-outline-variant/10 hover:border-secondary hover:shadow-lg transition-all duration-500 group cursor-default"
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="material-symbols-outlined text-secondary/40 text-lg group-hover:text-secondary transition-colors duration-500">anchor</span>
|
||||
<span className="font-headline text-xs tracking-[0.25em] text-primary uppercase group-hover:text-secondary transition-colors duration-500">{item}</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Atelier Gallery Section */}
|
||||
{yacht.gallery && yacht.gallery.length > 0 && (
|
||||
<section className="py-24 md:py-32 bg-white">
|
||||
<div className="flex flex-col items-center text-center mb-16">
|
||||
<span className="font-label text-xs tracking-[0.4em] text-secondary uppercase block mb-4">{t('atelier_experience')}</span>
|
||||
<h2 className="font-headline text-4xl md:text-5xl text-primary leading-tight uppercase tracking-widest">
|
||||
{t('gallery')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={staggerContainer}
|
||||
className="w-full max-w-[1600px] mx-auto px-4 md:px-8 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 auto-rows-[400px]"
|
||||
>
|
||||
{yacht.gallery.map((imgUrl, index) => {
|
||||
const isLarge = index === 0;
|
||||
return (
|
||||
<motion.div
|
||||
key={index}
|
||||
variants={fadeInUp}
|
||||
onClick={() => openLightbox(index)}
|
||||
className={`relative overflow-hidden group cursor-pointer ${isLarge ? 'md:col-span-2 lg:col-span-2 row-span-2' : 'col-span-1 row-span-1'}`}
|
||||
>
|
||||
<CldImage
|
||||
src={imgUrl}
|
||||
alt={`${yacht.name} Gallery ${index + 1}`}
|
||||
fill
|
||||
crop="fill"
|
||||
gravity="auto"
|
||||
className="object-cover transition-transform duration-1000 group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/10 group-hover:bg-transparent transition-colors duration-500 flex items-center justify-center">
|
||||
<span className="material-symbols-outlined text-white text-3xl opacity-0 group-hover:opacity-80 transition-opacity duration-500">zoom_in</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Charter Rates Section */}
|
||||
{yacht.prices && yacht.prices.length > 0 && (
|
||||
<section className="py-32 md:py-48 bg-[#0a0a0a] text-white overflow-hidden relative">
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none select-none opacity-[0.02] w-full text-center">
|
||||
<span className="font-headline text-[10rem] md:text-[20rem] font-bold leading-none tracking-tighter uppercase">RATES</span>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
variants={staggerContainer}
|
||||
className="max-w-6xl mx-auto px-6 md:px-12 relative z-10"
|
||||
>
|
||||
<div className="flex flex-col items-center text-center mb-24 border-b border-white/10 pb-16">
|
||||
<motion.span variants={fadeInUp} className="font-label text-xs tracking-[0.4em] text-secondary uppercase mb-6 block">
|
||||
Charter Investment
|
||||
</motion.span>
|
||||
<motion.h2 variants={fadeInUp} className="font-headline text-4xl md:text-6xl uppercase tracking-widest mb-6">
|
||||
{t('rates')}
|
||||
</motion.h2>
|
||||
<motion.p variants={fadeInUp} className="font-label text-xs tracking-widest text-white/50 uppercase">
|
||||
{t('rates_desc')}
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-px bg-white/10 border border-white/10">
|
||||
{yacht.prices.map((p) => (
|
||||
<motion.div key={p.month} variants={fadeInUp} className="bg-[#0a0a0a] p-12 hover:bg-white/5 transition-colors duration-500 flex flex-col items-center justify-center text-center group">
|
||||
<span className="block font-label text-[10px] tracking-[0.4em] text-secondary uppercase mb-4 transition-transform duration-500 group-hover:-translate-y-1">
|
||||
{p.month}
|
||||
</span>
|
||||
<span className="text-3xl md:text-4xl font-headline tracking-tighter text-white">
|
||||
{p.price}
|
||||
</span>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div variants={fadeInUp} className="mt-16 text-center">
|
||||
<p className="font-body text-xs text-white/40 leading-loose max-w-2xl mx-auto">
|
||||
* {t('apa_note')} <br />
|
||||
* {t('vat_note')}
|
||||
</p>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* Editorial Navigation */}
|
||||
<section className="py-32 md:py-48 bg-surface text-center px-6 flex flex-col items-center justify-center">
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={staggerContainer}
|
||||
className="flex flex-col items-center max-w-3xl"
|
||||
>
|
||||
<motion.span variants={fadeInUp} className="font-label text-xs tracking-[0.4em] text-secondary uppercase mb-8 block">
|
||||
{t('journey_title')}
|
||||
</motion.span>
|
||||
<motion.h2 variants={fadeInUp} className="text-primary font-headline text-4xl md:text-7xl mb-16 italic tracking-tight leading-[1.1]">
|
||||
{t('ready_title')}
|
||||
</motion.h2>
|
||||
<motion.div variants={fadeInUp} className="flex flex-col md:flex-row items-center justify-center gap-6 w-full">
|
||||
<Link href="/contact" className="px-12 py-5 bg-secondary text-white font-headline text-xs tracking-[0.3em] uppercase w-full md:w-auto hover:bg-primary transition-colors duration-500">
|
||||
{t('start_inquiry_btn')}
|
||||
</Link>
|
||||
<Link href="/fleet" className="px-12 py-5 border border-primary/20 text-primary font-headline text-xs tracking-[0.3em] uppercase w-full md:w-auto hover:bg-primary hover:text-white transition-colors duration-500">
|
||||
{t('explore_btn')}
|
||||
</Link>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
|
||||
{/* Lightbox Overlay */}
|
||||
<AnimatePresence>
|
||||
{lightboxIndex !== null && yacht.gallery && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="fixed inset-0 z-[100] bg-black/95 flex items-center justify-center"
|
||||
onClick={closeLightbox}
|
||||
>
|
||||
{/* Close Button */}
|
||||
<button
|
||||
onClick={closeLightbox}
|
||||
className="absolute top-8 right-8 text-white/60 hover:text-white transition-colors z-[110]"
|
||||
>
|
||||
<span className="material-symbols-outlined text-4xl">close</span>
|
||||
</button>
|
||||
|
||||
{/* Image Counter */}
|
||||
<div className="absolute top-8 left-8 font-label text-xs tracking-[0.3em] text-white/50 uppercase z-[110]">
|
||||
{lightboxIndex + 1} / {yacht.gallery.length}
|
||||
</div>
|
||||
|
||||
{/* Previous Arrow */}
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); goPrev(); }}
|
||||
className="absolute left-4 md:left-8 top-1/2 -translate-y-1/2 w-14 h-14 flex items-center justify-center text-white/40 hover:text-white transition-colors z-[110]"
|
||||
>
|
||||
<span className="material-symbols-outlined text-4xl">chevron_left</span>
|
||||
</button>
|
||||
|
||||
{/* Next Arrow */}
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); goNext(); }}
|
||||
className="absolute right-4 md:right-8 top-1/2 -translate-y-1/2 w-14 h-14 flex items-center justify-center text-white/40 hover:text-white transition-colors z-[110]"
|
||||
>
|
||||
<span className="material-symbols-outlined text-4xl">chevron_right</span>
|
||||
</button>
|
||||
|
||||
{/* Main Image */}
|
||||
<motion.div
|
||||
key={lightboxIndex}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="relative w-[90vw] h-[80vh] max-w-[1400px]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<CldImage
|
||||
src={yacht.gallery[lightboxIndex]}
|
||||
alt={`${yacht.name} Gallery ${lightboxIndex + 1}`}
|
||||
fill
|
||||
crop="pad"
|
||||
className="object-contain"
|
||||
sizes="90vw"
|
||||
priority
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
76
app/[locale]/fleet/page.tsx
Normal file
76
app/[locale]/fleet/page.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { yachts } from "../../data/yachts";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { useTranslations, useLocale } from "next-intl";
|
||||
import { CldImage } from "next-cloudinary";
|
||||
|
||||
export default function FleetPage() {
|
||||
const t = useTranslations('FleetList');
|
||||
const locale = useLocale();
|
||||
return (
|
||||
<div className="pt-40 pb-24 px-6 md:px-24 min-h-screen bg-surface">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row items-end gap-16 mb-24">
|
||||
<div className="w-full md:w-1/2">
|
||||
<span className="font-label text-xs tracking-[0.4em] text-secondary uppercase mb-4 block">{t('collection')}</span>
|
||||
<h1 className="font-headline text-5xl md:text-7xl text-primary leading-tight mb-8">
|
||||
{t('title1')} <br />
|
||||
<span className="text-secondary italic">{t('title2')}</span>
|
||||
</h1>
|
||||
<div className="h-px w-24 bg-secondary mb-8"></div>
|
||||
<p className="font-body text-on-surface-variant text-lg leading-relaxed font-light">
|
||||
{t('description')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full md:w-1/2 flex justify-end">
|
||||
<div className="text-right">
|
||||
<span className="font-label text-6xl md:text-8xl text-outline-variant/30 font-bold leading-none uppercase">{t('bg_text')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-40">
|
||||
{yachts.map((yacht, index) => (
|
||||
<div key={yacht.slug} className={`grid grid-cols-1 md:grid-cols-12 gap-0 group ${index % 2 !== 0 ? 'md:flex-row-reverse' : ''}`}>
|
||||
<div className={`md:col-span-7 relative h-[500px] md:h-[700px] overflow-hidden ${index % 2 !== 0 ? 'md:order-2' : ''}`}>
|
||||
<CldImage
|
||||
src={yacht.heroImage}
|
||||
alt={yacht.name}
|
||||
fill
|
||||
crop="fill"
|
||||
gravity="auto"
|
||||
className="w-full h-full object-cover transition-all duration-1000 scale-100 group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-primary/20 group-hover:bg-transparent transition-colors duration-700" />
|
||||
</div>
|
||||
<div className={`md:col-span-5 flex flex-col justify-center py-12 md:py-0 ${index % 2 !== 0 ? 'md:order-1 md:pr-20 md:text-right md:items-end' : 'md:pl-20'}`}>
|
||||
<span className="font-label text-xs tracking-[0.4em] text-secondary uppercase mb-4">{t('masterpiece')}{index + 1}</span>
|
||||
<h3 className="font-headline text-4xl md:text-5xl text-primary mb-8 tracking-wide uppercase">{yacht.name}</h3>
|
||||
<p className="text-on-surface-variant font-light leading-relaxed mb-12 max-w-sm">
|
||||
{locale === 'tr' && yacht.description_tr ? yacht.description_tr : yacht.description}
|
||||
</p>
|
||||
<div className="grid grid-cols-3 border-t border-outline-variant/20 pt-8 w-full text-left">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="material-symbols-outlined text-secondary text-xl">straighten</span>
|
||||
<span className="font-label text-[10px] tracking-widest text-outline uppercase mt-2">{t('length')}</span>
|
||||
<span className="font-body text-sm text-primary font-semibold">{yacht.length}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="material-symbols-outlined text-secondary text-xl">groups</span>
|
||||
<span className="font-label text-[10px] tracking-widest text-outline uppercase mt-2">{t('guests')}</span>
|
||||
<span className="font-body text-sm text-primary font-semibold">{yacht.guests} {t('guests_suffix')}</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Link href={`/fleet/${yacht.slug}`} className="mt-auto">
|
||||
<span className="font-label text-[10px] tracking-widest text-secondary border-b border-secondary pb-1 uppercase hover:opacity-70 smooth-transition">{t('discover')}</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user