235 lines
8.3 KiB
TypeScript
Raw Normal View History

import { prisma } from "@/lib/prisma";
import { redirect } from "next/navigation";
import { auth } from "@/lib/auth";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import Link from "next/link";
export default async function ListingDetailPage({
params,
}: {
params: { slug: string };
}) {
const session = await auth();
if (!session) {
redirect("/login");
}
const listing = await prisma.listing.findUnique({
where: { slug: params.slug },
include: {
images: true,
notes: true,
sleepingOptions: true,
tags: {
include: {
tag: true,
},
},
},
});
if (!listing) {
redirect("/listings");
}
return (
<div className="min-h-screen bg-slate-50 p-8">
<div className="container mx-auto max-w-6xl">
<Link
href="/listings"
className="text-blue-600 hover:underline mb-6 inline-block"
>
Zurück zur Übersicht
</Link>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Main Content */}
<div className="lg:col-span-2 space-y-6">
{/* Images */}
<Card>
<CardContent className="p-0 overflow-hidden">
{listing.coverImage ? (
<img
src={listing.coverImage}
alt={listing.title}
className="w-full h-96 object-cover"
/>
) : (
<div className="w-full h-96 bg-slate-200 flex items-center justify-center">
Kein Bild
</div>
)}
</CardContent>
</Card>
{/* Details */}
<Card>
<CardContent className="p-6">
<h2 className="text-xl font-semibold mb-4">📋 Details</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div>
<p className="text-sm text-slate-500">Schlafzimmer</p>
<p className="text-xl font-bold">{listing.bedrooms || "—"}</p>
</div>
<div>
<p className="text-sm text-slate-500">Betten</p>
<p className="text-xl font-bold">{listing.beds || "—"}</p>
</div>
<div>
<p className="text-sm text-slate-500">Badezimmer</p>
<p className="text-xl font-bold">{listing.bathrooms || "—"}</p>
</div>
<div>
<p className="text-sm text-slate-500">Max Gäste</p>
<p className="text-xl font-bold">{listing.maxSleepingPlaces || "—"}</p>
</div>
</div>
{listing.description && (
<div>
<h3 className="font-medium mb-2">Beschreibung</h3>
<p className="text-slate-600">{listing.description}</p>
</div>
)}
</CardContent>
</Card>
{/* Sleep Analysis */}
<Card>
<CardContent className="p-6">
<h2 className="text-xl font-semibold mb-4">🛏 Schlafplatz-Analyse</h2>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="p-4 bg-slate-50 rounded">
<p className="text-sm text-slate-500">4 Personen geeignet</p>
<p className={`text-2xl font-bold ${listing.suitableFor4 ? "text-green-600" : "text-red-500"}`}>
{listing.suitableFor4 ? "✅ Ja" : "❌ Nein"}
</p>
</div>
<div className="p-4 bg-slate-50 rounded">
<p className="text-sm text-slate-500">Extra Matratzen für 4</p>
<p className={`text-2xl font-bold ${listing.extraMattressesNeededFor4 === 0 ? "text-green-600" : "text-amber-600"}`}>
{listing.extraMattressesNeededFor4 ?? "—"}
</p>
</div>
</div>
{listing.sleepingOptions.length > 0 && (
<div>
<h3 className="font-medium mb-2">Schlafmöglichkeiten</h3>
<div className="space-y-2">
{listing.sleepingOptions.map((opt) => (
<div key={opt.id} className="flex justify-between p-2 bg-slate-50 rounded">
<span>{opt.label || opt.bedType}</span>
<span className="font-medium">
{opt.quantity}× ({opt.spotsPerUnit} Plätze)
</span>
</div>
))}
</div>
</div>
)}
</CardContent>
</Card>
{/* Notes */}
{listing.notes.length > 0 && (
<Card>
<CardContent className="p-6">
<h2 className="text-xl font-semibold mb-4">📝 Notizen</h2>
<div className="space-y-3">
{listing.notes.map((note) => (
<div key={note.id} className="p-3 bg-slate-50 rounded">
<p className="text-sm">{note.body}</p>
<p className="text-xs text-slate-400 mt-1">
{new Date(note.createdAt).toLocaleDateString("de-DE")}
</p>
</div>
))}
</div>
</CardContent>
</Card>
)}
</div>
{/* Sidebar */}
<div className="space-y-6">
{/* Price Card */}
<Card>
<CardContent className="p-6">
<h1 className="text-2xl font-bold mb-2">{listing.title}</h1>
<p className="text-slate-500 mb-4">📍 {listing.locationText || "Kein Ort"}</p>
<div className="flex items-baseline gap-2 mb-4">
<span className="text-4xl font-bold">
{listing.nightlyPrice?.toFixed(2) || "—"}
</span>
<span className="text-slate-500">/ Nacht</span>
</div>
<div className="flex items-center gap-2 mb-4">
<span className="text-xl"></span>
<span className="font-semibold">{listing.rating?.toFixed(2) || "—"}</span>
{listing.reviewCount && (
<span className="text-slate-500">
({listing.reviewCount} Bewertungen)
</span>
)}
</div>
{listing.hostName && (
<p className="text-sm text-slate-500 mb-4">
👤 Host: {listing.hostName}
</p>
)}
<div className="flex flex-wrap gap-2 mb-4">
{listing.tags.map((lt) => (
<Badge
key={lt.tag.id}
style={{ backgroundColor: lt.tag.color || "#6366f1" }}
className="text-white"
>
{lt.tag.name}
</Badge>
))}
</div>
<a
href={listing.airbnbUrl}
target="_blank"
rel="noopener noreferrer"
className="block w-full bg-red-500 text-white text-center py-3 rounded-lg hover:bg-red-600 transition"
>
🔗 Auf Airbnb ansehen
</a>
</CardContent>
</Card>
{/* Status */}
<Card>
<CardContent className="p-6">
<h3 className="font-semibold mb-2">Status</h3>
<Badge
className={
listing.status === "SHORTLIST"
? "bg-green-500"
: listing.status === "REJECTED"
? "bg-red-500"
: "bg-slate-500"
}
>
{listing.status}
</Badge>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
);
}