mirror of
https://github.com/Ichitux/lambada-fiesta-live.git
synced 2026-09-27 21:22:20 +00:00
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
name: Deploy NPM app
|
name: Deploy Docker container
|
||||||
run-name: ${{ gitea.actor }} is deploying to PROD servers.
|
run-name: ${{ gitea.actor }} is deploying to PROD servers.
|
||||||
on: [ push, fork, pull ]
|
on: [ push, fork, pull ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Deploy NPM:
|
Deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository code
|
- name: Check out repository code
|
||||||
@@ -18,7 +18,7 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
cd /home/zouklambadabcn.com/public_html/
|
cd /home/zouklambadabcn.com/public_html/
|
||||||
git pull
|
git pull
|
||||||
npm install
|
docker compose down
|
||||||
npm run build
|
docker compose up -d --build
|
||||||
pm2 restart ZLB
|
docker image prune -f
|
||||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { motion } from "framer-motion";
|
|||||||
import { MIXED_BOOKING_PACKAGES, ROOM_TYPES, getFullPassPrice } from "@/data/event-data";
|
import { MIXED_BOOKING_PACKAGES, ROOM_TYPES, getFullPassPrice } from "@/data/event-data";
|
||||||
import type { RoomType } from "@/data/event-data";
|
import type { RoomType } from "@/data/event-data";
|
||||||
import { useTranslation } from "react-i18next";
|
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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
const FEATURED_PASS = "full";
|
const FEATURED_PASS = "full";
|
||||||
const NON_HOTEL_FEE = 50;
|
const NON_HOTEL_FEE = 50;
|
||||||
const PARTY_PRICE = 25;
|
const PARTY_PRICE = 25;
|
||||||
|
const GOOGLE_MAPS_URL = "https://www.google.com/maps/search/Sala+Vivaldi+Barcelona";
|
||||||
|
|
||||||
const MixedBookingSection = () => {
|
const MixedBookingSection = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -43,12 +44,10 @@ const MixedBookingSection = () => {
|
|||||||
<p className="text-muted-foreground max-w-2xl mx-auto">{t("mixedBooking.subtitle")}</p>
|
<p className="text-muted-foreground max-w-2xl mx-auto">{t("mixedBooking.subtitle")}</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="grid sm:grid-cols-2 gap-6 lg:gap-8 max-w-4xl mx-auto items-stretch">
|
<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.map((pkg, i) => {
|
{MIXED_BOOKING_PACKAGES.filter(pkg => pkg.id !== "whiteparty").map((pkg, i) => {
|
||||||
const selectedRoom = selectedRooms[pkg.id] || "individual";
|
const selectedRoom = selectedRooms[pkg.id] || "individual";
|
||||||
const hasHotel = wantsHotel[pkg.id] === true;
|
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 basePrice = pkg.id === "full" ? fullPassPricing.price : PARTY_PRICE;
|
||||||
let roomPrice = 0;
|
let roomPrice = 0;
|
||||||
if (hasHotel) {
|
if (hasHotel) {
|
||||||
@@ -233,6 +232,96 @@ const MixedBookingSection = () => {
|
|||||||
</motion.div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ export const GALLERY_IMAGES = [
|
|||||||
|
|
||||||
// ---- PAQUETES MIXTOS (Room + Pass) ----
|
// ---- PAQUETES MIXTOS (Room + Pass) ----
|
||||||
export type RoomType = "individual" | "double" | "suite";
|
export type RoomType = "individual" | "double" | "suite";
|
||||||
export type PassType = "full" | "party";
|
export type PassType = "full" | "party" | "whiteparty";
|
||||||
|
|
||||||
export const ROOM_TYPES: { id: RoomType }[] = [
|
export const ROOM_TYPES: { id: RoomType }[] = [
|
||||||
{ id: "individual" },
|
{ id: "individual" },
|
||||||
@@ -314,6 +314,7 @@ export function getFullPassPrice(): { price: number; isLastPrice: boolean; valid
|
|||||||
export const MIXED_BOOKING_PACKAGES = [
|
export const MIXED_BOOKING_PACKAGES = [
|
||||||
{ id: "full" as PassType, label: "full", roomPrices: { individual: 100, double: 150, suite: 213 } },
|
{ 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 } },
|
{ id: "party" as PassType, label: "party", roomPrices: { individual: 100, double: 150, suite: 213 } },
|
||||||
|
{ id: "whiteparty" as PassType, label: "whiteparty", roomPrices: { individual: 0, double: 0, suite: 0 } },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ---- SECCIONES VISIBLES ----
|
// ---- SECCIONES VISIBLES ----
|
||||||
|
|||||||
+9
-2
@@ -147,13 +147,20 @@
|
|||||||
},
|
},
|
||||||
"passTypes": {
|
"passTypes": {
|
||||||
"full": "Full Pass",
|
"full": "Full Pass",
|
||||||
"party": "Party Pass"
|
"party": "Party Pass",
|
||||||
|
"whiteparty": "White Party"
|
||||||
},
|
},
|
||||||
"fullDescription": "Full access to all workshops, socials, and festival activities",
|
"fullDescription": "Full access to all workshops, socials, and festival activities",
|
||||||
"fullFeatures": "All workshops|All social dances|Live shows|DJ sets",
|
"fullFeatures": "All workshops|All social dances|Live shows|DJ sets",
|
||||||
"partyDescription": "Price for each party individually, even pool parties.",
|
"partyDescription": "Price for each party individually, even pool parties.",
|
||||||
"partyFeatures": "Friday party|Saturday party|Sunday farewell party|DJ sets",
|
"partyFeatures": "Friday party|Saturday party|Sunday farewell party|DJ sets",
|
||||||
"goToReservations": "Go to reservations"
|
"whitepartyDescription": "Closing party in Sala Vivaldi, with an elegant and exclusive atmosphere.",
|
||||||
|
"whitepartyFeatures": "Dress code: White|Amazing DJ",
|
||||||
|
"whitepartyNotIncluded": "Not included in Full Pass - Separate payment required",
|
||||||
|
"whitepartyTime": "21h to 1h",
|
||||||
|
"whitepartyDate": "Monday 7 September",
|
||||||
|
"goToReservations": "Go to reservations",
|
||||||
|
"viewOnMap": "View on Google Maps"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"title": "Practical Information",
|
"title": "Practical Information",
|
||||||
|
|||||||
+9
-2
@@ -147,13 +147,20 @@
|
|||||||
},
|
},
|
||||||
"passTypes": {
|
"passTypes": {
|
||||||
"full": "Full Pass",
|
"full": "Full Pass",
|
||||||
"party": "Party Pass"
|
"party": "Party Pass",
|
||||||
|
"whiteparty": "White Party"
|
||||||
},
|
},
|
||||||
"fullDescription": "Acceso completo a todos los workshops, sociales y actividades del festival",
|
"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",
|
"fullFeatures": "Todos los workshops|Todas las social dances|Shows en vivo|DJ sets",
|
||||||
"partyDescription": "Precio individual por cada fiesta, incluso las pool parties.",
|
"partyDescription": "Precio individual por cada fiesta, incluso las pool parties.",
|
||||||
"partyFeatures": "Fiesta del viernes|Fiesta del sábado|Fiesta despedida del domingo|DJ sets",
|
"partyFeatures": "Fiesta del viernes|Fiesta del sábado|Fiesta despedida del domingo|DJ sets",
|
||||||
"goToReservations": "Ir a reservas"
|
"whitepartyDescription": "Fiesta de clausura en la Sala Vivaldi, con un ambiente elegante y exclusivo.",
|
||||||
|
"whitepartyFeatures": "Código de vestimenta: Blanco|DJ Inigualable",
|
||||||
|
"whitepartyNotIncluded": "No incluido en Full Pass - Pago separado requerido",
|
||||||
|
"whitepartyTime": "21h a 1h",
|
||||||
|
"whitepartyDate": "Lunes 7 de Septiembre",
|
||||||
|
"goToReservations": "Ir a reservas",
|
||||||
|
"viewOnMap": "Ver en Google Maps"
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"title": "Información Práctica",
|
"title": "Información Práctica",
|
||||||
|
|||||||
Reference in New Issue
Block a user