49 Commits

Author SHA1 Message Date
51544bd38f Merge version_7 into main
Merge version_7 into main
2026-03-05 23:00:06 +00:00
89bcb4aa00 Update src/app/page.tsx 2026-03-05 23:00:01 +00:00
f816ce0398 Update src/app/contact/page.tsx 2026-03-05 23:00:00 +00:00
a2dda1e1fa Merge version_6 into main
Merge version_6 into main
2026-03-05 22:54:47 +00:00
f77990cfbc Update src/app/page.tsx 2026-03-05 22:54:43 +00:00
c185064a1e Update src/app/layout.tsx 2026-03-05 22:54:42 +00:00
8c24cf88c2 Add src/app/api/bookings/route.ts 2026-03-05 22:54:42 +00:00
1ad102e242 Merge version_5 into main
Merge version_5 into main
2026-03-05 22:48:45 +00:00
0d57279cb9 Update src/app/page.tsx 2026-03-05 22:48:41 +00:00
0cb96a87fc Update src/app/contact/page.tsx 2026-03-05 22:48:41 +00:00
709809a43d Switch to version 3: modified src/app/styles/variables.css 2026-03-05 22:43:18 +00:00
14845f6a43 Switch to version 3: modified src/app/page.tsx 2026-03-05 22:43:18 +00:00
a2236262a7 Switch to version 4: modified src/app/styles/variables.css 2026-03-05 22:26:40 +00:00
685ded6818 Switch to version 4: modified src/app/page.tsx 2026-03-05 22:26:39 +00:00
2b47d52b41 Switch to version 3: modified src/app/page.tsx 2026-03-05 22:26:34 +00:00
4b1a154c72 Switch to version 3: added src/app/contact/page.tsx 2026-03-05 22:26:34 +00:00
610e785ce8 Switch to version 2: modified src/app/page.tsx 2026-03-05 22:26:31 +00:00
f5a8271149 Switch to version 1: modified src/app/page.tsx 2026-03-05 22:26:19 +00:00
afc2ea67c8 Switch to version 2: remove src/app/contact/page.tsx 2026-03-05 22:26:11 +00:00
afd0f20994 Switch to version 2: modified src/app/page.tsx 2026-03-05 22:26:11 +00:00
5e87e99246 Switch to version 3: modified src/app/styles/variables.css 2026-03-05 22:26:00 +00:00
42da17e0ed Switch to version 3: modified src/app/page.tsx 2026-03-05 22:25:59 +00:00
d0dbd8f953 Merge version_4 into main
Merge version_4 into main
2026-03-05 22:25:19 +00:00
53d950981e Update theme colors 2026-03-05 22:25:11 +00:00
f69e24ccde Merge version_4 into main
Merge version_4 into main
2026-03-05 22:24:29 +00:00
c63c1c7bcd Update theme colors 2026-03-05 22:24:23 +00:00
8fad654f10 Merge version_4 into main
Merge version_4 into main
2026-03-05 22:23:06 +00:00
f688991241 Update theme colors 2026-03-05 22:23:00 +00:00
aecd434918 Merge version_4 into main
Merge version_4 into main
2026-03-05 22:22:31 +00:00
3bfc9ee9ca Update theme colors 2026-03-05 22:22:25 +00:00
69ffbd86ea Merge version_4 into main
Merge version_4 into main
2026-03-05 22:21:50 +00:00
0f8cfb13fb Update theme colors 2026-03-05 22:21:44 +00:00
659dcd2387 Merge version_4 into main
Merge version_4 into main
2026-03-05 22:15:48 +00:00
f9ecbba79e Update src/app/page.tsx 2026-03-05 22:15:44 +00:00
0b017ed241 Switch to version 3: modified src/app/page.tsx 2026-03-05 22:11:52 +00:00
996e1d468d Switch to version 3: added src/app/contact/page.tsx 2026-03-05 22:11:51 +00:00
19ad5f2935 Switch to version 2: modified src/app/page.tsx 2026-03-05 22:11:38 +00:00
5463af8225 Switch to version 1: modified src/app/page.tsx 2026-03-05 22:11:19 +00:00
781355feb4 Switch to version 2: remove src/app/contact/page.tsx 2026-03-05 22:09:58 +00:00
c50bf4e63f Switch to version 2: modified src/app/page.tsx 2026-03-05 22:09:57 +00:00
4f5c85bf6e Merge version_3 into main
Merge version_3 into main
2026-03-05 22:07:58 +00:00
ae1e84fd9f Update src/app/page.tsx 2026-03-05 22:07:48 +00:00
05130d2151 Update src/app/contact/page.tsx 2026-03-05 22:07:48 +00:00
ad1f0594d2 Merge version_3 into main
Merge version_3 into main
2026-03-05 22:07:07 +00:00
e1ac8386b6 Update src/app/page.tsx 2026-03-05 22:07:03 +00:00
decd098528 Add src/app/contact/page.tsx 2026-03-05 22:07:02 +00:00
181669a257 Merge version_2 into main
Merge version_2 into main
2026-03-05 22:00:21 +00:00
1954429fd0 Update src/app/page.tsx 2026-03-05 22:00:16 +00:00
884f40c72e Merge version_1 into main
Merge version_1 into main
2026-03-05 21:52:46 +00:00
4 changed files with 715 additions and 53 deletions

View 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
View 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>
);
}

View File

@@ -23,11 +23,13 @@ const poppins = Poppins({
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"),
alternates: {
canonical: "https://www.rageroomvienna.at"},
canonical: "https://www.rageroomvienna.at"
},
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: [
{
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: {

View File

@@ -9,9 +9,223 @@ import MetricCardThree from '@/components/sections/metrics/MetricCardThree';
import FaqSplitText from '@/components/sections/faq/FaqSplitText';
import ContactCTA from '@/components/sections/contact/ContactCTA';
import FooterMedia from '@/components/sections/footer/FooterMedia';
import { Zap, Flame, Sparkles, Crown, Users, Star, Clock, TrendingUp, Mail } from 'lucide-react';
import { Zap, Flame, Sparkles, Crown, Users, Star, Clock, TrendingUp, Mail, Calendar, X, CheckCircle, AlertCircle } from 'lucide-react';
import { useState } from 'react';
export default function LandingPage() {
const [showBookingModal, setShowBookingModal] = useState(false);
const [showSuccessPopup, setShowSuccessPopup] = useState(false);
const [selectedPackage, setSelectedPackage] = useState<string | null>(null);
const [groupSize, setGroupSize] = useState(1);
const [selectedDate, setSelectedDate] = 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) => {
if (quantity >= 4) {
return 40;
}
return basePrice;
};
const packagePrices: Record<string, number> = {
basic: 49,
destroyer: 79,
elite: 129,
};
const openBookingModal = (packageId: string) => {
setSelectedPackage(packageId);
setGroupSize(1);
setSelectedAddOns([]);
setName('');
setEmail('');
setPhone('');
setShowBookingModal(true);
};
const closeBookingModal = () => {
setShowBookingModal(false);
setSelectedPackage(null);
setGroupSize(1);
setSelectedDate('');
setSelectedTime('');
setSelectedAddOns([]);
setName('');
setEmail('');
setPhone('');
};
const handleAddOnChange = (addOnId: string) => {
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 packageTotal = pricePerPerson * groupSize;
const addOnsTotal = selectedAddOns.reduce((sum, addOnId) => {
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 (
<ThemeProvider
defaultButtonVariant="expand-hover"
@@ -45,7 +259,7 @@ export default function LandingPage() {
tagIcon={Zap}
tagAnimation="slide-up"
buttons={[
{ text: "Book Your Session", href: "#pricing" },
{ text: "Book Your Session", onClick: () => openBookingModal('destroyer') },
{ text: "Learn More", href: "#about" },
]}
buttonAnimation="slide-up"
@@ -53,19 +267,23 @@ export default function LandingPage() {
testimonials={[
{
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,
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,
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,
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}
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"
mediaAnimation="blur-reveal"
imagePosition="right"
@@ -88,42 +306,62 @@ export default function LandingPage() {
</div>
<div id="pricing" data-section="pricing">
<PricingCardEight
title="Choose Your Explosion"
description="Three fury-fueled packages designed to match your rage level. All include protective gear, tools, and unlimited smashing."
tag="Pricing"
tagIcon={Zap}
tagAnimation="slide-up"
plans={[
{
id: "basic", badge: "Beginner", badgeIcon: Flame,
price: "€49", subtitle: "Perfect First Strike 30 Minutes", buttons: [
{ text: "Select Package", href: "https://booking.rageroomvienna.local/basic" },
],
features: [
"30 minutes of pure destruction", "All safety gear included", "Glass and ceramics", "Solo or duo session", "Perfect for first-timers"],
},
{
id: "destroyer", badge: "Most Popular", badgeIcon: Sparkles,
price: "€79", subtitle: "Bestes Preis-Leistungs-Verhältnis 60 Minuten pure Eskalation", buttons: [
{ text: "Book Now", href: "https://booking.rageroomvienna.local/destroyer" },
],
features: [
"60 minutes of maximum chaos", "Premium safety equipment", "Glass, ceramics & electronics", "Doubles or small group", "Most items to smash", "Best value experience"],
},
{
id: "elite", badge: "Ultimate", badgeIcon: Crown,
price: "€129", subtitle: "Total Annihilation 90 Minutes", buttons: [
{ text: "Reserve Elite", href: "https://booking.rageroomvienna.local/elite" },
],
features: [
"90 minutes unlimited destruction", "VIP treatment & priorities", "All destruction categories", "Small group packages", "Premium room setup", "Professional photos included"],
},
]}
animationType="slide-up"
textboxLayout="default"
useInvertedBackground={false}
/>
<div className="w-full bg-background 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="mb-12 bg-amber-50 border-l-4 border-amber-500 p-6 rounded-r-lg">
<div className="flex items-start gap-4">
<AlertCircle className="w-6 h-6 text-amber-600 flex-shrink-0 mt-0.5" />
<div>
<h3 className="text-xl font-bold text-amber-900 mb-2">💰 Group Discount Alert!</h3>
<p className="text-amber-800 text-lg font-semibold">Book for 4+ people and get 40 per person our best rate!</p>
<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>
</div>
</div>
</div>
<PricingCardEight
title="Choose Your Explosion"
description="Three fury-fueled packages designed to match your rage level. All include protective gear, tools, and unlimited smashing."
tag="Pricing"
tagIcon={Zap}
tagAnimation="slide-up"
plans={[
{
id: "basic", badge: "Beginner", badgeIcon: Flame,
price: "€49", subtitle: "Perfect First Strike 30 Minutes", buttons: [
{ text: "Book Package", onClick: () => openBookingModal('basic') },
],
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)"
],
},
{
id: "destroyer", badge: "Most Popular", badgeIcon: Sparkles,
price: "€79", subtitle: "Best Value 60 Minutes", buttons: [
{ text: "Book Now", onClick: () => openBookingModal('destroyer') },
],
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 id="metrics" data-section="metrics">
@@ -154,21 +392,57 @@ export default function LandingPage() {
useInvertedBackground={false}
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 PM10 PM, Friday 2 PM12 AM, Saturday & Sunday 12 PM12 AM."},
id: "6", title: "Can I book in advance?", content: "Yes! Check available time slots on our booking calendar. We're open Thursday 5 PM10 PM, Friday 2 PM12 AM, Saturday & Sunday 12 PM12 AM. MondayWednesday are closed."
},
]}
/>
</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">
<ContactCTA
tag="Ready to Rage?"
@@ -177,7 +451,7 @@ export default function LandingPage() {
title="Let's Get You Booked"
description="Choose your package above and select your time slot on our calendar. Or get in touch with questions our team is ready to help you plan the perfect destruction session."
buttons={[
{ text: "Book a Session", href: "#pricing" },
{ text: "Book a Session", onClick: () => openBookingModal('destroyer') },
{ text: "Contact Us", href: "mailto:bookings@rageroomvienna.local" },
]}
buttonAnimation="slide-up"
@@ -188,7 +462,7 @@ export default function LandingPage() {
<div id="footer" data-section="footer">
<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"
logoText="Rage Room Vienna"
copyrightText="© 2025 Rage Room Vienna | Adrenaline. Chaos. Freedom."
@@ -217,6 +491,244 @@ export default function LandingPage() {
]}
/>
</div>
{showBookingModal && (
<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 max-h-[90vh] overflow-y-auto">
<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>
<button
onClick={closeBookingModal}
className="text-gray-500 hover:text-gray-700"
>
<X size={24} />
</button>
</div>
<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>
<label className="block text-sm font-medium mb-2">Package</label>
<select
value={selectedPackage || ''}
onChange={(e) => setSelectedPackage(e.target.value)}
className="w-full border rounded px-3 py-2"
>
<option value="">Select a package</option>
<option value="basic">Basic - 49</option>
<option value="destroyer">Destroyer - 79</option>
<option value="elite">Elite - 129</option>
</select>
</div>
<div>
<label className="block text-sm font-medium mb-2">Group Size (Max 4 people)</label>
<div className="flex items-center gap-4">
<button
onClick={() => setGroupSize(Math.max(1, groupSize - 1))}
className="px-3 py-2 border rounded hover:bg-gray-100"
>
</button>
<span className="text-lg font-bold min-w-12 text-center">{groupSize}</span>
<button
onClick={() => setGroupSize(Math.min(4, groupSize + 1))}
className="px-3 py-2 border rounded hover:bg-gray-100"
>
+
</button>
</div>
{groupSize === 4 && (
<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>
<label className="block text-sm font-medium mb-2">Date</label>
<input
type="date"
value={selectedDate}
onChange={(e) => setSelectedDate(e.target.value)}
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>
<label className="block text-sm font-medium mb-2">Time</label>
<select
value={selectedTime}
onChange={(e) => setSelectedTime(e.target.value)}
className="w-full border rounded px-3 py-2"
>
<option value="">Select a time</option>
{getAvailableTimes(selectedDate).map(time => (
<option key={time} value={time}>
{isTimeAvailable(selectedDate, time) ? time : `${time} (Booked)`}
</option>
))}
</select>
{selectedDate && getAvailableTimes(selectedDate).length === 0 && (
<p className="text-sm text-red-600 mt-2">No available times for this date</p>
)}
</div>
<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">
<p className="text-sm text-gray-600">Price per person</p>
<p className="text-2xl font-bold">{calculatePrice(packagePrices[selectedPackage], 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>
)}
<button
onClick={handleBooking}
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"
>
Confirm Booking
</button>
</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>
);
}