diff --git a/app/admin/routes.py b/app/admin/routes.py index 9a74904d651621fc43ca7f9cc1742413bb546f3d..d9676e837f8f40e4e98c24e12f48670449e94c6b 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -3,7 +3,7 @@ from app import db from app import admin_permission, permission_required, super_admin_permission from app.models import Listings, ListingImages from app.admin import bp -from app.main.utils import save_booking_image +from app.main.utils import generate_time_options @bp.route('/home') @@ -27,19 +27,20 @@ def manage_bookings(): def edit_booking(id): locations = Listings.get_all_locations() listing_information = Listings.search_listing(id) - time_options = [ - "00:00", "00:30", "01:00", "01:30", "02:00", "02:30", "03:00", "03:30", "04:00", "04:30", - "05:00", "05:30", "06:00", "06:30", "07:00", "07:30", "08:00", "08:30", "09:00", "09:30", - "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", - "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", - "20:00", "20:30", "21:00", "21:30", "22:00", "22:30", "23:00", "23:30" - ] - + + time_options = generate_time_options() # Use the instance of the listing_information object to format the times depart_time_str = listing_information.depart_time.strftime("%H:%M") destination_time_str = listing_information.destination_time.strftime("%H:%M") - return render_template('admin/edit_booking.html', locations=locations, listing=listing_information, time_options=time_options, depart_time_str=depart_time_str, destination_time_str=destination_time_str) + return render_template( + 'admin/edit_booking.html', + locations=locations, + listing=listing_information, + time_options=time_options, + depart_time_str=depart_time_str, + destination_time_str=destination_time_str + ) @bp.route('/manage_users') @@ -52,10 +53,6 @@ def manage_users(): def manage_user_bookings(): return render_template('admin/index.html') -ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} - -def allowed_file(filename): - return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @bp.route('update_booking/<int:id>', methods=['POST']) @permission_required(admin_permission) @@ -161,7 +158,7 @@ def get_bookings(): @bp.route('create_listing', methods=['GET']) @permission_required(admin_permission) def create_listing(): - locations = Listings.get_all_locations() + locations = Listings.get_all_locations(True) return render_template('admin/create_listing.html', locations=locations) @@ -215,7 +212,7 @@ def create_listing_post(): db.session.rollback() locations = Listings.get_all_locations() flash('An error occurred while creating the booking. Please try again', 'error') - return render_template('admin/create_booking.html', locations=locations) + return render_template('admin/create_listing.html', locations=locations) flash('Successfully created booking', 'success') return redirect(url_for('admin.manage_bookings')) diff --git a/app/bookings/routes.py b/app/bookings/routes.py index 851c684cc3f7cdad6a180fc1009ffafa1a1988c2..abb9ba245dc0959320b9252925f08a0b897eed9d 100644 --- a/app/bookings/routes.py +++ b/app/bookings/routes.py @@ -31,5 +31,4 @@ def listings(): @bp.route('/listing/<int:id>') def show_listing(id): - Listings.get return render_template('bookings/listings.html', id=1) \ No newline at end of file diff --git a/app/main/utils.py b/app/main/utils.py index a4f73ed6f910d125e56c06ed7f3177cfa832a505..8ae4ed2f6bf178728d4a263c7ca44bea6b5ac5fb 100644 --- a/app/main/utils.py +++ b/app/main/utils.py @@ -2,24 +2,15 @@ import os from flask import current_app -from werkzeug.utils import secure_filename +from datetime import time - -def allowed_file(filename): +def allowed_image_files(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS'] -def save_booking_image(file): - if file and allowed_file(file.filename): - filename = secure_filename(file.filename) - file_path = os.path.join(os.path.join(current_app.config['BOOKING_IMAGE_UPLOADS'], filename)) - - # Ensure the directory exists before saving images - os.makedirs(os.path.join(current_app.config['BOOKING_IMAGE_UPLOADS']), exist_ok=True) - - print(f"File Path: {file_path}") - - - file.save(file_path) - return filename - else: - return None +def generate_time_options(): + time_options = [] + for hour in range(24): + for minute in range(0, 60, 5): + formatted_time = time(hour, minute).strftime('%H:%M') + time_options.append(formatted_time) + return time_options \ No newline at end of file diff --git a/app/models/listing_images.py b/app/models/listing_images.py index bddaf71c9a22b9e17a68e625397a8fe06b43edf8..d6b43d2abf69922682420f34fa92ba4efce8a42e 100644 --- a/app/models/listing_images.py +++ b/app/models/listing_images.py @@ -2,7 +2,7 @@ from sqlalchemy import Integer, ForeignKey from sqlalchemy.orm import relationship from app import db import os -from werkzeug.utils import secure_filename +from app.main.utils import allowed_image_files from flask import current_app import uuid @@ -27,14 +27,9 @@ class ListingImages(db.Model): return ordered_listing_images - @staticmethod - def allowed_file(filename): - allowed_extensions = current_app.config['ALLOWED_EXTENSIONS'] - return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions - @staticmethod def save_image(file, listing_id): - if file and ListingImages.allowed_file(file.filename): + if file and allowed_image_files(file.filename): extension = file.filename.rsplit('.', 1)[1].lower() filename = f"{listing_id}_{uuid.uuid4().hex}.{extension}" # Unique filename upload_folder = current_app.config['BOOKING_IMAGE_UPLOADS'] diff --git a/app/models/listings.py b/app/models/listings.py index fae84c2bea9fc3dceb7bad37918ba72c685de983..4a5348dc30f53fcde733073c00f302c9b5cf95ec 100644 --- a/app/models/listings.py +++ b/app/models/listings.py @@ -20,10 +20,15 @@ class Listings(db.Model): return cls.query.all() @classmethod - def get_all_locations(cls): + def get_all_locations(cls, ordered=False): all_locations = text("SELECT depart_location AS location FROM listings UNION SELECT destination_location AS location FROM listings") result = db.session.execute(all_locations) - return [location[0] for location in result] + listed_results = [location[0] for location in result] + + if ordered: + return sorted(listed_results) + + return listed_results @classmethod def create_listing(cls, depart_location, depart_time, destination_location, destination_time, fair_cost, transport_type): diff --git a/app/static/generic.js b/app/static/generic.js index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a9d8acf66cdfc1e857c5bc823b3b7ec5e9ecb147 100644 --- a/app/static/generic.js +++ b/app/static/generic.js @@ -0,0 +1,22 @@ +// Custom time JS dropdown... +const generateTimeOptions = () => { + const timeOptions = []; + for (let hour = 0; hour < 24; hour++) { + for (let minute = 0; minute < 60; minute += 5) { + const formattedHour = hour.toString().padStart(2, '0'); + const formattedMinute = minute.toString().padStart(2, '0'); + timeOptions.push(`${formattedHour}:${formattedMinute}`); + } + } + return timeOptions; +}; + +const populateTimeDropdowns = (dropdownIds) => { + const timeOptions = generateTimeOptions(); + dropdownIds.forEach(id => { + const dropdown = document.getElementById(id); + timeOptions.forEach(time => { + dropdown.append(new Option(time, time)); + }); + }); +}; diff --git a/app/templates/admin/create_listing.html b/app/templates/admin/create_listing.html index 18a577033f514dd9c1f3c2573032622b55711d10..ccd45144c33a93fcdf13d36e31ff870f7a5c57e3 100644 --- a/app/templates/admin/create_listing.html +++ b/app/templates/admin/create_listing.html @@ -1,5 +1,8 @@ {% extends 'base.html' %} {% block content %} +<head> + <script src="{{ url_for('static', filename='generic.js') }}"></script> +</head> <div class="container mt-4"> <h2>Create New Booking</h2> <!-- UPDATE BELOW TO MAKE NEW BOOKING DASFHISOD)HAISOD --> @@ -28,18 +31,14 @@ <label for="departTime" class="form-label">Departure Time:</label> <select class="form-control select2-dropdown" id="departTime" name="departTime" required> <option value="" disabled>Select a time</option> - {% for time in time_options %} <option value="{{ time }}">{{ time }}</option> - {% endfor %} </select> </div> <div class="col-md-6"> <label for="destinationTime" class="form-label">Arrival Time:</label> <select class="form-control select2-dropdown" id="destinationTime" name="destinationTime" required> <option value="" disabled>Select a time</option> - {% for time in time_options %} <option value="{{ time }}">{{ time }}</option> - {% endfor %} </select> </div> <div class="col-md-6"> @@ -118,29 +117,9 @@ <script> $('.select2-dropdown').select2({ width: '100%', - minimumResultsForSearch: Infinity - }); - - const time_options = [ - "00:00", "00:30", "01:00", "01:30", "02:00", "02:30", "03:00", "03:30", "04:00", "04:30", - "05:00", "05:30", "06:00", "06:30", "07:00", "07:30", "08:00", "08:30", "09:00", "09:30", - "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", - "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", - "20:00", "20:30", "21:00", "21:30", "22:00", "22:30", "23:00", "23:30" - ]; - - // Add time_options to filter elements - time_options.forEach(time => { - $('#departTime').append(new Option(time, time)); - $('#destinationTime').append(new Option(time, time)); }); - // Function to update dropdowns with new locations - const updateDropdowns = (newLocations, dropdown) => { - newLocations.forEach(location => { - $(dropdown).append(new Option(location, location)); - }); - }; + populateTimeDropdowns(['departTime', 'destinationTime']); // JavaScript to handle adding new locations let locations = JSON.parse('{{ locations|tojson|safe }}'); @@ -158,6 +137,12 @@ $('#locationErrorModal').modal('show'); } }); + + const updateDropdowns = (newLocations, dropdown) => { + newLocations.forEach(location => { + $(dropdown).append(new Option(location, location)); + }); + }; </script> <style> .image-container { diff --git a/app/templates/admin/edit_booking.html b/app/templates/admin/edit_booking.html index 5434b938234ba6f7e12000b8faf75e33d84e202e..c47cec538cef87d99df230c2da02150586bd8d17 100644 --- a/app/templates/admin/edit_booking.html +++ b/app/templates/admin/edit_booking.html @@ -1,5 +1,8 @@ {% extends 'base.html' %} {% block content %} +<head> + <script src="{{ url_for('static', filename='generic.js') }}"></script> +</head> <div class="container mt-4"> <h2>Edit Booking</h2> <form id="editBookingForm" class="row g-3" action="{{ url_for('admin.update_booking', id=listing.id) }}" method="post" enctype="multipart/form-data"> @@ -136,22 +139,9 @@ <script> $('.select2-dropdown').select2({ width: '100%', - minimumResultsForSearch: Infinity }); - const time_options = [ - "00:00", "00:30", "01:00", "01:30", "02:00", "02:30", "03:00", "03:30", "04:00", "04:30", - "05:00", "05:30", "06:00", "06:30", "07:00", "07:30", "08:00", "08:30", "09:00", "09:30", - "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", - "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00", "19:30", - "20:00", "20:30", "21:00", "21:30", "22:00", "22:30", "23:00", "23:30" - ]; - - // Add time_options to filter elements - time_options.forEach(time => { - $('#departTime').append(new Option(time, time)); - $('#destinationTime').append(new Option(time, time)); - }); + populateTimeDropdowns(['departTime', 'destinationTime']) // Add New location to dropdowns (will clear upon reload OR if not exist in db) const updateDropdowns = (newLocations, dropdown) => { diff --git a/app/templates/base.html b/app/templates/base.html index 72c4c331c04a3798eb8b6dcc0f3f72804f0ea1a6..5ac5d6c815e6381ef142b004420d13b5c25dac67 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -21,6 +21,7 @@ <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/js/select2.min.js"></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/timepicker@1.13.18/jquery.timepicker.min.css"> <script src="https://cdn.jsdelivr.net/npm/timepicker@1.13.18/jquery.timepicker.min.js"></script> + <link rel="stylesheet" href="{{ url_for('static', filename='generic.js') }}"> <title>Horizon Travels</title> </head> <body> diff --git a/app/uploads/listing_images/17_2d852af9f5b64b1abdd5784e8172ce6e.gif b/app/uploads/listing_images/17_2d852af9f5b64b1abdd5784e8172ce6e.gif new file mode 100644 index 0000000000000000000000000000000000000000..71aede03087e88f8e47af526b1bcb71adb687192 Binary files /dev/null and b/app/uploads/listing_images/17_2d852af9f5b64b1abdd5784e8172ce6e.gif differ diff --git a/app/uploads/listing_images/22_200629e3a75949aabbe9b800fa7f91db.gif b/app/uploads/listing_images/22_200629e3a75949aabbe9b800fa7f91db.gif new file mode 100644 index 0000000000000000000000000000000000000000..71aede03087e88f8e47af526b1bcb71adb687192 Binary files /dev/null and b/app/uploads/listing_images/22_200629e3a75949aabbe9b800fa7f91db.gif differ