Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions src/app/booking/checkout/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
'use client'

import React, { useState, useEffect } from 'react'
import { useParams, useRouter } from 'next/navigation'
import { PublicLayout } from '@/components/layout/PublicLayout'
import { useBookingDetails, useSinglePayBooking } from '@/hooks/useApi'
import { Timer, CreditCard, Users, ShieldCheck, ChevronRight, AlertCircle, ArrowLeft } from 'lucide-react'

export default function CheckoutCockpit() {
const params = useParams()
const router = useRouter()
const bookingId = params.id as string

const { data: booking, isLoading } = useBookingDetails(bookingId)
const singlePay = useSinglePayBooking()

// 10 minute countdown timer (600 seconds)
const [timeLeft, setTimeLeft] = useState(600)

useEffect(() => {
if (timeLeft <= 0) return
const timer = setInterval(() => {
setTimeLeft(prev => prev - 1)
}, 1000)
return () => clearInterval(timer)
}, [timeLeft])

const formatTime = (seconds: number) => {
const m = Math.floor(seconds / 60)
const s = seconds % 60
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
}

const handleSinglePay = async () => {
try {
const idempotencyKey = crypto.randomUUID()
const result = await singlePay.mutateAsync({ bookingId, idempotencyKey })

// If Stripe returns clientSecret, redirect to Stripe checkout or embed elements
// For now, if we get a URL, we redirect (assuming hosted checkout for simplicity, or we can use elements)
if (result.clientSecret) {
// Just as a placeholder for the actual stripe element integration
alert("Stripe initialized! Redirecting to payment...")
}
} catch (err) {
console.error("Payment failed", err)
alert("Failed to initialize payment gateway.")
}
}

const handleSplitPay = () => {
alert("Split Pay group initialization is coming soon!")
}

if (isLoading) {
return (
<PublicLayout>
<div className="w-full h-screen bg-[#0F172A] flex items-center justify-center">
<div className="animate-spin w-8 h-8 border-4 border-[#7C3AED] border-t-transparent rounded-full" />
</div>
</PublicLayout>
)
}

if (!booking) {
return (
<PublicLayout>
<div className="w-full h-screen bg-[#0F172A] flex flex-col items-center justify-center text-white">
<h1 className="text-2xl font-bold mb-2">Booking Session Expired</h1>
<button onClick={() => router.push('/')} className="text-[#7C3AED] hover:underline flex items-center gap-1">
<ArrowLeft className="w-4 h-4" /> Go back home
</button>
</div>
</PublicLayout>
)
}

return (
<PublicLayout>
<div className="w-full bg-[#0F172A] min-h-[calc(100vh-80px)] text-white py-12">
<div className="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">

{/* Header Row */}
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-6 mb-10">
<div>
<button onClick={() => router.back()} className="text-white/50 hover:text-white flex items-center gap-1 mb-4 text-sm transition-colors">
<ArrowLeft className="w-4 h-4" /> Back to Seat Selection
</button>
<h1 className="text-3xl md:text-4xl font-black text-white">Checkout</h1>
<p className="text-white/50 mt-1">Complete your purchase to secure your seats.</p>
</div>

{/* The Countdown Timer */}
<div className="bg-red-500/10 border border-red-500/20 px-6 py-4 rounded-2xl flex items-center gap-4 shadow-[0_0_30px_rgba(239,68,68,0.1)]">
<div className="w-12 h-12 rounded-full bg-red-500/20 flex items-center justify-center animate-pulse">
<Timer className="w-6 h-6 text-red-500" />
</div>
<div>
<p className="text-xs text-red-400 font-bold uppercase tracking-wider mb-0.5">Time Remaining</p>
<p className="text-2xl font-black text-red-500 tabular-nums">{formatTime(timeLeft)}</p>
</div>
</div>
</div>

<div className="grid grid-cols-1 lg:grid-cols-12 gap-8">

{/* Left Section - Booking Summary */}
<div className="lg:col-span-4 flex flex-col gap-6">
<div className="bg-[#1E293B]/50 border border-white/10 rounded-3xl p-6 md:p-8">
<h2 className="text-xl font-bold text-white mb-6 border-b border-white/10 pb-4">Order Summary</h2>

<div className="space-y-6">
{/* Mock Event Info for now since booking details might not have full event entity populated deeply yet depending on backend */}
<div>
<p className="text-sm text-white/50 mb-1">Event</p>
<p className="font-bold text-lg text-white">{(booking as any).event?.name || 'RushTix Event'}</p>
</div>

<div>
<p className="text-sm text-white/50 mb-1">Selected Seats</p>
<div className="flex flex-wrap gap-2">
{(booking as any).seats?.map((seat: any) => (
<span key={seat.id} className="px-3 py-1 bg-[#7C3AED]/20 text-[#7C3AED] border border-[#7C3AED]/30 text-xs font-bold rounded-md">
{seat.rowLabel}{seat.columnNumber}
</span>
))}
</div>
</div>

<div className="pt-6 border-t border-white/10">
<div className="flex items-center justify-between mb-2">
<p className="text-white/70">Subtotal ({(booking as any).seats?.length || 0} seats)</p>
<p className="text-white font-medium">₹{booking.totalAmount}</p>
</div>
<div className="flex items-center justify-between mb-6">
<p className="text-white/70">Taxes & Fees</p>
<p className="text-white font-medium">₹0</p>
</div>
<div className="flex items-center justify-between pt-4 border-t border-white/10">
<p className="text-lg font-bold text-white">Total Amount</p>
<p className="text-2xl font-black text-white">₹{booking.totalAmount}</p>
</div>
</div>
</div>
</div>

<div className="bg-green-500/10 border border-green-500/20 rounded-2xl p-4 flex items-start gap-3">
<ShieldCheck className="w-5 h-5 text-green-400 shrink-0 mt-0.5" />
<p className="text-sm text-green-400/90 leading-relaxed">
Your connection is secure. We use 256-bit encryption to protect your payment details.
</p>
</div>
</div>

{/* Right Section - The Two Payment Pillars */}
<div className="lg:col-span-8 flex flex-col gap-6">
<h2 className="text-xl font-bold text-white mb-2">How would you like to pay?</h2>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6">

{/* Card A (Single Pay) */}
<div className="bg-[#1E293B] border-2 border-transparent hover:border-[#7C3AED]/50 rounded-3xl p-8 flex flex-col transition-all hover:shadow-2xl hover:shadow-[#7C3AED]/10 group">
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#7C3AED] to-[#4F46E5] flex items-center justify-center mb-6 shadow-lg shadow-[#7C3AED]/20">
<CreditCard className="w-8 h-8 text-white" />
</div>
<h3 className="text-2xl font-bold text-white mb-3">Pay Full Amount Now</h3>
<p className="text-white/60 mb-8 leading-relaxed flex-1">
Pay for all tickets instantly using credit card, debit card, or UPI. Secure your seats immediately for the entire group.
</p>
<button
onClick={handleSinglePay}
disabled={singlePay.isPending}
className="w-full py-4 bg-white hover:bg-gray-100 text-[#0F172A] rounded-xl font-bold text-lg transition-colors flex items-center justify-center gap-2 group-hover:scale-[1.02]"
>
{singlePay.isPending ? 'Processing...' : 'Proceed to Payment'} <ChevronRight className="w-5 h-5" />
</button>
</div>

{/* Card B (Split Pay) */}
<div className="bg-[#1E293B] border-2 border-transparent hover:border-orange-500/50 rounded-3xl p-8 flex flex-col transition-all hover:shadow-2xl hover:shadow-orange-500/10 group">
<div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-orange-500 to-red-500 flex items-center justify-center mb-6 shadow-lg shadow-orange-500/20">
<Users className="w-8 h-8 text-white" />
</div>
<h3 className="text-2xl font-bold text-white mb-3">Split Bill With Friends 👥</h3>
<p className="text-white/60 mb-8 leading-relaxed flex-1">
Lock these seats right now for free. We will generate split payment links for your group chat so everyone pays their own share.
</p>
<button
onClick={handleSplitPay}
className="w-full py-4 bg-white/5 border border-white/10 hover:bg-white/10 text-white rounded-xl font-bold text-lg transition-colors flex items-center justify-center gap-2 group-hover:scale-[1.02] group-hover:border-orange-500/50"
>
Initialize Split-Pay Group <ChevronRight className="w-5 h-5" />
</button>
</div>

</div>
</div>

</div>
</div>
</div>
</PublicLayout>
)
}
File renamed without changes.
Loading
Loading