Files
MarkusandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2b24334bfa Initial upload of BB-Apartment 2026 website.
Adds the complete Next.js website including i18n routing, house and
apartment pages, Beds24 v2 integration for live availability/pricing,
Stripe checkout, contact forms, Docker setup and deployment docs.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-19 22:54:49 +02:00

207 lines
8.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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 { notFound } from "next/navigation";
import { useTranslations } from "next-intl";
import { getTranslations } from "next-intl/server";
import { getHouseBySlug } from "@/src/config/apartments";
import { ApartmentCard } from "@/components/ui/ApartmentCard";
import { HouseHighlights } from "@/components/ui/HouseHighlights";
import { MapPin, Home, Car, Clock, Navigation } from "lucide-react";
import Image from "next/image";
export async function generateMetadata({
params: { locale, houseSlug },
}: {
params: { locale: string; houseSlug: string };
}) {
const house = getHouseBySlug(houseSlug);
if (!house) return { title: "Not Found" };
const t = await getTranslations({ locale, namespace: "metadata" });
return {
title: `${house.nameEn} - ${t("title")}`,
description: house.descriptionEn,
};
}
export default function HousePage({
params: { locale, houseSlug },
}: {
params: { locale: string; houseSlug: string };
}) {
const t = useTranslations("houses");
const house = getHouseBySlug(houseSlug);
if (!house) notFound();
const displayName = locale === "de" ? house.name : house.nameEn;
const displayDescription =
locale === "de" ? house.description : house.descriptionEn;
const displayCountry = locale === "de" ? house.country : house.countryEn;
return (
<div className="py-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="relative h-80 md:h-96 rounded-2xl overflow-hidden mb-12">
<Image
src={house.images[0] || "/images/placeholder.jpg"}
alt={displayName}
fill
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
<div className="absolute bottom-0 left-0 p-8 text-white">
<h1 className="text-4xl font-bold mb-2 text-white">{displayName}</h1>
<div className="flex items-center gap-2 text-white/90">
<MapPin className="w-5 h-5" />
<span>
{house.address}, {house.zipCode} {house.city}, {displayCountry}
</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
<div className="lg:col-span-2">
<h2 className="text-2xl font-bold mb-4">
{t("overview")}
</h2>
<p className="text-brand-grey leading-relaxed mb-8">
{displayDescription}
</p>
<HouseHighlights house={house} locale={locale} />
<h2 className="text-2xl font-bold mb-6">
{t("apartments")}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{house.apartments.map((apartment) => (
<ApartmentCard
key={apartment.id}
apartment={apartment}
locale={locale}
/>
))}
</div>
</div>
<div className="flex flex-col gap-8 h-fit">
<div className="bg-brand-grey-soft rounded-2xl p-8 h-fit">
<div className="flex items-center gap-3 mb-4">
<Home className="w-6 h-6 text-brand-green" />
<h3 className="text-xl font-bold">{displayName}</h3>
</div>
<p className="text-brand-grey text-sm mb-4">
{house.apartments.length} {locale === "de" ? "Apartments" : "Apartments"}
</p>
<div className="text-sm text-brand-grey">
<p className="mb-2">
<strong>{locale === "de" ? "Adresse:" : "Address:"}</strong> {house.address}
</p>
<p>
<strong>{locale === "de" ? "Stadt:" : "City:"}</strong> {house.zipCode} {house.city}
</p>
</div>
</div>
<div className="bg-white rounded-2xl border border-brand-grey-soft shadow-sm p-6 h-fit">
<div className="flex items-center gap-2 mb-4">
<Navigation className="w-5 h-5 text-brand-green" />
<h2 className="text-lg font-bold">
{locale === "de" ? "Lage & Entfernungen" : "Location & Distances"}
</h2>
</div>
<div className="flex flex-col gap-3 mb-4">
{house.id === "herdweg" ? (
<>
<a
href="https://www.google.com/maps/dir/Herdweg+71,+71032+Böblingen/Panzer+Kaserne+Böblingen"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 p-3 bg-brand-green text-white rounded-xl hover:bg-brand-green-dark transition-colors"
>
<Car className="w-5 h-5 shrink-0" />
<div>
<p className="font-bold text-sm">
{locale === "de" ? "Panzer Kaserne – gleiche Straße" : "Panzer Kaserne – Same Street"}
</p>
<p className="text-sm text-white/85">
{locale === "de" ? "Kein Abbiegen nötig" : "Zero turns needed"}
</p>
</div>
</a>
<a
href="https://www.google.com/maps/dir/Herdweg+71,+71032+Böblingen/Böblingen+Zentrum"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 p-3 bg-brand-green/10 rounded-xl hover:bg-brand-green/20 transition-colors"
>
<Clock className="w-5 h-5 text-brand-green shrink-0" />
<div>
<p className="font-bold text-brand-grey-dark text-sm">
{locale === "de" ? "Stadtzentrum Böblingen" : "Böblingen City Center"}
</p>
<p className="text-sm text-brand-grey">5–8 min</p>
</div>
</a>
</>
) : (
<>
<a
href="https://www.google.com/maps/dir/Schönaicher+Straße+26,+71032+Böblingen/Daimler+Werk+Sindelfingen"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 p-3 bg-brand-green/10 rounded-xl hover:bg-brand-green/20 transition-colors"
>
<Car className="w-5 h-5 text-brand-green shrink-0" />
<div>
<p className="font-bold text-brand-grey-dark text-sm">
{locale === "de" ? "Daimler Werk Sindelfingen" : "Daimler Plant Sindelfingen"}
</p>
<p className="text-sm text-brand-grey">10–15 min</p>
</div>
</a>
<a
href="https://www.google.com/maps/dir/Schönaicher+Straße+26,+71032+Böblingen/Böblingen+Zentrum"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 p-3 bg-brand-green/10 rounded-xl hover:bg-brand-green/20 transition-colors"
>
<Clock className="w-5 h-5 text-brand-green shrink-0" />
<div>
<p className="font-bold text-brand-grey-dark text-sm">
{locale === "de" ? "Stadtzentrum Böblingen" : "Böblingen City Center"}
</p>
<p className="text-sm text-brand-grey">&lt; 5 min</p>
</div>
</a>
</>
)}
</div>
<div className="relative w-full h-48 rounded-xl overflow-hidden border border-brand-grey-soft">
<iframe
src={`https://maps.google.com/maps?q=${encodeURIComponent(`${house.address}, ${house.zipCode} ${house.city}`)}&z=15&output=embed`}
width="100%"
height="100%"
style={{ border: 0 }}
allowFullScreen
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
title={displayName}
className="grayscale-[20%] hover:grayscale-0 transition-all duration-500"
/>
</div>
<p className="text-xs text-brand-grey mt-3 text-center">
{locale === "de"
? "Karte ist interaktiv – klicken Sie auf einen Ort, um die Route zu planen."
: "Map is interactive – click a destination to plan the route."}
</p>
</div>
</div>
</div>
</div>
</div>
);
}