source: src/main/resources/static/FlightSearch.html@ de83113

Last change on this file since de83113 was de83113, checked in by ste08 <sjovanoska@…>, 4 months ago

Signup,Login,FlightSearch,Booking and Payment working!

  • Property mode set to 100644
File size: 14.6 KB
Line 
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Flight Search</title>
7 <link rel="stylesheet" href="/css/main.css">
8 <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
9 <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
10 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
11 <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css'>
12 <style>
13 body {
14 background: url('images/flight.jpg') no-repeat center center fixed;
15 background-size: cover;
16 display: flex;
17 flex-direction: column;
18 align-items: center;
19 justify-content: center;
20 height: 100vh;
21 margin: 0;
22 font-family: Arial, sans-serif;
23 }
24
25 .header {
26 position: absolute;
27 top: 0;
28 left: 0;
29 display: flex;
30 align-items: center;
31 background-color: rebeccapurple;
32 padding: 10px;
33 width: 100%;
34 z-index: 10;
35 }
36 .header img {
37 width: 40px;
38 height: 40px;
39 margin-right: 20px;
40 }
41
42 .header h1 {
43 color: white;
44 margin: 0;
45 font-size: 15px;
46 padding-right: 50px;
47 }
48
49 .header button {
50 background-color: transparent;
51 border: none;
52 color: white;
53 font-size: 14px;
54 }
55
56 .header button:hover {
57 background-color: transparent;
58 cursor: pointer;
59 }
60
61 .split {
62 height: 100%;
63 width: 50%;
64 position: fixed;
65 z-index: 1;
66 top: 0;
67 overflow-x: hidden;
68 padding-top: 20px;
69 }
70
71 .left {
72 left: 0;
73 padding:50px;
74 }
75
76 .right {
77 right: 0;
78 padding:10px;
79 }
80 select {
81 -webkit-appearance: none;
82 -moz-appearance: none;
83 appearance: none;
84 border: 0;
85 outline: 0;
86 font: inherit;
87 width: 20em;
88 height: 3em;
89 padding: 0 4em 0 1em;
90 background: url(https://upload.wikimedia.org/wikipedia/commons/9/9d/Caret_down_font_awesome_whitevariation.svg) no-repeat right 0.8em center/1.4em, linear-gradient(to left, rgba(255, 255, 255, 0.3) 3em, rgba(255, 255, 255, 0.2) 3em);
91 color: white;
92 border-radius: 0.25em;
93 box-shadow: 0 0 1em 0 rgba(0, 0, 0, 0.2);
94 cursor: pointer;
95 }
96 select option {
97 color: inherit;
98 background-color: #320a28;
99 }
100 select:focus {
101 outline: none;
102 }
103 select::-ms-expand {
104 display: none;
105 }
106
107 .search-form-container h2 {
108 color: white;
109 padding-top: 110px;
110 }
111
112 .search-form {
113 padding: 10px;
114 color:white;
115 font-family: Cambria;
116 }
117 .search {
118 background-color: rebeccapurple;
119 color: white;
120 width: 20em;
121 height: 3em;
122 }
123
124 .book {
125 background-color: darkblue;
126 color: white;
127 width: 20em;
128 height: 3em;
129 }
130
131 .search:hover {
132 background-color: mediumpurple;
133 }
134
135 .flights-list-container {
136 color: white;
137 }
138
139 .flights-list-container h2{
140 padding-top: 110px;
141 }
142 .flights-list .flight-item {
143 margin-bottom: 15px;
144 }
145
146 .flights-list .flight-item span {
147 margin-right: 15px;
148 }
149 .flight-item {
150 display: flex;
151 align-items: center;
152 justify-content: space-between;
153 padding: 15px;
154 background: url(https://upload.wikimedia.org/wikipedia/commons/9/9d/Caret_down_font_awesome_whitevariation.svg) no-repeat right 0.8em center/1.4em, linear-gradient(to left, rgba(255, 255, 255, 0.3) 3em, rgba(255, 255, 255, 0.2) 3em);
155 border-radius: 8px;
156 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
157 transition: transform 0.3s ease, background-color 0.3s ease;
158 width: 70%;
159 height: 15px;
160 }
161
162 .popup-overlay {
163 position: fixed;
164 top: 0;
165 left: 0;
166 width: 100%;
167 height: 100%;
168 background: rgba(0, 0, 0, 0.7);
169 display: flex;
170 align-items: center;
171 justify-content: center;
172 }
173
174 .popup {
175 background-color: white;
176 padding: 20px;
177 width: 300px;
178 border-radius: 10px;
179 }
180
181 .popup button {
182 margin-top: 10px;
183 background-color: rebeccapurple;
184 color: white;
185 border: none;
186 padding: 5px;
187 cursor: pointer;
188 }
189
190 .popup button:hover {
191 background-color: mediumpurple;
192 }
193
194 .calendar {
195 background-color: transparent;
196 border-radius: 2px;
197 border-color: white;
198 width: 18.6em;
199 height: 2.5em;
200 color:white;
201 }
202 </style>
203</head>
204<body>
205
206<div id="app" class="flight-search">
207 <header class="header">
208 <button @click="home"><img src="/images/home.png" alt="Home Icon"></button>
209 <button @click="showReportPopup">Report Issue</button>
210 <button @click="goToWishlistPage">🤍</button>
211 <button @click="home">Log Out</button>
212 </header>
213
214 <div class="split left">
215 <div class="search-form-container">
216 <h2>Search flights</h2>
217
218 <div class="search-form">
219 <label for="departure-city">Departure From</label>
220 <br>
221 <select v-model="departureCity" id="departure-city">
222 <option value="" disabled selected>Select a departure city</option>
223 <option v-for="city in cities" :key="city" :value="city">{{ city }}</option>
224 </select>
225
226 <label for="destination">Destination</label>
227 <br>
228 <select v-model="destination" id="destination">
229 <option value="" disabled selected>Select a destination</option>
230 <option v-for="place in places" :key="place" :value="place">{{ place }}</option>
231 </select>
232
233 <label for="departure-date">Departure Date</label>
234 <br>
235 <input class="calendar" type="date" v-model="departureDate" id="departureDate" />
236 <br>
237 <div class="toggle-wrapper">
238 <label for="return-date-toggle">Include Return Date<input type="checkbox" v-model="showReturnDate" id="return-date-toggle" /></label>
239 </div>
240 <br>
241 <div v-if="showReturnDate">
242 <label for="return-date">Return Date</label>
243 <input class="calendar" type="date" v-model="returnDate" id="returnDate" />
244 </div>
245 <br>
246 <button class="search" @click="searchFlights">Search Flights</button>
247 </div>
248 </div>
249 </div>
250 <div class="split right">
251 <div class="flights-list-container" v-show="isContainerVisible" v-if="flights.length">
252 <h2>Available Flights</h2>
253 <div class="flights-list">
254 <div class="flight-item" v-for="flight in flights" :key="flight.flightID">
255 <input type="checkbox" v-model="flight.selected" />
256 <span>{{ flight.departureTime }} | {{ flight.arrivalTime }} | ${{ flight.price }} | {{ flight.availableSeats }}</span>
257
258 <span class="wishlist-heart" @click="toggleWishlist(flight)">
259 {{ flight.wishlisted ? '❤️' : '🤍' }}
260 </span>
261 </div>
262 </div>
263
264 <button v-if="selectedFlights.length" @click="bookFlights" class="book">
265 Book
266 </button>
267 </div>
268 </div>
269
270 <div v-if="showPopup" class="popup-overlay">
271 <div class="popup">
272 <h3>Report an Issue</h3>
273 <textarea v-model="issueDescription" placeholder="Describe the issue here..." rows="5"></textarea>
274 <div class="popup-actions">
275 <button @click="submitIssue" class="submit-btn">Submit</button>
276 <button @click="closePopup" class="cancel-btn">Cancel</button>
277 </div>
278 </div>
279 </div>
280</div>
281
282<script>
283 new Vue({
284 el: '#app',
285 data: {
286 isContainerVisible:false,
287 departureCity: '',
288 destination: '',
289 departureDate: '',
290 returnDate: '',
291 flights: [],
292 cities: [],
293 places: [],
294 bookings: [],
295 showReturnDate: false,
296 showPopup: false,
297 issueDescription: '',
298 userId:''
299 },
300 computed: {
301 selectedFlights() {
302 return this.flights.filter(flight => flight.selected);
303 }
304 },
305
306 methods: {
307 async searchFlights() {
308 this.isContainerVisible = !this.isContainerVisible;
309 if (this.departureCity && this.destination && this.departureDate) {
310 try {
311 const response = await axios.get('/api/flights/flight-search', {
312 params: {
313 departureCity: this.departureCity,
314 destination: this.destination,
315 departureDate: this.departureDate,
316 returnDate: this.showReturnDate ? this.returnDate : null
317 }
318 });
319
320 if (response.data && response.data.length > 0) {
321 this.flights = response.data;
322 } else {
323 this.flights = [];
324 alert("No flights found for the given criteria.");
325 }
326 } catch (error) {
327 console.error("Error fetching flights", error);
328 }
329 } else {
330 alert("Please select departure city, destination, and date.");
331 this.flights = [];
332 }
333 },
334 bookFlights() {
335 if (!this.selectedFlights.length) {
336 alert("Please select at least one flight.");
337 return;
338 }
339
340 const flight = this.selectedFlights[0];
341 console.log(flight);
342 const totalCost = flight.price;
343
344 const bookingData = {
345 flightId: flight.flightID,
346 bookingDate: new Date().toISOString().split('T')[0],
347 status: 'PENDING',
348 totalCost: totalCost,
349 userId:this.userId
350 };
351 axios.post('/api/bookings', bookingData)
352 .then(response => {
353 const bookingID = response.data.bookingId;
354 alert("Booked successfully!");
355 window.location.href = `/transaction?amount=${encodeURIComponent(totalCost)}&bookingId=${encodeURIComponent(bookingID)}&flightId=${encodeURIComponent(flight.flightID)}&userId=${encodeURIComponent(this.userId)}`;
356 })
357 .catch(error => {
358 console.error("Error booking flight", error);
359 alert("There was an error creating your booking. Please try again.");
360 });
361 },
362 home() {
363 window.location.href = '/';
364 },
365 async toggleWishlist(flight) {
366 flight.wishlisted = !flight.wishlisted;
367 const wishlistData = {
368 targetId: flight.flightId,
369 wishlisted: flight.wishlisted
370 };
371 try {
372 await axios.post('/api/wishlists', wishlistData);
373 } catch (error) {
374 console.error("Error updating wishlist:", error);
375 }
376 },
377 goToWishlistPage() {
378 window.location.href = '/api/wishlists';
379 },
380 showReportPopup() {
381 this.showPopup = true;
382 },
383 closePopup() {
384 this.showPopup = false;
385 },
386 submitIssue() {
387 if (this.issueDescription.trim()) {
388 const reviewData = {
389 userID: this.userId,
390 subject: "Issue Report",
391 description: this.issueDescription
392 };
393
394 axios.post('/api/support-tickets', reviewData)
395 .then(response => {
396 alert("Issue reported successfully!");
397 this.closePopup();
398 })
399 .catch(error => {
400 console.error("Error submitting issue:", error);
401 alert("There was an error submitting your issue. Please try again.");
402 });
403 } else {
404 alert("Please enter a description for the issue.");
405 }
406 },
407 async fetchFlights() {
408 try {
409 const response = await axios.get('/api/destinations');
410 this.cities = response.data.map(departureCity => departureCity.name);
411 this.places = response.data.map(destination => destination.name);
412 } catch (error) {
413 console.error("Error fetching Destinations", error);
414 }
415 }
416 },
417 mounted() {
418 this.fetchFlights();
419 const params = new URLSearchParams(window.location.search);
420 this.userId = params.get("userId");
421 axios.get('api/flights')
422 .then(response => {
423 this.flights = response.data;
424 })
425 .catch(error => {
426 console.error("Error fetching flights", error);
427 });
428
429 }
430 });
431</script>
432
433</body>
434</html>
Note: See TracBrowser for help on using the repository browser.