White Party + Docker version
Deploy Docker container / Deploy (push) Successful in 2m15s

This commit is contained in:
Antoni Nuñez Romeu
2026-07-22 16:00:30 +02:00
parent 8d0373facf
commit e6b1bfe9a4
5 changed files with 121 additions and 17 deletions
+96 -7
View File
@@ -3,7 +3,7 @@ import { motion } from "framer-motion";
import { MIXED_BOOKING_PACKAGES, ROOM_TYPES, getFullPassPrice } from "@/data/event-data";
import type { RoomType } from "@/data/event-data";
import { useTranslation } from "react-i18next";
import { Check, Star, Circle, CheckCircle2, ExternalLink } from "lucide-react";
import { Check, Star, Circle, CheckCircle2, ExternalLink, MapPin, Calendar, Clock, Navigation } from "lucide-react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
@@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
const FEATURED_PASS = "full";
const NON_HOTEL_FEE = 50;
const PARTY_PRICE = 25;
const GOOGLE_MAPS_URL = "https://www.google.com/maps/search/Sala+Vivaldi+Barcelona";
const MixedBookingSection = () => {
const { t } = useTranslation();
@@ -43,23 +44,21 @@ const MixedBookingSection = () => {
<p className="text-muted-foreground max-w-2xl mx-auto">{t("mixedBooking.subtitle")}</p>
</motion.div>
<div className="grid sm:grid-cols-2 gap-6 lg:gap-8 max-w-4xl mx-auto items-stretch">
{MIXED_BOOKING_PACKAGES.map((pkg, i) => {
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 lg:gap-8 max-w-6xl mx-auto items-stretch">
{MIXED_BOOKING_PACKAGES.filter(pkg => pkg.id !== "whiteparty").map((pkg, i) => {
const selectedRoom = selectedRooms[pkg.id] || "individual";
const hasHotel = wantsHotel[pkg.id] === true;
// Use dynamic pricing for Full Pass, fixed price for Party Pass
// Only Full Pass has the +50€ non-hotel fee
let basePrice = pkg.id === "full" ? fullPassPricing.price : PARTY_PRICE;
let roomPrice = 0;
if (hasHotel) {
roomPrice = pkg.roomPrices[selectedRoom as keyof typeof pkg.roomPrices];
}
const passPrice = pkg.id === "full"
? (hasHotel ? basePrice : basePrice + NON_HOTEL_FEE)
: basePrice;
const price = passPrice + roomPrice;
const isFeatured = pkg.id === FEATURED_PASS;
const isLastPrice = pkg.id === "full" && fullPassPricing.isLastPrice;
const showNonHotelNote = pkg.id === "full" && !hasHotel && basePrice > 0;
@@ -233,6 +232,96 @@ const MixedBookingSection = () => {
</motion.div>
);
})}
{/* White Party Special Card */}
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false, amount: 0.15 }}
transition={{ delay: 0.2 }}
className="relative flex h-full min-h-[520px] flex-col rounded-2xl overflow-hidden transition-shadow duration-300 shadow-card hover:shadow-elevated border border-border"
>
{/* Tropical beach background */}
<div className="absolute inset-0 bg-gradient-to-br from-cyan-400 via-blue-400 to-cyan-300" />
<div className="absolute inset-0 opacity-20" style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext x='10' y='30' font-size='20' fill='%2322c55e'%3E%F0%9F%8C%B4%3C/text%3E%3Ctext x='70' y='20' font-size='15' fill='%2322c55e'%3E%F0%9F%8C%BF%3C/text%3E%3Ctext x='20' y='80' font-size='18' fill='%23f97316'%3E%F0%9F%8C%8A%3C/text%3E%3Ctext x='80' y='70' font-size='12' fill='%23ec4899'%3E%F0%9F%8C%B9%3C/text%3E%3C/svg%3E")`,
backgroundSize: '80px 80px'
}} />
{/* Content overlay */}
<div className="relative z-10 flex flex-col h-full">
{/* Header */}
<div className="px-6 pt-8 pb-4 text-center relative min-h-[160px] flex flex-col justify-center">
<div className="absolute inset-0 bg-white/70 backdrop-blur-sm" />
<div className="relative z-10">
<h3 className="font-display text-3xl md:text-4xl font-black text-slate-800 mb-2">
WHITE PARTY
</h3>
<p className="text-slate-700 text-sm font-medium">
{t("mixedBooking.whitepartyDescription")}
</p>
</div>
</div>
{/* Event details */}
<div className="px-6 py-4 flex-1">
<div className="bg-white/30 backdrop-blur-sm rounded-xl p-4 space-y-3">
<div className="flex items-center gap-3 text-slate-800">
<Calendar className="w-5 h-5 flex-shrink-0" />
<span className="text-sm font-medium">{t("mixedBooking.whitepartyDate")}</span>
</div>
<div className="flex items-center gap-3 text-slate-800">
<Clock className="w-5 h-5 flex-shrink-0" />
<span className="text-sm font-medium">{t("mixedBooking.whitepartyTime")}</span>
</div>
<div className="flex items-center gap-3 text-slate-800">
<Navigation className="w-5 h-5 flex-shrink-0" />
<div className="text-sm">
<span className="font-bold block">Sala Vivaldi</span>
<span className="text-slate-600">C/Llança, 5</span>
</div>
</div>
</div>
{/* Warning */}
<div className="mt-4 bg-red-600 backdrop-blur-sm rounded-xl px-4 py-3">
<p className="text-white text-xs font-bold text-center uppercase">
{t("mixedBooking.whitepartyNotIncluded")}
</p>
</div>
</div>
{/* Features */}
<div className="px-6 py-2">
<ul className="space-y-2">
{t("mixedBooking.whitepartyFeatures").split("|").map((feature: string, idx: number) => (
<li key={idx} className="flex items-center gap-2 text-sm text-slate-800">
<Check className="w-4 h-4 flex-shrink-0" />
<span>{feature.trim()}</span>
</li>
))}
</ul>
</div>
{/* Google Maps Button */}
<div className="px-6 pt-4 pb-6 mt-auto">
<Button
asChild
className="w-full rounded-xl font-medium py-3 bg-white hover:bg-white/90 text-cyan-600 shadow-lg"
>
<a
href={GOOGLE_MAPS_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center gap-2"
>
<MapPin className="w-5 h-5" />
{t("mixedBooking.viewOnMap")}
</a>
</Button>
</div>
</div>
</motion.div>
</div>
</div>
</section>