popsite-ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/App.jsx +95 -0
- package/README.md +92 -0
- package/components/layout/PortalHeader.jsx +18 -0
- package/components/layout/SystemSidebar.jsx +33 -0
- package/components/modules/AnalyticsDashboardModule.jsx +17 -0
- package/components/modules/ChatMessagingModule.jsx +17 -0
- package/components/modules/EcommerceStoreModule.jsx +17 -0
- package/components/modules/EventTicketBookingModule.jsx +17 -0
- package/components/modules/FlightBookingModule.jsx +17 -0
- package/components/modules/FoodOrderingModule.jsx +17 -0
- package/components/modules/HospitalAppointmentModule.jsx +17 -0
- package/components/modules/HotelBookingModule.jsx +17 -0
- package/components/modules/InvoiceBillingModule.jsx +17 -0
- package/components/modules/LibraryManagementModule.jsx +17 -0
- package/components/modules/ModuleContentDeck.jsx +44 -0
- package/components/modules/MovieBookingModule.jsx +17 -0
- package/components/modules/QuizExamModule.jsx +17 -0
- package/components/modules/StudentRegistrationModule.jsx +17 -0
- package/components/modules/SystemModuleRenderer.jsx +19 -0
- package/components/modules/SystemModuleTemplate.jsx +62 -0
- package/components/modules/SystemVisualWidget.jsx +123 -0
- package/components/modules/moduleContentMap.js +238 -0
- package/components/modules/moduleEnhancementsMap.js +439 -0
- package/components/modules/systemModuleMap.js +31 -0
- package/components/system/DynamicSystemForm.jsx +154 -0
- package/components/system/SystemHero.jsx +21 -0
- package/components/system/SystemSummaryCard.jsx +53 -0
- package/data/systems/analyticsDashboard.js +48 -0
- package/data/systems/chatMessaging.js +43 -0
- package/data/systems/ecommerceStore.js +50 -0
- package/data/systems/eventTicketBooking.js +50 -0
- package/data/systems/flightBooking.js +38 -0
- package/data/systems/foodOrdering.js +48 -0
- package/data/systems/hospitalAppointment.js +50 -0
- package/data/systems/hotelBooking.js +38 -0
- package/data/systems/index.js +31 -0
- package/data/systems/invoiceBilling.js +50 -0
- package/data/systems/libraryManagement.js +43 -0
- package/data/systems/movieBooking.js +48 -0
- package/data/systems/quizExam.js +38 -0
- package/data/systems/studentRegistration.js +43 -0
- package/dist/popsite-ui.es.js +4368 -0
- package/dist/popsite-ui.umd.js +60 -0
- package/dist/style.css +1 -0
- package/index.html +13 -0
- package/library/index.js +20 -0
- package/main.jsx +15 -0
- package/package.json +40 -0
- package/src/App.jsx +12 -0
- package/src/components/modules/AnalyticsDashboardModule.jsx +224 -0
- package/src/components/modules/ChatMessagingModule.jsx +294 -0
- package/src/components/modules/EcommerceStoreModule.jsx +405 -0
- package/src/components/modules/EventTicketBookingModule.jsx +253 -0
- package/src/components/modules/FlightBookingModule.jsx +399 -0
- package/src/components/modules/FoodOrderingModule.jsx +316 -0
- package/src/components/modules/HospitalAppointmentModule.jsx +267 -0
- package/src/components/modules/HotelBookingModule.jsx +317 -0
- package/src/components/modules/InvoiceBillingModule.jsx +302 -0
- package/src/components/modules/LandingPageModule.jsx +185 -0
- package/src/components/modules/LibraryManagementModule.jsx +189 -0
- package/src/components/modules/MovieBookingModule.jsx +337 -0
- package/src/components/modules/QuizExamModule.jsx +255 -0
- package/src/components/modules/StudentRegistrationModule.jsx +292 -0
- package/src/components/system/SystemHero.jsx +44 -0
- package/src/components/system/SystemSummaryCard.jsx +29 -0
- package/src/components/system/Toast.jsx +69 -0
- package/src/data/systems/analyticsDashboard.js +32 -0
- package/src/data/systems/chatMessaging.js +59 -0
- package/src/data/systems/ecommerceStore.js +84 -0
- package/src/data/systems/eventBooking.js +33 -0
- package/src/data/systems/flightBooking.js +59 -0
- package/src/data/systems/foodOrdering.js +48 -0
- package/src/data/systems/hospitalAppointment.js +48 -0
- package/src/data/systems/hotelBooking.js +59 -0
- package/src/data/systems/invoiceBilling.js +19 -0
- package/src/data/systems/landingPage.js +29 -0
- package/src/data/systems/libraryManagement.js +17 -0
- package/src/data/systems/movieBooking.js +49 -0
- package/src/data/systems/quizExam.js +31 -0
- package/src/data/systems/studentRegistration.js +9 -0
- package/src/index.js +22 -0
- package/src/main.jsx +10 -0
- package/src/styles.css +296 -0
- package/styles.css +820 -0
- package/utils/systemEngine.js +128 -0
- package/vite.config.js +8 -0
- package/vite.lib.config.js +27 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { flightBookingMockData } from '../../data/systems/flightBooking';
|
|
3
|
+
import { useToast } from '../system/Toast';
|
|
4
|
+
|
|
5
|
+
export function FlightBookingModule() {
|
|
6
|
+
const [currentView, setCurrentView] = useState('search'); // search, results, passenger, pass
|
|
7
|
+
|
|
8
|
+
const [searchParams, setSearchParams] = useState({
|
|
9
|
+
from: flightBookingMockData.airports[0],
|
|
10
|
+
to: flightBookingMockData.airports[1],
|
|
11
|
+
date: new Date().toISOString().split('T')[0],
|
|
12
|
+
passengers: 1
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const [selectedFlight, setSelectedFlight] = useState(null);
|
|
16
|
+
const [selectedClass, setSelectedClass] = useState('economy'); // economy, business
|
|
17
|
+
const [baggageCount, setBaggageCount] = useState(0);
|
|
18
|
+
const [passengerDetails, setPassengerDetails] = useState({ firstName: '', lastName: '', passport: '' });
|
|
19
|
+
const [bookedFlight, setBookedFlight] = useState(null);
|
|
20
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
21
|
+
|
|
22
|
+
const { showToast, ToastContainer } = useToast();
|
|
23
|
+
|
|
24
|
+
// Simulated persisted state
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const saved = localStorage.getItem('popsite_flight');
|
|
27
|
+
if (saved) {
|
|
28
|
+
try { setBookedFlight(JSON.parse(saved)); } catch (e) {}
|
|
29
|
+
}
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (bookedFlight) {
|
|
34
|
+
localStorage.setItem('popsite_flight', JSON.stringify(bookedFlight));
|
|
35
|
+
}
|
|
36
|
+
}, [bookedFlight]);
|
|
37
|
+
|
|
38
|
+
const withDelay = (callback, delay = 1500) => {
|
|
39
|
+
setIsLoading(true);
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
setIsLoading(false);
|
|
42
|
+
callback();
|
|
43
|
+
}, delay);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const handleSearch = (e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
if (searchParams.from === searchParams.to) {
|
|
49
|
+
showToast('Destination cannot be the same as origin', 'error');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
withDelay(() => {
|
|
53
|
+
setCurrentView('results');
|
|
54
|
+
showToast('Flights loaded successfully', 'success');
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handleSelectFlight = (flight) => {
|
|
59
|
+
setSelectedFlight(flight);
|
|
60
|
+
setCurrentView('passenger');
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const calculateTotal = () => {
|
|
64
|
+
if (!selectedFlight) return 0;
|
|
65
|
+
const basePrice = selectedClass === 'business' ? selectedFlight.businessPrice : selectedFlight.price;
|
|
66
|
+
const baggageTotal = baggageCount * flightBookingMockData.addOns.checkedBag;
|
|
67
|
+
return (basePrice * searchParams.passengers) + baggageTotal;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const handleBook = (e) => {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
if (!passengerDetails.firstName || !passengerDetails.lastName || !passengerDetails.passport) {
|
|
73
|
+
showToast('Please fill out all passenger details', 'error');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
withDelay(() => {
|
|
78
|
+
const ticket = {
|
|
79
|
+
bookingRef: `PNR${Math.random().toString(36).substring(2, 8).toUpperCase()}`,
|
|
80
|
+
flight: selectedFlight,
|
|
81
|
+
seatClass: selectedClass,
|
|
82
|
+
baggage: baggageCount,
|
|
83
|
+
passenger: passengerDetails,
|
|
84
|
+
date: searchParams.date,
|
|
85
|
+
total: calculateTotal()
|
|
86
|
+
};
|
|
87
|
+
setBookedFlight(ticket);
|
|
88
|
+
setCurrentView('pass');
|
|
89
|
+
showToast('Booking confirmed!', 'success');
|
|
90
|
+
}, 2000);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const renderSearch = () => (
|
|
94
|
+
<div className="pk-container animate-fade-in" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '80vh' }}>
|
|
95
|
+
<div style={{ textAlign: 'center', marginBottom: '3rem' }}>
|
|
96
|
+
<h1 className="pk-heading-xl" style={{ marginBottom: '1rem' }}>{flightBookingMockData.airlineName}</h1>
|
|
97
|
+
<p className="pk-text-body" style={{ fontSize: '1.25rem' }}>Where will your next adventure take you?</p>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div className="pk-card pk-glass" style={{ width: '100%', maxWidth: '900px', padding: '3rem' }}>
|
|
101
|
+
<form onSubmit={handleSearch} className="pk-grid" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '1.5rem', alignItems: 'end' }}>
|
|
102
|
+
<div className="pk-input-group" style={{ margin: 0 }}>
|
|
103
|
+
<label className="pk-label">From</label>
|
|
104
|
+
<select
|
|
105
|
+
className="pk-input"
|
|
106
|
+
value={searchParams.from}
|
|
107
|
+
onChange={e => setSearchParams({...searchParams, from: e.target.value})}
|
|
108
|
+
>
|
|
109
|
+
{flightBookingMockData.airports.map(apt => <option key={apt} value={apt}>{apt}</option>)}
|
|
110
|
+
</select>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div className="pk-input-group" style={{ margin: 0 }}>
|
|
114
|
+
<label className="pk-label">To</label>
|
|
115
|
+
<select
|
|
116
|
+
className="pk-input"
|
|
117
|
+
value={searchParams.to}
|
|
118
|
+
onChange={e => setSearchParams({...searchParams, to: e.target.value})}
|
|
119
|
+
>
|
|
120
|
+
{flightBookingMockData.airports.map(apt => <option key={apt} value={apt}>{apt}</option>)}
|
|
121
|
+
</select>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div className="pk-input-group" style={{ margin: 0 }}>
|
|
125
|
+
<label className="pk-label">Departure Date</label>
|
|
126
|
+
<input
|
|
127
|
+
type="date"
|
|
128
|
+
className="pk-input"
|
|
129
|
+
value={searchParams.date}
|
|
130
|
+
onChange={e => setSearchParams({...searchParams, date: e.target.value})}
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div className="pk-input-group" style={{ margin: 0 }}>
|
|
135
|
+
<label className="pk-label">Passengers</label>
|
|
136
|
+
<input
|
|
137
|
+
type="number"
|
|
138
|
+
min="1" max="9"
|
|
139
|
+
className="pk-input"
|
|
140
|
+
value={searchParams.passengers}
|
|
141
|
+
onChange={e => setSearchParams({...searchParams, passengers: parseInt(e.target.value) || 1})}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<button type="submit" className="pk-btn pk-btn-primary" style={{ padding: '1.25rem', height: '100%' }}>
|
|
146
|
+
Search Flights
|
|
147
|
+
</button>
|
|
148
|
+
</form>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{bookedFlight && (
|
|
152
|
+
<button className="pk-btn pk-btn-outline" style={{ marginTop: '2rem' }} onClick={() => setCurrentView('pass')}>
|
|
153
|
+
View Upcoming Trip ({bookedFlight.bookingRef})
|
|
154
|
+
</button>
|
|
155
|
+
)}
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const renderResults = () => {
|
|
160
|
+
// For demo: Fuzzy filter flights where 'from' and 'to' partially match
|
|
161
|
+
const flights = flightBookingMockData.flights.filter(f =>
|
|
162
|
+
f.from.includes(searchParams.from.split(' ')[0]) ||
|
|
163
|
+
f.to.includes(searchParams.to.split(' ')[0]) ||
|
|
164
|
+
true // Just return all so demo looks populated
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div className="pk-container animate-fade-in">
|
|
169
|
+
<div className="pk-flex pk-justify-between pk-items-center" style={{ marginBottom: '2rem' }}>
|
|
170
|
+
<div>
|
|
171
|
+
<h2 className="pk-heading-lg">Select Departure Flight</h2>
|
|
172
|
+
<p className="pk-text-body">{searchParams.from} → {searchParams.to} | {searchParams.date} | {searchParams.passengers} Passenger(s)</p>
|
|
173
|
+
</div>
|
|
174
|
+
<button className="pk-btn pk-btn-outline" onClick={() => setCurrentView('search')}>Edit Search</button>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div className="pk-flex pk-flex-col pk-gap-6">
|
|
178
|
+
{flights.map(flight => (
|
|
179
|
+
<div key={flight.id} className="pk-card pk-card-interactive" style={{ padding: '2rem', display: 'flex', flexWrap: 'wrap', gap: '2rem', alignItems: 'center' }}>
|
|
180
|
+
<div style={{ flex: '1 1 300px' }}>
|
|
181
|
+
<div className="pk-flex pk-justify-between pk-items-center" style={{ marginBottom: '1rem' }}>
|
|
182
|
+
<span className="pk-badge">{flight.flightNumber}</span>
|
|
183
|
+
<span className="pk-text-sm" style={{ color: 'var(--pk-primary)', fontWeight: 600 }}>{flight.duration}</span>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{/* Timeline Visualizer */}
|
|
187
|
+
<div className="pk-flex pk-justify-between pk-items-center">
|
|
188
|
+
<div className="pk-text-center">
|
|
189
|
+
<h3 className="pk-heading-md">{flight.departureTime}</h3>
|
|
190
|
+
<p className="pk-text-muted">{flight.from.split(' ')[0]}</p>
|
|
191
|
+
</div>
|
|
192
|
+
<div style={{ flex: 1, margin: '0 1rem', display: 'flex', alignItems: 'center', justifyItems: 'center' }}>
|
|
193
|
+
<div style={{ height: '2px', background: 'var(--pk-border)', flex: 1 }}></div>
|
|
194
|
+
<span style={{ margin: '0 1rem', fontSize: '1.5rem', opacity: 0.5 }}>✈️</span>
|
|
195
|
+
<div style={{ height: '2px', background: 'var(--pk-border)', flex: 1 }}></div>
|
|
196
|
+
</div>
|
|
197
|
+
<div className="pk-text-center">
|
|
198
|
+
<h3 className="pk-heading-md">{flight.arrivalTime}</h3>
|
|
199
|
+
<p className="pk-text-muted">{flight.to.split(' ')[0]}</p>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<div style={{ width: '1px', background: 'var(--pk-border)', height: '100px', alignSelf: 'stretch' }} className="pk-hidden-mobile"></div>
|
|
205
|
+
|
|
206
|
+
<div style={{ flex: '0 0 auto', display: 'flex', flexDirection: 'column', gap: '1rem', minWidth: '150px' }}>
|
|
207
|
+
<div className="pk-text-center">
|
|
208
|
+
<span className="pk-text-sm pk-text-muted">Economy from</span>
|
|
209
|
+
<h3 className="pk-heading-lg" style={{ color: 'var(--pk-secondary)' }}>${flight.price}</h3>
|
|
210
|
+
</div>
|
|
211
|
+
<button className="pk-btn pk-btn-outline" onClick={() => { setSelectedClass('economy'); handleSelectFlight(flight); }}>
|
|
212
|
+
Select Economy
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<div style={{ flex: '0 0 auto', display: 'flex', flexDirection: 'column', gap: '1rem', minWidth: '150px' }}>
|
|
217
|
+
<div className="pk-text-center">
|
|
218
|
+
<span className="pk-text-sm pk-text-muted">Business from</span>
|
|
219
|
+
<h3 className="pk-heading-lg" style={{ color: 'var(--pk-primary)' }}>${flight.businessPrice}</h3>
|
|
220
|
+
</div>
|
|
221
|
+
<button className="pk-btn pk-btn-primary" onClick={() => { setSelectedClass('business'); handleSelectFlight(flight); }}>
|
|
222
|
+
Select Business
|
|
223
|
+
</button>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
))}
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const renderPassenger = () => (
|
|
233
|
+
<div className="pk-container animate-fade-in">
|
|
234
|
+
<button className="pk-btn pk-btn-outline" style={{ marginBottom: '2rem' }} onClick={() => setCurrentView('results')}>← Back to Flights</button>
|
|
235
|
+
|
|
236
|
+
<div className="pk-grid" style={{ gridTemplateColumns: 'minmax(300px, 2fr) minmax(300px, 1fr)' }}>
|
|
237
|
+
<div className="pk-flex pk-flex-col pk-gap-6">
|
|
238
|
+
<div className="pk-card" style={{ padding: '2rem' }}>
|
|
239
|
+
<h2 className="pk-heading-md" style={{ marginBottom: '1.5rem', borderBottom: '1px solid var(--pk-border)', paddingBottom: '0.5rem' }}>Primary Passenger</h2>
|
|
240
|
+
<form onSubmit={handleBook} id="booking-form">
|
|
241
|
+
<div className="pk-grid" style={{ gridTemplateColumns: '1fr 1fr' }}>
|
|
242
|
+
<div className="pk-input-group">
|
|
243
|
+
<label className="pk-label">First Name</label>
|
|
244
|
+
<input type="text" className="pk-input" value={passengerDetails.firstName} onChange={e => setPassengerDetails({...passengerDetails, firstName: e.target.value})} required />
|
|
245
|
+
</div>
|
|
246
|
+
<div className="pk-input-group">
|
|
247
|
+
<label className="pk-label">Last Name</label>
|
|
248
|
+
<input type="text" className="pk-input" value={passengerDetails.lastName} onChange={e => setPassengerDetails({...passengerDetails, lastName: e.target.value})} required />
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
<div className="pk-input-group">
|
|
252
|
+
<label className="pk-label">Passport Number</label>
|
|
253
|
+
<input type="text" className="pk-input" value={passengerDetails.passport} onChange={e => setPassengerDetails({...passengerDetails, passport: e.target.value})} required />
|
|
254
|
+
</div>
|
|
255
|
+
</form>
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div className="pk-card" style={{ padding: '2rem' }}>
|
|
259
|
+
<h2 className="pk-heading-md" style={{ marginBottom: '1.5rem', borderBottom: '1px solid var(--pk-border)', paddingBottom: '0.5rem' }}>Customize Your Trip</h2>
|
|
260
|
+
|
|
261
|
+
<div className="pk-flex pk-items-center pk-justify-between" style={{ padding: '1rem', background: 'var(--pk-bg-main)', borderRadius: 'var(--pk-radius-md)', marginBottom: '1rem' }}>
|
|
262
|
+
<div>
|
|
263
|
+
<h4 className="pk-label">Checked Baggage</h4>
|
|
264
|
+
<p className="pk-text-sm">Up to 23kg per bag (${flightBookingMockData.addOns.checkedBag} each)</p>
|
|
265
|
+
</div>
|
|
266
|
+
<div className="pk-flex pk-items-center pk-gap-4">
|
|
267
|
+
<button type="button" className="pk-btn pk-btn-outline" style={{ padding: '0.5rem', borderRadius: '50%', width: 40, height: 40 }} onClick={() => setBaggageCount(Math.max(0, baggageCount - 1))}>-</button>
|
|
268
|
+
<span className="pk-heading-md">{baggageCount}</span>
|
|
269
|
+
<button type="button" className="pk-btn pk-btn-outline" style={{ padding: '0.5rem', borderRadius: '50%', width: 40, height: 40 }} onClick={() => setBaggageCount(baggageCount + 1)}>+</button>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<div>
|
|
276
|
+
<div className="pk-card" style={{ position: 'sticky', top: '100px' }}>
|
|
277
|
+
<div className="pk-card-header bg-gradient" style={{ background: 'linear-gradient(135deg, var(--pk-bg-main), var(--pk-primary-light))', padding: '2rem' }}>
|
|
278
|
+
<span className="pk-badge" style={{ marginBottom: '1rem', display: 'inline-block' }}>{selectedClass.toUpperCase()}</span>
|
|
279
|
+
<h3 className="pk-heading-md">{selectedFlight.from.split(' ')[0]} ✈ {selectedFlight.to.split(' ')[0]}</h3>
|
|
280
|
+
<p className="pk-text-sm">{searchParams.date}</p>
|
|
281
|
+
</div>
|
|
282
|
+
<div className="pk-card-body pk-flex pk-flex-col pk-gap-4">
|
|
283
|
+
<div className="pk-flex pk-justify-between">
|
|
284
|
+
<span className="pk-text-body">1x Ticket</span>
|
|
285
|
+
<span style={{ fontWeight: 600 }}>${selectedClass === 'business' ? selectedFlight.businessPrice : selectedFlight.price}</span>
|
|
286
|
+
</div>
|
|
287
|
+
{baggageCount > 0 && (
|
|
288
|
+
<div className="pk-flex pk-justify-between">
|
|
289
|
+
<span className="pk-text-body">{baggageCount}x Checked Bag</span>
|
|
290
|
+
<span style={{ fontWeight: 600 }}>${baggageCount * flightBookingMockData.addOns.checkedBag}</span>
|
|
291
|
+
</div>
|
|
292
|
+
)}
|
|
293
|
+
<hr style={{ border: 0, borderTop: '1px solid var(--pk-border)' }} />
|
|
294
|
+
<div className="pk-flex pk-justify-between pk-items-center">
|
|
295
|
+
<span className="pk-heading-md">Total</span>
|
|
296
|
+
<span className="pk-heading-lg" style={{ color: 'var(--pk-primary)' }}>${calculateTotal()}</span>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<button type="submit" form="booking-form" className="pk-btn pk-btn-primary pk-w-full" style={{ marginTop: '1rem', padding: '1.25rem' }}>
|
|
300
|
+
Confirm & Pay
|
|
301
|
+
</button>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const renderPass = () => {
|
|
310
|
+
if (!bookedFlight) return null;
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<div className="pk-container animate-fade-in pk-flex pk-justify-center pk-items-center" style={{ minHeight: '80vh' }}>
|
|
314
|
+
<div className="pk-card" style={{ width: '100%', maxWidth: '400px', overflow: 'hidden', boxShadow: 'var(--pk-shadow-xl)' }}>
|
|
315
|
+
<div style={{ background: 'var(--pk-primary)', color: 'white', padding: '2rem', textAlign: 'center', position: 'relative' }}>
|
|
316
|
+
<h2 className="pk-heading-md" style={{ opacity: 0.9 }}>{flightBookingMockData.airlineName}</h2>
|
|
317
|
+
<h1 className="pk-heading-xl" style={{ margin: '1rem 0' }}>BOARDING</h1>
|
|
318
|
+
<p style={{ letterSpacing: '2px', opacity: 0.8 }}>PASS</p>
|
|
319
|
+
|
|
320
|
+
{/* Visual cutout effect */}
|
|
321
|
+
<div style={{ position: 'absolute', bottom: '-10px', left: '-10px', width: '20px', height: '20px', background: 'var(--pk-bg-main)', borderRadius: '50%' }}></div>
|
|
322
|
+
<div style={{ position: 'absolute', bottom: '-10px', right: '-10px', width: '20px', height: '20px', background: 'var(--pk-bg-main)', borderRadius: '50%' }}></div>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<div className="pk-card-body" style={{ padding: '2rem', position: 'relative' }}>
|
|
326
|
+
<div className="pk-flex pk-justify-between pk-items-center" style={{ marginBottom: '2rem', borderBottom: '1px solid var(--pk-border)', paddingBottom: '1rem' }}>
|
|
327
|
+
<div className="pk-text-center">
|
|
328
|
+
<h2 className="pk-heading-xl" style={{ color: 'var(--pk-text-main)' }}>{bookedFlight.flight.from.split(' ')[0]}</h2>
|
|
329
|
+
<p className="pk-text-sm pk-text-muted">{bookedFlight.flight.departureTime}</p>
|
|
330
|
+
</div>
|
|
331
|
+
<span style={{ fontSize: '2rem', color: 'var(--pk-primary)' }}>✈️</span>
|
|
332
|
+
<div className="pk-text-center">
|
|
333
|
+
<h2 className="pk-heading-xl" style={{ color: 'var(--pk-text-main)' }}>{bookedFlight.flight.to.split(' ')[0]}</h2>
|
|
334
|
+
<p className="pk-text-sm pk-text-muted">{bookedFlight.flight.arrivalTime}</p>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
<div className="pk-grid" style={{ gridTemplateColumns: '1fr 1fr', gap: '1.5rem', marginBottom: '2rem' }}>
|
|
339
|
+
<div>
|
|
340
|
+
<p className="pk-label pk-text-muted">Passenger</p>
|
|
341
|
+
<p className="pk-heading-sm" style={{ fontWeight: 600 }}>{bookedFlight.passenger.firstName} {bookedFlight.passenger.lastName}</p>
|
|
342
|
+
</div>
|
|
343
|
+
<div>
|
|
344
|
+
<p className="pk-label pk-text-muted">Flight</p>
|
|
345
|
+
<p className="pk-heading-sm" style={{ fontWeight: 600 }}>{bookedFlight.flight.flightNumber}</p>
|
|
346
|
+
</div>
|
|
347
|
+
<div>
|
|
348
|
+
<p className="pk-label pk-text-muted">Date</p>
|
|
349
|
+
<p className="pk-heading-sm" style={{ fontWeight: 600 }}>{bookedFlight.date}</p>
|
|
350
|
+
</div>
|
|
351
|
+
<div>
|
|
352
|
+
<p className="pk-label pk-text-muted">Class</p>
|
|
353
|
+
<p className="pk-heading-sm" style={{ fontWeight: 600, textTransform: 'capitalize' }}>{bookedFlight.seatClass}</p>
|
|
354
|
+
</div>
|
|
355
|
+
<div>
|
|
356
|
+
<p className="pk-label pk-text-muted">Seat</p>
|
|
357
|
+
<p className="pk-heading-sm" style={{ fontWeight: 800, color: 'var(--pk-primary)', fontSize: '1.5rem' }}>12A</p>
|
|
358
|
+
</div>
|
|
359
|
+
<div>
|
|
360
|
+
<p className="pk-label pk-text-muted">Gate</p>
|
|
361
|
+
<p className="pk-heading-sm" style={{ fontWeight: 800, fontSize: '1.5rem' }}>G4</p>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<div style={{ textAlign: 'center', marginTop: '2rem' }}>
|
|
366
|
+
{/* Mock QR Code */}
|
|
367
|
+
<div style={{ display: 'inline-block', padding: '10px', border: '2px solid var(--pk-border)', borderRadius: '12px' }}>
|
|
368
|
+
<div style={{ width: '120px', height: '120px', background: 'repeating-linear-gradient(45deg, #000, #000 10px, transparent 10px, transparent 20px), repeating-linear-gradient(135deg, #000, #000 10px, transparent 10px, transparent 20px)' }}></div>
|
|
369
|
+
</div>
|
|
370
|
+
<p className="pk-text-sm" style={{ marginTop: '0.5rem', letterSpacing: '4px', fontFamily: 'monospace' }}>{bookedFlight.bookingRef}</p>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<div className="pk-card-footer pk-flex pk-justify-center" style={{ gap: '1rem', background: 'var(--pk-primary-light)' }}>
|
|
375
|
+
<button className="pk-btn pk-btn-outline" style={{ background: 'white' }} onClick={() => setCurrentView('search')}>New Booking</button>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
);
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
return (
|
|
383
|
+
<div style={{ position: 'relative', minHeight: '100vh', backgroundColor: 'var(--pk-bg-main)' }}>
|
|
384
|
+
{isLoading && (
|
|
385
|
+
<div style={{ position: 'fixed', inset: 0, backgroundColor: 'rgba(255,255,255,0.7)', zIndex: 100, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', backdropFilter: 'blur(4px)' }}>
|
|
386
|
+
<div className="pk-skeleton" style={{ width: '80px', height: '80px', borderRadius: '50%', marginBottom: '1rem' }}></div>
|
|
387
|
+
<h3 className="pk-heading-md">Searching Global Skies...</h3>
|
|
388
|
+
</div>
|
|
389
|
+
)}
|
|
390
|
+
|
|
391
|
+
{currentView === 'search' && renderSearch()}
|
|
392
|
+
{currentView === 'results' && renderResults()}
|
|
393
|
+
{currentView === 'passenger' && renderPassenger()}
|
|
394
|
+
{currentView === 'pass' && renderPass()}
|
|
395
|
+
|
|
396
|
+
<ToastContainer />
|
|
397
|
+
</div>
|
|
398
|
+
);
|
|
399
|
+
}
|