Added highlighting for selected category

This commit is contained in:
Luke Else 2024-01-06 01:14:20 +00:00
parent a86edf01ad
commit 20f28739a2
14 changed files with 278 additions and 183 deletions

View File

@ -0,0 +1,64 @@
from .database import DatabaseController
from models.category import Category
class CategoryController(DatabaseController):
FIELDS = ['id', 'name']
def __init__(self):
super().__init__()
def create(self, category: Category):
params = [
category.name,
]
self._conn.execute(
"INSERT INTO Categories (name) VALUES (?)",
params
)
self._conn.commit()
def read(self, id: int = 0) -> Category | None:
params = [
id
]
cursor = self._conn.execute(
"SELECT * FROM Categories WHERE id = ?",
params
)
row = cursor.fetchone()
if row == None:
return None
params = dict(zip(self.FIELDS, row))
obj = self.new_instance(Category, params)
return obj
def read_all(self) -> list[Category] | None:
cursor = self._conn.execute(
"SELECT * FROM Categories",
)
rows = cursor.fetchall()
if rows == None:
return None
categories = list()
for category in rows:
params = dict(zip(self.FIELDS, category))
obj = self.new_instance(Category, params)
categories.append(obj)
return categories
def update(self):
print("Doing work")
def delete(self):
print("Doing work")

View File

@ -46,15 +46,21 @@ class ProductController(DatabaseController):
for product in rows: for product in rows:
params = dict(zip(self.FIELDS, product)) params = dict(zip(self.FIELDS, product))
obj = self.new_instance(Product, params) obj = self.new_instance(Product, params)
print(obj.__dict__)
products.push(obj) products.push(obj)
return products return products
def read_all(self) -> list[Product] | None: def read_all(self, category: str = "") -> list[Product] | None:
params = [
"%" + category + "%"
]
cursor = self._conn.execute( cursor = self._conn.execute(
"SELECT * FROM Products", """SELECT * FROM Products
INNER JOIN Categories ON Products.categoryID = Categories.id
WHERE Categories.name LIKE ? """,
params
) )
rows = cursor.fetchall() rows = cursor.fetchall()
@ -67,7 +73,6 @@ class ProductController(DatabaseController):
for product in rows: for product in rows:
params = dict(zip(self.FIELDS, product)) params = dict(zip(self.FIELDS, product))
obj = self.new_instance(Product, params) obj = self.new_instance(Product, params)
print(obj.__dict__)
products.append(obj) products.append(obj)
return products return products

View File

@ -39,7 +39,6 @@ class UserController(DatabaseController):
if row != None: if row != None:
params = dict(zip(self.FIELDS, row)) params = dict(zip(self.FIELDS, row))
obj = self.new_instance(Customer, params) obj = self.new_instance(Customer, params)
print(obj.__dict__)
return obj return obj
return None return None

View File

@ -2,9 +2,16 @@ from flask import Blueprint
from flask import render_template, session, flash from flask import render_template, session, flash
from controllers.database.product import ProductController from controllers.database.product import ProductController
from controllers.database.category import CategoryController
blueprint = Blueprint("products", __name__, url_prefix="/products") blueprint = Blueprint("products", __name__, url_prefix="/products")
@blueprint.context_processor
def category_list():
database = CategoryController()
categories = database.read_all()
return dict(categories=categories)
# Loads the front product page # Loads the front product page
@blueprint.route('/') @blueprint.route('/')
def index(): def index():
@ -20,7 +27,15 @@ def index():
# Loads a given product category page # Loads a given product category page
@blueprint.route('/<string:category>') @blueprint.route('/<string:category>')
def category(category: str): def category(category: str):
return "Category: " + category print(category)
database = ProductController()
products = database.read_all(category)
# No Products visible
if products == None:
flash("No Products available in " + category)
return render_template('index.html', content="content.html", user = session.get('user'), products = products, category = category)
# Loads a given product based on ID # Loads a given product based on ID
@blueprint.route('/<int:id>') @blueprint.route('/<int:id>')

7
models/category.py Normal file
View File

@ -0,0 +1,7 @@
class Category:
'''
Constructor for a category object
'''
def __init__(self):
self.id = 0
self.name = ""

View File

@ -11,6 +11,21 @@ CREATE TABLE IF NOT EXISTS Users (
INSERT INTO Users (first_name, last_name, username, email, phone, password, role) VALUES ("Luke", "Else", "lukejelse04", "test@test.com", "07498 289321", "test213", "Customer"); INSERT INTO Users (first_name, last_name, username, email, phone, password, role) VALUES ("Luke", "Else", "lukejelse04", "test@test.com", "07498 289321", "test213", "Customer");
CREATE TABLE IF NOT EXISTS Categories (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
INSERT INTO Categories (name) VALUES ("Car Parts");
INSERT INTO Categories (name) VALUES ("Animals");
INSERT INTO Categories (name) VALUES ("Sports");
INSERT INTO Categories (name) VALUES ("Books");
INSERT INTO Categories (name) VALUES ("Phones");
INSERT INTO Categories (name) VALUES ("Music");
CREATE TABLE IF NOT EXISTS Products ( CREATE TABLE IF NOT EXISTS Products (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
@ -21,20 +36,23 @@ CREATE TABLE IF NOT EXISTS Products (
REFERENCES Users (id) REFERENCES Users (id)
ON DELETE CASCADE ON DELETE CASCADE
ON UPDATE NO ACTION, ON UPDATE NO ACTION,
category TEXT NOT NULL categoryID INTEGER NOT NULL
REFERENCES Categories (id)
ON DELETE CASCADE
ON UPDATE NO ACTION
); );
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
INSERT INTO Products (name, image, description, cost, sellerID, category) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, "CarParts"); INSERT INTO Products (name, image, description, cost, sellerID, categoryID) VALUES ("test", "assets/img/wmgzon.png", "this is a product", 20.99, 1, 1);
CREATE TABLE IF NOT EXISTS Orders ( CREATE TABLE IF NOT EXISTS Orders (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,

69
static/css/carparts.css Normal file
View File

@ -0,0 +1,69 @@
.filter-pane {
flex: 0 1 auto;
width: 60%;
padding: .5rem 2rem;
background-color: var(--bg-grad-3);
border-radius: 1rem;
}
.filter-items {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
}
.product-filter {
width: 100%;
padding: 16px 20px;
border: none;
border-radius: 4px;
background-color: var(--bg-grad-2);
color: var(--fg);
font-size: .75rem;
}
/* Number Plate*/
.number-plate {
display: flex;
align-items: center;
}
.country-identifier {
width: auto;
display: flex;
flex-direction: column;
align-items: center;
color: #ffffff;
font-weight: bold;
height: 60px;
justify-content: center;
}
.country-identifier img {
border-radius: 8px 0 0 8px;
}
.vrn {
width: 10rem;
height: 63px;
border: 1px solid #ead809;
background-color: #ead809;
border-radius: 0 8px 8px 0;
}
.vrn .vrn-text {
width: -webkit-fill-available;
height: -webkit-fill-available;
border-radius: 0;
border: 0;
font-family: "UKNumberPlate", sans-serif;
font-size: 180%;
text-transform: uppercase;
text-align: center;
outline: 0;
background-color: transparent;
}
.vrn .vrn-text:focus {
outline: 0;
}

43
static/css/products.css Normal file
View File

@ -0,0 +1,43 @@
/* Product Information*/
.product-container {
width: 95%;
background-color: var(--bg-grad-3);
border-radius: 1rem 1rem 0rem 0rem;
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 2rem 1.5rem;
padding: 1rem;
transition: all 0.2s;
overflow-y: scroll;
}
.product {
display: flex;
flex-direction: column;
justify-content: space-between;
flex-wrap: wrap;
flex: 4 0 1rem;
padding: .5rem 1rem 2rem 2rem;
background: var(--bg-secondary);
border-radius: .5rem .5rem;
transition: all 0.2s;
}
.product-information {
display: flex;
flex-direction: row;
gap: 1rem 1rem;
}
.product-details {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem 1rem;
}
.product-description {
font-size: 70%;
}

View File

@ -172,122 +172,3 @@ a:active {
flex-grow: 1; flex-grow: 1;
height: 1%; height: 1%;
} }
.filter-pane {
padding: .5rem 2rem;
background-color: var(--bg-grad-3);
border-radius: 1rem;
}
.filter-items {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
}
.product-filter {
width: 100%;
padding: 16px 20px;
border: none;
border-radius: 4px;
background-color: var(--bg-grad-2);
color: var(--fg);
font-size: .75rem;
}
/* Number Plate*/
.number-plate {
display: flex;
align-items: center;
}
.country-identifier {
width: auto;
display: flex;
flex-direction: column;
align-items: center;
color: #ffffff;
font-weight: bold;
height: 60px;
justify-content: center;
}
.country-identifier img {
border-radius: 8px 0 0 8px;
}
.vrn {
width: 10rem;
height: 63px;
border: 1px solid #ead809;
background-color: #ead809;
border-radius: 0 8px 8px 0;
}
.vrn .vrn-text {
width: -webkit-fill-available;
height: -webkit-fill-available;
border-radius: 0;
border: 0;
font-family: "UKNumberPlate", sans-serif;
font-size: 180%;
text-transform: uppercase;
text-align: center;
outline: 0;
background-color: transparent;
}
.vrn .vrn-text:focus {
outline: 0;
}
/* Product Information*/
.product-container {
width: 95%;
background-color: var(--bg-grad-3);
border-radius: 1rem 1rem 0rem 0rem;
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 2rem 1.5rem;
padding: 1rem;
transition: all 0.2s;
overflow-y: scroll;
}
.product {
display: flex;
flex-direction: column;
justify-content: space-between;
flex-wrap: wrap;
flex: 4 0 1rem;
padding: .5rem 1rem 2rem 2rem;
background: var(--bg-secondary);
border-radius: .5rem .5rem;
transition: all 0.2s;
}
.product-information {
display: flex;
flex-direction: row;
gap: 1rem 1rem;
}
.product-details {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem 1rem;
}
.product-description {
font-size: 70%;
}

22
templates/Car Parts.html Normal file
View File

@ -0,0 +1,22 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/carparts.css') }}">
<div class="filter-pane">
<form action="" class="filter-items">
<div class="number-plate">
<span class="country-identifier">
<img src="https://mycarneedsa.com/assets/flint/img/flag_europe_gb.png" alt="">
</span>
<span class="vrn">
<input type="text" class="vrn-text" placeholder="YOUR REG" name="vrn">
</span>
</div>
<select class="product-filter not-required" name="filter">
<option value="relevance">Most Relevant</option>
<option value="price-lh">Price: Low -> High</option>
<option value="price-hl">Price: High -> Low</option>
</select>
<input type="submit" class="search-button" value="Filter">
</form>
</div>

View File

@ -1,39 +0,0 @@
<div class="filter-pane">
<form action="" class="filter-items">
<div class="number-plate">
<span class="country-identifier">
<img src="https://mycarneedsa.com/assets/flint/img/flag_europe_gb.png" alt="">
</span>
<span class="vrn">
<input type="text" class="vrn-text" placeholder="YOUR REG" name="vrn">
</span>
</div>
<select class="product-filter not-required" name="filter">
<option value="relevance">Most Relevant</option>
<option value="price-lh">Price: Low -> High</option>
<option value="price-hl">Price: High -> Low</option>
</select>
</form>
</div>
<div class="product-container">
{% if products != None %}
{% for product in products %}
<div class="product">
<div class="product-title">{{product.name}}</div>
<div class="product-information">
<div class="product-image">
<img src="{{url_for('static', filename=product.image)}}" alt="Brake Disks"/>
</div>
<div class="product-details">
<div class="product-price">£{{product.cost}}</div>
<div class="product-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation</div>
</div>
</div>
<div class="product-add-to-cart"></div>
</div>
{% endfor %}
{% endif %}
</div>

View File

@ -1,3 +1,5 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/products.css') }}">
<div class="product-container"> <div class="product-container">
{% if products != None %} {% if products != None %}
{% for product in products %} {% for product in products %}
@ -9,7 +11,7 @@
</div> </div>
<div class="product-details"> <div class="product-details">
<div class="product-price">£{{product.cost}}</div> <div class="product-price">£{{product.cost}}</div>
<div class="product-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation</div> <div class="product-description">{{product.description}}</div>
</div> </div>
</div> </div>
<div class="product-add-to-cart"></div> <div class="product-add-to-cart"></div>

View File

@ -14,12 +14,14 @@
<centre> <centre>
<div class="categories"> <div class="categories">
<a href="/products/CarParts" class="category">Car Parts</a> {# List all categories and ensure the selected one is highlighted #}
<a href="/products/Animals" class="category">Animals</a> {% for c in categories %}
<a href="/products/Sports" class="category">Sports</a> {% if category == c.name %}
<a href="/products/Books" class="category">Books</a> <a style="color: cyan" href="/products/{{c.name}}" class="category">{{c.name}}</a>
<a href="/products/Phones" class="category">Phones</a> {% else %}
<a href="/products/Music" class="category">Music</a> <a href="/products/{{c.name}}" class="category">{{c.name}}</a>
{% endif %}
{% endfor %}
</div> </div>
</centre> </centre>
</div> </div>

View File

@ -11,7 +11,14 @@
</head> </head>
<body> <body>
{% include 'header.html' %} {% include 'header.html' %}
<div class="container"> <div class="container">
{% if category is defined %}
{% set include_file = category+".html" %}
{% include include_file ignore missing %}
{% endif %}
{% include content %} {% include content %}
</div> </div>
</body> </body>