From fce84ff4a626e7a0a1dcecfa82dbe851fbd986bf Mon Sep 17 00:00:00 2001 From: Ichitux Date: Fri, 24 Apr 2026 01:59:50 +0200 Subject: [PATCH] Changes in module "mixed reservations" --- src/components/MixedBookingSection.tsx | 142 ++++++++++++++++++------- src/data/event-data.ts | 32 +++++- src/locales/en.json | 15 +-- src/locales/es.json | 15 +-- 4 files changed, 150 insertions(+), 54 deletions(-) diff --git a/src/components/MixedBookingSection.tsx b/src/components/MixedBookingSection.tsx index afa6e1f..7c6f177 100644 --- a/src/components/MixedBookingSection.tsx +++ b/src/components/MixedBookingSection.tsx @@ -1,20 +1,26 @@ import { useState } from "react"; import { motion } from "framer-motion"; -import { MIXED_BOOKING_PACKAGES, ROOM_TYPES } from "@/data/event-data"; +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 } from "lucide-react"; +import { Check, Star, Circle, CheckCircle2 } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Label } from "@/components/ui/label"; const FEATURED_PASS = "full"; +const NON_HOTEL_FEE = 50; +const PARTY_PRICE = 25; const MixedBookingSection = () => { const { t } = useTranslation(); + const fullPassPricing = getFullPassPrice(); const [selectedRooms, setSelectedRooms] = useState>({ full: "individual", party: "individual", - single: "individual", + }); + const [wantsHotel, setWantsHotel] = useState>({ + full: null, + party: null, }); return ( @@ -36,11 +42,26 @@ const MixedBookingSection = () => {

{t("mixedBooking.subtitle")}

-
+
{MIXED_BOOKING_PACKAGES.map((pkg, i) => { const selectedRoom = selectedRooms[pkg.id] || "individual"; - const price = pkg.roomPrices[selectedRoom]; + 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; const features = t(`mixedBooking.${pkg.id}Features`).split("|").map((f: string) => f.trim()); return ( @@ -56,14 +77,19 @@ const MixedBookingSection = () => { : "shadow-card hover:shadow-elevated border border-border" }`} > - {isFeatured && ( -
+
+ {isFeatured && ( {t("mixedBooking.popular")} -
- )} + )} + {isLastPrice && ( + + {t("mixedBooking.lastPrice")} + + )} +
{

{price > 0 ? `${price}€` : t("mixedBooking.priceTBD")}

-

- {t("mixedBooking.perPerson")} +

+ {t(`mixedBooking.passTypes.${pkg.id}`)}: {passPrice}€ {pkg.id === "full" ? t("mixedBooking.perPerson") : t("mixedBooking.perParty")}

+ {showNonHotelNote && ( +

+ {t("mixedBooking.nonHotelNote")} +

+ )} + {hasHotel && ( +

+ {t(`mixedBooking.roomTypes.${selectedRoom}`)}: {roomPrice}€ +

+ )}
-
diff --git a/src/data/event-data.ts b/src/data/event-data.ts index fed05da..dad8da9 100644 --- a/src/data/event-data.ts +++ b/src/data/event-data.ts @@ -276,7 +276,7 @@ export const GALLERY_IMAGES = [ // ---- PAQUETES MIXTOS (Room + Pass) ---- export type RoomType = "individual" | "double" | "suite"; -export type PassType = "full" | "party" | "single"; +export type PassType = "full" | "party"; export const ROOM_TYPES: { id: RoomType }[] = [ { id: "individual" }, @@ -284,10 +284,32 @@ export const ROOM_TYPES: { id: RoomType }[] = [ { id: "suite" }, ]; +// ---- PRECIOS DINÁMICOS FULL PASS ---- +/** + * Calcula el precio del Full Pass según la fecha actual. + * - Hasta 1 de julio: 170€ + * - 1 de julio - 1 de septiembre: 190€ + * - Después del inicio del evento: 200€ (último precio) + */ +export function getFullPassPrice(): { price: number; isLastPrice: boolean } { + const now = new Date(); + const julyFirst = new Date("2026-07-01T00:00:00"); + const septemberFirst = new Date("2026-09-01T00:00:00"); + const eventStart = new Date(EVENT_INFO.date); + + if (now < julyFirst) { + return { price: 170, isLastPrice: false }; + } else if (now < septemberFirst) { + return { price: 190, isLastPrice: false }; + } else { + // After event starts + return { price: 200, isLastPrice: true }; + } +} + export const MIXED_BOOKING_PACKAGES = [ - { id: "full" as PassType, label: "full", roomPrices: { individual: 0, double: 0, suite: 0 } }, - { id: "party" as PassType, label: "party", roomPrices: { individual: 0, double: 0, suite: 0 } }, - { id: "single" as PassType, label: "single", roomPrices: { individual: 0, double: 0, suite: 0 } }, + { id: "full" as PassType, label: "full", roomPrices: { individual: 100, double: 150, suite: 213 } }, + { id: "party" as PassType, label: "party", roomPrices: { individual: 100, double: 150, suite: 213 } }, ]; // ---- SECCIONES VISIBLES ---- @@ -299,7 +321,7 @@ export const SECTIONS = { schedule: true, booking: false, mixed_booking: true, - hotel: true, + hotel: false, practical: true, gallery: true, }; diff --git a/src/locales/en.json b/src/locales/en.json index 7f4c72c..0f4b78e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -130,7 +130,13 @@ "priceTBD": "Price TBA", "selectRoom": "Choose room type", "popular": "Popular", + "lastPrice": "Last Price", "perPerson": "/ person", + "perParty": "/ party", + "hotelQuestion": "Do you need a hotel room?", + "hotelYes": "Yes", + "hotelNo": "No", + "nonHotelNote": "+50€ fee for not staying in hotel", "roomTypes": { "individual": "Single Room", "double": "Double Room", @@ -138,15 +144,12 @@ }, "passTypes": { "full": "Full Pass", - "party": "Party Pass", - "single": "Single Day Pass" + "party": "Party Pass" }, "fullDescription": "Full access to all workshops, socials, and festival activities", "fullFeatures": "All workshops|All social dances|Live shows|DJ sets", - "partyDescription": "Access to all parties (Friday, Saturday, and Sunday)", - "partyFeatures": "Friday party|Saturday party|Sunday farewell party|DJ sets", - "singleDescription": "Access to a single day of the festival", - "singleFeatures": "One day access|Workshops|Social dance|DJ set" + "partyDescription": "Price for each party individually, even pool parties.", + "partyFeatures": "Friday party|Saturday party|Sunday farewell party|DJ sets" }, "info": { "title": "Practical Information", diff --git a/src/locales/es.json b/src/locales/es.json index e58d802..71093e5 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -130,7 +130,13 @@ "priceTBD": "Precio por confirmar", "selectRoom": "Elige tipo de habitación", "popular": "Popular", + "lastPrice": "Último Precio", "perPerson": "/ persona", + "perParty": "/ fiesta", + "hotelQuestion": "¿Necesitas habitación de hotel?", + "hotelYes": "Sí", + "hotelNo": "No", + "nonHotelNote": "+50€ por no alojarse en hotel", "roomTypes": { "individual": "Habitación Individual", "double": "Habitación Doble", @@ -138,15 +144,12 @@ }, "passTypes": { "full": "Full Pass", - "party": "Party Pass", - "single": "Single Day Pass" + "party": "Party Pass" }, "fullDescription": "Acceso completo a todos los workshops, sociales y actividades del festival", "fullFeatures": "Todos los workshops|Todas las social dances|Shows en vivo|DJ sets", - "partyDescription": "Acceso a todas las fiestas (Viernes, Sábado y Domingo)", - "partyFeatures": "Fiesta del viernes|Fiesta del sábado|Fiesta despedida del domingo|DJ sets", - "singleDescription": "Acceso a un solo día del festival", - "singleFeatures": "Acceso un día|Workshops|Social dance|DJ set" + "partyDescription": "Precio individual por cada fiesta, incluso las pool parties.", + "partyFeatures": "Fiesta del viernes|Fiesta del sábado|Fiesta despedida del domingo|DJ sets" }, "info": { "title": "Información Práctica",