#10 Added ability to update user details

This commit is contained in:
Luke Else 2024-02-14 21:59:31 +00:00
parent d6f7b3e549
commit 3b8be99e15
10 changed files with 340 additions and 211 deletions

View File

@ -69,8 +69,29 @@ class UserController(DatabaseController):
return self.get_many(query, params)
def update(self):
print("Doing work")
def update(self, user: User):
params = [
user.username,
user.firstName,
user.lastName,
user.email,
user.phone,
user.role,
user.id
]
query = """
UPDATE Users
SET username = ?,
first_name = ?,
last_name = ?,
email = ?,
phone = ?,
role = ?
WHERE id = ?
"""
return self.do(query, params)
def delete(self, id: int):
params = [

View File

@ -7,12 +7,27 @@ from flask import render_template, redirect, request, session, flash, url_for
from controllers.database.user import UserController
from models.users.customer import Customer
from models.users.seller import Seller
from models.users.user import User
from utils.user_utils import is_role
from hashlib import sha512
# Blueprint to append user endpoints to
blueprint = Blueprint("users", __name__, url_prefix="/users")
ROLES = [
"Customer",
"Seller",
"Admin"
]
@blueprint.context_processor
def roles_list():
""" Places a list of all the available roles in the
users context
"""
return dict(roles=ROLES)
# LOGIN FUNCTIONALITY
@blueprint.route('/login')
@ -44,6 +59,62 @@ def login():
return redirect(url_for('main.index'))
# SIGNUP FUNCTIONALITY
@blueprint.route('/update/<int:id>')
def display_update(id: int):
""" Function responsible for delivering the Update User page
for the site
"""
db = UserController()
user = db.read_id(id)
return render_template('index.html', content="user.html", user=user)
@blueprint.post('/update/<int:id>')
def update(id: int):
""" Function to handle the backend processing of a signup request """
if not is_role("Admin"):
flash("You must be logged in an admin to update users!", "error")
return redirect(url_for('main.index'))
user = User(
request.form['username'],
"", # Password
request.form['firstname'],
request.form['lastname'],
request.form['email'],
request.form['phone'],
request.form['role'],
)
user.id = id
db = UserController()
checking_user = db.read(user.username)
# User with this new username is already taken
if checking_user is not None and checking_user.id != user.id:
flash(
f"User with the username {user.username} already exists!",
"warning"
)
return redirect(url_for('main.users.display_update', id=id))
db.update(user)
return redirect(url_for('main.admin.users'))
# SIGN OUT FUNCTIONALITY
@blueprint.route('/logout')
def logout():
""" Function responsible for handling logouts from the site """
# Clear the current user from the session if they are logged in
session.pop('user_id', None)
return redirect(url_for('main.index'))
# SIGNUP FUNCTIONALITY
@blueprint.route('/signup')
def display_signup():
@ -85,20 +156,10 @@ def signup():
)
database.create(user)
# Code 307 Preserves the original request (POST)
return redirect(url_for('main.users.login'), code=307)
# SIGN OUT FUNCTIONALITY
@blueprint.route('/logout')
def logout():
""" Function responsible for handling logouts from the site """
# Clear the current user from the session if they are logged in
session.pop('user_id', None)
return redirect(url_for('main.index'))
# DELETE USER FUNCTIONALITY
@blueprint.post('/delete/<int:id>')
def delete(id: int):

View File

@ -1,156 +1,157 @@
h2 {
font-weight:300;
text-align:center;
}
#input-form-wrap {
background-color: rgba(255, 255, 255, .15);
backdrop-filter: blur(200px);
width: 35%;
text-align: center;
padding: 1em 0 0 0;
border-radius: 4px;
box-shadow: 0px 30px 50px 0px rgba(0, 0, 0, 0.2);
}
.input-form {
padding: 1em 2em;
display: flex;
flex-direction: column;
justify-items: center;
gap: 1em;
}
.input-form-row {
display: flex;
flex-direction: row;
justify-content: space-evenly;
gap: 1em 1em;
}
.input-form-row input, .input-form-row select, .input-form-row option, .input-form-row textarea {
width: 100%;
padding: 0 0 0 10px;
margin: 0;
color: var(--fg);
border: 1px solid var(--fg);
box-sizing: border-box;
outline: none;
height: 60px;
line-height: 60px;
border-radius: 4px;
font-style: normal;
font-size: 16px;
appearance: none;
position: relative;
display: inline-block;
background: none;
&:focus {
&:invalid {
color: var(--red);
border-color: var(--red);
}
}
&:valid {
border-color: var(--green);
}
}
.input-form-row textarea {
min-height: 120px;
max-width: 100%;
}
.input-form-row input[type="submit"] {
border: none;
display:block;
background-color: rgba(255, 255, 255, .10);
color: var(--fg);
font-weight: bold;
text-transform:uppercase;
cursor: pointer;
transition: all 0.2s ease;
font-size: 18px;
position: relative;
display: inline-block;
cursor: pointer;
text-align: center;
&:hover {
background-color: rgba(255, 255, 255, .20);
transition: all 0.2s ease;
}
}
#create-account-wrap {
background-color: rgba(255, 255, 255, .15);
color:#dfdfdf;
font-size:14px;
width:100%;
padding:10px 0;
border-radius: 0 0 4px 4px;
}
.checkbox {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default checkbox */
.checkbox input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create a custom checkbox */
.checkmark {
position: absolute;
height: 25px;
width: 25px;
background-color: #808080;
}
/* On mouse-over, add a grey background color */
.checkbox:hover input ~ .checkmark {
background-color: #ccc;
}
/* When the checkbox is checked, add a blue background */
.checkbox input:checked ~ .checkmark {
background-color: #2196F3;
}
/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.checkbox input:checked ~ .checkmark:after {
display: block;
}
/* Style the checkmark/indicator */
.checkbox .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
h2 {
font-weight:300;
text-align:center;
}
#form-footer {
background-color: rgba(255, 255, 255, .15);
color:#dfdfdf;
font-size:14px;
width:100%;
padding:10px 0;
border-radius: 0 0 4px 4px;
}
#form-wrapper {
background-color: rgba(255, 255, 255, .15);
backdrop-filter: blur(200px);
width: 35%;
text-align: center;
padding: 1em 0 0 0;
border-radius: 4px;
box-shadow: 0px 30px 50px 0px rgba(0, 0, 0, 0.2);
}
.input-form {
padding: 1em 2em;
display: flex;
flex-direction: column;
justify-items: center;
gap: 1em;
}
.form-row {
display: flex;
flex-direction: row;
justify-content: space-evenly;
gap: 1em 1em;
}
.form-row input, .form-row select, .form-row option, .form-row textarea {
width: 100%;
padding: 0 0 0 10px;
margin: 0;
color: var(--fg);
border: 1px solid var(--fg);
box-sizing: border-box;
outline: none;
height: 60px;
line-height: 60px;
border-radius: 4px;
font-style: normal;
font-size: 16px;
appearance: none;
position: relative;
display: inline-block;
background: none;
&:focus {
&:invalid {
color: var(--red);
border-color: var(--red);
}
}
&:valid {
border-color: var(--green);
}
}
.form-row textarea {
min-height: 120px;
max-width: 100%;
}
.form-row input[type="submit"] {
border: none;
display:block;
background-color: rgba(255, 255, 255, .10);
color: var(--fg);
font-weight: bold;
text-transform:uppercase;
cursor: pointer;
transition: all 0.2s ease;
font-size: 18px;
position: relative;
display: inline-block;
cursor: pointer;
text-align: center;
&:hover {
background-color: rgba(255, 255, 255, .20);
transition: all 0.2s ease;
}
}
.checkbox {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default checkbox */
.checkbox input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create a custom checkbox */
.checkmark {
position: absolute;
height: 25px;
width: 25px;
background-color: #808080;
}
/* On mouse-over, add a grey background color */
.checkbox:hover input ~ .checkmark {
background-color: #ccc;
}
/* When the checkbox is checked, add a blue background */
.checkbox input:checked ~ .checkmark {
background-color: #2196F3;
}
/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.checkbox input:checked ~ .checkmark:after {
display: block;
}
/* Style the checkmark/indicator */
.checkbox .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}

View File

@ -1,5 +1,5 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/admin.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/loginform.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/modal.css') }}">
<!-- USER MANAGEMENT-->
@ -26,13 +26,15 @@
<td>{{user.phone}}</td>
<td>{{user.role}}</td>
<td>
<div class="input-form-row">
<div class="button neutral">
<p class="btnText">Edit User</p>
<div class="btnTwo">
<p class="btnText2">{{user.id}}</p>
<div class="form-row">
<a href="{{ url_for('main.users.display_update', id=user.id) }}">
<div class="button neutral">
<p class="btnText">Edit User</p>
<div class="btnTwo">
<p class="btnText2">{{user.id}}</p>
</div>
</div>
</div>
</a>
<label for="deleteModal{{user.id}}">
<div class="button error">
<p class="btnText">DELETE USER</p>
@ -54,7 +56,7 @@
<h2>Confirm Delete</h2>
<p>Are you sure you want to <b>delete</b> {{user.role}} <b>{{user.username}}</b></p>
<form method="POST" action="{{ url_for('main.users.delete', id=user.id) }}">
<div class="input-form-row">
<div class="form-row">
<input type="submit" class="modal-btn error" for="deleteModal{{user.id}}" value="Delete" />
</div>
</form>
@ -92,7 +94,7 @@
<td>{{product.category}}</td>
<td>{{product.quantityAvailable}}</td>
<td>
<div class="input-form-row">
<div class="form-row">
<a href="{{url_for('main.products.product', id=product.id)}}">
<div class="button neutral">
<p class="btnText">Edit Product</p>
@ -122,7 +124,7 @@
<h2>Confirm Delete</h2>
<p>Are you sure you want to <b>delete</b> <b>{{product.name}}</b></p>
<form method="POST" action="{{ url_for('main.seller.delete', id=product.id) }}">
<div class="input-form-row">
<div class="form-row">
<input type="submit" class="modal-btn error" for="deleteModal{{product.id}}" value="Delete" />
</div>
</form>

View File

@ -1,22 +1,22 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/loginform.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}" />
<div id="input-form-wrap">
<div id="form-wrapper">
<h2>Login</h2>
<form class="input-form" method="POST" action="{{ url_for('main.users.login') }}">
<div class="input-form-row">
<div class="form-row">
<input type="text" id="username" name="username" placeholder="Username" required>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="password" id="password" name="password" placeholder="Password" required>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="submit" id="login" value="Login">
</div>
</form>
<div id="create-account-wrap">
<div id="form-footer">
<p>Not a member? <a href="{{ url_for('main.users.display_signup') }}">Create Account</a><p>
</div>
</div>

View File

@ -1,18 +1,18 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/loginform.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}" />
<div id="input-form-wrap">
<div id="form-wrapper">
<h2>Create New Product</h2>
<form class="input-form" method="POST" action="{{ url_for('main.seller.add') }}" enctype="multipart/form-data">
<div class="input-form-row">
<div class="form-row">
<input type="text" id="name" name="name" placeholder="Product Name" required>
<input type="file" id="image" name="image" accept="image/x" required>
</div>
<div class="input-form-row">
<div class="form-row">
<textarea id="description" name="description" placeholder="Product Description" required></textarea>
</div>
<div class="input-form-row">
<div class="form-row">
<select name="category" id="category">
{% for category in categories %}
<option value="{{category.id}}">{{category.name}}</option>
@ -20,17 +20,17 @@
</select>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="number" id="cost" name="cost" placeholder="Cost" min=0 step=0.01 required>
<input type="number" id="quantity" name="quantity" placeholder="Quantity Available" min=0 required>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="submit" id="Create Product" value="Create Product">
</div>
</form>
<div id="create-account-wrap">
<div id="form-footer">
<p>Want to view all of your products? <a href="{{ url_for('main.seller.display_own') }}">Click Here</a><p>
</div>
</div>

View File

@ -1,5 +1,5 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/products.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/loginform.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}">
{% if product != None %}
{% if user.id == product.sellerID or user.role == "Admin" %}
@ -7,14 +7,14 @@
<form class="product-fs" method="POST" action="{{ url_for('main.seller.update', id=product.id) }}" enctype="multipart/form-data">
<img class="product-image" src="{{ url_for('static', filename='assets/img/products/' + product.image) }}" alt="Brake Disks"/>
<div class="product-details">
<div class="input-form-row">
<div class="form-row">
<input type="text" id="name" name="name" placeholder="Product Name" value="{{product.name}}" required>
</div>
<div class="input-form-row">
<div class="form-row">
<textarea id="description" name="description" placeholder="Product Description" required>{{product.description}}</textarea>
</div>
<div class="input-form-row">
<div class="form-row">
<select name="category" id="category">
{% for category in categories %}
{% if category.id == product.category %}
@ -26,11 +26,11 @@
</select>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="file" id="image" name="image" accept="image/x">
</div>
<div class="input-form-row">
<div class="form-row">
<input type="submit" id="Create Product" value="Update Product">
</div>
@ -44,21 +44,21 @@
</label>
</div>
<div class="product-acquisition-pane">
<div class="input-form-row">
<div class="form-row">
<input type="number" id="cost" name="cost" placeholder="Cost" min=0 step=0.01 value="{{product.cost}}"required>
</div>
<div class="product-delivery">Earliest Delivery Friday 24th December</div>
<div class="product-postage">+£{{product.cost}} P&P</div>
<div class="product-stock">
<div class="input-form-row">
<div class="form-row">
<input type="number" id="quantity" name="quantity" placeholder="Quantity Available" min=0 value="{{product.quantityAvailable}}" required>
</div>
{% if product.quantityAvailable > 0 %}
<div class="input-form-row">
<div class="form-row">
<div class="product-instock">In Stock</div>
<div class="product-quantity">{{product.quantityAvailable}} Available</div>
</div>
<div class="input-form-row">
<div class="form-row">
<a href="{{ url_for('main.stats.view_product_stats', id=product.id) }}">
<div class="button success">
<p class="btnText">VIEW STATS</p>
@ -84,7 +84,7 @@
<h2>Confirm Delete</h2>
<p>Are you sure you want to <b>delete {{product.name}}</b> from your products</p>
<form method="POST" action="{{ url_for('main.seller.delete', id=product.id) }}">
<div class="input-form-row">
<div class="form-row">
<input type="submit" class="modal-btn error" for="deleteModal" value="Delete" />
</div>
</form>

View File

@ -1,19 +1,19 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/loginform.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}" />
<div id="input-form-wrap">
<div id="form-wrapper">
<h2>Sign Up</h2>
<form class="input-form" action="{{ url_for('main.users.signup') }}" method="POST">
<div class="input-form-row">
<div class="form-row">
<input type="text" id="firstname" name="firstname" placeholder="First Name" required>
<input type="text" id="lastname" name="lastname" placeholder="Last Name" required>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="text" id="username" name="username" placeholder="Username" required>
<input type="email" id="email" name="email" placeholder="Email Address" required>
</div>
<div class="input-form-row">
<div class="form-row">
<input type="password" id="password" name="password" minlength=8 placeholder="Password" required>
</div>
@ -22,12 +22,12 @@
<span class="checkmark"></span>
</label>
<div class="input-form-row">
<div class="form-row">
<input type="submit" id="Sign Up" value="Sign Up">
</div>
</form>
<div id="create-account-wrap">
<div id="form-footer">
<p>Already have an account? <a href="{{ url_for('main.users.display_login') }}">Login</a><p>
</div>
</div>

View File

@ -1,12 +1,12 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/products.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/stats.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/loginform.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}">
<div class="stats">
<div>
<canvas id="myChart" width="800px" height="500px"></canvas>
<br >
<div class="input-form-row">
<div class="form-row">
<a href="?prev_days=7">1 Week</a>
<a href="?prev_days=14">2 Weeks</a>
<a href="?prev_days=31">1 Month</a>

44
templates/user.html Normal file
View File

@ -0,0 +1,44 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}" />
<div id="form-wrapper">
<h2>Update User</h2>
{% if user != None %}
<form class="input-form" action="{{ url_for('main.users.update', id=user.id) }}" method="POST">
<div class="form-row">
<input type="text" id="firstname" name="firstname" placeholder="First Name" value="{{user.firstName}}" required>
<input type="text" id="lastname" name="lastname" placeholder="Last Name" value="{{user.lastName}}" required>
</div>
<div class="form-row">
<input type="text" id="username" name="username" placeholder="Username" value="{{user.username}}" required>
<input type="email" id="email" name="email" placeholder="Email Address" value="{{user.email}}" required>
</div>
<div class="form-row">
<input type="tel" id="phone" name="phone" placeholder="Telephone Number" value="{{user.phone}}" required>
</div>
<div class="form-row">
<select name="role" id="role">
{% for role in roles %}
{% if role == user.role %}
<option value="{{role}}" selected>{{role}}</option>
{% else %}
<option value="{{role}}">{{role}}</option>
{% endif%}
{% endfor %}
</select>
</div>
<div class="form-row">
<input type="submit" id="Update" value="Update User">
</div>
</form>
{% else %}
<p>No user found with the given ID.</p>
{% endif %}
<div id="form-footer">
<p>Already have an account? <a href="{{ url_for('main.users.display_login') }}">Login</a><p>
</div>
</div>