summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--app/.gitignore4
-rw-r--r--app/database.py0
-rw-r--r--app/forms.py24
-rw-r--r--app/model.py39
-rw-r--r--app/placeholders.py7
-rw-r--r--app/routes.py215
-rw-r--r--app/static/fonts/PublicSans-Black.otfbin0 -> 49640 bytes
-rw-r--r--app/static/fonts/PublicSans-BlackItalic.otfbin0 -> 51720 bytes
-rw-r--r--app/static/fonts/PublicSans-Bold.otfbin0 -> 56032 bytes
-rw-r--r--app/static/fonts/PublicSans-BoldItalic.otfbin0 -> 60100 bytes
-rw-r--r--app/static/fonts/PublicSans-ExtraBold.otfbin0 -> 56468 bytes
-rw-r--r--app/static/fonts/PublicSans-ExtraBoldItalic.otfbin0 -> 60340 bytes
-rw-r--r--app/static/fonts/PublicSans-ExtraLight.otfbin0 -> 56584 bytes
-rw-r--r--app/static/fonts/PublicSans-ExtraLightItalic.otfbin0 -> 60244 bytes
-rw-r--r--app/static/fonts/PublicSans-Italic.otfbin0 -> 60316 bytes
-rw-r--r--app/static/fonts/PublicSans-Light.otfbin0 -> 55120 bytes
-rw-r--r--app/static/fonts/PublicSans-LightItalic.otfbin0 -> 58588 bytes
-rw-r--r--app/static/fonts/PublicSans-Medium.otfbin0 -> 56180 bytes
-rw-r--r--app/static/fonts/PublicSans-MediumItalic.otfbin0 -> 59748 bytes
-rw-r--r--app/static/fonts/PublicSans-Regular.otfbin0 -> 56792 bytes
-rw-r--r--app/static/fonts/PublicSans-SemiBold.otfbin0 -> 56720 bytes
-rw-r--r--app/static/fonts/PublicSans-SemiBoldItalic.otfbin0 -> 60168 bytes
-rw-r--r--app/static/fonts/PublicSans-Thin.otfbin0 -> 50824 bytes
-rw-r--r--app/static/fonts/PublicSans-ThinItalic.otfbin0 -> 52780 bytes
-rw-r--r--app/static/styles/style.css79
-rw-r--r--app/templates/add_product.html44
-rw-r--r--app/templates/base.html30
-rw-r--r--app/templates/errors/module-not-found.html14
-rw-r--r--app/templates/home.html22
-rw-r--r--app/templates/modules/calculator.html8
-rw-r--r--app/templates/modules/catalog.html35
-rw-r--r--app/templates/modules/creator.html8
-rw-r--r--app/templates/modules/logger.html8
-rw-r--r--app/templates/modules/stock.html8
35 files changed, 549 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ed08861
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# .gitignore
+
+.venv/
+*~undo-tree~
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..1ac0ed9
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1,4 @@
+# .gitignore for app
+
+*__pycache__
+*.db \ No newline at end of file
diff --git a/app/database.py b/app/database.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/app/database.py
diff --git a/app/forms.py b/app/forms.py
new file mode 100644
index 0000000..daa394b
--- /dev/null
+++ b/app/forms.py
@@ -0,0 +1,24 @@
+from flask_wtf import FlaskForm
+from wtforms import StringField, PasswordField, SubmitField, BooleanField
+from wtforms.validators import DataRequired, Length, EqualTo
+
+
+class RegistrationForm(FlaskForm):
+ alias = StringField("Alias", validators=[DataRequired(), Length(min=2, max=20)])
+ password = PasswordField("Password", validators=[DataRequired()])
+ password_confirm = PasswordField(
+ "Confirm Password", validators=[DataRequired(), EqualTo("password")]
+ )
+ submit = SubmitField("Create Alias")
+
+
+class LoginForm(FlaskForm):
+ alias = StringField("Alias", validators=[DataRequired(), Length(min=2, max=20)])
+ password = PasswordField("Password", validators=[DataRequired()])
+ remember = BooleanField("Remember Alias")
+ submit = SubmitField("Login Alias")
+
+
+class NewMessage(FlaskForm):
+ recipient = StringField("Recipient", validators=[DataRequired()])
+ message = StringField("message", validators=[DataRequired()])
diff --git a/app/model.py b/app/model.py
new file mode 100644
index 0000000..fa4e00a
--- /dev/null
+++ b/app/model.py
@@ -0,0 +1,39 @@
+import SQAlchemy
+
+class Users(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name_first = db.Column(db.String(20), nullable=False)
+ name_last = db.Column(db.String(20), nullable=False)
+
+ def __repr__(self):
+ return f"<User {self.name_first} {self.name_last}>"
+
+
+class Projects(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(20), nullable=False)
+ name_full = db.Column(db.String(20), nullable=False)
+ nickname = db.Column(db.String(20), nullable=False)
+ city = db.Column(db.String(20), nullable=False)
+
+ def __repr__(self):
+ return f"<Project {self.name}>"
+
+
+class Modules(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(20), unique=True, nullable=False)
+ description = db.Column(db.String(50), nullable=False)
+
+ def __repr__(self):
+ return f"<Module {self.name}>"
+
+
+class Doobie:
+ def __init__(self, name, prices, quantity):
+ self.name = name
+ self.prices = prices
+ self.quantity = quantity
+
+ def __repr__(self):
+ return self.name
diff --git a/app/placeholders.py b/app/placeholders.py
new file mode 100644
index 0000000..a2360b4
--- /dev/null
+++ b/app/placeholders.py
@@ -0,0 +1,7 @@
+modules = [
+ "catalog",
+ "creator",
+ "logger",
+ "calculator",
+ "stock",
+]
diff --git a/app/routes.py b/app/routes.py
new file mode 100644
index 0000000..d2bd3f0
--- /dev/null
+++ b/app/routes.py
@@ -0,0 +1,215 @@
+"""
+routes.py module
+----------------
+
+This Python module contains the logic supporting:
+1. Navigating between website pages
+2. Interpreting user requests to the server
+3. Dispatching requested content back to the user
+
+Python dependencies:
+- flask: provides web application features
+- forms: provides secure user form submission
+- sqlalchemy: provides communication with database on server.
+
+Personal imports:
+These are used to avoid cluttering this file with
+placeholder data for posts' content.
+"""
+
+
+from flask import Flask, render_template, request, redirect, flash, url_for, jsonify
+from flask_sqlalchemy import SQLAlchemy
+from flask_bootstrap import Bootstrap
+
+from flask_wtf import FlaskForm
+from wtforms import (
+ SubmitField,
+ SelectField,
+ RadioField,
+ HiddenField,
+ StringField,
+ IntegerField,
+ FloatField,
+)
+from wtforms.validators import InputRequired, Length, Regexp, NumberRange
+from datetime import datetime
+
+import placeholders as p
+
+app = Flask(__name__)
+
+# Flask-Bootstrap requires this line
+Bootstrap(app)
+
+
+# Flask-WTF encryption key
+app.config["SECRET_KEY"] = "Scooby_Lu,_where_are_you?"
+
+# Our database name
+db_name = "fapg.db"
+app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///" + db_name
+app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
+db = SQLAlchemy(app)
+
+
+class Users(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name_first = db.Column(db.String(20), nullable=False)
+ name_last = db.Column(db.String(20), nullable=False)
+ email = db.Column(db.String(20), nullable=False)
+ phone_mobile = db.Column(db.Integer, nullable=False)
+ phone_alternative = db.Column(db.Integer)
+ updated = db.Column(db.String)
+
+ def __init__(
+ self, name_first, name_last, email, phone_mobile, phone_alternative, updated
+ ):
+ self.name_first = name_first
+ self.name_last = name_last
+ self.email = email
+ self.phone_mobile = phone_mobile
+ self.phone_alternative = phone_alternative
+ self.updated = updated
+
+ def __repr__(self):
+ return f"<User {self.name_first} {self.name_last}>"
+
+
+class Products(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(20), nullable=False)
+ supplier = db.Column(db.String(20), nullable=False)
+ price = db.Column(db.Float(10), nullable=False)
+ updated = db.Column(db.String)
+
+ def __init__(self, name, supplier, price, updated):
+ self.name = name
+ self.supplier = supplier
+ self.price = price
+ self.updated = updated
+
+ def __repr__(self):
+ return f"<Product {self.name} by {self.supplier}>"
+
+
+class Projects(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(20), nullable=False)
+ name_full = db.Column(db.String(20), nullable=False)
+ nickname = db.Column(db.String(20), nullable=False)
+ city = db.Column(db.String(20), nullable=False)
+
+ def __repr__(self):
+ return f"<Project {self.name}>"
+
+
+class Modules(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(20), unique=True, nullable=False)
+ description = db.Column(db.String(50), nullable=False)
+
+ def __init__(self, name, description, updated):
+ self.name = name
+ self.description = description
+ self.updated = updated
+
+ def __repr__(self):
+ return f"<Module {self.name}>"
+
+
+class AddProduct(FlaskForm):
+ # id used only by update/edit
+ id = HiddenField()
+ name = StringField("Product name", validators=[InputRequired()])
+ supplier = SelectField(
+ "Choose a supplier",
+ choices=[
+ ("", ""),
+ ("Mister Brown", "Mister Brown"),
+ ("Madame Cerise", "Madame Cerise"),
+ ("Biton la Malice", "G. Biton la Malice"),
+ ("Leroy Merlin", "Leroy Merlin"),
+ ("other", "Other"),
+ ],
+ )
+ price = FloatField("Retail price per unit")
+ # updated - date - handled in the route function
+ updated = HiddenField()
+ submit = SubmitField("Add/Update Product")
+
+
+# add a new product to the database
+@app.route("/add_product", methods=["GET", "POST"])
+def add_product():
+ form = AddProduct()
+ if form.validate_on_submit():
+ name = request.form["name"]
+ supplier = request.form["supplier"]
+ price = request.form["price"]
+ # get today's date from function, above all the routes
+ updated = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ # the data to be inserted into FAPG model - the table, products
+ record = Products(name, supplier, price, updated)
+ # Flask-SQLAlchemy magic adds record to database
+ db.session.add(record)
+ db.session.commit()
+ # create a message to send to the template
+ message = f"The data for product {name} has been submitted."
+ return render_template("add_product.html", message=message)
+ else:
+ # show validaton errors
+ # see https://pythonprogramming.net/flash-flask-tutorial/
+ for field, errors in form.errors.items():
+ for error in errors:
+ flash(
+ "Error in {}: {}".format(getattr(form, field).label.text, error),
+ "error",
+ )
+ return render_template("add_product.html", form=form)
+
+
+@app.route("/")
+@app.route("/fapg/home")
+def project():
+ """This is our project welcome page."""
+ michel = Users(
+ name_first="Michel",
+ name_last="Peter",
+ email="le-boss@fapg.com",
+ phone_mobile="00000000",
+ phone_alternative="0000000000",
+ updated="2022-04-21",
+ )
+ modules = Modules.query.all()
+ print(module.name for module in modules)
+ return render_template("home.html", user=michel, project=fapg, modules=modules)
+
+
+@app.route("/module/<module>")
+def render_module(module):
+ modules = Modules.query.all()
+ catalog = Products.query.all()
+ user_modules = [module.name for module in modules]
+ print(user_modules)
+ # If a module was purchased by a user and added to their database,
+ # they have access to the corresponding module route.
+ if module in user_modules:
+ return render_template(
+ f"modules/{module}.html",
+ modules=modules,
+ catalog=catalog,
+ )
+ else:
+ return render_template("errors/module-not-found.html", module=module)
+
+
+# If this file is executed as a script (i.e. double-clicked),
+# the Python interpreter will run the Flask process and begin serving
+# the web pages on the standard localhost address (127.0.0.1).
+# But if this file is called as a module by another Python script, it will not
+# serve content to the web pages, but the function definitions contained in
+# this file will be available to the calling script.
+# E.g. calling script will know what the yes() function is.
+if __name__ == "__main__":
+ app.run(debug=True)
diff --git a/app/static/fonts/PublicSans-Black.otf b/app/static/fonts/PublicSans-Black.otf
new file mode 100644
index 0000000..bbbaa26
--- /dev/null
+++ b/app/static/fonts/PublicSans-Black.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-BlackItalic.otf b/app/static/fonts/PublicSans-BlackItalic.otf
new file mode 100644
index 0000000..46e3f71
--- /dev/null
+++ b/app/static/fonts/PublicSans-BlackItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-Bold.otf b/app/static/fonts/PublicSans-Bold.otf
new file mode 100644
index 0000000..7a2b62b
--- /dev/null
+++ b/app/static/fonts/PublicSans-Bold.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-BoldItalic.otf b/app/static/fonts/PublicSans-BoldItalic.otf
new file mode 100644
index 0000000..718357f
--- /dev/null
+++ b/app/static/fonts/PublicSans-BoldItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-ExtraBold.otf b/app/static/fonts/PublicSans-ExtraBold.otf
new file mode 100644
index 0000000..09b52dd
--- /dev/null
+++ b/app/static/fonts/PublicSans-ExtraBold.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-ExtraBoldItalic.otf b/app/static/fonts/PublicSans-ExtraBoldItalic.otf
new file mode 100644
index 0000000..5b33222
--- /dev/null
+++ b/app/static/fonts/PublicSans-ExtraBoldItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-ExtraLight.otf b/app/static/fonts/PublicSans-ExtraLight.otf
new file mode 100644
index 0000000..49b407d
--- /dev/null
+++ b/app/static/fonts/PublicSans-ExtraLight.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-ExtraLightItalic.otf b/app/static/fonts/PublicSans-ExtraLightItalic.otf
new file mode 100644
index 0000000..c76e855
--- /dev/null
+++ b/app/static/fonts/PublicSans-ExtraLightItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-Italic.otf b/app/static/fonts/PublicSans-Italic.otf
new file mode 100644
index 0000000..38996ad
--- /dev/null
+++ b/app/static/fonts/PublicSans-Italic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-Light.otf b/app/static/fonts/PublicSans-Light.otf
new file mode 100644
index 0000000..126544e
--- /dev/null
+++ b/app/static/fonts/PublicSans-Light.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-LightItalic.otf b/app/static/fonts/PublicSans-LightItalic.otf
new file mode 100644
index 0000000..1e6aa6f
--- /dev/null
+++ b/app/static/fonts/PublicSans-LightItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-Medium.otf b/app/static/fonts/PublicSans-Medium.otf
new file mode 100644
index 0000000..93507a5
--- /dev/null
+++ b/app/static/fonts/PublicSans-Medium.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-MediumItalic.otf b/app/static/fonts/PublicSans-MediumItalic.otf
new file mode 100644
index 0000000..f5ddb90
--- /dev/null
+++ b/app/static/fonts/PublicSans-MediumItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-Regular.otf b/app/static/fonts/PublicSans-Regular.otf
new file mode 100644
index 0000000..d2b3f16
--- /dev/null
+++ b/app/static/fonts/PublicSans-Regular.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-SemiBold.otf b/app/static/fonts/PublicSans-SemiBold.otf
new file mode 100644
index 0000000..4ab6b89
--- /dev/null
+++ b/app/static/fonts/PublicSans-SemiBold.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-SemiBoldItalic.otf b/app/static/fonts/PublicSans-SemiBoldItalic.otf
new file mode 100644
index 0000000..a28f6c0
--- /dev/null
+++ b/app/static/fonts/PublicSans-SemiBoldItalic.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-Thin.otf b/app/static/fonts/PublicSans-Thin.otf
new file mode 100644
index 0000000..dee0ae2
--- /dev/null
+++ b/app/static/fonts/PublicSans-Thin.otf
Binary files differ
diff --git a/app/static/fonts/PublicSans-ThinItalic.otf b/app/static/fonts/PublicSans-ThinItalic.otf
new file mode 100644
index 0000000..c6b481a
--- /dev/null
+++ b/app/static/fonts/PublicSans-ThinItalic.otf
Binary files differ
diff --git a/app/static/styles/style.css b/app/static/styles/style.css
new file mode 100644
index 0000000..ca60a7b
--- /dev/null
+++ b/app/static/styles/style.css
@@ -0,0 +1,79 @@
+:root {
+ --primary-color: #003B5C;
+ --secondary-color: #C3D7EE;
+ --home: #c8c8c8;
+ --yes: #80FF80;
+ --no: #FF8080;
+ font-size: 18;
+ --fast-speed: 0.2s;
+ --med-speed: 0.4s;
+ --slow-speed: 1s;
+}
+
+body {
+ font-family: 'Public Sans', sans-serif;
+ line-height: 1.2;
+ margin: 0;
+ padding: 0;
+}
+
+/* h1 { */
+/* margin-left: 2rem; */
+/* margin-right: 1rem; */
+/* } */
+
+/* h2 { */
+/* margin-left: 1rem; */
+/* margin-right: 1rem; */
+/* } */
+
+/* p { */
+/* margin-left: 1rem; */
+/* margin-right: 1rem; */
+/* } */
+
+
+@font-face {
+ font-family: "Public Sans";
+ src: url("/static/fonts/PublicSans-Regular.otf");
+}
+
+nav {
+ display: flex;
+ background: darkgrey;
+ color: white;
+ margin: 0.5em;
+ padding: 0.5em;
+}
+
+h1 {
+ margin: 0;
+}
+
+nav ul {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0;
+ justify-content: right;
+ list-style: none;
+}
+
+nav ul li {
+ margin: 0 0 0.5em 0;
+ padding: 0.5em 0;
+}
+
+.button {
+ height: 1em;
+ padding: 0.5em;
+ margin: 0 0.5em;
+ background: dimgray;
+ color: white;
+ border-radius: 8px;
+ text-decoration: none;
+}
+
+.button:hover {
+ background: white;
+ color: black;
+}
diff --git a/app/templates/add_product.html b/app/templates/add_product.html
new file mode 100644
index 0000000..ad7ea0a
--- /dev/null
+++ b/app/templates/add_product.html
@@ -0,0 +1,44 @@
+{# -*- mode: jinja2; -*- #}
+
+{% extends "base.html" %}
+{% import "bootstrap/wtf.html" as wtf %}
+
+{% block content %}
+
+ {% block title %}Add a New Product{% endblock %}
+
+ {% if message %}
+
+ {# the form was submitted and message exists #}
+ <p class="lead"><strong>{{ message }}</strong></p>
+ {# links #}
+ <p><a href="{{ url_for('add_product') }}" class="button">Submit another product.</a></p>
+<p><a href="/fapg/home">Return to the index.</a></p>
+
+{% else %}
+
+ {# the form is displayed when template opens via GET not POST #}
+ <p class="lead alert alert-primary">Add a new sock to our inventory.</p>
+<p class="ml-4"><a href="/fapg/home" class="button">Return to the index.</a></p>
+{# show flash - based on WTForms validators
+see https://pythonprogramming.net/flash-flask-tutorial/
+get_flashed_messages() exists here because of flash()
+in the route function
+#}
+{% with errors = get_flashed_messages() %}
+ {% if errors %}
+ {% for err in errors %}
+ <div class="alert alert-danger alert-dismissible" role="alert">
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
+ {{ err }}
+ </div>
+ {% endfor %}
+ {% endif %}
+{% endwith %}
+{# end of flash #}
+
+{# the form, thanks to WTForms #}
+{{ wtf.quick_form(form) }}
+
+{% endif %}
+{% endblock %}
diff --git a/app/templates/base.html b/app/templates/base.html
new file mode 100644
index 0000000..0b53b5c
--- /dev/null
+++ b/app/templates/base.html
@@ -0,0 +1,30 @@
+{# -*- mode: jinja2; -*- #}
+
+<!doctype html>
+
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to">
+ <title>Farm Manager</title>
+ <meta name="author" content="Marius Peter">
+ <meta name="description" content="A draft page for Farm Manager.">
+ <!-- <link rel="icon" href="/favicon.ico"> -->
+ <link rel="stylesheet" href="{{ url_for('static', filename='styles/style.css') }}">
+ </head>
+ <body>
+ <nav>
+ <a href="/" class="button">Home</a>
+ <h1>{% block title %}{% endblock %}</h1>
+ <ul>
+ {% for module in modules %}
+ <li><a href="/module/{{ module.name }}" class="button">{{ module.name }}</a></li>
+ {% endfor %}
+ </ul>
+ </nav>
+ <div id="content">
+ {% block content %}{% endblock %}
+ </div>
+ <!-- <script src="js/scripts.js"></script> -->
+ </body>
+</html>
diff --git a/app/templates/errors/module-not-found.html b/app/templates/errors/module-not-found.html
new file mode 100644
index 0000000..6496503
--- /dev/null
+++ b/app/templates/errors/module-not-found.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block title %}
+Module not found
+{% endblock %}
+
+{% block content %}
+<p>
+ No module found with name <strong>{{ module }}</strong>.
+</p>
+<p>
+ If you'd like to suggest a module, please send us an e-mail
+</p>
+{% endblock %}
diff --git a/app/templates/home.html b/app/templates/home.html
new file mode 100644
index 0000000..a9bac51
--- /dev/null
+++ b/app/templates/home.html
@@ -0,0 +1,22 @@
+<!-- -*- mode: jinja2; -*- -->
+
+{% extends "base.html" %}
+
+{% block title %}
+ Welcome, {{ user.name_first }}!
+{% endblock %}
+
+
+{% block content %}
+
+ <p>You are logged in as <strong>{{ user }}</strong> on
+ project <strong>{{ project }}</strong>.</p>
+
+ <h2>Available modules</h2>
+ <dl>
+ {% for module in modules %}
+ <dt style="font-weight: bold">{{ module.name }}</dt><dd>{{ module.description }}<dd>
+ {% endfor %}
+ </dl>
+
+{% endblock %}
diff --git a/app/templates/modules/calculator.html b/app/templates/modules/calculator.html
new file mode 100644
index 0000000..f951a17
--- /dev/null
+++ b/app/templates/modules/calculator.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% block title %}
+Calculator
+{% endblock %}
+
+{% block content %}
+<i>insert module content here.</i>
+{% endblock %}
diff --git a/app/templates/modules/catalog.html b/app/templates/modules/catalog.html
new file mode 100644
index 0000000..857c654
--- /dev/null
+++ b/app/templates/modules/catalog.html
@@ -0,0 +1,35 @@
+<!-- -*- mode: jinja2; -*- -->
+
+{% extends "base.html" %}
+{% block title %}
+ Catalog
+{% endblock %}
+
+{% block content %}
+ <i>insert module content here.</i>
+
+ <p>
+ <a href="{{ url_for('add_product') }}" class="button">Add product to your catalog</a>
+ </p>
+
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Price</th>
+ <th>Supplier</th>
+ <th>Updated</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for product in catalog %}
+ <tr>
+ <td>{{ product.name }}</td>
+ <td>{{ product.price }}</td>
+ <td>{{ product.supplier }}</td>
+ <td>{{ product.updated }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+{% endblock %}
diff --git a/app/templates/modules/creator.html b/app/templates/modules/creator.html
new file mode 100644
index 0000000..588f428
--- /dev/null
+++ b/app/templates/modules/creator.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% block title %}
+Creator
+{% endblock %}
+
+{% block content %}
+<i>insert module content here.</i>
+{% endblock %}
diff --git a/app/templates/modules/logger.html b/app/templates/modules/logger.html
new file mode 100644
index 0000000..7ba93b3
--- /dev/null
+++ b/app/templates/modules/logger.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% block title %}
+Logger
+{% endblock %}
+
+{% block content %}
+<i>insert module content here.</i>
+{% endblock %}
diff --git a/app/templates/modules/stock.html b/app/templates/modules/stock.html
new file mode 100644
index 0000000..5d20d5c
--- /dev/null
+++ b/app/templates/modules/stock.html
@@ -0,0 +1,8 @@
+{% extends "base.html" %}
+{% block title %}
+Stock
+{% endblock %}
+
+{% block content %}
+<i>insert module content here.</i>
+{% endblock %}
Copyright 2019--2024 Marius PETER