Files
marmarislocal/app/[locale]/[category]/[slug]/page.tsx
T
2026-07-12 20:18:55 +03:00

273 lines
11 KiB
TypeScript

import { getTranslations, setRequestLocale } from 'next-intl/server'
import { mockDb } from '@/lib/mockDb'
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import ListingCard from '@/components/ListingCard'
import { notFound } from 'next/navigation'
import Image from 'next/image'
import { Phone, Globe, MapPin, Clock, Star, MessageSquare } from 'lucide-react'
interface DetailPageProps {
params: Promise<{ locale: string; category: string; slug: string }>
}
export default async function ListingDetailPage({ params }: DetailPageProps) {
const { locale, category, slug } = await params
setRequestLocale(locale)
const t = await getTranslations('detail')
const listing = await mockDb.getListingBySlug(slug)
if (!listing) {
notFound()
}
// Fetch similar listings in same category (limit to 3, excluding current)
const allCategoryListings = await mockDb.getListings({
categoryId: listing.categoryId
})
const relatedListings = allCategoryListings
.filter(l => l.id !== listing.id)
.slice(0, 3)
// Localized values
const name =
locale === 'ru'
? listing.nameRu
: locale === 'en'
? listing.nameEn
: listing.nameTr
const description =
locale === 'ru'
? listing.descriptionRu
: locale === 'en'
? listing.descriptionEn
: listing.descriptionTr
const priceSymbols = '₺'.repeat(listing.priceRange)
// Format WhatsApp Link
const getWhatsAppLink = (number: string) => {
// Clean spaces, parenthesis, plus sign
const cleanNum = number.replace(/\D/g, '')
return `https://wa.me/${cleanNum}`
}
return (
<div className="flex-1 flex flex-col min-h-screen bg-stone text-ink">
<Navbar />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10 flex-1">
{/* Breadcrumb / Category Link */}
<div className="mb-6 text-xs font-mono uppercase tracking-wider text-shutter flex items-center gap-2">
<span className="hover:text-turquoise transition-colors">marmaris local</span>
<span>/</span>
<span className="hover:text-turquoise transition-colors">
{locale === 'ru' ? listing.category?.nameRu : locale === 'en' ? listing.category?.nameEn : listing.category?.nameTr}
</span>
<span>/</span>
<span className="text-pine font-bold">{name}</span>
</div>
{/* Hero Details Block */}
<div className="bg-paper rounded-3xl border border-pine/8 p-6 sm:p-10 shadow-sm mb-10 space-y-8">
<div className="flex flex-col lg:flex-row gap-10">
{/* Left: Gallery Panel */}
<div className="flex-1 space-y-4">
<div className="aspect-[16/10] w-full relative rounded-2xl overflow-hidden bg-stone-deep shadow-sm">
<Image
src={listing.images && listing.images.length > 0 ? listing.images[0].url : 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=800&auto=format&fit=crop&q=80'}
alt={name}
fill
priority
className="object-cover"
/>
</div>
{/* Thumbnails if multiple images exist */}
{listing.images && listing.images.length > 1 && (
<div className="grid grid-cols-4 gap-4">
{listing.images.slice(1, 5).map((img, idx) => (
<div key={img.id} className="aspect-square relative rounded-xl overflow-hidden bg-stone-deep border border-pine/5 shadow-sm">
<Image
src={img.url}
alt={`${name} thumbnail ${idx + 1}`}
fill
className="object-cover hover:scale-105 transition duration-300"
/>
</div>
))}
</div>
)}
</div>
{/* Right: Info Panel */}
<div className="flex-1 flex flex-col justify-between space-y-6">
{/* Badge & Title */}
<div className="space-y-4">
<div className="flex flex-wrap gap-2.5 items-center">
<span className="text-[10px] font-mono text-bougainvillea font-bold uppercase tracking-wider bg-bougainvillea/5 border border-bougainvillea/10 px-2.5 py-1 rounded-full">
{locale === 'ru' ? listing.category?.nameRu : locale === 'en' ? listing.category?.nameEn : listing.category?.nameTr}
</span>
{listing.isLocalApproved && (
<span className="inline-flex items-center gap-1 text-[10px] font-mono text-turquoise font-bold uppercase tracking-wider bg-turquoise/5 border border-turquoise/10 px-2.5 py-1 rounded-full">
{t('approved')}
</span>
)}
</div>
<h1 className="font-heading font-extrabold text-2xl sm:text-4xl text-pine leading-tight lowercase">
{name}
</h1>
{/* Stars and Price level */}
<div className="flex items-center gap-6 font-mono text-sm border-b border-dashed border-pine/8 pb-4">
{listing.rating && (
<div className="flex items-center gap-1.5 text-gold font-bold">
<Star className="w-4 h-4 fill-gold stroke-gold" />
<span>{t('rating')}: {listing.rating.toFixed(1)}</span>
</div>
)}
<div className="text-pine font-semibold">
<span>{t('price')}: {priceSymbols}</span>
</div>
</div>
</div>
{/* Description */}
<div className="text-ink/80 text-sm leading-relaxed font-medium">
{description}
</div>
{/* Contact Actions */}
<div className="space-y-4">
<h3 className="font-heading font-bold text-xs uppercase tracking-wider text-shutter">{t('contact')}</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{listing.phone && (
<a
href={`tel:${listing.phone}`}
className="flex items-center justify-center gap-2 bg-pine hover:bg-pine/90 text-stone font-bold text-xs py-3.5 px-4 rounded-xl transition"
>
<Phone className="w-4 h-4" />
{t('call')}
</a>
)}
{listing.whatsapp && (
<a
href={getWhatsAppLink(listing.whatsapp)}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-2 bg-turquoise hover:bg-turquoise/90 text-paper font-bold text-xs py-3.5 px-4 rounded-xl transition"
>
<MessageSquare className="w-4 h-4" />
{t('whatsapp')}
</a>
)}
</div>
{/* External links */}
<div className="flex gap-4 pt-2 text-xs font-semibold text-shutter">
{listing.website && (
<a href={listing.website} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1 hover:text-turquoise transition">
<Globe className="w-4 h-4" />
{t('website')}
</a>
)}
{listing.instagram && (
<a href={listing.instagram} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1 hover:text-turquoise transition">
<Globe className="w-4 h-4" />
{t('instagram')}
</a>
)}
</div>
</div>
</div>
</div>
{/* Details footer (Hours & Location map) */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 pt-8 border-t border-dashed border-pine/12">
{/* Address */}
<div className="space-y-2">
<div className="flex items-center gap-2 font-heading font-bold text-xs text-pine uppercase tracking-wider">
<MapPin className="w-4 h-4 text-turquoise" />
<span>{t('address')}</span>
</div>
<p className="text-xs text-ink/75 font-medium leading-relaxed">
{listing.address}
</p>
</div>
{/* Hours */}
<div className="space-y-2">
<div className="flex items-center gap-2 font-heading font-bold text-xs text-pine uppercase tracking-wider">
<Clock className="w-4 h-4 text-turquoise" />
<span>{t('hours')}</span>
</div>
<div className="text-xs text-ink/75 font-medium">
{listing.openingHours ? (
<p>{(listing.openingHours as any).all || t('noHours')}</p>
) : (
<p>{t('noHours')}</p>
)}
</div>
</div>
{/* Map Frame */}
{listing.latitude && listing.longitude && (
<div className="space-y-2">
<div className="flex items-center gap-2 font-heading font-bold text-xs text-pine uppercase tracking-wider">
<Globe className="w-4 h-4 text-turquoise" />
<span>{t('location')}</span>
</div>
<div className="rounded-xl overflow-hidden border border-pine/8 aspect-[16/10] sm:aspect-auto sm:h-36">
<iframe
width="100%"
height="100%"
frameBorder="0"
scrolling="no"
marginHeight={0}
marginWidth={0}
src={`https://maps.google.com/maps?q=${listing.latitude},${listing.longitude}&t=&z=15&ie=UTF8&iwloc=&output=embed`}
className="w-full h-full shadow-sm"
/>
</div>
</div>
)}
</div>
</div>
{/* Related Section */}
{relatedListings.length > 0 && (
<div className="space-y-6 pt-10">
<h3 className="text-xl font-heading font-extrabold text-pine lowercase">
{t('related')}
</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-8">
{relatedListings.map((listingItem) => (
<ListingCard key={listingItem.id} listing={listingItem} />
))}
</div>
</div>
)}
</main>
<Footer />
</div>
)
}