This commit is contained in:
2026-04-19 17:23:31 +03:00
parent 9cad199125
commit 4f2188363a
122 changed files with 3215 additions and 116 deletions

View File

@@ -0,0 +1,120 @@
'use client'
import { motion } from "framer-motion"
import Image from "next/image"
import Link from "next/link"
import { Calendar, ArrowRight } from 'lucide-react'
export default function NewsClient({ lang, dict }: { lang: string, dict: any }) {
const newsItems = [
{
id: 'hidden-gems-oren',
title: dict.news_page.list.n1.title,
excerpt: dict.news_page.list.n1.excerpt,
image: 'https://images.unsplash.com/photo-1544124499-58912cbddaad?q=80&w=2127&auto=format&fit=crop',
date: 'April 15, 2026',
author: dict.news_page.list.n1.author
},
{
id: 'summer-cocktails-retreat',
title: dict.news_page.list.n2.title,
excerpt: dict.news_page.list.n2.excerpt,
image: 'https://images.unsplash.com/photo-1519046904884-53103b34b206?q=80&w=2073&auto=format&fit=crop',
date: 'April 10, 2026',
author: dict.news_page.list.n2.author
},
{
id: 'luxury-interior-trends',
title: dict.news_page.list.n3.title,
excerpt: dict.news_page.list.n3.excerpt,
image: 'https://images.unsplash.com/photo-1618773928121-c32242e63f39?q=80&w=1964&auto=format&fit=crop',
date: 'April 05, 2026',
author: dict.news_page.list.n3.author
}
]
return (
<main className="bg-[#FAF7F0] min-h-screen">
{/* HEADER SECTION */}
<section className="pt-44 pb-24 px-6">
<div className="max-w-5xl mx-auto text-center space-y-10">
<motion.h1
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 1 }}
className="text-7xl md:text-[120px] font-serif text-[#1A1A1A] leading-[0.9] tracking-tight uppercase"
>
{dict.news_page.title}
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
className="text-[#1A1A1A]/70 text-lg md:text-xl max-w-3xl mx-auto font-medium leading-relaxed italic"
>
{dict.news_page.subtitle}
</motion.p>
</div>
</section>
{/* ARTICLES GRID SECTION */}
<section className="pb-44 px-6 md:px-12 max-w-[1400px] mx-auto">
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
{newsItems.map((article, idx) => (
<motion.div
key={article.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.8, delay: idx * 0.15 }}
className="group cursor-pointer"
>
<Link href={`/${lang}/news/${article.id}`}>
<div className="space-y-8">
{/* Image Card */}
<div className="aspect-[4/5] relative overflow-hidden rounded-[2px] shadow-sm">
<Image
src={article.image}
alt={article.title}
fill
className="object-cover transition-transform duration-1000 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-black/5 group-hover:bg-transparent transition-colors duration-500" />
</div>
{/* Metadata and Title */}
<div className="space-y-4">
<div className="flex items-center space-x-6 text-[11px] font-bold tracking-[0.3em] uppercase text-[#1A1A1A]/40 mb-2">
<span className="flex items-center space-x-2">
<Calendar size={14} className="opacity-50" />
<span>{article.date}</span>
</span>
</div>
<h2 className="text-3xl md:text-4xl font-serif text-[#1A1A1A] leading-tight group-hover:text-[#C88C4B] transition-colors duration-300">
{article.title}
</h2>
<p className="text-[#1A1A1A]/60 text-lg leading-relaxed italic line-clamp-2">
{article.excerpt}
</p>
<div className="inline-flex items-center space-x-2 text-[12px] font-bold tracking-widest uppercase text-[#1A1A1A] pt-2 border-b-2 border-transparent group-hover:border-[#C88C4B] transition-all pb-1">
<span>{dict.news_page.read}</span>
<ArrowRight size={16} className="transform group-hover:translate-x-1 transition-transform" />
</div>
</div>
</div>
</Link>
</motion.div>
))}
</div>
</section>
</main>
)
}

View File

@@ -0,0 +1,157 @@
'use client'
import { motion } from "framer-motion"
import Image from "next/image"
import Link from "next/link"
import { Calendar, User, Share2, ArrowLeft } from 'lucide-react'
export default function NewsDetailClient({ lang, slug, dict }: { lang: string, slug: string, dict: any }) {
// Use dictionary instead of hardcoded data where possible
// For demo, we still use a local map but referring to Ören
const newsData: Record<string, any> = {
'hidden-gems-oren': {
title: dict.news_page.list.n1.title,
date: 'April 15, 2026',
author: dict.news_page.list.n1.author,
category: 'Travel Guide',
image: 'https://images.unsplash.com/photo-1544124499-58912cbddaad?q=80&w=2127&auto=format&fit=crop',
content: [
{ type: 'paragraph', text: dict.news_page.list.n1.excerpt + ' ' + (lang === 'tr' ? 'Örenin saklı koyları, zamanın ötesinde bir huzur sunuyor. Turkuaz suların üzerinden sabah sisi kalkarken, standart haritaların ötesine bakmaya istekli olanlar için gizli sığınaklar kendilerini göstermeye başlıyor. Bugünkü yolculuğumuz bizi Egenin saklı kalbinde bir keşfe çıkarıyor.' : 'Beyond the crowded beaches, the secret coves of Oren offer a peace beyond time. As the morning mist lifts from the turquoise waters, hidden havens begin to reveal themselves to those willing to look beyond standard maps. Our journey today takes us on an exploration in the hidden heart of the Aegean.') },
{ type: 'quote', text: lang === 'tr' ? 'Gerçek keşif yolculuğu yeni manzaralar aramak değil, yeni gözlere sahip olmaktan geçer.' : 'The real voyage of discovery consists not in seeking new landscapes, but in having new eyes.' },
{ type: 'paragraph', text: lang === 'tr' ? 'Gemile koyunun sessiz kıyılarında, zeytinlikler arasında sessizce oturan antik kalıntılar bunlardan sadece biri. Bu sessiz taşların arasında yürürken, Bizanslı tüccarların yankılarını neredeyse duyabilirsiniz. Burası, tarihin, doğanın ve sessiz lüksün harmanlandığı, Ayris Apart\'ın gerçek özünü bulduğumuz yerdir.' : 'The ancient ruins sitting quietly amidst olive groves on the silent shores of Gemile Bay are just one of them. Walking among these silent stones, you can almost hear the echoes of Byzantine merchants. This is where we find the true essence of Ayris Apart, where history, nature, and silent luxury blend.' },
{ type: 'image', url: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=2073&auto=format&fit=crop' }
]
},
'summer-cocktails-retreat': {
title: dict.news_page.list.n2.title,
date: 'April 10, 2026',
author: dict.news_page.list.n2.author,
category: 'Lifestyle',
image: 'https://images.unsplash.com/photo-1519046904884-53103b34b206?q=80&w=2073&auto=format&fit=crop',
content: [
{ type: 'paragraph', text: dict.news_page.list.n2.excerpt }
]
},
'luxury-interior-trends': {
title: dict.news_page.list.n3.title,
date: 'April 05, 2026',
author: dict.news_page.list.n3.author,
category: 'Design',
image: 'https://images.unsplash.com/photo-1618773928121-c32242e63f39?q=80&w=1964&auto=format&fit=crop',
content: [
{ type: 'paragraph', text: dict.news_page.list.n3.excerpt }
]
}
}
const post = newsData[slug] || newsData['hidden-gems-oren']
return (
<main className="bg-[#FAF7F0] min-h-screen">
{/* HEADER SECTION */}
<section className="pt-44 pb-20 px-6">
<div className="max-w-4xl mx-auto text-center space-y-8">
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="flex items-center justify-center space-x-6 text-[11px] font-bold tracking-[0.4em] uppercase text-[#1A1A1A]/40"
>
<span>{post.category}</span>
<span className="w-1 h-1 bg-[#C88C4B] rounded-full" />
<span>{post.date}</span>
</motion.div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="text-5xl md:text-7xl lg:text-[88px] font-serif text-[#1A1A1A] leading-[1.1] tracking-tight uppercase"
>
{post.title}
</motion.h1>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4 }}
className="flex items-center justify-center space-x-4 pt-4"
>
<div className="w-12 h-12 rounded-full bg-[#1A1A1A]/5 flex items-center justify-center text-[#1A1A1A]/40 overflow-hidden">
<User size={24} />
</div>
<div className="text-left leading-tight">
<p className="text-[11px] font-bold tracking-widest uppercase text-[#1A1A1A]/30 mb-1">Written By</p>
<p className="text-sm font-medium text-[#1A1A1A]">{post.author}</p>
</div>
</motion.div>
</div>
</section>
{/* FEATURE IMAGE */}
<section className="px-6 md:px-12 max-w-[1400px] mx-auto overflow-hidden">
<motion.div
initial={{ opacity: 0, scale: 1.05 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 1.5 }}
className="aspect-[21/9] relative rounded-[2px]"
>
<Image src={post.image} alt={post.title} fill className="object-cover" />
</motion.div>
</section>
{/* CONTENT SECTION */}
<section className="py-24 px-6">
<div className="max-w-2xl mx-auto space-y-12">
{post.content.map((block: any, idx: number) => {
if (block.type === 'paragraph') {
return (
<p key={idx} className={`text-[#1A1A1A]/80 text-xl leading-relaxed italic ${idx === 0 ? 'first-letter:text-7xl first-letter:font-serif first-letter:mr-3 first-letter:float-left first-letter:leading-[0.8] first-letter:mt-1' : ''}`}>
{block.text}
</p>
)
}
if (block.type === 'quote') {
return (
<blockquote key={idx} className="border-l-4 border-[#C88C4B] pl-8 py-4 my-16">
<p className="text-3xl font-serif text-[#1A1A1A] leading-snug">
&ldquo;{block.text}&rdquo;
</p>
</blockquote>
)
}
if (block.type === 'image') {
return (
<div key={idx} className="my-16 -mx-6 md:-mx-24 aspect-video relative overflow-hidden rounded-[2px] shadow-2xl">
<Image src={block.url} alt="Article imagery" fill className="object-cover" />
</div>
)
}
return null
})}
{/* SHARE & BACK */}
<div className="pt-16 border-t border-[#1A1A1A]/10 flex items-center justify-between">
<Link
href={`/${lang}/news`}
className="inline-flex items-center space-x-2 text-[12px] font-bold tracking-widest uppercase text-[#1A1A1A]/40 hover:text-[#1A1A1A] transition-colors"
>
<ArrowLeft size={16} />
<span>{lang === 'tr' ? 'Haberlere Dön' : 'Back to News'}</span>
</Link>
<div className="flex items-center space-x-4">
<span className="text-[11px] font-bold tracking-widest uppercase text-[#1A1A1A]/40">Share</span>
<button className="w-10 h-10 rounded-full border border-[#1A1A1A]/10 flex items-center justify-center hover:bg-[#1A1A1A] hover:text-white transition-all">
<Share2 size={16} />
</button>
</div>
</div>
</div>
</section>
</main>
)
}

View File

@@ -0,0 +1,20 @@
import { getDictionary } from "@/dictionaries/get-dictionary"
import NewsDetailClient from "./NewsDetailClient"
export async function generateMetadata({ params }: { params: Promise<{ lang: string, slug: string }> }) {
const { lang, slug } = await params
const dict = await getDictionary(lang as 'en' | 'tr')
// Dynamic title based on slug if possible, or just section title
return {
title: `News - Ayris Apart`,
description: dict.news_page.subtitle,
}
}
export default async function NewsDetailPage({ params }: { params: Promise<{ lang: string, slug: string }> }) {
const { lang, slug } = await params
const dict = await getDictionary(lang as 'en' | 'tr')
return <NewsDetailClient lang={lang} slug={slug} dict={dict} />
}

18
app/[lang]/news/page.tsx Normal file
View File

@@ -0,0 +1,18 @@
import { getDictionary } from "@/dictionaries/get-dictionary"
import NewsClient from "./NewsClient"
export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const dict = await getDictionary(lang as 'en' | 'tr')
return {
title: `${dict.news_page.title} - Ayris Apart`,
description: dict.news_page.subtitle,
}
}
export default async function NewsPage({ params }: { params: Promise<{ lang: string }> }) {
const { lang } = await params
const dict = await getDictionary(lang as 'en' | 'tr')
return <NewsClient lang={lang} dict={dict} />
}