Update application features and add scripts (core changes)
This commit is contained in:
@@ -3,6 +3,8 @@ import { Link } from '@/i18n/routing'
|
||||
import Image from 'next/image'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { CheckCircle2, Maximize, Users, BedDouble, ArrowRight } from 'lucide-react'
|
||||
import { db } from '@/lib/db'
|
||||
import openinaryLoader from '@/lib/openinary-loader'
|
||||
|
||||
// Bu sayfa parametre olarak dinamik slug alacak
|
||||
export default async function RoomDetailPage({ params }: { params: Promise<{ locale: string, slug: string }> }) {
|
||||
@@ -11,77 +13,61 @@ export default async function RoomDetailPage({ params }: { params: Promise<{ loc
|
||||
|
||||
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 = await db.room.findUnique({
|
||||
where: { slug }
|
||||
})
|
||||
|
||||
const room = MOCK_ROOMS.find(r => r.slug === slug);
|
||||
|
||||
if (!room) {
|
||||
if (!room || !room.available) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const name = locale === 'tr' ? room.nameTr : locale === 'de' ? room.nameDe : room.nameEn
|
||||
const description = locale === 'tr' ? room.descriptionTr : locale === 'de' ? room.descriptionDe : room.descriptionEn
|
||||
const image = room.imageUrl || 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80'
|
||||
const isExternalImage = image.startsWith('http') && !image.includes('media.ayris.tech')
|
||||
const gallery = room.images && room.images.length > 0 ? room.images : [image]
|
||||
|
||||
// Size helper mapping based on room type
|
||||
const getRoomSize = (type: string) => {
|
||||
if (type === 'STUDIO_1_0') return '25m²'
|
||||
if (type === 'SUITE_1_1') return '45m²'
|
||||
return '20m²'
|
||||
}
|
||||
|
||||
// Bed helper based on capacity/type roughly
|
||||
const getBeds = (type: string, capacity: number) => {
|
||||
if (type === 'STUDIO_1_0') return '1'
|
||||
if (type === 'SUITE_1_1') return '2'
|
||||
if (capacity === 3) return '1 + Ek Yatak'
|
||||
return '1'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-background min-h-screen text-on-surface">
|
||||
{/* Hero Section */}
|
||||
<section className="relative h-[60vh] md:h-[70vh] w-full">
|
||||
<Image
|
||||
src={room.image}
|
||||
alt={room.name}
|
||||
fill
|
||||
sizes="100vw"
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
{isExternalImage ? (
|
||||
<img
|
||||
src={image}
|
||||
alt={name}
|
||||
className="object-cover w-full h-full"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={image}
|
||||
alt={name}
|
||||
fill
|
||||
loader={openinaryLoader}
|
||||
sizes="100vw"
|
||||
className="object-cover"
|
||||
priority
|
||||
/>
|
||||
)}
|
||||
<div className="absolute inset-0 bg-black/40" />
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="font-serif text-[48px] md:text-[64px] text-white font-bold drop-shadow-md">
|
||||
{room.name}
|
||||
{name}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,55 +88,71 @@ export default async function RoomDetailPage({ params }: { params: Promise<{ loc
|
||||
</div>
|
||||
<div className="flex items-center gap-2 bg-surface-container px-4 py-2 rounded-full text-secondary font-medium">
|
||||
<Maximize className="w-5 h-5 text-primary" />
|
||||
<span>{room.size}</span>
|
||||
<span>{getRoomSize(room.type)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 bg-surface-container px-4 py-2 rounded-full text-secondary font-medium">
|
||||
<BedDouble className="w-5 h-5 text-primary" />
|
||||
<span>{room.beds} Yatak</span>
|
||||
<span>{getBeds(room.type, room.capacity)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="prose prose-lg text-on-surface-variant">
|
||||
<p className="text-[18px] leading-relaxed">
|
||||
{room.description}
|
||||
<p className="text-[18px] leading-relaxed whitespace-pre-wrap">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Amenities */}
|
||||
<div>
|
||||
<h3 className="font-serif text-[28px] text-secondary font-semibold mb-6">
|
||||
{tRooms('details.features')}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{room.amenities.map((amenity, index) => (
|
||||
<div key={index} className="flex items-center gap-3">
|
||||
<CheckCircle2 className="w-5 h-5 text-accent-green flex-shrink-0" />
|
||||
<span className="text-on-surface-variant">{amenity}</span>
|
||||
</div>
|
||||
))}
|
||||
{room.amenities.length > 0 && (
|
||||
<div>
|
||||
<h3 className="font-serif text-[28px] text-secondary font-semibold mb-6">
|
||||
{tRooms('details.features')}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{room.amenities.map((amenity, index) => (
|
||||
<div key={index} className="flex items-center gap-3">
|
||||
<CheckCircle2 className="w-5 h-5 text-accent-green flex-shrink-0" />
|
||||
<span className="text-on-surface-variant">{amenity}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mini Gallery */}
|
||||
<div>
|
||||
<h3 className="font-serif text-[28px] text-secondary font-semibold mb-6">
|
||||
{tRooms('details.gallery')}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
{room.gallery.map((img, i) => (
|
||||
<div key={i} className="relative aspect-square rounded-2xl overflow-hidden shadow-sm hover:shadow-md transition-shadow">
|
||||
<Image
|
||||
src={img}
|
||||
alt={`${room.name} gallery ${i + 1}`}
|
||||
fill
|
||||
sizes="(max-width: 640px) 100vw, 33vw"
|
||||
className="object-cover hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{gallery.length > 0 && (
|
||||
<div>
|
||||
<h3 className="font-serif text-[28px] text-secondary font-semibold mb-6">
|
||||
{tRooms('details.gallery')}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
{gallery.map((img, i) => {
|
||||
const isGalleryExternal = img.startsWith('http') && !img.includes('media.ayris.tech')
|
||||
return (
|
||||
<div key={i} className="relative aspect-square rounded-2xl overflow-hidden shadow-sm hover:shadow-md transition-shadow">
|
||||
{isGalleryExternal ? (
|
||||
<img
|
||||
src={img}
|
||||
alt={`${name} gallery ${i + 1}`}
|
||||
className="object-cover w-full h-full hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={img}
|
||||
alt={`${name} gallery ${i + 1}`}
|
||||
fill
|
||||
loader={openinaryLoader}
|
||||
sizes="(max-width: 640px) 100vw, 33vw"
|
||||
className="object-cover hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { getTranslations, setRequestLocale } from 'next-intl/server'
|
||||
import { Link } from '@/i18n/routing'
|
||||
import Image from 'next/image'
|
||||
import { Users, Square, Info } from 'lucide-react'
|
||||
import { db } from '@/lib/db'
|
||||
import openinaryLoader from '@/lib/openinary-loader'
|
||||
|
||||
export default async function RoomsPage({ params }: { params: Promise<{ locale: string }> }) {
|
||||
const { locale } = await params
|
||||
@@ -10,35 +12,17 @@ export default async function RoomsPage({ params }: { params: Promise<{ 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']
|
||||
}
|
||||
];
|
||||
const dbRooms = await db.room.findMany({
|
||||
where: { available: true },
|
||||
orderBy: { createdAt: 'asc' }
|
||||
})
|
||||
|
||||
// Size helper mapping based on room type
|
||||
const getRoomSize = (type: string) => {
|
||||
if (type === 'STUDIO_1_0') return '25m²'
|
||||
if (type === 'SUITE_1_1') return '45m²'
|
||||
return '20m²'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-1 bg-gray-50 py-12 pt-32">
|
||||
@@ -51,55 +35,73 @@ export default async function RoomsPage({ params }: { params: Promise<{ locale:
|
||||
</div>
|
||||
|
||||
<div className="space-y-12 max-w-5xl mx-auto">
|
||||
{MOCK_ROOMS.map((room) => (
|
||||
<div key={room.slug} className="bg-white rounded-2xl overflow-hidden border shadow-sm flex flex-col md:flex-row group">
|
||||
<div className="relative w-full md:w-2/5 h-64 md:h-auto bg-gray-100">
|
||||
<Image
|
||||
src={room.image}
|
||||
alt={room.name}
|
||||
fill
|
||||
sizes="(max-width: 768px) 100vw, 40vw"
|
||||
className="object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-6 md:p-8 flex-1 flex flex-col">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<h2 className="text-2xl font-bold text-gray-900">{room.name}</h2>
|
||||
{dbRooms.map((room) => {
|
||||
const name = locale === 'tr' ? room.nameTr : locale === 'de' ? room.nameDe : room.nameEn
|
||||
const description = locale === 'tr' ? room.descriptionTr : locale === 'de' ? room.descriptionDe : room.descriptionEn
|
||||
const image = room.imageUrl || 'https://images.unsplash.com/photo-1631049307264-da0ec9d70304?auto=format&fit=crop&q=80'
|
||||
const isExternal = image.startsWith('http') && !image.includes('media.ayris.tech')
|
||||
|
||||
return (
|
||||
<div key={room.id} className="bg-white rounded-2xl overflow-hidden border shadow-sm flex flex-col md:flex-row group">
|
||||
<div className="relative w-full md:w-2/5 h-64 md:h-auto bg-gray-100">
|
||||
{isExternal ? (
|
||||
<img
|
||||
src={image}
|
||||
alt={name}
|
||||
className="object-cover w-full h-full group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
) : (
|
||||
<Image
|
||||
src={image}
|
||||
alt={name}
|
||||
fill
|
||||
loader={openinaryLoader}
|
||||
sizes="(max-width: 768px) 100vw, 40vw"
|
||||
className="object-cover group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-6 flex-1">
|
||||
{room.description}
|
||||
</p>
|
||||
<div className="p-6 md:p-8 flex-1 flex flex-col">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<h2 className="text-2xl font-bold text-gray-900">{name}</h2>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-600 mb-6 flex-1 line-clamp-3">
|
||||
{description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-4 mb-8">
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-500 font-medium">
|
||||
<Users className="w-4 h-4 text-[#2FB6C4]" />
|
||||
<span>{room.capacity} {tRooms('capacity')}</span>
|
||||
<div className="flex flex-wrap gap-4 mb-8">
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-500 font-medium">
|
||||
<Users className="w-4 h-4 text-[#2FB6C4]" />
|
||||
<span>{room.capacity} {tRooms('capacity')}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-500 font-medium">
|
||||
<Square className="w-4 h-4 text-[#2FB6C4]" />
|
||||
<span>{getRoomSize(room.type)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-500 font-medium">
|
||||
<Info className="w-4 h-4 text-[#2FB6C4]" />
|
||||
<span>{room.amenities.length} {tRooms('amenities')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-500 font-medium">
|
||||
<Square className="w-4 h-4 text-[#2FB6C4]" />
|
||||
<span>{room.size}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-sm text-gray-500 font-medium">
|
||||
<Info className="w-4 h-4 text-[#2FB6C4]" />
|
||||
<span>{room.amenities.length} {tRooms('amenities')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between mt-auto border-t pt-6">
|
||||
<div className="text-sm text-gray-500">
|
||||
<span className="font-bold text-gray-900 text-lg">{tRooms('ask_price')}</span> {tRooms('per_night')}
|
||||
<div className="flex items-center justify-between mt-auto border-t pt-6">
|
||||
<div className="text-sm text-gray-500">
|
||||
<span className="font-bold text-gray-900 text-lg">
|
||||
{room.price ? `${room.price}₺` : tRooms('ask_price')}
|
||||
</span> {room.price ? '' : tRooms('per_night')}
|
||||
</div>
|
||||
<Link
|
||||
href={`/dairelerimiz/${room.slug}`}
|
||||
className="px-6 py-2.5 bg-[#2FB6C4] hover:bg-[#2597a3] text-white rounded-md font-medium transition-colors duration-300 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2FB6C4] focus-visible:ring-offset-2"
|
||||
>
|
||||
{tRooms('details_btn')}
|
||||
</Link>
|
||||
</div>
|
||||
<Link
|
||||
href={`/dairelerimiz/${room.slug}`}
|
||||
className="px-6 py-2.5 bg-[#2FB6C4] hover:bg-[#2597a3] text-white rounded-md font-medium transition-colors duration-300 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[#2FB6C4] focus-visible:ring-offset-2"
|
||||
>
|
||||
{tRooms('details_btn')}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user