source: src/main/resources/static/FlightSearch.html@ 62bba0c

Last change on this file since 62bba0c was 62bba0c, checked in by ste08 <sjovanoska@…>, 3 months ago

Report working, Wishlist partly working.

  • Property mode set to 100644
File size: 15.8 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,sans-serif;
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 z-index: 1000;
173 overflow:hidden;
174 }
175
176 .popup textarea{
177 width: 100%;
178 padding: 10px;
179 margin-top: 10px;
180 border: 1px solid #ccc;
181 border-radius: 4px;
182 resize: vertical;
183 box-sizing: border-box;
184 }
185
186 .popup {
187 background-color: white;
188 padding: 20px;
189 width: 300px;
190 border-radius: 10px;
191 }
192
193 .popup button {
194 margin-top: 10px;
195 background-color: rebeccapurple;
196 color: white;
197 border: none;
198 padding: 5px;
199 cursor: pointer;
200 }
201
202 .popup button:hover {
203 background-color: mediumpurple;
204 }
205
206 .calendar {
207 background-color: transparent;
208 border-radius: 2px;
209 border-color: white;
210 width: 18.6em;
211 height: 2.5em;
212 color:white;
213 }
214 </style>
215</head>
216<body>
217
218<div id="app" class="flight-search">
219 <header class="header">
220 <button @click="home"><img src="/images/home.png" alt="Home Icon"></button>
221 <button @click="showReportPopup">Report Issue</button>
222 <button @click="goToWishlistPage">🤍</button>
223 <button @click="goToReports">Monthly Report</button>
224 <button @click="home">Log Out</button>
225 </header>
226
227 <div class="split left">
228 <div class="search-form-container">
229 <h2>Search flights</h2>
230
231 <div class="search-form">
232 <label for="departure-city">Departure From</label>
233 <br>
234 <select v-model="departureCity" id="departure-city">
235 <option value="" disabled selected>Select a departure city</option>
236 <option v-for="city in cities" :key="city" :value="city">{{ city }}</option>
237 </select>
238
239 <label for="destination">Destination</label>
240 <br>
241 <select v-model="destination" id="destination">
242 <option value="" disabled selected>Select a destination</option>
243 <option v-for="place in places" :key="place" :value="place">{{ place }}</option>
244 </select>
245
246 <label for="departure-date">Departure Date</label>
247 <br>
248 <input class="calendar" type="date" v-model="departureDate" id="departureDate" />
249 <br>
250 <div class="toggle-wrapper">
251 <label for="return-date-toggle">Include Return Date<input type="checkbox" v-model="showReturnDate" id="return-date-toggle" /></label>
252 </div>
253 <br>
254 <div v-if="showReturnDate">
255 <label for="return-date">Return Date</label>
256 <input class="calendar" type="date" v-model="returnDate" id="returnDate" />
257 </div>
258 <br>
259 <button class="search" @click="searchFlights">Search Flights</button>
260 </div>
261 </div>
262 </div>
263 <div class="split right">
264 <div class="flights-list-container" v-show="isContainerVisible" v-if="flights.length">
265 <h2>Available Flights</h2>
266 <div class="flights-list">
267 <div class="flight-item" v-for="flight in flights" :key="flight.flightID">
268 <input type="checkbox" v-model="flight.selected" />
269 <span>{{ flight.departureTime }} | {{ flight.arrivalTime }} | ${{ flight.price }} | {{ flight.availableSeats }}</span>
270
271 <span class="wishlist-heart" @click="toggleWishlist(flight)">
272 {{ flight.wishlisted ? '❤️' : '🤍' }}
273 </span>
274 </div>
275 </div>
276
277 <button v-if="selectedFlights.length" @click="bookFlights" class="book">
278 Book
279 </button>
280 </div>
281 </div>
282
283 <div v-if="showPopup" class="popup-overlay">
284 <div class="popup">
285 <h3>Report an Issue</h3>
286 <textarea v-model="issueDescription" placeholder="Describe the issue here..." rows="5"></textarea>
287 <div class="popup-actions">
288 <button @click="submitIssue" class="submit-btn">Submit</button>
289 <button @click="closePopup" class="cancel-btn">Cancel</button>
290 </div>
291 </div>
292 </div>
293</div>
294
295<script>
296 new Vue({
297 el: '#app',
298 data: {
299 isContainerVisible:false,
300 departureCity: '',
301 destination: '',
302 departureDate: '',
303 returnDate: '',
304 flights: [],
305 cities: [],
306 places: [],
307 bookings: [],
308 showReturnDate: false,
309 showPopup: false,
310 issueDescription: '',
311 userId:'',
312 wishlisted:false
313 },
314 computed: {
315 selectedFlights() {
316 return this.flights.filter(flight => flight.selected);
317 }
318 },
319
320 methods: {
321 async searchFlights() {
322 this.isContainerVisible = !this.isContainerVisible;
323 if (this.departureCity && this.destination && this.departureDate) {
324 try {
325 const response = await axios.get('/api/flights/flight-search', {
326 params: {
327 departureCity: this.departureCity,
328 destination: this.destination,
329 departureDate: this.departureDate,
330 returnDate: this.showReturnDate ? this.returnDate : null
331 }
332 });
333
334 if (response.data && response.data.length > 0) {
335 this.flights = response.data;
336 } else {
337 this.flights = [];
338 alert("No flights found for the given criteria.");
339 }
340 } catch (error) {
341 console.error("Error fetching flights", error);
342 }
343 } else {
344 alert("Please select departure city, destination, and date.");
345 this.flights = [];
346 }
347 },
348 bookFlights() {
349 if (!this.selectedFlights.length) {
350 alert("Please select at least one flight.");
351 return;
352 }
353
354 const flight = this.selectedFlights[0];
355 console.log(flight);
356 const totalCost = flight.price;
357
358 const bookingData = {
359 flightId: flight.flightID,
360 bookingDate: new Date().toISOString().split('T')[0],
361 status: 'PENDING',
362 totalCost: totalCost,
363 userId:this.userId
364 };
365 axios.post('/api/bookings', bookingData)
366 .then(response => {
367 const bookingID = response.data.bookingId;
368 alert("Booked successfully!");
369 window.location.href = `/transaction?amount=${encodeURIComponent(totalCost)}&bookingId=${encodeURIComponent(bookingID)}&flightId=${encodeURIComponent(flight.flightID)}&userId=${encodeURIComponent(this.userId)}`;
370 })
371 .catch(error => {
372 console.error("Error booking flight", error);
373 alert("There was an error creating your booking. Please try again.");
374 });
375 },
376 home() {
377 window.location.href = '/';
378 },
379 goToReports(){
380 window.location.href = '/views'
381 },
382 async toggleWishlist(flight) {
383 flight.wishlisted = !flight.wishlisted;
384 this.$set(this.flights, this.flights.indexOf(flight), flight);
385 const wishlistData = {
386 userId: parseInt(this.userId),
387 targetId: flight.flightID
388 };
389 console.log(wishlistData);
390 if (!flight.wishlisted) {
391 try {
392 await axios.delete('/api/wishlists', { params: wishlistData });
393 console.log("Removed from wishlist");
394 } catch (error) {
395 console.error("Error removing from wishlist:", error);
396 }
397 } else {
398 try {
399 await axios.post('/api/wishlists/add', wishlistData);
400 console.log("Added to wishlist");
401 } catch (error) {
402 console.error("Error adding to wishlist:", error);
403 }
404 }
405 },
406 async goToWishlistPage() {
407 window.location.href = `/api/wishlists?userId=${encodeURIComponent(this.userId)}`;
408
409 },
410 showReportPopup() {
411 this.showPopup = true;
412 },
413 closePopup() {
414 this.showPopup = false;
415 },
416 submitIssue() {
417 if (this.issueDescription.trim()) {
418 const reviewData = {
419 userId: this.userId,
420 subject: "Issue Report",
421 description: this.issueDescription
422 };
423
424 axios.post('/api/support-tickets', reviewData)
425 .then(response => {
426 alert("Issue reported successfully!");
427 this.closePopup();
428 })
429 .catch(error => {
430 console.error("Error submitting issue:", error);
431 alert("There was an error submitting your issue. Please try again.");
432 });
433 } else {
434 alert("Please enter a description for the issue.");
435 }
436 },
437 async fetchFlights() {
438 try {
439 const response = await axios.get('/api/destinations');
440 this.cities = response.data.map(departureCity => departureCity.name);
441 this.places = response.data.map(destination => destination.name);
442 } catch (error) {
443 console.error("Error fetching Destinations", error);
444 }
445 }
446 },
447 mounted() {
448 this.fetchFlights();
449 const params = new URLSearchParams(window.location.search);
450 this.userId = params.get("userId");
451 axios.get('api/flights')
452 .then(response => {
453 this.flights = response.data;
454 })
455 .catch(error => {
456 console.error("Error fetching flights", error);
457 });
458
459 }
460 });
461</script>
462
463</body>
464</html>
Note: See TracBrowser for help on using the repository browser.