From da6d76b5f2b8ac4658cd5ba926d075fce3efa2d0 Mon Sep 17 00:00:00 2001 From: Ethan Clay <Ethan2.Clay@live.uwe.ac.uk> Date: Tue, 11 Feb 2025 10:17:10 +0000 Subject: [PATCH] Begin to work on manage users --- .env-example | 2 +- app/admin/routes.py | 33 ++++- app/models/user.py | 16 +++ app/profile/routes.py | 14 +-- app/templates/admin/manage_bookings.html | 1 - app/templates/admin/manage_users.html | 148 +++++++++++++++++++++++ 6 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 app/templates/admin/manage_users.html diff --git a/.env-example b/.env-example index 40a2857..20e7d46 100644 --- a/.env-example +++ b/.env-example @@ -3,5 +3,5 @@ DATABASE_HOST=database #If using pydebug you will need to use 127.0.0.1 DATABASE_USER=user DATABASE_PASSWORD=password1 -DATABASE_NAME=database +DATABASE_NAME=mydatabase SECRET_KEY=fsodfhiosdfhdsiofh34903urwej09jf diff --git a/app/admin/routes.py b/app/admin/routes.py index 3ef6e8d..5ad8bf8 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -1,7 +1,7 @@ from flask import render_template, redirect, url_for, request, jsonify, flash from app import db from app import admin_permission, permission_required, super_admin_permission -from app.models import Listings, ListingImages +from app.models import Listings, ListingImages, User from app.admin import bp from app.main.utils import generate_time_options @@ -46,7 +46,7 @@ def edit_booking(id): @bp.route('/manage_users') @permission_required(super_admin_permission) def manage_users(): - return render_template('admin/index.html') + return render_template('admin/manage_users.html') @bp.route('/manage_user_bookings') @permission_required(admin_permission) @@ -155,6 +155,23 @@ def get_bookings(): return jsonify(result) + +@bp.route('get_users', methods=['GET']) +@permission_required(super_admin_permission) +def get_users(): + all_users = User.get_all_users() + + result = [ + { + 'id': user.id, + 'username': user.username, + 'email': user.email, + 'role': user.role.name, + } for user in all_users + ] + + return jsonify(result) + @bp.route('create_listing', methods=['GET']) @permission_required(admin_permission) def create_listing(): @@ -230,6 +247,18 @@ def delete_booking(): return jsonify(success), http_code +@bp.route('delete_user', methods=['DELETE']) +@permission_required(admin_permission) +def delete_user(): + http_code = 400 + user_id = request.form.get('id') + success = User.delete_user(user_id) + + if success: + http_code = 200 + + return jsonify(success), http_code + @bp.route('/delete_image/<int:image_id>', methods=['POST']) @permission_required(admin_permission) def delete_image(image_id): diff --git a/app/models/user.py b/app/models/user.py index c0b838a..ae88eb7 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -56,3 +56,19 @@ class User(UserMixin, db.Model): if user and user.role: return user.role.name return None + + @classmethod + def get_all_users(cls): + return cls.query.all() + + @classmethod + def delete_user(cls, user_id = None): + + user = cls.search_user_id(user_id) + + if user: + db.session.delete(user) + db.session.commit() + return True + + return False diff --git a/app/profile/routes.py b/app/profile/routes.py index 3479ff0..70ac51d 100644 --- a/app/profile/routes.py +++ b/app/profile/routes.py @@ -40,9 +40,9 @@ def signup(): login_user(new_user) - if session['callback']: - flash("Account successfully created. Please review your booking before continuing", 'success') + if 'callback' in session.keys(): callback = session.pop('callback') + flash("Account successfully created. Please review your booking before continuing", 'success') return redirect(callback) flash('Successfully created your account. You have been logged in automatically', 'success') @@ -74,8 +74,6 @@ def is_valid_username(username): return all(c.isalnum() or c in allowed_special_chars for c in username) -from flask_principal import Identity, identity_changed - @bp.route('/login', methods=['POST']) def login_post(): username_field = request.form.get('username') @@ -95,10 +93,10 @@ def login_post(): identity_changed.send(current_app._get_current_object(), identity=Identity(user.id)) - if session['callback']: - callback = session.pop('callback') - flash("You have been successfully logged in. Please review your booking before continuing", 'success') - return redirect(callback) + if 'callback' in session.keys(): + callback = session.pop('callback') + flash("You have been successfully logged in. Please review your booking before continuing", 'success') + return redirect(callback) return redirect(url_for('profile.index')) diff --git a/app/templates/admin/manage_bookings.html b/app/templates/admin/manage_bookings.html index e3c686a..2573160 100644 --- a/app/templates/admin/manage_bookings.html +++ b/app/templates/admin/manage_bookings.html @@ -85,7 +85,6 @@ </tbody> </table> </div> - <!-- Include this modal in your HTML code --> <div class="modal fade" id="confirm_booking_deletion" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> diff --git a/app/templates/admin/manage_users.html b/app/templates/admin/manage_users.html new file mode 100644 index 0000000..ef9b208 --- /dev/null +++ b/app/templates/admin/manage_users.html @@ -0,0 +1,148 @@ +{% extends 'base.html' %} +{% block content %} +<div class="container my-4"> + <div class="d-flex justify-content-between mb-3"> + <h2>Manage Bookings</h2> + </div> + <!-- Manage bookings table starts --> + <div class="table-container"> + <table id="manage_bookings" class="table table-striped table-bordered display hover" style="width:100%"> + <thead> + <tr> + <th>Id</th> + <th>User Name</th> + <th>Email</th> + <th>Role</th> + <th>Actions</th> + </tr> + </thead> + <tbody> + </tbody> + </table> + </div> +</div> +<div class="modal fade" id="confirm_user_deletion" tabindex="-1"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="confirm_user_deletion">Confirm Deletion</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + <p>Type 'CONFIRM' to delete this entry:</p> + <input type="text" id="conifrmation_input" class="form-control"> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-primary" id="confirm_deletion_button">Delete</button> + </div> + </div> + </div> +</div> +<style> + .table-container { + width: 100%; + overflow-x: auto; + overflow-y: hidden; + } + + @media (max-width: 800px) { + .table-container { + padding: 0 10px; + } + } + + .dataTables_wrapper { + width: 100%; + } + + table.dataTable.no-footer { + margin-bottom: 30px; + } + +</style> +<script> + $(document).ready(function() { + // Load table + const table = $('#manage_bookings').DataTable({ + pageLength: 10, + ordering: false, + ajax: { + url: "{{ url_for('admin.get_users') }}", + dataSrc: '', + data: function(d) { + d.username = $('#username').val(); + d.email = $('#email').val(); + d.role_id = $('#role').val(); + } + }, + columns: [ + { data: 'id', visible: false }, // Hidden id column + { data: 'username' }, + { data: 'email' }, + { data: 'role' }, + { + 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="#">Edit</a> + <a class="dropdown-item delete-btn" href="#">Delete</a> + </div> + </div>` + } + ], + language: { + emptyTable: "No users could be found" + }, + createdRow: function(row, data, dataIndex) { //Attach ID to edit button and delete button. + $(row).find('.edit-btn').attr('data-id', data.id); + $(row).find('.delete-btn').attr('data-id', data.id); + } + }); + + $('#manage_bookings tbody').on('click', '.edit-btn', function() { + const id = $(this).data('id'); + window.location.href = `manage_user/edit/${id}`; + }); + + let delete_user; + $('#manage_bookings tbody').on('click', '.delete-btn', function() { + delete_user = table.row($(this).parents('tr')); + $('#confirm_user_deletion').modal('show'); + }); + + $('#confirm_deletion_button').on('click', function() { + const confirmation_input = $('#conifrmation_input').val().trim(); + if (confirmation_input === 'CONFIRM') { + $.ajax({ + url: "{{ url_for('admin.delete_user') }}", + method: "DELETE", + data: { id: delete_user.data().id }, + success: function() { + delete_user.remove().draw(); + $('#confirm_user_deletion').modal('hide'); + }, + error: function() { + alert('Failed to delete the booking. Please try again.'); + } + }); + } else { + alert('Please type "CONFIRM" to delete the booking.'); + } + }); + + $('#confirm_user_deletion').on('hidden.bs.modal', function () { + $('#conifrmation_input').val(''); + }); + }); +</script> + + + + +{% endblock %} -- GitLab