Skip to content

Commit f62c6de

Browse files
committed
feat: high-fidelity three-step booking flow - itinerary, checkout, and confirmation sync
1 parent 2c485c8 commit f62c6de

4 files changed

Lines changed: 251 additions & 105 deletions

File tree

src/components/CheckoutModal.tsx

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { motion, AnimatePresence } from "framer-motion";
2+
import { Plane, Hotel, MapPin, X, CreditCard, Wallet, CheckCircle2 } from "lucide-react";
3+
4+
interface ItineraryItem {
5+
type: 'flight' | 'hotel' | 'activity';
6+
title: string;
7+
details: string;
8+
price: number;
9+
}
10+
11+
export const CheckoutModal = ({ items, total, onConfirm, onClose }: { items: ItineraryItem[], total: number, onConfirm: () => void, onClose: () => void }) => {
12+
return (
13+
<motion.div
14+
initial={{ opacity: 0 }}
15+
animate={{ opacity: 1 }}
16+
exit={{ opacity: 0 }}
17+
className="fixed inset-0 z-[100] flex items-center justify-center p-4 backdrop-blur-2xl bg-black/80"
18+
>
19+
<motion.div
20+
initial={{ opacity: 0, scale: 0.9, y: 30 }}
21+
animate={{ opacity: 1, scale: 1, y: 0 }}
22+
className="w-full max-w-5xl bg-[#0B0F1A] border border-slate-800 rounded-[40px] shadow-[0_0_100px_rgba(0,0,0,0.8)] overflow-hidden flex flex-col md:flex-row max-h-[90vh]"
23+
>
24+
<button onClick={onClose} className="absolute top-8 right-8 text-slate-500 hover:text-white transition-colors">
25+
<X className="h-8 w-8" />
26+
</button>
27+
28+
{/* Left Side: Review */}
29+
<div className="flex-1 p-10 md:p-14 overflow-y-auto border-r border-slate-800/50">
30+
<h2 className="text-4xl font-bold text-white mb-2 tracking-tight">One-Booking Checkout</h2>
31+
<p className="text-slate-400 font-medium mb-12">Review your complete Flow and book everything at once.</p>
32+
33+
<div className="space-y-6">
34+
{items.map((item, i) => (
35+
<div key={i} className="flex items-center justify-between p-6 rounded-3xl bg-slate-900/30 border border-slate-800/30">
36+
<div className="flex items-center gap-6">
37+
<div className="h-14 w-14 rounded-2xl bg-cyan-500/10 flex items-center justify-center text-cyan-400 border border-cyan-500/20">
38+
{item.type === 'flight' && <Plane className="h-6 w-6" />}
39+
{item.type === 'hotel' && <Hotel className="h-6 w-6" />}
40+
{item.type === 'activity' && <MapPin className="h-6 w-6" />}
41+
</div>
42+
<div>
43+
<h4 className="font-bold text-white text-xl mb-1">{item.title}</h4>
44+
<p className="text-sm text-slate-500 font-medium">{item.details}</p>
45+
</div>
46+
</div>
47+
<div className="text-2xl font-bold text-cyan-400 tracking-tight">${item.price}</div>
48+
</div>
49+
))}
50+
</div>
51+
</div>
52+
53+
{/* Right Side: Payment */}
54+
<div className="w-full md:w-[400px] bg-slate-900/20 p-10 md:p-14 flex flex-col justify-between">
55+
<div>
56+
<h3 className="text-xl font-bold text-white mb-8">Summary</h3>
57+
<div className="space-y-4 mb-8">
58+
{items.map((item, i) => (
59+
<div key={i} className="flex justify-between text-sm">
60+
<span className="text-slate-500 font-medium">{item.title}</span>
61+
<span className="text-slate-300 font-bold">${item.price}</span>
62+
</div>
63+
))}
64+
</div>
65+
<div className="flex justify-between items-center pt-6 border-t border-slate-800 mb-12">
66+
<span className="text-lg font-bold text-white">Total</span>
67+
<span className="text-3xl font-display font-bold text-cyan-400">${total}</span>
68+
</div>
69+
70+
<h3 className="text-sm font-bold uppercase tracking-widest text-slate-500 mb-6">Payment</h3>
71+
<div className="space-y-3 mb-10">
72+
<div className="flex items-center gap-4 p-5 rounded-2xl border-2 border-cyan-500/50 bg-cyan-500/10 text-white cursor-pointer group">
73+
<CreditCard className="h-5 w-5 text-cyan-400" />
74+
<span className="font-bold">Card</span>
75+
</div>
76+
<div className="flex items-center gap-4 p-5 rounded-2xl border border-slate-800 bg-slate-900/50 text-slate-500 cursor-not-allowed opacity-50">
77+
<Wallet className="h-5 w-5" />
78+
<span className="font-bold">MiniPay</span>
79+
</div>
80+
</div>
81+
</div>
82+
83+
<button
84+
onClick={onConfirm}
85+
className="w-full bg-cyan-500 text-slate-900 py-6 rounded-[22px] font-black text-lg shadow-2xl shadow-cyan-500/20 hover:bg-cyan-400 hover:scale-[1.01] transition-all active:scale-[0.98]"
86+
>
87+
Confirm & Book ${total}
88+
</button>
89+
</div>
90+
</motion.div>
91+
</motion.div>
92+
);
93+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { motion } from "framer-motion";
2+
import { Check, ArrowRight } from "lucide-react";
3+
import { useNavigate } from "react-router-dom";
4+
5+
export const FlowConfirmation = ({ onNavigate }: { onNavigate: () => void }) => {
6+
return (
7+
<motion.div
8+
initial={{ opacity: 0 }}
9+
animate={{ opacity: 1 }}
10+
className="fixed inset-0 z-[200] flex items-center justify-center p-4 backdrop-blur-3xl bg-black/90"
11+
>
12+
<motion.div
13+
initial={{ opacity: 0, scale: 0.92, y: 20 }}
14+
animate={{ opacity: 1, scale: 1, y: 0 }}
15+
className="w-full max-w-[500px] aspect-square bg-[#0B0F1A] border border-slate-800 rounded-[48px] flex flex-col items-center justify-center p-12 text-center shadow-2xl"
16+
>
17+
<div className="h-24 w-24 rounded-full bg-emerald-500/10 flex items-center justify-center border border-emerald-500/20 mb-8 shadow-[0_0_40px_rgba(16,185,129,0.1)]">
18+
<Check className="h-12 w-12 text-emerald-500" strokeWidth={3} />
19+
</div>
20+
21+
<h2 className="text-4xl font-bold text-white mb-4 tracking-tight">Flow Confirmed!</h2>
22+
<p className="text-lg text-slate-400 font-medium leading-relaxed mb-12 max-w-[320px]">
23+
Your AI Pilot is now monitoring your trip. You'll receive real-time updates.
24+
</p>
25+
26+
<button
27+
onClick={onNavigate}
28+
className="w-full h-16 bg-cyan-500 text-slate-900 rounded-2xl font-black text-lg flex items-center justify-center gap-3 hover:bg-cyan-400 hover:scale-[1.02] active:scale-95 transition-all shadow-xl shadow-cyan-500/10"
29+
>
30+
View My Trips <ArrowRight className="h-6 w-6" />
31+
</button>
32+
</motion.div>
33+
</motion.div>
34+
);
35+
};

src/components/ItineraryCard.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { motion } from "framer-motion";
2+
import { Plane, Hotel, MapPin, Search } from "lucide-react";
3+
4+
interface ItineraryItem {
5+
type: 'flight' | 'hotel' | 'activity';
6+
title: string;
7+
details: string;
8+
price: number;
9+
}
10+
11+
export const ItineraryCard = ({ items, total, onBook }: { items: ItineraryItem[], total: number, onBook: () => void }) => {
12+
return (
13+
<div className="w-full max-w-2xl bg-[#0F172A]/40 border border-slate-800 rounded-[32px] p-8 mt-6 shadow-2xl backdrop-blur-xl group">
14+
<div className="space-y-4">
15+
{items.map((item, i) => (
16+
<motion.div
17+
key={i}
18+
initial={{ opacity: 0, x: -10 }}
19+
animate={{ opacity: 1, x: 0 }}
20+
transition={{ delay: i * 0.1 }}
21+
className="flex items-center justify-between p-5 rounded-2xl bg-slate-900/50 border border-slate-800/50 hover:border-cyan-500/30 transition-all"
22+
>
23+
<div className="flex items-center gap-5">
24+
<div className="h-12 w-12 rounded-xl bg-cyan-500/10 flex items-center justify-center border border-cyan-500/20 text-cyan-400">
25+
{item.type === 'flight' && <Plane className="h-5 w-5" />}
26+
{item.type === 'hotel' && <Hotel className="h-5 w-5" />}
27+
{item.type === 'activity' && <MapPin className="h-5 w-5" />}
28+
</div>
29+
<div>
30+
<h4 className="font-bold text-slate-100 text-lg">{item.title}</h4>
31+
<p className="text-sm text-slate-400 font-medium">{item.details}</p>
32+
</div>
33+
</div>
34+
<div className="text-xl font-bold text-cyan-400 tracking-tight">
35+
${item.price}
36+
</div>
37+
</motion.div>
38+
))}
39+
</div>
40+
41+
<div className="mt-8 pt-8 border-t border-slate-800 flex items-center justify-between">
42+
<div className="text-slate-400 font-medium text-lg">
43+
Total: <span className="text-2xl font-bold text-slate-100 ml-2">${total}</span>
44+
</div>
45+
<button
46+
onClick={onBook}
47+
className="bg-cyan-500 text-slate-900 px-8 py-4 rounded-2xl font-black text-sm uppercase tracking-wider hover:bg-cyan-400 hover:scale-[1.02] active:scale-95 transition-all shadow-lg shadow-cyan-500/10"
48+
>
49+
Book This Flow
50+
</button>
51+
</div>
52+
</div>
53+
);
54+
};

0 commit comments

Comments
 (0)