235 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
}