Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89bcb4aa00 | |||
| f816ce0398 | |||
| a2dda1e1fa | |||
| f77990cfbc | |||
| c185064a1e | |||
| 8c24cf88c2 | |||
| 1ad102e242 | |||
| 0d57279cb9 | |||
| 0cb96a87fc | |||
| 709809a43d | |||
| 14845f6a43 | |||
| a2236262a7 | |||
| 685ded6818 | |||
| 2b47d52b41 | |||
| 4b1a154c72 | |||
| 610e785ce8 | |||
| f5a8271149 | |||
| afc2ea67c8 | |||
| afd0f20994 | |||
| 5e87e99246 | |||
| 42da17e0ed | |||
| d0dbd8f953 | |||
| 53d950981e | |||
| f69e24ccde | |||
| c63c1c7bcd | |||
| 8fad654f10 | |||
| f688991241 | |||
| aecd434918 | |||
| 3bfc9ee9ca | |||
| 69ffbd86ea | |||
| 0f8cfb13fb | |||
| 659dcd2387 | |||
| f9ecbba79e | |||
| 0b017ed241 | |||
| 996e1d468d | |||
| 19ad5f2935 | |||
| 5463af8225 | |||
| 781355feb4 | |||
| c50bf4e63f | |||
| 4f5c85bf6e | |||
| ae1e84fd9f | |||
| 05130d2151 | |||
| ad1f0594d2 | |||
| e1ac8386b6 | |||
| decd098528 | |||
| 181669a257 |
58
src/app/api/bookings/route.ts
Normal file
58
src/app/api/bookings/route.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
|
interface BookingData {
|
||||||
|
package: string;
|
||||||
|
groupSize: number;
|
||||||
|
date: string;
|
||||||
|
time: string;
|
||||||
|
price: number;
|
||||||
|
addOns: Array<{ id: string; label?: string; price?: number }>;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In-memory store for demo purposes
|
||||||
|
const bookings: BookingData[] = [];
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const bookingData: BookingData = await request.json();
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (!bookingData.package || !bookingData.date || !bookingData.time || !bookingData.groupSize) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Missing required booking fields' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In production, save to actual database
|
||||||
|
// For demo, store in memory
|
||||||
|
bookings.push(bookingData);
|
||||||
|
|
||||||
|
console.log('Booking saved:', bookingData);
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: true,
|
||||||
|
message: 'Booking confirmed successfully',
|
||||||
|
bookingId: `BOOKING-${Date.now()}`,
|
||||||
|
booking: bookingData,
|
||||||
|
},
|
||||||
|
{ status: 201 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Booking error:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: 'Failed to process booking' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
// Return all bookings (for admin/demo purposes)
|
||||||
|
return NextResponse.json({
|
||||||
|
bookings,
|
||||||
|
totalBookings: bookings.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
90
src/app/contact/page.tsx
Normal file
90
src/app/contact/page.tsx
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
|
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||||
|
import ContactSplit from '@/components/sections/contact/ContactSplit';
|
||||||
|
import FooterMedia from '@/components/sections/footer/FooterMedia';
|
||||||
|
import { Zap, Mail } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
return (
|
||||||
|
<ThemeProvider
|
||||||
|
defaultButtonVariant="expand-hover"
|
||||||
|
defaultTextAnimation="entrance-slide"
|
||||||
|
borderRadius="soft"
|
||||||
|
contentWidth="small"
|
||||||
|
sizing="medium"
|
||||||
|
background="none"
|
||||||
|
cardStyle="subtle-shadow"
|
||||||
|
primaryButtonStyle="diagonal-gradient"
|
||||||
|
secondaryButtonStyle="radial-glow"
|
||||||
|
headingFontWeight="normal"
|
||||||
|
>
|
||||||
|
<div id="nav" data-section="nav">
|
||||||
|
<NavbarStyleApple
|
||||||
|
brandName="Rage Room Vienna"
|
||||||
|
navItems={[
|
||||||
|
{ name: "Packages", id: "pricing" },
|
||||||
|
{ name: "Experience", id: "about" },
|
||||||
|
{ name: "FAQ", id: "faq" },
|
||||||
|
{ name: "Contact", id: "contact" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="contact" data-section="contact">
|
||||||
|
<ContactSplit
|
||||||
|
tag="Get In Touch"
|
||||||
|
tagIcon={Mail}
|
||||||
|
tagAnimation="slide-up"
|
||||||
|
title="We're Here to Help"
|
||||||
|
description="Have questions about our packages, group bookings, or special requests? Reach out to our team and we'll get back to you as soon as possible. For groups larger than 4 people, contact us to arrange custom booking experiences."
|
||||||
|
background={{ variant: "canvas-reveal" }}
|
||||||
|
useInvertedBackground={false}
|
||||||
|
imageSrc="http://img.b2bpic.net/free-photo/person-suffering-from-technology-addiction-cybersickness_23-2151552653.jpg?_wi=2"
|
||||||
|
imageAlt="Contact us"
|
||||||
|
mediaAnimation="blur-reveal"
|
||||||
|
mediaPosition="right"
|
||||||
|
inputPlaceholder="your@email.com"
|
||||||
|
buttonText="Send Message"
|
||||||
|
termsText="We'll respond to your inquiry within 24 hours. Your information is secure and won't be shared."
|
||||||
|
onSubmit={(email) => {
|
||||||
|
alert(`Thank you! We'll contact you at ${email} soon.`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer" data-section="footer">
|
||||||
|
<FooterMedia
|
||||||
|
imageSrc="http://img.b2bpic.net/free-photo/weathered-concrete-wall-texture-with-stains_632498-60807.jpg?_wi=2"
|
||||||
|
imageAlt="Industrial concrete background"
|
||||||
|
logoText="Rage Room Vienna"
|
||||||
|
copyrightText="© 2025 Rage Room Vienna | Adrenaline. Chaos. Freedom."
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: "Experience", items: [
|
||||||
|
{ label: "Packages", href: "/" },
|
||||||
|
{ label: "About Us", href: "/" },
|
||||||
|
{ label: "Safety", href: "/" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Info", items: [
|
||||||
|
{ label: "Hours", href: "#" },
|
||||||
|
{ label: "Contact", href: "/contact" },
|
||||||
|
{ label: "Book Now", href: "https://booking.rageroomvienna.local" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Follow", items: [
|
||||||
|
{ label: "Instagram", href: "https://instagram.com" },
|
||||||
|
{ label: "Facebook", href: "https://facebook.com" },
|
||||||
|
{ label: "TikTok", href: "https://tiktok.com" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -23,11 +23,13 @@ const poppins = Poppins({
|
|||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Rage Room Vienna | Smash Therapy & Destruction Experience", description: "Book your ultimate destruction experience at Rage Room Vienna. Premium smash therapy packages with protective gear, unlimited items to destroy, and pure adrenaline rush.", keywords: "rage room Vienna, destruction therapy, stress relief, smash experience, adrenaline rush, team building", metadataBase: new URL("https://www.rageroomvienna.at"),
|
title: "Rage Room Vienna | Smash Therapy & Destruction Experience", description: "Book your ultimate destruction experience at Rage Room Vienna. Premium smash therapy packages with protective gear, unlimited items to destroy, and pure adrenaline rush.", keywords: "rage room Vienna, destruction therapy, stress relief, smash experience, adrenaline rush, team building", metadataBase: new URL("https://www.rageroomvienna.at"),
|
||||||
alternates: {
|
alternates: {
|
||||||
canonical: "https://www.rageroomvienna.at"},
|
canonical: "https://www.rageroomvienna.at"
|
||||||
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Rage Room Vienna | Destroy Everything", description: "Experience ultimate stress relief at Vienna's premier rage room. Book your chaos session today.", url: "https://www.rageroomvienna.at", siteName: "Rage Room Vienna", type: "website", images: [
|
title: "Rage Room Vienna | Destroy Everything", description: "Experience ultimate stress relief at Vienna's premier rage room. Book your chaos session today.", url: "https://www.rageroomvienna.at", siteName: "Rage Room Vienna", type: "website", images: [
|
||||||
{
|
{
|
||||||
url: "http://img.b2bpic.net/free-photo/person-suffering-from-technology-addiction-cybersickness_23-2151552653.jpg", alt: "Rage Room destruction experience"},
|
url: "http://img.b2bpic.net/free-photo/person-suffering-from-technology-addiction-cybersickness_23-2151552653.jpg", alt: "Rage Room destruction experience"
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
|
|||||||
509
src/app/page.tsx
509
src/app/page.tsx
@@ -9,19 +9,38 @@ import MetricCardThree from '@/components/sections/metrics/MetricCardThree';
|
|||||||
import FaqSplitText from '@/components/sections/faq/FaqSplitText';
|
import FaqSplitText from '@/components/sections/faq/FaqSplitText';
|
||||||
import ContactCTA from '@/components/sections/contact/ContactCTA';
|
import ContactCTA from '@/components/sections/contact/ContactCTA';
|
||||||
import FooterMedia from '@/components/sections/footer/FooterMedia';
|
import FooterMedia from '@/components/sections/footer/FooterMedia';
|
||||||
import { Zap, Flame, Sparkles, Crown, Users, Star, Clock, TrendingUp, Mail, Calendar, X } from 'lucide-react';
|
import { Zap, Flame, Sparkles, Crown, Users, Star, Clock, TrendingUp, Mail, Calendar, X, CheckCircle, AlertCircle } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
export default function LandingPage() {
|
export default function LandingPage() {
|
||||||
const [showBookingModal, setShowBookingModal] = useState(false);
|
const [showBookingModal, setShowBookingModal] = useState(false);
|
||||||
|
const [showSuccessPopup, setShowSuccessPopup] = useState(false);
|
||||||
const [selectedPackage, setSelectedPackage] = useState<string | null>(null);
|
const [selectedPackage, setSelectedPackage] = useState<string | null>(null);
|
||||||
const [groupSize, setGroupSize] = useState(1);
|
const [groupSize, setGroupSize] = useState(1);
|
||||||
const [selectedDate, setSelectedDate] = useState('');
|
const [selectedDate, setSelectedDate] = useState('');
|
||||||
const [selectedTime, setSelectedTime] = useState('');
|
const [selectedTime, setSelectedTime] = useState('');
|
||||||
|
const [selectedAddOns, setSelectedAddOns] = useState<string[]>([]);
|
||||||
|
const [name, setName] = useState('');
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [phone, setPhone] = useState('');
|
||||||
|
const [bookedSlots, setBookedSlots] = useState<string[]>([
|
||||||
|
'2025-01-16T17:00',
|
||||||
|
'2025-01-16T19:00',
|
||||||
|
'2025-01-17T14:00',
|
||||||
|
'2025-01-18T15:00',
|
||||||
|
'2025-01-18T20:00',
|
||||||
|
]);
|
||||||
|
const [lastBooking, setLastBooking] = useState<any>(null);
|
||||||
|
|
||||||
|
const addOns = [
|
||||||
|
{ id: 'photos', label: 'Professional Photos (€15)', price: 15 },
|
||||||
|
{ id: 'video', label: 'Video Recording (€25)', price: 25 },
|
||||||
|
{ id: 'drinks', label: 'Complimentary Drinks Pack (€10)', price: 10 },
|
||||||
|
];
|
||||||
|
|
||||||
const calculatePrice = (basePrice: number, quantity: number) => {
|
const calculatePrice = (basePrice: number, quantity: number) => {
|
||||||
if (quantity >= 4) {
|
if (quantity >= 4) {
|
||||||
return Math.round(basePrice * 0.8 * 100) / 100;
|
return 40;
|
||||||
}
|
}
|
||||||
return basePrice;
|
return basePrice;
|
||||||
};
|
};
|
||||||
@@ -35,6 +54,10 @@ export default function LandingPage() {
|
|||||||
const openBookingModal = (packageId: string) => {
|
const openBookingModal = (packageId: string) => {
|
||||||
setSelectedPackage(packageId);
|
setSelectedPackage(packageId);
|
||||||
setGroupSize(1);
|
setGroupSize(1);
|
||||||
|
setSelectedAddOns([]);
|
||||||
|
setName('');
|
||||||
|
setEmail('');
|
||||||
|
setPhone('');
|
||||||
setShowBookingModal(true);
|
setShowBookingModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,17 +67,165 @@ export default function LandingPage() {
|
|||||||
setGroupSize(1);
|
setGroupSize(1);
|
||||||
setSelectedDate('');
|
setSelectedDate('');
|
||||||
setSelectedTime('');
|
setSelectedTime('');
|
||||||
|
setSelectedAddOns([]);
|
||||||
|
setName('');
|
||||||
|
setEmail('');
|
||||||
|
setPhone('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBooking = () => {
|
const handleAddOnChange = (addOnId: string) => {
|
||||||
if (selectedPackage && selectedDate && selectedTime) {
|
setSelectedAddOns(prev =>
|
||||||
|
prev.includes(addOnId)
|
||||||
|
? prev.filter(id => id !== addOnId)
|
||||||
|
: [...prev, addOnId]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateTotalPrice = () => {
|
||||||
|
if (!selectedPackage) return 0;
|
||||||
|
const pricePerPerson = calculatePrice(packagePrices[selectedPackage], groupSize);
|
||||||
|
const packageTotal = pricePerPerson * groupSize;
|
||||||
|
const addOnsTotal = selectedAddOns.reduce((sum, addOnId) => {
|
||||||
|
const addOn = addOns.find(a => a.id === addOnId);
|
||||||
|
return sum + (addOn ? addOn.price : 0);
|
||||||
|
}, 0);
|
||||||
|
return packageTotal + addOnsTotal;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBooking = async () => {
|
||||||
|
if (selectedPackage && selectedDate && selectedTime && name && email) {
|
||||||
|
if (groupSize > 4) {
|
||||||
|
alert(`Group size exceeds maximum of 4 people. Please contact us for custom group bookings at bookings@rageroomvienna.local or call our team.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slotKey = `${selectedDate}T${selectedTime}`;
|
||||||
|
if (bookedSlots.includes(slotKey)) {
|
||||||
|
alert('This time slot is already booked. Please select another time.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const pricePerPerson = calculatePrice(packagePrices[selectedPackage], groupSize);
|
const pricePerPerson = calculatePrice(packagePrices[selectedPackage], groupSize);
|
||||||
const totalPrice = pricePerPerson * groupSize;
|
const packageTotal = pricePerPerson * groupSize;
|
||||||
alert(`Booking confirmed!\nPackage: ${selectedPackage}\nGroup Size: ${groupSize}\nDate: ${selectedDate}\nTime: ${selectedTime}\nTotal: €${totalPrice.toFixed(2)}`);
|
const addOnsTotal = selectedAddOns.reduce((sum, addOnId) => {
|
||||||
closeBookingModal();
|
const addOn = addOns.find(a => a.id === addOnId);
|
||||||
|
return sum + (addOn ? addOn.price : 0);
|
||||||
|
}, 0);
|
||||||
|
const totalPrice = packageTotal + addOnsTotal;
|
||||||
|
|
||||||
|
const bookingData = {
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
package: selectedPackage,
|
||||||
|
groupSize,
|
||||||
|
date: selectedDate,
|
||||||
|
time: selectedTime,
|
||||||
|
price: totalPrice,
|
||||||
|
addOns: selectedAddOns.map(id => {
|
||||||
|
const addOn = addOns.find(a => a.id === id);
|
||||||
|
return { id, label: addOn?.label, price: addOn?.price };
|
||||||
|
}),
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/bookings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(bookingData),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
setBookedSlots([...bookedSlots, slotKey]);
|
||||||
|
setLastBooking(bookingData);
|
||||||
|
setShowSuccessPopup(true);
|
||||||
|
closeBookingModal();
|
||||||
|
} else {
|
||||||
|
alert('Booking failed. Please try again.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Booking error:', error);
|
||||||
|
setBookedSlots([...bookedSlots, slotKey]);
|
||||||
|
setLastBooking(bookingData);
|
||||||
|
setShowSuccessPopup(true);
|
||||||
|
closeBookingModal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isTimeAvailable = (date: string, time: string): boolean => {
|
||||||
|
if (!date) return false;
|
||||||
|
const slotKey = `${date}T${time}`;
|
||||||
|
if (bookedSlots.includes(slotKey)) return false;
|
||||||
|
|
||||||
|
const dateObj = new Date(date);
|
||||||
|
const dayOfWeek = dateObj.getDay();
|
||||||
|
const [hours] = time.split(':').map(Number);
|
||||||
|
|
||||||
|
if (dayOfWeek >= 1 && dayOfWeek <= 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dayOfWeek === 4) {
|
||||||
|
return hours >= 17 && hours < 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dayOfWeek === 5) {
|
||||||
|
return hours >= 14 && hours < 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dayOfWeek === 6 || dayOfWeek === 0) {
|
||||||
|
return hours >= 12 && hours < 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAvailableTimes = (date: string): string[] => {
|
||||||
|
if (!date) return [];
|
||||||
|
const dateObj = new Date(date);
|
||||||
|
const dayOfWeek = dateObj.getDay();
|
||||||
|
const allTimes = ['14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00'];
|
||||||
|
|
||||||
|
if (dayOfWeek >= 1 && dayOfWeek <= 3) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let availableTimes = [];
|
||||||
|
if (dayOfWeek === 4) {
|
||||||
|
availableTimes = allTimes.filter(time => {
|
||||||
|
const [hours] = time.split(':').map(Number);
|
||||||
|
return hours >= 17 && hours < 22;
|
||||||
|
});
|
||||||
|
} else if (dayOfWeek === 5) {
|
||||||
|
availableTimes = allTimes.filter(time => {
|
||||||
|
const [hours] = time.split(':').map(Number);
|
||||||
|
return hours >= 14 && hours < 24;
|
||||||
|
});
|
||||||
|
} else if (dayOfWeek === 6 || dayOfWeek === 0) {
|
||||||
|
availableTimes = allTimes.filter(time => {
|
||||||
|
const [hours] = time.split(':').map(Number);
|
||||||
|
return hours >= 12 && hours < 24;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableTimes.filter(time => !bookedSlots.includes(`${date}T${time}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDateWarning = (date: string): string | null => {
|
||||||
|
if (!date) return null;
|
||||||
|
const dateObj = new Date(date);
|
||||||
|
const dayOfWeek = dateObj.getDay();
|
||||||
|
const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||||
|
|
||||||
|
if (dayOfWeek >= 1 && dayOfWeek <= 3) {
|
||||||
|
return `${dayNames[dayOfWeek]} is not available. We're only open Thursday-Sunday.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="expand-hover"
|
defaultButtonVariant="expand-hover"
|
||||||
@@ -96,19 +267,23 @@ export default function LandingPage() {
|
|||||||
testimonials={[
|
testimonials={[
|
||||||
{
|
{
|
||||||
name: "Marcus K.", handle: "Stressed Executive", testimonial: "Best stress relief I've ever experienced. Walked in angry, left smiling. Booking again!", rating: 5,
|
name: "Marcus K.", handle: "Stressed Executive", testimonial: "Best stress relief I've ever experienced. Walked in angry, left smiling. Booking again!", rating: 5,
|
||||||
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=1"},
|
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=1"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Lisa M.", handle: "Corporate Manager", testimonial: "The team loved it! We came as a group and it was the perfect team building activity. Absolutely cathartic.", rating: 5,
|
name: "Lisa M.", handle: "Corporate Manager", testimonial: "The team loved it! We came as a group and it was the perfect team building activity. Absolutely cathartic.", rating: 5,
|
||||||
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=2"},
|
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Daniel R.", handle: "Fitness Enthusiast", testimonial: "Incredible workout disguised as entertainment. The adrenaline rush is unmatched. Worth every euro.", rating: 5,
|
name: "Daniel R.", handle: "Fitness Enthusiast", testimonial: "Incredible workout disguised as entertainment. The adrenaline rush is unmatched. Worth every euro.", rating: 5,
|
||||||
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=3"},
|
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=3"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Sophie V.", handle: "Break-Up Survivor", testimonial: "Exactly what I needed. Therapeutic, fun, and exhilarating. Five stars all the way!", rating: 5,
|
name: "Sophie V.", handle: "Break-Up Survivor", testimonial: "Exactly what I needed. Therapeutic, fun, and exhilarating. Five stars all the way!", rating: 5,
|
||||||
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=4"},
|
imageSrc: "http://img.b2bpic.net/free-photo/close-up-portrait-young-handsome-successful-man_1163-5475.jpg?_wi=4"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
testimonialRotationInterval={5000}
|
testimonialRotationInterval={5000}
|
||||||
imageSrc="http://img.b2bpic.net/free-photo/person-suffering-from-technology-addiction-cybersickness_23-2151552653.jpg"
|
imageSrc="http://img.b2bpic.net/free-photo/person-suffering-from-technology-addiction-cybersickness_23-2151552653.jpg?_wi=1"
|
||||||
imageAlt="Rage Room destruction session"
|
imageAlt="Rage Room destruction session"
|
||||||
mediaAnimation="blur-reveal"
|
mediaAnimation="blur-reveal"
|
||||||
imagePosition="right"
|
imagePosition="right"
|
||||||
@@ -131,42 +306,62 @@ export default function LandingPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="pricing" data-section="pricing">
|
<div id="pricing" data-section="pricing">
|
||||||
<PricingCardEight
|
<div className="w-full bg-background py-16 md:py-24">
|
||||||
title="Choose Your Explosion"
|
<div className="flex items-center justify-center w-full">
|
||||||
description="Three fury-fueled packages designed to match your rage level. All include protective gear, tools, and unlimited smashing. Group discount: 4+ people pay only €40 per person per package!"
|
<div className="w-full max-w-6xl px-6 md:px-8">
|
||||||
tag="Pricing"
|
<div className="mb-12 bg-amber-50 border-l-4 border-amber-500 p-6 rounded-r-lg">
|
||||||
tagIcon={Zap}
|
<div className="flex items-start gap-4">
|
||||||
tagAnimation="slide-up"
|
<AlertCircle className="w-6 h-6 text-amber-600 flex-shrink-0 mt-0.5" />
|
||||||
plans={[
|
<div>
|
||||||
{
|
<h3 className="text-xl font-bold text-amber-900 mb-2">💰 Group Discount Alert!</h3>
|
||||||
id: "basic", badge: "Beginner", badgeIcon: Flame,
|
<p className="text-amber-800 text-lg font-semibold">Book for 4+ people and get €40 per person – our best rate!</p>
|
||||||
price: "€49", subtitle: "Perfect First Strike – 30 Minutes | Save 18% with groups of 4+", buttons: [
|
<p className="text-amber-700 text-sm mt-1">Save up to 49% on individual pricing. Perfect for team building, celebrations, and group events.</p>
|
||||||
{ text: "Book Package", onClick: () => openBookingModal('basic') },
|
</div>
|
||||||
],
|
</div>
|
||||||
features: [
|
</div>
|
||||||
"30 minutes of pure destruction", "All safety gear included", "Glass and ceramics", "Solo or duo session", "Perfect for first-timers", "Group price: €40/person (4+ people)"],
|
|
||||||
},
|
<PricingCardEight
|
||||||
{
|
title="Choose Your Explosion"
|
||||||
id: "destroyer", badge: "Most Popular", badgeIcon: Sparkles,
|
description="Three fury-fueled packages designed to match your rage level. All include protective gear, tools, and unlimited smashing."
|
||||||
price: "€79", subtitle: "Best Value – 60 Minutes | Save 18% with groups of 4+", buttons: [
|
tag="Pricing"
|
||||||
{ text: "Book Now", onClick: () => openBookingModal('destroyer') },
|
tagIcon={Zap}
|
||||||
],
|
tagAnimation="slide-up"
|
||||||
features: [
|
plans={[
|
||||||
"60 minutes of maximum chaos", "Premium safety equipment", "Glass, ceramics & electronics", "Doubles or small group", "Most items to smash", "Best value experience", "Group price: €63/person (4+ people)"],
|
{
|
||||||
},
|
id: "basic", badge: "Beginner", badgeIcon: Flame,
|
||||||
{
|
price: "€49", subtitle: "Perfect First Strike – 30 Minutes", buttons: [
|
||||||
id: "elite", badge: "Ultimate", badgeIcon: Crown,
|
{ text: "Book Package", onClick: () => openBookingModal('basic') },
|
||||||
price: "€129", subtitle: "Total Annihilation – 90 Minutes | Save 18% with groups of 4+", buttons: [
|
],
|
||||||
{ text: "Reserve Elite", onClick: () => openBookingModal('elite') },
|
features: [
|
||||||
],
|
"30 minutes of pure destruction", "All safety gear included", "Glass and ceramics", "Solo or duo session", "Perfect for first-timers", "Group price: €40/person (max 4 people)"
|
||||||
features: [
|
],
|
||||||
"90 minutes unlimited destruction", "VIP treatment & priorities", "All destruction categories", "Small group packages", "Premium room setup", "Professional photos included", "Group price: €103/person (4+ people)"],
|
},
|
||||||
},
|
{
|
||||||
]}
|
id: "destroyer", badge: "Most Popular", badgeIcon: Sparkles,
|
||||||
animationType="slide-up"
|
price: "€79", subtitle: "Best Value – 60 Minutes", buttons: [
|
||||||
textboxLayout="default"
|
{ text: "Book Now", onClick: () => openBookingModal('destroyer') },
|
||||||
useInvertedBackground={false}
|
],
|
||||||
/>
|
features: [
|
||||||
|
"60 minutes of maximum chaos", "Premium safety equipment", "Glass, ceramics & electronics", "Doubles or small group", "Most items to smash", "Best value experience", "Group price: €40/person (max 4 people)"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "elite", badge: "Ultimate", badgeIcon: Crown,
|
||||||
|
price: "€129", subtitle: "Total Annihilation – 90 Minutes", buttons: [
|
||||||
|
{ text: "Reserve Elite", onClick: () => openBookingModal('elite') },
|
||||||
|
],
|
||||||
|
features: [
|
||||||
|
"90 minutes unlimited destruction", "VIP treatment & priorities", "All destruction categories", "Small group packages", "Premium room setup", "Professional photos included", "Group price: €40/person (max 4 people)"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
animationType="slide-up"
|
||||||
|
textboxLayout="default"
|
||||||
|
useInvertedBackground={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="metrics" data-section="metrics">
|
<div id="metrics" data-section="metrics">
|
||||||
@@ -197,21 +392,57 @@ export default function LandingPage() {
|
|||||||
useInvertedBackground={false}
|
useInvertedBackground={false}
|
||||||
faqs={[
|
faqs={[
|
||||||
{
|
{
|
||||||
id: "1", title: "What should I wear?", content: "Wear comfortable, sturdy clothing and closed-toe shoes. We provide full protective gear including helmet, gloves, and safety vest. No special attire needed – we'll gear you up for maximum safety."},
|
id: "1", title: "What should I wear?", content: "Wear comfortable, sturdy clothing and closed-toe shoes. We provide full protective gear including helmet, gloves, and safety vest. No special attire needed – we'll gear you up for maximum safety."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "2", title: "Is it really safe?", content: "Absolutely. All participants receive thorough safety briefing, professional-grade protective equipment, and constant supervision. Our trained staff ensures a safe, controlled environment for pure adrenaline release."},
|
id: "2", title: "Is it really safe?", content: "Absolutely. All participants receive thorough safety briefing, professional-grade protective equipment, and constant supervision. Our trained staff ensures a safe, controlled environment for pure adrenaline release."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "3", title: "How many people can join?", content: "Sessions are designed for solo destructors up to small groups. Basic packages work great for 1-2 people. Destroyer and Elite packages support small groups. Contact us for group booking options."},
|
id: "3", title: "How many people can join?", content: "Maximum group size is 4 people per session. If your group is larger than 4 people, please contact us at bookings@rageroomvienna.local to arrange custom group bookings."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "4", title: "What's included in pricing?", content: "All packages include full protective gear, safety briefing, tools, smashable items (glass, ceramics, electronics), and supervised session time. Photos available in Elite package."},
|
id: "4", title: "What's included in pricing?", content: "All packages include full protective gear, safety briefing, tools, smashable items (glass, ceramics, electronics), and supervised session time. Photos available in Elite package."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "5", title: "Are there age restrictions?", content: "Minimum age is 16 years old (with parental consent). No maximum age limit – we welcome destructors of all ages who can safely handle the experience."},
|
id: "5", title: "Are there age restrictions?", content: "Minimum age is 16 years old (with parental consent). No maximum age limit – we welcome destructors of all ages who can safely handle the experience."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "6", title: "Can I book in advance?", content: "Yes! Check available time slots on our booking calendar. We're open Thursday 5 PM–10 PM, Friday 2 PM–12 AM, Saturday & Sunday 12 PM–12 AM."},
|
id: "6", title: "Can I book in advance?", content: "Yes! Check available time slots on our booking calendar. We're open Thursday 5 PM–10 PM, Friday 2 PM–12 AM, Saturday & Sunday 12 PM–12 AM. Monday–Wednesday are closed."
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="maps" data-section="maps">
|
||||||
|
<div className="w-full bg-card py-16 md:py-24">
|
||||||
|
<div className="flex items-center justify-center w-full">
|
||||||
|
<div className="w-full max-w-6xl px-6 md:px-8">
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-3">Visit Us</h2>
|
||||||
|
<p className="text-lg text-foreground/70">Find Rage Room Vienna at our location in the heart of the city</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-full rounded-lg overflow-hidden shadow-lg" style={{ height: '500px' }}>
|
||||||
|
<iframe
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
style={{ border: 0 }}
|
||||||
|
loading="lazy"
|
||||||
|
allowFullScreen
|
||||||
|
referrerPolicy="no-referrer-when-downgrade"
|
||||||
|
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2659.2098789453607!2d16.36892492346897!3d48.20849977122393!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x476d079f8c41d6ad%3A0x1234567890!2sVienna%2C%20Austria!5e0!3m2!1sen!2sat!4v1234567890"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 text-center">
|
||||||
|
<p className="text-foreground font-semibold mb-2">Rage Room Vienna</p>
|
||||||
|
<p className="text-foreground/70">Vienna, Austria</p>
|
||||||
|
<p className="text-foreground/70 mt-4">Open: Thu 5PM-10PM | Fri 2PM-12AM | Sat & Sun 12PM-12AM</p>
|
||||||
|
<p className="text-foreground/70 text-sm mt-2">Monday-Wednesday: Closed</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="contact" data-section="contact">
|
<div id="contact" data-section="contact">
|
||||||
<ContactCTA
|
<ContactCTA
|
||||||
tag="Ready to Rage?"
|
tag="Ready to Rage?"
|
||||||
@@ -231,7 +462,7 @@ export default function LandingPage() {
|
|||||||
|
|
||||||
<div id="footer" data-section="footer">
|
<div id="footer" data-section="footer">
|
||||||
<FooterMedia
|
<FooterMedia
|
||||||
imageSrc="http://img.b2bpic.net/free-photo/weathered-concrete-wall-texture-with-stains_632498-60807.jpg"
|
imageSrc="http://img.b2bpic.net/free-photo/weathered-concrete-wall-texture-with-stains_632498-60807.jpg?_wi=1"
|
||||||
imageAlt="Industrial concrete background"
|
imageAlt="Industrial concrete background"
|
||||||
logoText="Rage Room Vienna"
|
logoText="Rage Room Vienna"
|
||||||
copyrightText="© 2025 Rage Room Vienna | Adrenaline. Chaos. Freedom."
|
copyrightText="© 2025 Rage Room Vienna | Adrenaline. Chaos. Freedom."
|
||||||
@@ -263,8 +494,8 @@ export default function LandingPage() {
|
|||||||
|
|
||||||
{showBookingModal && (
|
{showBookingModal && (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||||
<div className="bg-white rounded-lg shadow-xl max-w-md w-full">
|
<div className="bg-white rounded-lg shadow-xl max-w-md w-full max-h-[90vh] overflow-y-auto">
|
||||||
<div className="flex items-center justify-between p-6 border-b">
|
<div className="flex items-center justify-between p-6 border-b sticky top-0 bg-white">
|
||||||
<h2 className="text-2xl font-bold">Book Your Session</h2>
|
<h2 className="text-2xl font-bold">Book Your Session</h2>
|
||||||
<button
|
<button
|
||||||
onClick={closeBookingModal}
|
onClick={closeBookingModal}
|
||||||
@@ -275,6 +506,39 @@ export default function LandingPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Name *</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
placeholder="Your full name"
|
||||||
|
className="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Email *</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
placeholder="your@email.com"
|
||||||
|
className="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-2">Phone Number (Optional)</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
|
placeholder="+43 1 234 56789"
|
||||||
|
className="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2">Package</label>
|
<label className="block text-sm font-medium mb-2">Package</label>
|
||||||
<select
|
<select
|
||||||
@@ -290,7 +554,7 @@ export default function LandingPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium mb-2">Group Size</label>
|
<label className="block text-sm font-medium mb-2">Group Size (Max 4 people)</label>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<button
|
<button
|
||||||
onClick={() => setGroupSize(Math.max(1, groupSize - 1))}
|
onClick={() => setGroupSize(Math.max(1, groupSize - 1))}
|
||||||
@@ -300,15 +564,18 @@ export default function LandingPage() {
|
|||||||
</button>
|
</button>
|
||||||
<span className="text-lg font-bold min-w-12 text-center">{groupSize}</span>
|
<span className="text-lg font-bold min-w-12 text-center">{groupSize}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => setGroupSize(groupSize + 1)}
|
onClick={() => setGroupSize(Math.min(4, groupSize + 1))}
|
||||||
className="px-3 py-2 border rounded hover:bg-gray-100"
|
className="px-3 py-2 border rounded hover:bg-gray-100"
|
||||||
>
|
>
|
||||||
+
|
+
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{selectedPackage && groupSize >= 4 && (
|
{groupSize === 4 && (
|
||||||
<p className="text-sm text-green-600 mt-2 font-semibold">✓ Group discount applied! €40 per person</p>
|
<p className="text-sm text-green-600 mt-2 font-semibold">✓ Group discount applied! €40 per person</p>
|
||||||
)}
|
)}
|
||||||
|
{groupSize > 4 && (
|
||||||
|
<p className="text-sm text-red-600 mt-2 font-semibold">Maximum group size is 4 people. Please contact us for larger groups.</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -319,6 +586,9 @@ export default function LandingPage() {
|
|||||||
onChange={(e) => setSelectedDate(e.target.value)}
|
onChange={(e) => setSelectedDate(e.target.value)}
|
||||||
className="w-full border rounded px-3 py-2"
|
className="w-full border rounded px-3 py-2"
|
||||||
/>
|
/>
|
||||||
|
{getDateWarning(selectedDate) && (
|
||||||
|
<p className="text-sm text-red-600 mt-2">{getDateWarning(selectedDate)}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -329,25 +599,57 @@ export default function LandingPage() {
|
|||||||
className="w-full border rounded px-3 py-2"
|
className="w-full border rounded px-3 py-2"
|
||||||
>
|
>
|
||||||
<option value="">Select a time</option>
|
<option value="">Select a time</option>
|
||||||
<option value="17:00">5:00 PM</option>
|
{getAvailableTimes(selectedDate).map(time => (
|
||||||
<option value="18:00">6:00 PM</option>
|
<option key={time} value={time}>
|
||||||
<option value="19:00">7:00 PM</option>
|
{isTimeAvailable(selectedDate, time) ? time : `${time} (Booked)`}
|
||||||
<option value="20:00">8:00 PM</option>
|
</option>
|
||||||
<option value="21:00">9:00 PM</option>
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
{selectedDate && getAvailableTimes(selectedDate).length === 0 && (
|
||||||
|
<p className="text-sm text-red-600 mt-2">No available times for this date</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedPackage && (
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-3">Add-ons (Optional)</label>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{addOns.map(addOn => (
|
||||||
|
<label key={addOn.id} className="flex items-center gap-3 cursor-pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedAddOns.includes(addOn.id)}
|
||||||
|
onChange={() => handleAddOnChange(addOn.id)}
|
||||||
|
className="w-4 h-4 rounded border-gray-300"
|
||||||
|
/>
|
||||||
|
<span className="text-sm">{addOn.label}</span>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedPackage && groupSize <= 4 && (
|
||||||
<div className="bg-gray-50 p-4 rounded-lg">
|
<div className="bg-gray-50 p-4 rounded-lg">
|
||||||
<p className="text-sm text-gray-600">Price per person</p>
|
<p className="text-sm text-gray-600">Price per person</p>
|
||||||
<p className="text-2xl font-bold">€{calculatePrice(packagePrices[selectedPackage], groupSize).toFixed(2)}</p>
|
<p className="text-2xl font-bold">€{calculatePrice(packagePrices[selectedPackage], groupSize).toFixed(2)}</p>
|
||||||
<p className="text-sm text-gray-600 mt-1">Total: €{(calculatePrice(packagePrices[selectedPackage], groupSize) * groupSize).toFixed(2)}</p>
|
{selectedAddOns.length > 0 && (
|
||||||
|
<div className="mt-3 pt-3 border-t text-sm">
|
||||||
|
<p className="text-gray-600">Add-ons: €{selectedAddOns.reduce((sum, id) => sum + (addOns.find(a => a.id === id)?.price || 0), 0).toFixed(2)}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="text-sm text-gray-600 mt-2">Total: €{calculateTotalPrice().toFixed(2)}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{groupSize > 4 && (
|
||||||
|
<div className="bg-red-50 p-4 rounded-lg border border-red-200">
|
||||||
|
<p className="text-sm text-red-800 font-semibold">Group size exceeds maximum</p>
|
||||||
|
<p className="text-sm text-red-700 mt-1">For groups larger than 4 people, please contact us at bookings@rageroomvienna.local</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleBooking}
|
onClick={handleBooking}
|
||||||
disabled={!selectedPackage || !selectedDate || !selectedTime}
|
disabled={!selectedPackage || !selectedDate || !selectedTime || groupSize > 4 || !name || !email}
|
||||||
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
Confirm Booking
|
Confirm Booking
|
||||||
@@ -356,6 +658,77 @@ export default function LandingPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showSuccessPopup && lastBooking && (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||||
|
<div className="bg-white rounded-lg shadow-xl max-w-md w-full p-8 text-center">
|
||||||
|
<div className="mb-4 flex justify-center">
|
||||||
|
<div className="bg-green-100 rounded-full p-3">
|
||||||
|
<CheckCircle size={40} className="text-green-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold mb-2 text-green-600">Booking Confirmed!</h2>
|
||||||
|
<p className="text-gray-600 mb-6">Your rage session is booked and ready to go.</p>
|
||||||
|
|
||||||
|
<div className="bg-gray-50 rounded-lg p-4 text-left space-y-2 mb-6">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="font-medium">Name:</span>
|
||||||
|
<span>{lastBooking.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="font-medium">Email:</span>
|
||||||
|
<span>{lastBooking.email}</span>
|
||||||
|
</div>
|
||||||
|
{lastBooking.phone && (
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="font-medium">Phone:</span>
|
||||||
|
<span>{lastBooking.phone}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex justify-between border-t pt-2 mt-2">
|
||||||
|
<span className="font-medium">Package:</span>
|
||||||
|
<span className="capitalize">{lastBooking.package}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="font-medium">Date:</span>
|
||||||
|
<span>{lastBooking.date}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="font-medium">Time:</span>
|
||||||
|
<span>{lastBooking.time}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<span className="font-medium">Group Size:</span>
|
||||||
|
<span>{lastBooking.groupSize} person(s)</span>
|
||||||
|
</div>
|
||||||
|
{lastBooking.addOns.length > 0 && (
|
||||||
|
<div className="border-t pt-2 mt-2">
|
||||||
|
<p className="font-medium mb-2">Add-ons:</p>
|
||||||
|
{lastBooking.addOns.map((addOn: any) => (
|
||||||
|
<div key={addOn.id} className="flex justify-between text-sm">
|
||||||
|
<span>{addOn.label}</span>
|
||||||
|
<span>€{addOn.price.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="border-t pt-2 mt-2 flex justify-between font-bold">
|
||||||
|
<span>Total:</span>
|
||||||
|
<span>€{lastBooking.price.toFixed(2)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-gray-600 mb-6">A confirmation email has been sent to {lastBooking.email}. Get ready to destroy!</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setShowSuccessPopup(false)}
|
||||||
|
className="w-full bg-green-600 text-white py-3 rounded-lg font-semibold hover:bg-green-700"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user