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

103 lines
3.6 KiB
TypeScript

import Link from "next/link";
import Image from "next/image";
import { Apartment, House } from "@/src/config/apartments";
import { formatPrice } from "@/src/lib/pricing";
import { CheckCircle2, XCircle, Users, MapPin, ArrowRight } from "lucide-react";
import { useTranslations } from "next-intl";
interface AvailableApartmentCardProps {
apartment: Apartment;
house: House;
locale: string;
available: boolean;
minStayValid: boolean;
pricePerNight?: number;
currency?: string;
arrival: string;
departure: string;
}
export function AvailableApartmentCard({
apartment,
house,
locale,
available,
minStayValid,
pricePerNight,
currency,
arrival,
departure,
}: AvailableApartmentCardProps) {
const t = useTranslations("houses.search");
const displayName = locale === "de" ? apartment.name : apartment.nameEn;
const houseName = locale === "de" ? house.name : house.nameEn;
const canBook = available && minStayValid;
return (
<div
className={`bg-white rounded-2xl overflow-hidden shadow-sm border transition-shadow ${
canBook
? "border-brand-grey-soft hover:shadow-lg"
: "border-brand-grey-soft opacity-70"
}`}
>
<div className="relative h-48">
<Image
src={apartment.images[0] || "/images/placeholder.jpg"}
alt={displayName}
fill
className={`object-cover ${canBook ? "" : "grayscale"}`}
/>
<div
className={`absolute top-3 right-3 flex items-center gap-1.5 text-xs font-semibold px-2.5 py-1.5 rounded-full backdrop-blur-sm ${
canBook ? "bg-brand-green/90 text-white" : "bg-white/90 text-brand-grey"
}`}
>
{canBook ? (
<CheckCircle2 className="w-3.5 h-3.5" />
) : (
<XCircle className="w-3.5 h-3.5" />
)}
{canBook ? t("available") : !minStayValid ? t("minStayNotMet") : t("notAvailable")}
</div>
</div>
<div className="p-5">
<h3 className="text-lg font-bold mb-1 text-brand-grey-dark">{displayName}</h3>
<div className="flex items-center gap-1.5 text-sm text-brand-grey mb-3">
<MapPin className="w-4 h-4 text-brand-green" />
<span>{houseName}</span>
</div>
<div className="flex items-center gap-2 text-sm text-brand-grey mb-1">
<Users className="w-4 h-4 text-brand-green" />
<span>
{apartment.maxGuests} {locale === "de" ? "Gäste" : "guests"}
</span>
</div>
{canBook && pricePerNight !== undefined && (
<p className="text-lg font-bold text-brand-green-dark mb-4">
{formatPrice(pricePerNight, currency)}{" "}
<span className="text-sm font-normal text-brand-grey">/ {t("perNight")}</span>
</p>
)}
{canBook ? (
<Link
href={`/${locale}/apartments/${apartment.slug}?checkin=${arrival}&checkout=${departure}`}
className="inline-flex items-center justify-center gap-2 w-full bg-brand-green text-white font-semibold px-4 py-3 rounded-xl hover:bg-brand-green-dark transition-colors"
>
{t("bookThis")}
<ArrowRight className="w-4 h-4" />
</Link>
) : (
<Link
href={`/${locale}/apartments/${apartment.slug}`}
className="inline-flex items-center justify-center gap-2 w-full bg-brand-grey-soft text-brand-grey-dark font-semibold px-4 py-3 rounded-xl hover:bg-brand-grey-soft/70 transition-colors"
>
{locale === "de" ? "Details ansehen" : "View details"}
</Link>
)}
</div>
</div>
);
}