diff --git a/app/admin/routes.py b/app/admin/routes.py index 42e37e3964b313a30644a6208480503b752adaf1..8e4775e96b6c41e9f454f646e9e2e502d4fe3211 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -70,7 +70,8 @@ def update_booking(id): destination_location = request.form.get('destinationLocation') depart_time = request.form.get('departTime') destination_time = request.form.get('destinationTime') - fair_cost = request.form.get('fairCost') + economy_fair_cost = request.form.get('economyFairCost') + business_fair_cost = request.form.get('businessFairCost') transport_type = request.form.get('transportType') images = request.files.getlist('images') main_image_id = request.form.get('main_image') @@ -85,8 +86,10 @@ def update_booking(id): listing.depart_time = depart_time if destination_time: listing.destination_time = destination_time - if fair_cost: - listing.fair_cost = fair_cost + if economy_fair_cost: + listing.economy_fair_cost = economy_fair_cost + if business_fair_cost: + listing.business_fair_cost = business_fair_cost if transport_type: listing.transport_type = transport_type @@ -157,7 +160,8 @@ def get_bookings(): 'depart_time': listing.depart_time.strftime("%H:%M"), 'destination_location': listing.destination_location, 'destination_time': listing.destination_time.strftime("%H:%M"), - 'fair_cost': listing.fair_cost, + 'economy_fair_cost': listing.economy_fair_cost, + 'business_fair_cost': listing.business_fair_cost, 'transport_type': listing.transport_type } for listing in filtered_data ] diff --git a/app/bookings/routes.py b/app/bookings/routes.py index e82754cb9036a6afe32c6b73ed5d9282ee9ac409..a261b95910daea9a68b9e110ef33a34f8643d758 100644 --- a/app/bookings/routes.py +++ b/app/bookings/routes.py @@ -13,12 +13,12 @@ from app import user_permission, permission_required def redirect_index(): return redirect(url_for('bookings.index'), code=301) - @bp.route('/listings') def listings(): depart_location = request.args.get('departLocation') destination_location = request.args.get('destinationLocation') depart_date = request.args.get('departDate') + seat_type = request.args.get('seatType', 'economy') page = request.args.get('page', 1, type=int) per_page = 10 @@ -33,6 +33,15 @@ def listings(): if destination_location: all_listings = [listing for listing in all_listings if listing.destination_location == destination_location] + # Calculate total cost and apply discount, also change time to nicer format + for listing in all_listings: + if seat_type == 'economy': + listing.discounted_cost = listing.economy_fair_cost * (1 - discount / 100) + listing.original_cost = listing.economy_fair_cost + elif seat_type == 'business': + listing.discounted_cost = listing.business_fair_cost * (1 - discount / 100) + listing.original_cost = listing.business_fair_cost + # Calculate pagination items and how many listings exist total_items = len(all_listings) paginated_listings = all_listings[(page - 1) * per_page: page * per_page] @@ -41,20 +50,24 @@ def listings(): # Get all locations for dropdowns locations = Listings.get_all_locations(True) - - return render_template('bookings/listings.html', - items=paginated_listings, - page=page, - total_pages=(total_items + per_page - 1) // per_page, - locations=locations, - discount=discount, - days_away=days_away, - form_data= { - 'departLocation': depart_location, - 'destinationLocation': destination_location - } + + return render_template( + 'bookings/listings.html', + items=paginated_listings, + page=page, + total_pages=(total_items + per_page - 1) // per_page, + locations=locations, + discount=discount, + days_away=days_away, + depart_date=depart_date, + form_data={ + 'departLocation': depart_location, + 'destinationLocation': destination_location, + 'seatType': seat_type + } ) + @bp.route('/listing/apply_update', methods=['POST']) def listing_apply_update(): data = request.get_json() @@ -162,53 +175,53 @@ def validate_payment(card_number, card_expiry, card_cvc): @bp.route('/filter_bookings', methods=['POST']) def filter_bookings(): - try: - # Get filter criteria from the request - data = request.get_json() - depart_location = data.get('depart_location', []) - destination_location = data.get('destination_location', []) - min_fair_cost = data.get('min_fair_cost') - max_fair_cost = data.get('max_fair_cost') - depart_date = data.get('date') - page = int(data.get('page', 1)) # Get the page parameter or default to 1 - per_page = 10 # How many listings show per page - - discount, days_away = calculate_discount(depart_date) - - query = db.session.query(Listings) - - if depart_location: - query = query.filter(Listings.depart_location.in_(depart_location)) - if destination_location: - query = query.filter(Listings.destination_location.in_(destination_location)) - if min_fair_cost: - query = query.filter(Listings.fair_cost >= float(min_fair_cost)) - if max_fair_cost: - query = query.filter(Listings.fair_cost <= float(max_fair_cost)) - - filtered_items = query.all() - total_items = len(filtered_items) - - # Ignore pagination if any filters are applied - if depart_location or destination_location or min_fair_cost or max_fair_cost: - paginated_items = filtered_items - page = 1 - total_pages = 1 - else: - # Paginate the results - paginated_items = filtered_items[(page - 1) * per_page: page * per_page] - total_pages = (total_items + per_page - 1) // per_page + # Get filter criteria from the request + data = request.get_json() + depart_location = data.get('depart_location', []) + destination_location = data.get('destination_location', []) + depart_date = data.get('date') + seat_type = data.get('seatType', 'economy') # Default to 'economy' if not provided + page = int(data.get('page', 1)) # Get the page parameter or default to 1 + per_page = 10 # How many listings show per page - # Process images - process_images(paginated_items) + discount, days_away = calculate_discount(depart_date) - # Render only the relevant portion of the results - results_html = render_template('_results.html', items=paginated_items, page=page, total_pages=total_pages, discount=discount, days_away=days_away) - return jsonify({'html': results_html}) + query = db.session.query(Listings) + + if depart_location: + query = query.filter(Listings.depart_location.in_(depart_location)) + if destination_location: + query = query.filter(Listings.destination_location.in_(destination_location)) + + filtered_items = query.all() + total_items = len(filtered_items) + + # Ignore pagination if any filters are applied + if depart_location or destination_location: + paginated_items = filtered_items + page = 1 + total_pages = 1 + else: + # Paginate the results + paginated_items = filtered_items[(page - 1) * per_page: page * per_page] + total_pages = (total_items + per_page - 1) // per_page + + # Calculate total cost and apply discount + for item in paginated_items: + if seat_type == 'economy': + item.discounted_cost = item.economy_fair_cost * (1 - discount / 100) + item.original_cost = item.economy_fair_cost + elif seat_type == 'business': + item.discounted_cost = item.business_fair_cost * (1 - discount / 100) + item.original_cost = item.business_fair_cost + + # Process images + process_images(paginated_items) + + # Render only the relevant portion of the results + results_html = render_template('_results.html', items=paginated_items, page=page, total_pages=total_pages, discount=discount, days_away=days_away, seat_type=seat_type) + return jsonify({'html': results_html}) - except Exception as e: - error_logger.debug(e) - return jsonify({'error': str(e)}), 400 def process_images(listings): diff --git a/app/templates/_results.html b/app/templates/_results.html index 1a00699b6d8db93adce8ac74b3291473fa28381b..980e47f188220c99edfcffd12af024ad00f7b825 100644 --- a/app/templates/_results.html +++ b/app/templates/_results.html @@ -11,7 +11,7 @@ <th>Depart Location</th> <th>Price (£)</th> <th>Destination Location</th> - <th>Arrival Time</th> + <th>Arrival Time (24 Hour)</th> </tr> </thead> <tbody> @@ -21,16 +21,16 @@ <td>{{ item.depart_location }}</td> <td> {% if discount > 0 %} - <span style="text-decoration: line-through;">£ {{ item.fair_cost }}</span> - <span>£ {{ item.fair_cost * (1 - discount / 100) }}</span> + <span style="text-decoration: line-through;">£ {{ item.original_cost }}</span> + <span>£ {{ item.discounted_cost }}</span> {% else %} - £ {{ item.fair_cost }} + £ {{ item.original_cost }} {% endif %} </td> <td>{{ item.destination_location }}</td> - <td>{{ item.destination_time }}</td> + <td>{{ item.destination_time.strftime('%H:%M') }}</td> </tr> {% endfor %} </tbody> </table> -</div> \ No newline at end of file +</div> diff --git a/app/templates/admin/edit_booking.html b/app/templates/admin/edit_booking.html index c47cec538cef87d99df230c2da02150586bd8d17..6ef1188542b5ee11572e2800cd70d06f1d4f144c 100644 --- a/app/templates/admin/edit_booking.html +++ b/app/templates/admin/edit_booking.html @@ -45,8 +45,12 @@ </select> </div> <div class="col-md-6"> - <label for="fairCost" class="form-label">Fair Cost:</label> - <input type="number" step="0.01" class="form-control" id="fairCost" name="fairCost" value="{{ listing.fair_cost }}" required> + <label for="economyFairCost" class="form-label">Economy Fair Cost:</label> + <input type="number" step="0.01" class="form-control" id="economyFairCost" name="economyFairCost" value="{{ listing.economy_fair_cost }}" required> + </div> + <div class="col-md-6"> + <label for="businessFairCost" class="form-label">Business Fair Cost:</label> + <input type="number" step="0.01" class="form-control" id="businessFairCost" name="businessFairCost" value="{{ listing.business_fair_cost }}" required> </div> <div class="col-md-6"> <label for="transportType" class="form-label">Transport Type:</label> diff --git a/app/templates/admin/manage_bookings.html b/app/templates/admin/manage_bookings.html index 25731603706cb737d62f9c79fc6c207cc31169fb..e030c6905fb944bc65d33348a119a82139fe5ca0 100644 --- a/app/templates/admin/manage_bookings.html +++ b/app/templates/admin/manage_bookings.html @@ -76,7 +76,8 @@ <th>Depart Time</th> <th>Destination Location</th> <th>Destination Time</th> - <th>Fair Cost ($)</th> + <th>Economy Fair Cost ($)</th> + <th>Business Fair Cost ($)</th> <th>Transport Type</th> <th>Actions</th> </tr> @@ -186,7 +187,8 @@ { data: 'depart_time' }, { data: 'destination_location' }, { data: 'destination_time' }, - { data: 'fair_cost' }, + { data: 'economy_fair_cost' }, + { data: 'business_fair_cost' }, { data: 'transport_type' }, { data: null, diff --git a/app/templates/bookings/listings.html b/app/templates/bookings/listings.html index 18d07c0a146cf0dfe545960530eeffb6776f8559..0fd25326dca557d1f615842cf6e1585c2d785a9f 100644 --- a/app/templates/bookings/listings.html +++ b/app/templates/bookings/listings.html @@ -5,12 +5,21 @@ <div class="col-md-6 text-start mb-3 d-flex align-items-center flex-md-nowrap flex-wrap" id="dateContainer"> <label class="form-label me-2">Departure Date:</label> <div class="col-md-6"> - <input type="date" class="form-control" id="departDate" name="departDate" required> + <input type="date" class="form-control" id="departDate" name="departDate" value="{{ depart_date }}" required> </div> <button type="button" class="btn btn-warning ms-2" id="resetDate"> <i class="fa-solid fa-rotate-right"></i> Reset Date </button> </div> + <div class="col-md-6 text-start mb-3 d-flex align-items-center flex-md-nowrap flex-wrap" id="seatTypeContainer"> + <label class="form-label me-2">Seat Type:</label> + <div class="col-md-6"> + <select class="form-select" id="seatType" name="seatType"> + <option value="economy" {% if form_data['seatType'] == 'economy' %}selected{% endif %}>Economy</option> + <option value="business" {% if form_data['seatType'] == 'business' %}selected{% endif %}>Business</option> + </select> + </div> + </div> <div class="text-end mb-3"> <button type="button" class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#filterModal"> <i class="fa-solid fa-filter"></i> Filter @@ -179,7 +188,9 @@ // Set default date to today and prevent selecting past dates const today = new Date().toISOString().split('T')[0]; - departDateInput.value = today; + if (!departDateInput.value) { + departDateInput.value = today; + } departDateInput.setAttribute('min', today); // Set max date to 90 days from today @@ -192,8 +203,10 @@ departDateInput.value = today; }); - // Event listener for date picker change + // Event listener for date picker change & Seat type $('#departDate').change(applyFilters); + $('#seatType').change(applyFilters); + // Event listener for applying filters @@ -238,10 +251,13 @@ filterData.depart_location = $('#depart_location').val(); filterData.destination_location = $('#destination_location').val(); - //Get depart date from departure date field and inject into filtered data + // Get depart date from departure date field and inject into filtered data const selectedDate = $('#departDate').val(); filterData.date = selectedDate; + // Add seat type to filter data + filterData.seatType = $('#seatType').val(); + return filterData; } @@ -308,6 +324,5 @@ } </script> -</body> </html> {% endblock %} diff --git a/app/templates/index.html b/app/templates/index.html index 144f57ffc18799fa81fb300eb3f7aaaf9da5f2a5..6990c262168cde80d46fde1b38a49330450c8043 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -40,48 +40,18 @@ <option value="5">5</option> </select> </div> + <div class="col-md-6"> + <label for="seatType" class="form-label">Seat Type:</label> + <select class="form-select" id="seatType" name="seatType" required> + <option value="" disabled selected>Select seat type</option> + <option value="economy">Economy</option> + <option value="business">Business</option> + </select> + </div> <div class="col-md-6 d-flex align-items-end justify-content-left"> <button type="submit" class="btn btn-primary">Search</button> </div> </form> - <script> - $(document).ready(function () { - $('.select2-multiple').select2({ - placeholder: "Select locations", - width: '100%' - }); - - const locations = JSON.parse('{{ locations|tojson|safe }}'); - locations.forEach(location => { - $('#departLocation').append(new Option(location, location)); - $('#destinationLocation').append(new Option(location, location)); - }); - }); - - const departDateInput = document.getElementById('departDate'); - const resetDateButton = document.getElementById('resetDate'); - - // Open date picker when the date field is clicked - departDateInput.addEventListener('focus', (event) => { - event.preventDefault(); - departDateInput.showPicker(); - }); - - // Prevent any dates being changed by the user manually typing - departDateInput.addEventListener('keydown', (event) => { - event.preventDefault(); - }); - - // Set default date to today and prevent selecting past dates - const today = new Date().toISOString().split('T')[0]; - departDateInput.value = today; - departDateInput.setAttribute('min', today); - - // Set max date to 90 days from today - let maxDate = new Date(); - maxDate.setDate(maxDate.getDate() + 90); - departDateInput.setAttribute('max', maxDate.toISOString().split('T')[0]); - </script> </div> <div> <div class="deals_text"><span class="deals_underline">Currently Hot Locations</span></div> @@ -118,4 +88,42 @@ </div> </div> </div> +<script> + $(document).ready(function () { + $('.select2-multiple').select2({ + placeholder: "Select locations", + width: '100%' + }); + + const locations = JSON.parse('{{ locations|tojson|safe }}'); + locations.forEach(location => { + $('#departLocation').append(new Option(location, location)); + $('#destinationLocation').append(new Option(location, location)); + }); + }); + + const departDateInput = document.getElementById('departDate'); + const resetDateButton = document.getElementById('resetDate'); + + // Open date picker when the date field is clicked + departDateInput.addEventListener('focus', (event) => { + event.preventDefault(); + departDateInput.showPicker(); + }); + + // Prevent any dates being changed by the user manually typing + departDateInput.addEventListener('keydown', (event) => { + event.preventDefault(); + }); + + // Set default date to today and prevent selecting past dates + const today = new Date().toISOString().split('T')[0]; + departDateInput.value = today; + departDateInput.setAttribute('min', today); + + // Set max date to 90 days from today + let maxDate = new Date(); + maxDate.setDate(maxDate.getDate() + 90); + departDateInput.setAttribute('max', maxDate.toISOString().split('T')[0]); +</script> {% endblock %}