From bee47e54bbbaafb929371e9aa3273191f9604444 Mon Sep 17 00:00:00 2001
From: Ethan Clay <Ethan2.Clay@live.uwe.ac.uk>
Date: Fri, 21 Feb 2025 11:43:05 +0000
Subject: [PATCH] Finish manage reports, update manage bookings to include
 cancel within view booking, begin to add cancellation % and refund amount
 from spec

---
 app/bookings/routes.py                     |  4 +-
 app/main/utils.py                          | 15 ++++
 app/profile/routes.py                      | 10 ++-
 app/templates/admin/manage_bookings.html   |  1 -
 app/templates/admin/reports.html           | 79 ++++++++++++++++------
 app/templates/profile/manage_bookings.html | 77 ++-------------------
 app/templates/profile/view_booking.html    | 20 +++++-
 7 files changed, 110 insertions(+), 96 deletions(-)

diff --git a/app/bookings/routes.py b/app/bookings/routes.py
index b859775..f439d53 100644
--- a/app/bookings/routes.py
+++ b/app/bookings/routes.py
@@ -193,7 +193,7 @@ def listing(id):
     else:
         base_price = listing.economy_fair_cost
 
-    main_image_url = None
+    main_image_url = 'booking_image_not_found.jpg'
     for image in listing.listing_images:
         if image.main_image == 1:
             main_image_url = image.image_location
@@ -310,7 +310,7 @@ def filter_bookings():
     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
+    seat_type = data.get('seatType', 'economy')
     page = int(data.get('page', 1)) 
     per_page = 10  # How many listings show per page
 
diff --git a/app/main/utils.py b/app/main/utils.py
index 756b2ca..fb37247 100644
--- a/app/main/utils.py
+++ b/app/main/utils.py
@@ -38,6 +38,21 @@ def calculate_discount(date):
     else:
         return 0, days_away
     
+    
+def calculate_refund_amount(amount_paid):
+    depart_date = datetime.strptime(date, '%Y-%m-%d')
+    today = datetime.now()
+    days_away = (depart_date - today).days
+
+    if 80 <= days_away <= 90:
+        return 25, days_away
+    elif 60 <= days_away <= 79:
+        return 15, days_away
+    elif 45 <= days_away <= 59:
+        return 10, days_away
+    else:
+        return 0, days_away
+    
 
 def pretty_time(unformatted_time, to_12_hour=True):
     if not isinstance(unformatted_time, (datetime, time)):
diff --git a/app/profile/routes.py b/app/profile/routes.py
index d2d3560..614e548 100644
--- a/app/profile/routes.py
+++ b/app/profile/routes.py
@@ -4,7 +4,7 @@ from flask_principal import Identity, identity_changed
 from flask_login import login_user, logout_user, login_required, current_user
 from werkzeug.security import check_password_hash
 from app.profile import bp
-from app.main.utils import pretty_time
+from app.main.utils import pretty_time, calculate_refund_amount
 from app.models import User, Bookings, Listings
 from app.logger import auth_logger
 from app import db, permission_required, user_permission
@@ -281,4 +281,12 @@ def manage_profile_view_booking(id):
     booking = Bookings.search_booking(id)
     booking.listing.destination_time = pretty_time(booking.listing.destination_time)
     booking.listing.depart_time = pretty_time(booking.listing.depart_time)
+    booking.cancelled_date = pretty_time(booking.cancelled_date)
+    
+    cancel_amount, cancel_percentage = calculate_refund_amount()
+    refund = {
+        amount: 
+        percentage:
+    }
+    
     return render_template('profile/view_booking.html', booking=booking)
diff --git a/app/templates/admin/manage_bookings.html b/app/templates/admin/manage_bookings.html
index e030c69..ad58f3e 100644
--- a/app/templates/admin/manage_bookings.html
+++ b/app/templates/admin/manage_bookings.html
@@ -155,7 +155,6 @@
             $('#arrive_after_time').append(new Option(time, time));
         });
 
-        // Populate location options, TO UPDATE WITH LIVE LOCATIONS
         const locations = JSON.parse('{{ locations|tojson|safe }}');
         locations.forEach(location => {
             $('#depart_location').append(new Option(location, location));
diff --git a/app/templates/admin/reports.html b/app/templates/admin/reports.html
index 2b702bf..0437f90 100644
--- a/app/templates/admin/reports.html
+++ b/app/templates/admin/reports.html
@@ -6,19 +6,43 @@
     <title>Reporting</title>
     <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
     <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+    <style>
+        .chart-container {
+            position: relative;
+            width: 100%;
+            height: 300px;
+            margin-bottom: 30px; /* Margin at the bottom to prevent overlap */
+        }
+        .card h3 {
+            text-align: center;
+        }
+        .chart-center {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+        }
+    </style>
 </head>
 <body>
     <div class="container">
         <h1 class="text-center my-4">Reporting</h1>
-
-        <form method="get" action="{{ url_for('admin.reports') }}" class="form-inline mb-4">
+        <form method="get" action="{{ url_for('admin.reports') }}" class="form mb-4">
             <div class="form-group mx-sm-3 mb-2">
-                <label for="days" class="sr-only">Show results for the last</label>
-                <input type="number" class="form-control" id="days" name="days" value="{{ days }}" min="1" placeholder="Days">
+                <div class="row">
+                    <div class="col-12">
+                        <label for="days" class="form-label">Show results for the last {{ days }} days</label>
+                    </div>
+                </div>
+                <div class="row">
+                    <div class="col-4">
+                        <input type="number" class="form-control" id="days" name="days" value="{{ days }}" min="1" placeholder="Days">
+                    </div>
+                    <div class="col-1">
+                        <button type="submit" class="btn btn-primary mb-2">Update</button>
+                    </div>
+                </div>
             </div>
-            <button type="submit" class="btn btn-primary mb-2">Update</button>
-        </form>
-
+        </form>             
         <div class="row mb-4">
             <div class="col-md-6">
                 <div class="card text-white bg-info mb-3">
@@ -41,26 +65,34 @@
         <div class="row">
             <div class="col-md-6 mb-4">
                 <h3>Destination Locations</h3>
-                <canvas id="destinationChart" height="200"></canvas>
+                <div class="chart-container chart-center">
+                    <canvas id="destinationChart"></canvas>
+                </div>
             </div>
             <div class="col-md-6 mb-4">
                 <h3>User Count</h3>
-                <canvas id="roleCountChart" height="200"></canvas>
+                <div class="chart-container chart-center">
+                    <canvas id="roleCountChart"></canvas>
+                </div>
             </div>
         </div>
         <div class="row">
             <div class="col-md-6 mb-4">
                 <h3>Number of Seats Booked Per Day</h3>
-                <canvas id="seatsBookedChart" height="200"></canvas>
+                <div class="chart-container chart-center">
+                    <canvas id="seatsBookedChart"></canvas>
+                </div>
             </div>
             <div class="col-md-6 mb-4">
-                <h3>Depart Dates</h3>
-                <canvas id="departDateChart" height="200"></canvas>
+                <h3>Departure Dates</h3>
+                <div class="chart-container chart-center">
+                    <canvas id="departDateChart"></canvas>
+                </div>
             </div>
         </div>
-        </div>
+    </div>
     <script>
-        // Destination Locations Pie Chart
+        // Destination Location
         var destinationData = {
             labels: {{ destinations|map(attribute=0)|list|tojson }},
             datasets: [{
@@ -83,10 +115,14 @@
         var ctx = document.getElementById('destinationChart').getContext('2d');
         var destinationChart = new Chart(ctx, {
             type: 'pie',
-            data: destinationData
+            data: destinationData,
+            options: {
+                responsive: true,
+                maintainAspectRatio: false
+            }
         });
 
-        // User Role Count Bar Chart
+        // User Count
         var roleCountData = {
             labels: {{ user_counts|map(attribute=0)|list|tojson }},
             datasets: [{
@@ -103,6 +139,8 @@
             type: 'bar',
             data: roleCountData,
             options: {
+                responsive: true,
+                maintainAspectRatio: false,
                 scales: {
                     y: {
                         beginAtZero: true
@@ -111,7 +149,7 @@
             }
         });
 
-        // Number of Seats Booked Per Day Line Chart
+        // Seats booked per day
         var seatsBookedData = {
             labels: {{ seats_booked_per_day|map(attribute=0)|list|tojson }},
             datasets: [{
@@ -128,6 +166,8 @@
             type: 'bar',
             data: seatsBookedData,
             options: {
+                responsive: true,
+                maintainAspectRatio: false,
                 scales: {
                     y: {
                         beginAtZero: true
@@ -136,7 +176,7 @@
             }
         });
 
-        // Depart Dates Line Chart
+        // Departure dates
         var departDateData = {
             labels: {{ depart_dates|map(attribute=0)|list|tojson }},
             datasets: [{
@@ -153,6 +193,8 @@
             type: 'bar',
             data: departDateData,
             options: {
+                responsive: true,
+                maintainAspectRatio: false,
                 scales: {
                     y: {
                         beginAtZero: true
@@ -163,5 +205,4 @@
     </script>
 </body>
 </html>
-
 {% endblock %}
diff --git a/app/templates/profile/manage_bookings.html b/app/templates/profile/manage_bookings.html
index c2a0edc..ff32bbb 100644
--- a/app/templates/profile/manage_bookings.html
+++ b/app/templates/profile/manage_bookings.html
@@ -20,7 +20,7 @@
                     <th>Departure Location</th>
                     <th>Destination Location</th>
                     <th>Cancelled</th>
-                    <th>Actions</th>
+                    <th>View Booking</th>
                 </tr>
             </thead>
             <tbody>
@@ -33,40 +33,13 @@
                     <td>{{ booking.destination_location }}</td>
                     <td>{{ 'Yes' if booking.cancelled else 'No' }}</td>
                     <td class="dt-center">
-                        <div class="dropdown">
-                            <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-expanded="false">
-                                Actions
-                            </button>
-                            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
-                                <a class="dropdown-item edit-btn" href="#">View</a>
-                                <a class="dropdown-item delete-btn" href="#">Cancel Booking</a>
-                            </div>
-                        </div>
+                        <a class="btn btn-secondary view-booking-btn" href="manage_bookings/view/{{ booking.id }}">View Booking</a>
                     </td>
                 </tr>
                 {% endfor %}
             </tbody>
         </table>
     </div>
-    <!-- Cancel booking modal -->
-    <div class="modal fade" id="confirm_booking_deletion" tabindex="-1">
-        <div class="modal-dialog">
-            <div class="modal-content">
-                <div class="modal-header">
-                    <h5 class="modal-title" id="confirm_booking_deletion">Confirm Cancellation</h5>
-                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
-                </div>
-                <div class="modal-body">
-                    <p>Type 'CONFIRM' to cancel your booking:</p>
-                    <input type="text" id="confirmation_input" class="form-control">
-                </div>
-                <div class="modal-footer">
-                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
-                    <button type="button" class="btn btn-primary" id="confirm_deletion_button">Cancel Booking</button>
-                </div>
-            </div>
-        </div>
-    </div>
     <!-- Filter modal -->
     <div class="modal fade" id="filterModal" tabindex="-1" aria-labelledby="filterModalLabel" aria-hidden="true">
         <div class="modal-dialog">
@@ -109,7 +82,6 @@
         </div>
     </div>
 </div>
-
 <style>
     .table-container {
         width: 100%;
@@ -144,7 +116,6 @@
             minimumResultsForSearch: Infinity
         });
 
-        // Populate location options, TO UPDATE WITH LIVE LOCATIONS
         const locations = JSON.parse('{{ locations|tojson|safe }}');
         locations.forEach(location => {
             $('#depart_location').append(new Option(location, location));
@@ -179,23 +150,15 @@
                     data: null,
                     className: "dt-center",
                     defaultContent: `
-                        <div class="dropdown">
-                            <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-expanded="false">
-                                Actions
-                            </button>
-                            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
-                                <a class="dropdown-item edit-btn" href="#">View</a>
-                                <a class="dropdown-item delete-btn" href="#">Cancel Booking</a>
-                            </div>
-                        </div>`
+                        <a class="btn btn-secondary view-booking-btn" href="#">View Booking</a>
+                    `
                 }
             ],
             language: {
                 emptyTable: "No bookings could be found."
             },
             createdRow: function(row, data, dataIndex) {
-                $(row).find('.edit-btn').attr('data-id', data.id);
-                $(row).find('.delete-btn').attr('data-id', data.id);
+                $(row).find('.view-booking-btn').attr('href', `manage_bookings/view/${data.id}`);
             }
         });
 
@@ -203,36 +166,6 @@
             table.ajax.reload();
             $('#filterModal').modal('hide');
         });
-
-        $('#manage_bookings tbody').on('click', '.edit-btn', function() {
-            const id = $(this).data('id');
-            window.location.href = `manage_bookings/view/${id}`;
-        });
-
-        let delete_booking_id;
-        $('#manage_bookings tbody').on('click', '.delete-btn', function() {
-            delete_booking_id = $(this).data('id');
-            $('#confirm_booking_deletion').modal('show');
-        });
-
-        $('#confirm_deletion_button').on('click', function() {
-            const confirmationInput = $('#confirmation_input').val();
-            if (confirmationInput === 'CONFIRM') {
-                $.ajax({
-                    url: `{{ url_for('profile.cancel_booking') }}`,
-                    type: 'POST',
-                    contentType: 'application/json',
-                    data: JSON.stringify({ booking_id: delete_booking_id }),
-                    success: function(response) {
-                        $('#confirm_booking_deletion').modal('hide');
-                        table.ajax.reload();
-                    },
-                    error: function(xhr, status, error) {
-                        alert('An error occurred while cancelling the booking.');
-                    }
-                });
-            }
-        });
     });
 </script>
 {% endblock %}
diff --git a/app/templates/profile/view_booking.html b/app/templates/profile/view_booking.html
index 65d71a8..c0e36ca 100644
--- a/app/templates/profile/view_booking.html
+++ b/app/templates/profile/view_booking.html
@@ -15,6 +15,9 @@
                             <p><strong>Departure Time:</strong> {{ booking.listing.depart_time }}</p>
                             <p><strong>Seat Type:</strong> {{ booking.seat_type.capitalize() }}</p>
                             <p><strong>Total Cost:</strong> £{{ booking.amount_paid }}</p>
+                            {% if booking.cancelled %}
+                            <p><strong>Cancellation Date:</strong> £{{ booking.cancelled_date }}</p>
+                            {% endif %}
                         </div>
                         <div class="col-md-6">
                             <p><strong>Destination Location:</strong> {{ booking.listing.destination_location }}</p>
@@ -22,11 +25,14 @@
                             <p><strong>Number of Seats:</strong> {{ booking.num_seats }}</p>
                             <p><strong>Cost Per Person:</strong> £{{ booking.amount_paid / booking.num_seats }}</p>
                             <p><strong>Cancelled:</strong> {{ 'Yes' if booking.cancelled else 'No' }}</p>
+                            {% if booking.cancelled %}
+                            <p><strong>Refunded Amount:</strong> £{{ booking.cancelled_refund }}</p>
+                            {% endif %}
                         </div>
                     </div>
                 </div>
             </div>
-            <div class="card shadow-sm">
+            <div class="card mb-4 shadow-sm">
                 <div class="card-body text-center">
                     <h3 class="card-title mb-4">Re-Download Booking Details</h3>
                     {% if booking.cancelled %}
@@ -47,6 +53,18 @@
                     {% endif %}
                 </div>
             </div>
+            {% if not booking.cancelled %}
+            <div class="card shadow-sm">
+                <div class="card-body text-center">
+                    <h3 class="card-title mb-4">Cancel Booking</h3>
+                        <div class="alert alert-info" role="alert">
+                            Need to cancel? Not a problem as you are x days away you are entitled to a x% refund! Your refunded amount will be £xx.x which
+                            will automatically be refunded to your card ending in 1234
+                        </div>
+                        <button type="button" class="btn btn-danger btn-lg mr-2">Cancel Booking</button>
+                </div>
+            </div>
+            {% endif %}
         </div>
     </div>
 </div>
-- 
GitLab