991 lines
40 KiB
TypeScript
991 lines
40 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { navigateTo } from './Router';
|
|
import { Button } from './ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
|
|
import { Badge } from './ui/badge';
|
|
import { Input } from './ui/input';
|
|
import { Label } from './ui/label';
|
|
import { Textarea } from './ui/textarea';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
|
import { Calendar } from './ui/calendar';
|
|
import { Alert, AlertDescription } from './ui/alert';
|
|
import { Separator } from './ui/separator';
|
|
import { ImageWithFallback } from './figma/ImageWithFallback';
|
|
|
|
import {
|
|
Users,
|
|
MapPin,
|
|
Clock,
|
|
CheckCircle,
|
|
CreditCard,
|
|
Calendar as CalendarIcon,
|
|
Building,
|
|
Phone,
|
|
Mail,
|
|
User,
|
|
ArrowLeft,
|
|
ArrowRight,
|
|
Wifi,
|
|
Coffee,
|
|
Car,
|
|
Utensils,
|
|
Copy,
|
|
Download,
|
|
AlertCircle,
|
|
XCircle,
|
|
Zap,
|
|
Award,
|
|
Shield
|
|
} from 'lucide-react';
|
|
|
|
interface BookingFormData {
|
|
companyName: string;
|
|
contactName: string;
|
|
email: string;
|
|
phone: string;
|
|
role: string;
|
|
teamSize: string;
|
|
facilityZone: string;
|
|
additionalRequirements: string;
|
|
}
|
|
|
|
interface TimeSlot {
|
|
time: string;
|
|
available: boolean;
|
|
price: number;
|
|
}
|
|
|
|
interface BookingConfirmation {
|
|
referenceId: string;
|
|
facilityName: string;
|
|
date: string;
|
|
timeSlot: string;
|
|
totalAmount: number;
|
|
companyName: string;
|
|
contactName: string;
|
|
email: string;
|
|
phone: string;
|
|
}
|
|
|
|
const facilityData = {
|
|
id: 'executive-boardroom',
|
|
name: 'Executive Boardroom',
|
|
description: 'Premium boardroom designed for high-level meetings and strategic discussions with state-of-the-art technology.',
|
|
capacity: 20,
|
|
pricePerHour: 2500,
|
|
image: 'https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=800&h=600&fit=crop',
|
|
features: [
|
|
'4K video conferencing system',
|
|
'Premium leather seating',
|
|
'Climate controlled environment',
|
|
'Wireless presentation system',
|
|
'Premium catering options',
|
|
'Dedicated technical support'
|
|
],
|
|
amenities: [
|
|
{ icon: Wifi, name: 'High-Speed WiFi' },
|
|
{ icon: Coffee, name: 'Refreshment Station' },
|
|
{ icon: Car, name: 'Valet Parking' },
|
|
{ icon: Utensils, name: 'Premium Catering' }
|
|
]
|
|
};
|
|
|
|
const generateTimeSlots = (date: Date): TimeSlot[] => {
|
|
const slots: TimeSlot[] = [];
|
|
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
|
|
|
|
if (isWeekend) {
|
|
return []; // No slots available on weekends
|
|
}
|
|
|
|
const startHour = 9;
|
|
const endHour = 18;
|
|
|
|
for (let hour = startHour; hour < endHour; hour++) {
|
|
const timeString = `${hour.toString().padStart(2, '0')}:00`;
|
|
const available = Math.random() > 0.3; // Random availability for demo
|
|
slots.push({
|
|
time: timeString,
|
|
available,
|
|
price: facilityData.pricePerHour
|
|
});
|
|
}
|
|
|
|
return slots;
|
|
};
|
|
|
|
const generateReferenceId = (): string => {
|
|
return 'KLC-' + Date.now().toString().slice(-8).toUpperCase();
|
|
};
|
|
|
|
export function BookFacility() {
|
|
const [currentStep, setCurrentStep] = useState<'booking' | 'confirmation' | 'payment-success' | 'payment-failed'>('booking');
|
|
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
|
const [selectedTimeSlot, setSelectedTimeSlot] = useState<string>('');
|
|
const [timeSlots, setTimeSlots] = useState<TimeSlot[]>([]);
|
|
const [bookingForm, setBookingForm] = useState<BookingFormData>({
|
|
companyName: '',
|
|
contactName: '',
|
|
email: '',
|
|
phone: '',
|
|
role: '',
|
|
teamSize: '',
|
|
facilityZone: 'executive-wing',
|
|
additionalRequirements: ''
|
|
});
|
|
const [bookingConfirmation, setBookingConfirmation] = useState<BookingConfirmation | null>(null);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
document.title = 'Book Facility - Kautilya Leadership Centre';
|
|
window.scrollTo(0, 0);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (selectedDate) {
|
|
setTimeSlots(generateTimeSlots(selectedDate));
|
|
setSelectedTimeSlot(''); // Reset time slot when date changes
|
|
}
|
|
}, [selectedDate]);
|
|
|
|
const formatPrice = (price: number) => {
|
|
return new Intl.NumberFormat('en-IN', {
|
|
style: 'currency',
|
|
currency: 'INR',
|
|
maximumFractionDigits: 0
|
|
}).format(price);
|
|
};
|
|
|
|
const formatDate = (date: Date) => {
|
|
return date.toLocaleDateString('en-IN', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
};
|
|
|
|
const handleSubmitBooking = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!selectedDate || !selectedTimeSlot) return;
|
|
|
|
setIsSubmitting(true);
|
|
|
|
// Simulate API call
|
|
setTimeout(() => {
|
|
const selectedSlot = timeSlots.find(slot => slot.time === selectedTimeSlot);
|
|
const confirmation: BookingConfirmation = {
|
|
referenceId: generateReferenceId(),
|
|
facilityName: facilityData.name,
|
|
date: formatDate(selectedDate),
|
|
timeSlot: selectedTimeSlot,
|
|
totalAmount: selectedSlot?.price || facilityData.pricePerHour,
|
|
companyName: bookingForm.companyName,
|
|
contactName: bookingForm.contactName,
|
|
email: bookingForm.email,
|
|
phone: bookingForm.phone
|
|
};
|
|
|
|
setBookingConfirmation(confirmation);
|
|
setCurrentStep('confirmation');
|
|
setIsSubmitting(false);
|
|
}, 2000);
|
|
};
|
|
|
|
const handlePayNow = () => {
|
|
// Simulate payment processing
|
|
setIsSubmitting(true);
|
|
setTimeout(() => {
|
|
const success = Math.random() > 0.2; // 80% success rate for demo
|
|
setCurrentStep(success ? 'payment-success' : 'payment-failed');
|
|
setIsSubmitting(false);
|
|
}, 3000);
|
|
};
|
|
|
|
const copyReferenceId = () => {
|
|
if (bookingConfirmation) {
|
|
navigator.clipboard.writeText(bookingConfirmation.referenceId);
|
|
}
|
|
};
|
|
|
|
const isFormValid = () => {
|
|
return (
|
|
bookingForm.companyName &&
|
|
bookingForm.contactName &&
|
|
bookingForm.email &&
|
|
bookingForm.phone &&
|
|
bookingForm.role &&
|
|
bookingForm.teamSize &&
|
|
selectedDate &&
|
|
selectedTimeSlot
|
|
);
|
|
};
|
|
|
|
const availableTimeSlots = timeSlots.filter(slot => slot.available);
|
|
|
|
if (currentStep === 'payment-success') {
|
|
return (
|
|
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
|
<div className="pt-40 pb-16">
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="max-w-2xl mx-auto text-center">
|
|
<div
|
|
className="w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6"
|
|
style={{ backgroundColor: 'rgba(34, 197, 94, 0.1)' }}
|
|
>
|
|
<CheckCircle className="w-10 h-10 text-green-600" />
|
|
</div>
|
|
|
|
<h1 className="text-h2 mb-4">
|
|
Payment Successful!
|
|
</h1>
|
|
|
|
<p className="text-body-lg text-muted mb-8">
|
|
Your facility booking has been confirmed and payment processed successfully.
|
|
You'll receive a confirmation email shortly.
|
|
</p>
|
|
|
|
{bookingConfirmation && (
|
|
<Card className="mb-8 text-left" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardHeader>
|
|
<CardTitle className="text-subhead">Booking Confirmed</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4 text-body">
|
|
<div>
|
|
<span className="text-muted">Reference ID:</span>
|
|
<div className="text-subhead">{bookingConfirmation.referenceId}</div>
|
|
</div>
|
|
<div>
|
|
<span className="text-muted">Amount Paid:</span>
|
|
<div className="text-subhead text-green-600">
|
|
{formatPrice(bookingConfirmation.totalAmount)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<button
|
|
className="brand-button-system"
|
|
onClick={() => navigateTo('/')}
|
|
>
|
|
<Download className="w-5 h-5" />
|
|
Download Receipt
|
|
</button>
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
onClick={() => navigateTo('/')}
|
|
className="text-body"
|
|
>
|
|
Return to Homepage
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (currentStep === 'payment-failed') {
|
|
return (
|
|
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
|
<div className="pt-40 pb-16">
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="max-w-2xl mx-auto text-center">
|
|
<div
|
|
className="w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6"
|
|
style={{ backgroundColor: 'rgba(239, 68, 68, 0.1)' }}
|
|
>
|
|
<XCircle className="w-10 h-10 text-red-600" />
|
|
</div>
|
|
|
|
<h1 className="text-h2 mb-4">
|
|
Payment Failed
|
|
</h1>
|
|
|
|
<p className="text-body-lg text-muted mb-8">
|
|
We encountered an issue processing your payment. Your booking is still reserved
|
|
for the next 30 minutes. Please try again or contact our support team.
|
|
</p>
|
|
|
|
<Alert className="mb-8 text-left">
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertDescription className="text-body">
|
|
Your booking reference <strong>{bookingConfirmation?.referenceId}</strong> is
|
|
temporarily held. Please complete payment within 30 minutes to confirm your booking.
|
|
</AlertDescription>
|
|
</Alert>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<Button
|
|
size="lg"
|
|
onClick={handlePayNow}
|
|
disabled={isSubmitting}
|
|
className="text-body"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white'
|
|
}}
|
|
>
|
|
<CreditCard className="w-5 h-5 mr-2" />
|
|
{isSubmitting ? 'Processing...' : 'Try Payment Again'}
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
onClick={() => navigateTo('/contact')}
|
|
className="text-body"
|
|
>
|
|
Contact Support
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (currentStep === 'confirmation') {
|
|
return (
|
|
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
|
<div className="pt-40 pb-16">
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* Confirmation Header */}
|
|
<div className="text-center mb-8">
|
|
<div
|
|
className="w-20 h-20 rounded-full flex items-center justify-center mx-auto mb-6"
|
|
style={{ backgroundColor: 'rgba(34, 197, 94, 0.1)' }}
|
|
>
|
|
<CheckCircle className="w-10 h-10 text-green-600" />
|
|
</div>
|
|
<h1 className="text-h2 mb-4">
|
|
Booking Request Submitted
|
|
</h1>
|
|
<p className="text-body-lg text-muted">
|
|
Your facility booking request has been received and is being processed.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Confirmation Panel */}
|
|
{bookingConfirmation && (
|
|
<Card className="mb-8" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<CardTitle className="text-subhead">Booking Confirmation</CardTitle>
|
|
<Badge variant="outline" className="text-body px-4 py-2">
|
|
Pending Confirmation
|
|
</Badge>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
{/* Reference ID */}
|
|
<div
|
|
className="rounded-lg p-4"
|
|
style={{ backgroundColor: 'rgba(0, 0, 0, 0.03)' }}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<span className="text-body text-muted">Reference ID</span>
|
|
<div className="text-subhead text-primary">
|
|
{bookingConfirmation.referenceId}
|
|
</div>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={copyReferenceId}
|
|
className="text-body"
|
|
>
|
|
<Copy className="w-4 h-4 mr-2" />
|
|
Copy
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Booking Details */}
|
|
<div className="grid md:grid-cols-2 gap-6">
|
|
<div className="space-y-4">
|
|
<h3 className="text-subhead">Facility Details</h3>
|
|
<div className="space-y-3">
|
|
<div className="flex items-center gap-3">
|
|
<Building className="w-5 h-5 text-muted" />
|
|
<div>
|
|
<div className="text-body">{bookingConfirmation.facilityName}</div>
|
|
<div className="text-small text-muted">Executive Wing</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<CalendarIcon className="w-5 h-5 text-muted" />
|
|
<div>
|
|
<div className="text-body">{bookingConfirmation.date}</div>
|
|
<div className="text-small text-muted">{bookingConfirmation.timeSlot}</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Users className="w-5 h-5 text-muted" />
|
|
<div>
|
|
<div className="text-body">Up to {facilityData.capacity} people</div>
|
|
<div className="text-small text-muted">Maximum capacity</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<h3 className="text-subhead">Contact Information</h3>
|
|
<div className="space-y-3">
|
|
<div className="flex items-center gap-3">
|
|
<Building className="w-5 h-5 text-muted" />
|
|
<div>
|
|
<div className="text-body">{bookingConfirmation.companyName}</div>
|
|
<div className="text-small text-muted">Company</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<User className="w-5 h-5 text-muted" />
|
|
<div>
|
|
<div className="text-body">{bookingConfirmation.contactName}</div>
|
|
<div className="text-small text-muted">Contact Person</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Mail className="w-5 h-5 text-muted" />
|
|
<div>
|
|
<div className="text-body">{bookingConfirmation.email}</div>
|
|
<div className="text-small text-muted">Email</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Pricing */}
|
|
<div className="flex items-center justify-between text-subhead">
|
|
<span>Total Amount</span>
|
|
<span className="text-primary">
|
|
{formatPrice(bookingConfirmation.totalAmount)}
|
|
</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<Button
|
|
size="lg"
|
|
onClick={handlePayNow}
|
|
disabled={isSubmitting}
|
|
className="text-body-lg"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white'
|
|
}}
|
|
>
|
|
<CreditCard className="w-5 h-5 mr-2" />
|
|
{isSubmitting ? 'Processing Payment...' : 'Pay Now'}
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
size="lg"
|
|
onClick={() => navigateTo('/')}
|
|
className="text-body-lg"
|
|
>
|
|
Continue Browsing
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen" style={{ backgroundColor: '#F7F7FD' }}>
|
|
{/* Hero Section */}
|
|
<section className="relative h-[80vh] lg:h-[90vh] overflow-hidden pt-20" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<div className="h-full flex items-center">
|
|
{/* Left Side - Content (60%) */}
|
|
<div className="relative w-full lg:w-[60%] h-full flex items-center justify-center p-8 lg:p-16 py-12 lg:py-16">
|
|
<div className="max-w-2xl space-y-8">
|
|
{/* Price Badge */}
|
|
<div className="space-y-2">
|
|
<Badge
|
|
className="text-body px-4 py-2"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white',
|
|
border: 'none'
|
|
}}
|
|
>
|
|
₹2,500/hour
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* Main Headline */}
|
|
<div className="space-y-6">
|
|
<h1 className="text-h1 text-primary leading-tight">
|
|
Book Executive
|
|
<br />
|
|
<span className="text-primary">Boardroom</span>
|
|
</h1>
|
|
</div>
|
|
|
|
{/* Description */}
|
|
<div className="space-y-6">
|
|
<p className="text-body-lg text-muted leading-relaxed max-w-xl">
|
|
Premium boardroom designed for high-level meetings and strategic discussions with
|
|
state-of-the-art technology.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Facility Details */}
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
<div className="flex items-center gap-2">
|
|
<Users className="w-5 h-5 text-muted" />
|
|
<span className="text-body">20 people</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<MapPin className="w-5 h-5 text-muted" />
|
|
<span className="text-body">Executive Wing</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Clock className="w-5 h-5 text-muted" />
|
|
<span className="text-body">9 AM - 6 PM</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<CheckCircle className="w-5 h-5 text-green-600" />
|
|
<span className="text-body">Available</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Amenities */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
<div className="flex items-center gap-3">
|
|
<Wifi className="w-5 h-5 text-primary flex-shrink-0" />
|
|
<span className="text-body">High-Speed WiFi</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Coffee className="w-5 h-5 text-primary flex-shrink-0" />
|
|
<span className="text-body">Refreshment Station</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Car className="w-5 h-5 text-primary flex-shrink-0" />
|
|
<span className="text-body">Valet Parking</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Utensils className="w-5 h-5 text-primary flex-shrink-0" />
|
|
<span className="text-body">Premium Catering</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Zap className="w-5 h-5 text-primary flex-shrink-0" />
|
|
<span className="text-body">4K Video System</span>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Award className="w-5 h-5 text-primary flex-shrink-0" />
|
|
<span className="text-body">Premium Seating</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CTA Button */}
|
|
<div className="pt-4">
|
|
<Button
|
|
onClick={() => {
|
|
const bookingSection = document.getElementById('booking-section');
|
|
if (bookingSection) {
|
|
bookingSection.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
}}
|
|
className="group shadow-xl"
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white',
|
|
borderRadius: '9999px',
|
|
padding: '1rem 2rem'
|
|
}}
|
|
>
|
|
<span>Book This Boardroom</span>
|
|
<ArrowRight className="w-5 h-5 ml-2 group-hover:translate-x-1 transition-transform" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right Side - Luxury Interior Image (40%) */}
|
|
<div className="hidden lg:block relative w-[40%] h-full">
|
|
<div className="absolute inset-0">
|
|
<ImageWithFallback
|
|
src="https://images.unsplash.com/photo-1577791464704-3bbd2da56803?w=1080&h=800&fit=crop"
|
|
alt="Luxury executive boardroom with modern design"
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
{/* Subtle overlay for depth */}
|
|
<div className="absolute inset-0 bg-gradient-to-l from-transparent via-transparent to-white/20"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div id="booking-section" className="py-8 lg:py-12">
|
|
<div className="mx-auto section-margin-x">
|
|
<div className="max-w-6xl mx-auto">
|
|
{/* Back Button */}
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => navigateTo('/learning-facility/virtual-tour')}
|
|
className="mb-6 text-body"
|
|
>
|
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
Back to Virtual Tour
|
|
</Button>
|
|
|
|
{/* Facility Header Card */}
|
|
<Card className="mb-8" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardContent className="p-6">
|
|
<div className="grid lg:grid-cols-3 gap-6">
|
|
<div className="lg:col-span-1">
|
|
<div className="aspect-video rounded-lg overflow-hidden">
|
|
<ImageWithFallback
|
|
src={facilityData.image}
|
|
alt={facilityData.name}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="lg:col-span-2">
|
|
<div className="flex items-start justify-between mb-4">
|
|
<div>
|
|
<h1 className="text-h3 mb-2">
|
|
Book {facilityData.name}
|
|
</h1>
|
|
<p className="text-body text-muted mb-4">
|
|
{facilityData.description}
|
|
</p>
|
|
</div>
|
|
<Badge variant="secondary" className="text-body px-4 py-2">
|
|
{formatPrice(facilityData.pricePerHour)}/hour
|
|
</Badge>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
<div className="flex items-center gap-2">
|
|
<Users className="w-5 h-5 text-muted" />
|
|
<span className="text-body">{facilityData.capacity} people</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<MapPin className="w-5 h-5 text-muted" />
|
|
<span className="text-body">Executive Wing</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Clock className="w-5 h-5 text-muted" />
|
|
<span className="text-body">9 AM - 6 PM</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<CheckCircle className="w-5 h-5 text-green-600" />
|
|
<span className="text-body">Available</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-2">
|
|
{facilityData.amenities.map((amenity, index) => (
|
|
<Badge key={index} variant="outline" className="text-small px-3 py-1">
|
|
<amenity.icon className="w-3 h-3 mr-1" />
|
|
{amenity.name}
|
|
</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<div className="grid lg:grid-cols-3 gap-8">
|
|
{/* Booking Form */}
|
|
<div className="lg:col-span-2">
|
|
<form onSubmit={handleSubmitBooking} className="space-y-8">
|
|
{/* Company Information */}
|
|
<Card style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardHeader>
|
|
<CardTitle className="text-subhead">Company Information</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="companyName" className="text-body">Company Name *</Label>
|
|
<Input
|
|
id="companyName"
|
|
value={bookingForm.companyName}
|
|
onChange={(e) => setBookingForm({...bookingForm, companyName: e.target.value})}
|
|
required
|
|
className="text-body min-h-[44px]"
|
|
placeholder="Enter company name"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="contactName" className="text-body">Contact Person *</Label>
|
|
<Input
|
|
id="contactName"
|
|
value={bookingForm.contactName}
|
|
onChange={(e) => setBookingForm({...bookingForm, contactName: e.target.value})}
|
|
required
|
|
className="text-body min-h-[44px]"
|
|
placeholder="Enter contact name"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="email" className="text-body">Email Address *</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
value={bookingForm.email}
|
|
onChange={(e) => setBookingForm({...bookingForm, email: e.target.value})}
|
|
required
|
|
className="text-body min-h-[44px]"
|
|
placeholder="Enter email address"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="phone" className="text-body">Phone Number *</Label>
|
|
<Input
|
|
id="phone"
|
|
value={bookingForm.phone}
|
|
onChange={(e) => setBookingForm({...bookingForm, phone: e.target.value})}
|
|
required
|
|
className="text-body min-h-[44px]"
|
|
placeholder="Enter phone number"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="role" className="text-body">Your Role *</Label>
|
|
<Select value={bookingForm.role} onValueChange={(value) => setBookingForm({...bookingForm, role: value})}>
|
|
<SelectTrigger className="text-body min-h-[44px]">
|
|
<SelectValue placeholder="Select your role" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="hr-director">HR Director</SelectItem>
|
|
<SelectItem value="learning-development">L&D Manager</SelectItem>
|
|
<SelectItem value="executive-assistant">Executive Assistant</SelectItem>
|
|
<SelectItem value="ceo-founder">CEO/Founder</SelectItem>
|
|
<SelectItem value="other">Other</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="teamSize" className="text-body">Expected Team Size *</Label>
|
|
<Select value={bookingForm.teamSize} onValueChange={(value) => setBookingForm({...bookingForm, teamSize: value})}>
|
|
<SelectTrigger className="text-body min-h-[44px]">
|
|
<SelectValue placeholder="Select team size" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="1-5">1-5 people</SelectItem>
|
|
<SelectItem value="6-10">6-10 people</SelectItem>
|
|
<SelectItem value="11-15">11-15 people</SelectItem>
|
|
<SelectItem value="16-20">16-20 people</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="additionalRequirements" className="text-body">Additional Requirements</Label>
|
|
<Textarea
|
|
id="additionalRequirements"
|
|
value={bookingForm.additionalRequirements}
|
|
onChange={(e) => setBookingForm({...bookingForm, additionalRequirements: e.target.value})}
|
|
placeholder="Catering, special AV setup, accessibility needs..."
|
|
className="text-body min-h-[100px]"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Date and Time Selection */}
|
|
<Card style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardHeader>
|
|
<CardTitle className="text-subhead">Select Date & Time</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
{/* Calendar Widget */}
|
|
<div>
|
|
<Label className="text-body mb-4 block">Select Date *</Label>
|
|
<div className="border rounded-lg p-4">
|
|
<Calendar
|
|
mode="single"
|
|
selected={selectedDate}
|
|
onSelect={setSelectedDate}
|
|
disabled={(date) =>
|
|
date < new Date() ||
|
|
date.getDay() === 0 ||
|
|
date.getDay() === 6
|
|
}
|
|
className="rounded-md"
|
|
/>
|
|
</div>
|
|
<p className="text-small text-muted mt-2">
|
|
Bookings available Monday to Friday only
|
|
</p>
|
|
</div>
|
|
|
|
{/* Time Slots */}
|
|
{selectedDate && (
|
|
<div>
|
|
<Label className="text-body mb-4 block">Available Time Slots *</Label>
|
|
{availableTimeSlots.length > 0 ? (
|
|
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
|
|
{availableTimeSlots.map((slot) => (
|
|
<Button
|
|
key={slot.time}
|
|
type="button"
|
|
variant={selectedTimeSlot === slot.time ? "default" : "outline"}
|
|
className="text-body justify-between p-4 h-auto"
|
|
onClick={() => setSelectedTimeSlot(slot.time)}
|
|
style={{
|
|
backgroundColor: selectedTimeSlot === slot.time ? 'var(--color-primary)' : 'transparent',
|
|
color: selectedTimeSlot === slot.time ? 'white' : 'var(--color-black)'
|
|
}}
|
|
>
|
|
<span>{slot.time}</span>
|
|
<span className="text-small">{formatPrice(slot.price)}</span>
|
|
</Button>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<Alert>
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertDescription className="text-body">
|
|
No time slots available for the selected date. Please choose a different date.
|
|
</AlertDescription>
|
|
</Alert>
|
|
)}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Submit Button */}
|
|
<Card style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardContent className="p-6">
|
|
<Button
|
|
type="submit"
|
|
size="lg"
|
|
className="w-full text-body-lg"
|
|
disabled={!isFormValid() || isSubmitting}
|
|
style={{
|
|
backgroundColor: 'var(--color-primary)',
|
|
color: 'white'
|
|
}}
|
|
>
|
|
{isSubmitting ? (
|
|
'Processing Booking...'
|
|
) : (
|
|
<>
|
|
Submit Booking Request
|
|
<ArrowRight className="w-5 h-5 ml-2" />
|
|
</>
|
|
)}
|
|
</Button>
|
|
<p className="text-small text-muted text-center mt-4">
|
|
By submitting, you agree to our booking terms and conditions
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</form>
|
|
</div>
|
|
|
|
{/* Booking Summary Sidebar */}
|
|
<div className="lg:col-span-1">
|
|
<Card className="sticky top-24" style={{ backgroundColor: 'var(--color-bg-white)' }}>
|
|
<CardHeader>
|
|
<CardTitle className="text-subhead">Booking Summary</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between text-body">
|
|
<span>Facility:</span>
|
|
<span className="text-subhead">{facilityData.name}</span>
|
|
</div>
|
|
|
|
{selectedDate && (
|
|
<div className="flex justify-between text-body">
|
|
<span>Date:</span>
|
|
<span className="text-subhead">
|
|
{selectedDate.toLocaleDateString('en-IN', {
|
|
month: 'short',
|
|
day: 'numeric'
|
|
})}
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
{selectedTimeSlot && (
|
|
<div className="flex justify-between text-body">
|
|
<span>Time:</span>
|
|
<span className="text-subhead">{selectedTimeSlot}</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex justify-between text-body">
|
|
<span>Duration:</span>
|
|
<span className="text-subhead">1 hour</span>
|
|
</div>
|
|
|
|
{bookingForm.teamSize && (
|
|
<div className="flex justify-between text-body">
|
|
<span>Team Size:</span>
|
|
<span className="text-subhead">{bookingForm.teamSize}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="space-y-3">
|
|
<div className="flex justify-between text-body">
|
|
<span>Base Rate:</span>
|
|
<span>{formatPrice(facilityData.pricePerHour)}</span>
|
|
</div>
|
|
<div className="flex justify-between text-body">
|
|
<span>Service Charge:</span>
|
|
<span>Included</span>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex justify-between text-subhead">
|
|
<span>Total:</span>
|
|
<span className="text-primary">{formatPrice(facilityData.pricePerHour)}</span>
|
|
</div>
|
|
|
|
<div className="text-small text-muted">
|
|
<p>• Payment due upon confirmation</p>
|
|
<p>• Free cancellation up to 24 hours</p>
|
|
<p>• Technical support included</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |