Release 13 (devel -> main)

See merge request W13R/drinks-manager!11
This commit is contained in:
Julian Müller 2022-11-05 09:54:19 +00:00
commit fcf2f7b32e
40 changed files with 685 additions and 466 deletions

4
.gitignore vendored
View file

@ -4,6 +4,7 @@
/archive/* /archive/*
/logs/* /logs/*
/packages/* /packages/*
/profilepictures/*
/temp /temp
/tmp /tmp
__pycache__ __pycache__
@ -12,4 +13,5 @@ __pycache__
!/config/config.sample.sh !/config/config.sample.sh
!/config/Caddyfile !/config/Caddyfile
!/config/tls/ !/config/tls/
!.gitkeep !/profilepictures/default.svg
!.gitkeep

View file

@ -26,9 +26,6 @@ class CustomAdminSite(admin.AdminSite):
def index(self, request, extra_context=None): def index(self, request, extra_context=None):
return super().index(request, extra_context={ return super().index(request, extra_context={
"registerBalance": "{:10.2f}".format(
Global.objects.get(name="register_balance").value_float
),
"admin_info": Global.objects.get(name="admin_info").value_string, "admin_info": Global.objects.get(name="admin_info").value_string,
**(extra_context or {}) **(extra_context or {})
}) })
@ -49,6 +46,10 @@ class CustomUserAdmin(UserAdmin):
{"fields": ("balance", "allow_order_with_negative_balance")}, {"fields": ("balance", "allow_order_with_negative_balance")},
)) ))
fieldsets_.insert(2, ( fieldsets_.insert(2, (
"Supply",
{"fields": ("allowed_to_supply",)},
))
fieldsets_.insert(3, (
"Profile Picture", "Profile Picture",
{"fields": ("profile_picture_filename",)}, {"fields": ("profile_picture_filename",)},
)) ))

View file

@ -7,13 +7,6 @@ from django.forms import ValidationError
from django.utils import timezone from django.utils import timezone
# helper
def make_register_transaction(transaction_sum:float):
regbalance = Global.objects.get(name="register_balance")
regbalance.value_float += float(round(float(transaction_sum), 2))
regbalance.save()
# Custom user model # Custom user model
class User(AbstractUser): class User(AbstractUser):
@ -21,6 +14,7 @@ class User(AbstractUser):
balance = models.DecimalField(max_digits=8, decimal_places=2, default=0.00) balance = models.DecimalField(max_digits=8, decimal_places=2, default=0.00)
allow_order_with_negative_balance = models.BooleanField(default=False) allow_order_with_negative_balance = models.BooleanField(default=False)
profile_picture_filename = models.CharField(default="default.svg", max_length=25) profile_picture_filename = models.CharField(default="default.svg", max_length=25)
allowed_to_supply = models.BooleanField(default=False)
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
self.balance = 0 self.balance = 0
@ -58,9 +52,9 @@ class Drink(models.Model):
class RegisterTransaction(models.Model): class RegisterTransaction(models.Model):
class Meta: class Meta:
verbose_name = "register transaction" verbose_name = "transaction"
verbose_name_plural = "register" verbose_name_plural = "transactions"
transaction_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0.00) transaction_sum = models.DecimalField(max_digits=6, decimal_places=2, default=0.00)
# the following original_transaction_sum is needed when need to be # the following original_transaction_sum is needed when need to be
# updated, but the old value needs to be known (field is hidden) # updated, but the old value needs to be known (field is hidden)
@ -72,7 +66,6 @@ class RegisterTransaction(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self._state.adding: if self._state.adding:
make_register_transaction(self.transaction_sum)
if self.is_user_deposit == True: # update user balance if self.is_user_deposit == True: # update user balance
self.user.balance += self.transaction_sum self.user.balance += self.transaction_sum
self.user.save() self.user.save()
@ -81,7 +74,6 @@ class RegisterTransaction(models.Model):
else: else:
# update register transaction # update register transaction
sum_diff = self.transaction_sum - self.old_transaction_sum sum_diff = self.transaction_sum - self.old_transaction_sum
make_register_transaction(sum_diff)
# update user balance # update user balance
if self.is_user_deposit == True: if self.is_user_deposit == True:
ub_sum_diff = self.transaction_sum - self.old_transaction_sum ub_sum_diff = self.transaction_sum - self.old_transaction_sum
@ -91,7 +83,6 @@ class RegisterTransaction(models.Model):
super().save(*args, **kwargs) super().save(*args, **kwargs)
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
make_register_transaction(-self.transaction_sum)
# update user deposit # update user deposit
if self.is_user_deposit: if self.is_user_deposit:
self.user.balance -= self.transaction_sum self.user.balance -= self.transaction_sum

View file

@ -6,7 +6,6 @@
<div> <div>
<div> <div>
<p>Current Register Balance: {{ registerBalance }}{{ currency_suffix }}</p>
{% if global_message != "" %} {% if global_message != "" %}
<p>Global Message: {{ global_message }}</p> <p>Global Message: {{ global_message }}</p>
{% endif %} {% endif %}

View file

@ -16,13 +16,13 @@
<body> <body>
<div class="baseLayout"> <div class="baselayout">
{% include "globalMessage.html" %} {% include "globalmessage.html" %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% include "userPanel.html" %} {% include "userpanel.html" %}
{% endif %} {% endif %}
@ -30,14 +30,13 @@
{% if user.is_authenticated or "accounts/login/" in request.path or "accounts/logout/" in request.path or "admin/logout/" in request.path %} {% if user.is_authenticated or "accounts/login/" in request.path or "accounts/logout/" in request.path or "admin/logout/" in request.path %}
<h1>{% block heading %}{% endblock %}</h1>
<div class="content"> <div class="content">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
{% else %} {% else %}
<div class="centeringFlex"> <div class="centeringflex">
{% translate "An error occured. Please log out and log in again." %} {% translate "An error occured. Please log out and log in again." %}
<br> <br>
<a href="/accounts/logout">log out</a> <a href="/accounts/logout">log out</a>

View file

@ -1,4 +1,4 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
@ -7,29 +7,29 @@
{% endblock %} {% endblock %}
{% block headAdditional %} {% block headAdditional %}
<link rel="stylesheet" href="/static/css/deposit.css"> <link rel="stylesheet" href="/static/css/appform.css">
{% endblock %} {% endblock %}
{% block heading %}
{% translate "Deposit" %}
{% endblock %}
{% block content %} {% block content %}
<form id="depositForm"> <form id="depositform" class="appform">
{% csrf_token %} {% csrf_token %}
<div class="row"> <h1 class="formheading">{% translate "Deposit" %}</h1>
<div class="column">{% translate "Amount" %} {{ currency_suffix }}:</div>
<div class="column"><input type="number" name="depositAmount" id="depositAmount" max="9999.99" min="1.00" <div class="forminput">
step="0.01" autofocus></div> <span>{% translate "Amount" %} {{ currency_suffix }}:</span>
<span>
<input type="number" name="depositamount" id="depositamount" max="9999.99" min="1.00" step="0.01" autofocus>
</span>
</div> </div>
<div id="statusInfo"></div> <div id="statusinfo"></div>
<div class="horizontalButtonList"> <div class="formbuttons">
<a href="/" class="button">{% translate "cancel" %}</a> <a href="/" class="button">{% translate "cancel" %}</a>
<input type="submit" id="depositSubmitBtn" class="button" value='{% translate "confirm" %}'> <input type="submit" id="depositsubmitbtn" class="button" value='{% translate "confirm" %}'>
</div> </div>
</form> </form>

View file

@ -1,5 +1,5 @@
{% if global_message != "" %} {% if global_message != "" %}
<div class="globalMessage"> <div class="globalmessage">
<div>{{ global_message }}</div> <div>{{ global_message }}</div>
</div> </div>
{% endif %} {% endif %}

View file

@ -1,4 +1,4 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
@ -10,12 +10,11 @@
<link rel="stylesheet" href="/static/css/history.css"> <link rel="stylesheet" href="/static/css/history.css">
{% endblock %} {% endblock %}
{% block heading %}
{% translate "History" %}
{% endblock %}
{% block content %} {% block content %}
<h1 class="heading">{% translate "History" %}</h1>
{% if history %} {% if history %}
<table class="history"> <table class="history">
<tr> <tr>
@ -25,7 +24,7 @@
{% for h in history %} {% for h in history %}
<tr> <tr>
<td>{{ h.0 }}</td> <td>{{ h.0 }}</td>
<td class="historyDate">{{ h.1 }}</td> <td class="historydate">{{ h.1 }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View file

@ -1,4 +1,4 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
@ -10,15 +10,13 @@
<link rel="stylesheet" href="/static/css/index.css"> <link rel="stylesheet" href="/static/css/index.css">
{% endblock %} {% endblock %}
{% block heading %}
{% translate "Available Drinks" %}
{% endblock %}
{% block content %} {% block content %}
<h1 class="heading">{% translate "Available Drinks" %}</h1>
{% if available_drinks %} {% if available_drinks %}
<ul class="availableDrinksList"> <ul class="availabledrinkslist">
{% for drink in available_drinks %} {% for drink in available_drinks %}
{% if drink.do_not_count %} {% if drink.do_not_count %}
<li> <li>

View file

@ -1,4 +1,4 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
{% load l10n %} {% load l10n %}
@ -8,13 +8,10 @@
{% endblock %} {% endblock %}
{% block headAdditional %} {% block headAdditional %}
<link rel="stylesheet" href="/static/css/order.css"> <link rel="stylesheet" href="/static/css/appform.css">
<link rel="stylesheet" href="/static/css/customNumberInput.css"> <link rel="stylesheet" href="/static/css/custom_number_input.css">
{% endblock %} {% endblock %}
{% block heading %}
{% translate "Order" %}
{% endblock %}
{% block content %} {% block content %}
@ -22,66 +19,67 @@
{% if user.balance > 0 or user.allow_order_with_negative_balance %} {% if user.balance > 0 or user.allow_order_with_negative_balance %}
<form id="orderForm"> <form id="orderform" class="appform">
{% csrf_token %} {% csrf_token %}
<div class="row"> <h1 class="formheading">{% translate "Order" %}</h1>
<div class="column">{% translate "Drink" %}:</div>
<div class="column">{{ drink.product_name }}</div>
</div>
<div class="row"> <div class="forminfo">
<div class="column">{% translate "Price per Item" %} ({{ currency_suffix }}):</div> <span>{% translate "Drink" %}:</span>
<div class="column" id="pricePerDrink" data-drink-price="{% localize off %}{{ drink.price }}{% endlocalize %}">{{ drink.price }}</div> <span>{{ drink.product_name }}</span>
</div>
<div class="forminfo">
<span>{% translate "Price per Item" %} ({{ currency_suffix }}):</span>
<span id="priceperdrink" data-drink-price="{% localize off %}{{ drink.price }}{% endlocalize %}">
{{ drink.price }}
</span>
</div> </div>
{% if not drink.do_not_count %} {% if not drink.do_not_count %}
<div class="row"> <div class="forminfo">
<div class="column">{% translate "Available" %}:</div> <span>{% translate "Available" %}:</span>
<div class="column">{{ drink.available }}</div> <span>{{ drink.available }}</span>
</div> </div>
{% endif %} {% endif %}
<div class="row"> <div class="forminfo">
<div class="column">{% translate "Count" %}:</div> <span>{% translate "Sum" %} ({{ currency_suffix }}):</span>
<div class="column"> <span id="ordercalculatedsum">{{ drink.price }}</span>
<span class="customNumberInput">
<button type="button" class="customNumberInput-minus" id="numberOfDrinksBtnA">-</button>
{% if drink.do_not_count %}
<input type="number" class="customNumberInputField" name="numberOfDrinks" id="numberOfDrinks"
min="1" max="100" value="1">
{% else %}
<input type="number" class="customNumberInputField" name="numberOfDrinks" id="numberOfDrinks"
max="{{ drink.available }}" min="1" max="100" value="1">
{% endif %}
<button type="button" class="customNumberInput-plus" id="numberOfDrinksBtnB">+</button>
</span>
</div>
</div> </div>
<div class="row"> <div class="forminput">
<div class="column">{% translate "Sum" %} ({{ currency_suffix }}):</div> <span>{% translate "Count" %}:</span>
<div class="column" id="orderCalculatedSum">{{ drink.price }}</div> <span class="customnumberinput">
<button type="button" class="customnumberinput-minus" id="numberofdrinks-btn-minus">-</button>
{% if drink.do_not_count %}
<input type="number" class="customnumberinput-field" name="numberofdrinks" id="numberofdrinks"
min="1" max="100" value="1">
{% else %}
<input type="number" class="customnumberinput-field" name="numberofdrinks" id="numberofdrinks"
max="{{ drink.available }}" min="1" max="100" value="1">
{% endif %}
<button type="button" class="customnumberinput-plus" id="numberofdrinks-btn-plus">+</button>
</span>
</div> </div>
<div id="statusInfo"></div>
<input type="hidden" name="drinkID" id="drinkID" value="{{ drink.id }}"> <div id="statusinfo"></div>
<div class="horizontalButtonList"> <input type="hidden" name="drinkid" id="drinkid" value="{{ drink.id }}">
<div class="formbuttons">
<a href="/" class="button">{% translate "cancel" %}</a> <a href="/" class="button">{% translate "cancel" %}</a>
<input type="submit" id="orderSubmitBtn" class="button" value='{% translate "order" %}'> <input type="submit" id="ordersubmitbtn" class="button" value='{% translate "order" %}'>
</div> </div>
</form> </form>
<script src="/static/js/order.js"></script> <script src="/static/js/order.js"></script>
<script src="/static/js/customNumberInput.js"></script> <script src="/static/js/custom_number_input.js"></script>
{% else %} {% else %}
<div class="centeringFlex"> <div class="centeringflex">
<p>{% translate "Your balance is too low to order a drink." %}</p> <p>{% translate "Your balance is too low to order a drink." %}</p>
<a href="/">{% translate "back" %}</a> <a href="/">{% translate "back" %}</a>
</div> </div>
@ -90,7 +88,7 @@
{% else %} {% else %}
<div class="centeringFlex"> <div class="centeringflex">
<p>{% translate "This drink is not available." %}</p> <p>{% translate "This drink is not available." %}</p>
<a href="/">{% translate "back" %}</a> <a href="/">{% translate "back" %}</a>
</div> </div>

View file

@ -1,5 +1,5 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
@ -13,7 +13,7 @@
{% block content %} {% block content %}
<div class="centeringFlex"> <div class="centeringflex">
{% translate "Logged out! You will be redirected shortly." %} {% translate "Logged out! You will be redirected shortly." %}
<br><br> <br><br>
<a href="/">{% translate "Click here if automatic redirection does not work." %}</a> <a href="/">{% translate "Click here if automatic redirection does not work." %}</a>

View file

@ -1,5 +1,5 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
@ -15,14 +15,14 @@
{% block content %} {% block content %}
{% if error_message %} {% if error_message %}
<p class="errorText">{{ error_message }}</p> <p class="errortext">{{ error_message }}</p>
{% endif %} {% endif %}
<div class="passwordOverlayContainer nodisplay" id="passwordOverlayContainer"> <div class="passwordoverlaycontainer nodisplay" id="passwordoverlaycontainer">
<div class="passwordOverlay"> <div class="passwordoverlay">
<form method="post" action="{% url 'login' %}"> <form method="post" action="{% url 'login' %}" class="loginform">
{% csrf_token %} {% csrf_token %}
<h1>{% translate "Log in" %}</h1> <h1>{% translate "Log in" %}</h1>
<input type="text" name="username" autofocus="" autocapitalize="none" autocomplete="username" maxlength="150" required="" id="id_username"> <input type="text" name="username" autofocus="" autocapitalize="none" autocomplete="username" maxlength="150" required="" id="id_username">
@ -31,30 +31,30 @@
<div class="pinpad"> <div class="pinpad">
<table> <table>
<tr> <tr>
<td><button type="button" class="pinpadBtn" data-btn="1">1</button></td> <td><button type="button" class="pinpadbtn" data-btn="1">1</button></td>
<td><button type="button" class="pinpadBtn" data-btn="2">2</button></td> <td><button type="button" class="pinpadbtn" data-btn="2">2</button></td>
<td><button type="button" class="pinpadBtn" data-btn="3">3</button></td> <td><button type="button" class="pinpadbtn" data-btn="3">3</button></td>
</tr> </tr>
<tr> <tr>
<td><button type="button" class="pinpadBtn" data-btn="4">4</button></td> <td><button type="button" class="pinpadbtn" data-btn="4">4</button></td>
<td><button type="button" class="pinpadBtn" data-btn="5">5</button></td> <td><button type="button" class="pinpadbtn" data-btn="5">5</button></td>
<td><button type="button" class="pinpadBtn" data-btn="6">6</button></td> <td><button type="button" class="pinpadbtn" data-btn="6">6</button></td>
</tr> </tr>
<tr> <tr>
<td><button type="button" class="pinpadBtn" data-btn="7">7</button></td> <td><button type="button" class="pinpadbtn" data-btn="7">7</button></td>
<td><button type="button" class="pinpadBtn" data-btn="8">8</button></td> <td><button type="button" class="pinpadbtn" data-btn="8">8</button></td>
<td><button type="button" class="pinpadBtn" data-btn="9">9</button></td> <td><button type="button" class="pinpadbtn" data-btn="9">9</button></td>
</tr> </tr>
<tr> <tr>
<td><button type="button" class="pinpadBtn" data-btn="x">x</button></td> <td><button type="button" class="pinpadbtn" data-btn="x">x</button></td>
<td><button type="button" class="pinpadBtn" data-btn="0">0</button></td> <td><button type="button" class="pinpadbtn" data-btn="0">0</button></td>
<td><button type="button" class="pinpadBtn" data-btn="enter">&#9166;</button></td> <td><button type="button" class="pinpadbtn" data-btn="enter">&#9166;</button></td>
</tr> </tr>
</table> </table>
</div> </div>
<div class="horizontalButtonList"> <div class="horizontalbuttonlist">
<button type="button" id="pwoCancel">{% translate "cancel" %}</button> <button type="button" id="pwocancel">{% translate "cancel" %}</button>
<input class="button" id="submit_login" type="submit" value='{% translate "login" %}' /> <input class="button" id="submit_login" type="submit" value='{% translate "login" %}' />
</div> </div>
@ -65,11 +65,11 @@
<h1>{% translate "Choose your account" %}</h1> <h1>{% translate "Choose your account" %}</h1>
<div class="userlistContainer" id="userlistContainer"> <div class="userlistcontainer" id="userlistcontainer">
<ul class="userlist"> <ul class="userlist">
{% for user_ in user_list %} {% for user_ in user_list %}
<li class="userlistButton button" data-username="{{ user_.username }}"> <li class="userlistbutton button" data-username="{{ user_.username }}">
<img src="{% static 'profilepictures/'|add:user_.profile_picture_filename %}"> <img src="/profilepictures?name={{ user_.profile_picture_filename|urlencode }}">
<div> <div>
{% if user_.first_name %} {% if user_.first_name %}

View file

@ -1,4 +1,4 @@
{% extends "baseLayout.html" %} {% extends "baselayout.html" %}
{% load i18n %} {% load i18n %}
@ -11,18 +11,15 @@
{% endblock %} {% endblock %}
{% block heading %}
{% translate "Statistics" %}
{% endblock %}
{% block content %} {% block content %}
<div class="mainContainer"> <h1 class="heading">{% translate "Statistics" %}</h1>
<div class="tablesContainer"> <div class="maincontainer">
<div id="noyopd" class="statisticsTable"> <div class="tablescontainer">
<div id="noyopd" class="statisticstable">
<h1>{% translate "Your orders per drink" %}</h1> <h1>{% translate "Your orders per drink" %}</h1>
{% if noyopd %} {% if noyopd %}
<table> <table>
@ -42,7 +39,7 @@
{% endif %} {% endif %}
</div> </div>
<div id="noaopd" class="statisticsTable"> <div id="noaopd" class="statisticstable">
<h1>{% translate "All orders per drink" %}</h1> <h1>{% translate "All orders per drink" %}</h1>
{% if noaopd %} {% if noaopd %}
<table> <table>
@ -62,7 +59,7 @@
{% endif %} {% endif %}
</div> </div>
<div id="yopml12m" class="statisticsTable"> <div id="yopml12m" class="statisticstable">
<h1>{% translate "Your orders per month (last 12 months)" %}</h1> <h1>{% translate "Your orders per month (last 12 months)" %}</h1>
{% if yopml12m %} {% if yopml12m %}
<table> <table>
@ -82,7 +79,7 @@
{% endif %} {% endif %}
</div> </div>
<div id="aopml12m" class="statisticsTable"> <div id="aopml12m" class="statisticstable">
<h1>{% translate "All orders per month (last 12 months)" %}</h1> <h1>{% translate "All orders per month (last 12 months)" %}</h1>
{% if aopml12m %} {% if aopml12m %}
<table> <table>
@ -102,7 +99,7 @@
{% endif %} {% endif %}
</div> </div>
<div id="yopwd" class="statisticsTable"> <div id="yopwd" class="statisticstable">
<h1>{% translate "Your orders per weekday" %}</h1> <h1>{% translate "Your orders per weekday" %}</h1>
{% if yopwd %} {% if yopwd %}
<table> <table>
@ -122,7 +119,7 @@
{% endif %} {% endif %}
</div> </div>
<div id="aopwd" class="statisticsTable"> <div id="aopwd" class="statisticstable">
<h1>{% translate "All orders per weekday" %}</h1> <h1>{% translate "All orders per weekday" %}</h1>
{% if aopwd %} {% if aopwd %}
<table> <table>

View file

@ -0,0 +1,62 @@
{% extends "baselayout.html" %}
{% load i18n %}
{% load l10n %}
{% block title %}
{% translate "Drinks - Supply" %}
{% endblock %}
{% block headAdditional %}
<link rel="stylesheet" href="/static/css/appform.css">
<link rel="stylesheet" href="/static/css/custom_number_input.css">
{% endblock %}
{% block content %}
{% if user.is_superuser or user.allowed_to_supply %}
<form id="supplyform" class="appform">
{% csrf_token %}
<h1 class="formheading">{% translate "Supply" %}</h1>
<div class="forminput">
<span>{% translate "Description" %}:</span>
<span>
<input type="text" name="supplydescription" id="supplydescription" autofocus>
</span>
</div>
<div class="forminput">
<span>{% translate "Price" %} ({{ currency_suffix }}):</span>
<span>
<input type="number" name="supplyprice" id="supplyprice" max="9999.99" min="1.00" step="0.01">
</span>
</div>
<div id="statusinfo"></div>
<div class="formbuttons">
<a href="/" class="button">{% translate "cancel" %}</a>
<input type="submit" id="supplysubmitbtn" class="button" value='{% translate "submit" %}'>
</div>
</form>
<script src="/static/js/supply.js"></script>
<script src="/static/js/custom_number_input.js"></script>
{% else %}
<div class="centeringflex">
<p>{% translate "You are not allowed to view this site." %}</p>
<a href="/">{% translate "back" %}</a>
</div>
{% endif %}
<script src="/static/js/autoreload.js"></script>
{% endblock %}

View file

@ -1,39 +0,0 @@
{% load i18n %}
{% load static %}
<div class="userPanel">
<div class="userInfo">
<img src="{% static 'profilepictures/'|add:user.profile_picture_filename %}">
<span>
{% if user.first_name != "" %}
{% translate "User" %}: {{ user.first_name }} {{ user.last_name }} ({{ user.username }})
{% else %}
{% translate "User" %}: {{ user.username }}
{% endif %}
&nbsp;-&nbsp;
{% if user.balance < 0.01 %}
<span class="userBalanceWarn">{% translate "Balance" %}: {{ user.balance }}{{ currency_suffix }}</span>
{% else %}
<span>{% translate "Balance" %}: {{ user.balance }}{{ currency_suffix }}</span>
{% endif %}
</span>
</div>
<div class="horizontalButtonList">
<a class="button" id="navBarBtnHome" href="/">Home</a>
<a class="button" id="navBarBtnDeposit" href="/deposit">{% translate "Deposit" %}</a>
<a class="button" href="/accounts/logout">{% translate "Logout" %}</a>
<div class="dropDownMenu" id="dropDownMenu">
<button class="dropDownButton" id="dropDownMenuButton">
<div>{% translate "Account" %}</div>
</button>
<div class="dropDownList">
<a class="button dropDownChoice" id="navBarBtnHistory" href="/history">{% translate "History" %}</a>
<a class="button dropDownChoice" id="navBarBtnStatistics" href="/statistics">{% translate "Statistics" %}</a>
{% if user.is_superuser or user.is_staff %}
<a class="button dropDownChoice" href="/admin/">Admin Panel</a>
{% endif %}
<a class="button dropDownChoice" href="/accounts/password_change/">{% translate "Change Password" %}</a>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,42 @@
{% load i18n %}
{% load static %}
<div class="userpanel">
<div class="userinfo">
<img src="/profilepictures?name={{ user.profile_picture_filename|urlencode }}">
<span>
{% if user.first_name != "" %}
{% translate "User" %}: {{ user.first_name }} {{ user.last_name }} ({{ user.username }})
{% else %}
{% translate "User" %}: {{ user.username }}
{% endif %}
&nbsp;-&nbsp;
{% if user.balance < 0.01 %}
<span class="userbalancewarn">{% translate "Balance" %}: {{ user.balance }}{{ currency_suffix }}</span>
{% else %}
<span>{% translate "Balance" %}: {{ user.balance }}{{ currency_suffix }}</span>
{% endif %}
</span>
</div>
<div class="horizontalbuttonlist">
<a class="button" href="/">Home</a>
<a class="button" href="/deposit">{% translate "Deposit" %}</a>
<a class="button" href="/accounts/logout">{% translate "Logout" %}</a>
<div class="dropdownmenu" id="dropdownmenu">
<button class="dropdownbutton" id="dropdownmenu-button">
<div>{% translate "Account" %}</div>
</button>
<div class="dropdownlist">
<a class="button dropdownchoice" href="/history">{% translate "History" %}</a>
<a class="button dropdownchoice" href="/statistics">{% translate "Statistics" %}</a>
{% if user.is_superuser or user.is_staff %}
<a class="button dropdownchoice" href="/admin/">Admin Panel</a>
{% endif %}
{% if user.is_superuser or user.allowed_to_supply %}
<a class="button dropdownchoice" href="/supply/">{% translate "Supply" %}</a>
{% endif %}
<a class="button dropdownchoice" href="/accounts/password_change/">{% translate "Change Password" %}</a>
</div>
</div>
</div>
</div>

View file

@ -6,17 +6,20 @@ from .admin import adminSite
urlpatterns = [ urlpatterns = [
path('', views.index), path('', views.index),
path('order/<drinkID>/', views.order), path('order/<drinkid>/', views.order),
path('history/', views.history), path('history/', views.history),
path('deposit/', views.deposit), path('deposit/', views.deposit),
path('statistics/', views.statistics), path('statistics/', views.statistics),
path('supply/', views.supply),
path('accounts/login/', views.login_page, name="login"), path('accounts/login/', views.login_page, name="login"),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'), path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
path('accounts/password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'), path('accounts/password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
path('accounts/password_change_done/', views.redirect_home, name='password_change_done'), path('accounts/password_change_done/', views.redirect_home, name='password_change_done'),
path('admin/', adminSite.urls), path('admin/', adminSite.urls),
# custom-handled resources
path('profilepictures', views.profile_pictures),
# API # # API #
path('api/order-drink', views.api_order_drink), path('api/order-drink', views.api_order_drink),
path('api/deposit', views.api_deposit), path('api/deposit', views.api_deposit),
#path('api/get-statistics', views.api_get_statistics) path('api/supply', views.api_supply)
] ]

View file

@ -1,12 +1,16 @@
import json import json
import sys import sys
from pathlib import Path
from django.conf import settings
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth import login from django.contrib.auth import login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect
from django.http.response import FileResponse
from django.http.response import HttpResponse from django.http.response import HttpResponse
from django.shortcuts import render from django.shortcuts import render
@ -20,6 +24,9 @@ from .models import Drink
from .models import Order from .models import Order
from .models import RegisterTransaction from .models import RegisterTransaction
#
profile_pictures_path = Path(settings.PROFILE_PICTURES).resolve()
# login view # login view
@ -77,9 +84,9 @@ def history(request):
return render(request, "history.html", context) return render(request, "history.html", context)
@login_required @login_required
def order(request, drinkID): def order(request, drinkid):
try: try:
drink_ = Drink.objects.get(pk=drinkID) drink_ = Drink.objects.get(pk=drinkid)
context = { context = {
"drink": drink_ "drink": drink_
} }
@ -103,11 +110,31 @@ def statistics(request):
} }
return render(request, "statistics.html", context) return render(request, "statistics.html", context)
@login_required
def supply(request):
return render(request, "supply.html")
@login_required @login_required
def redirect_home(request): def redirect_home(request):
return HttpResponseRedirect("/") return HttpResponseRedirect("/")
# Custom-Handled Resources
def profile_pictures(request):
if not "name" in request.GET:
return HttpResponse(b"", status=400)
ppic_filepath = Path(profile_pictures_path / request.GET["name"]).resolve()
try:
ppic_filepath.relative_to(profile_pictures_path)
except:
return HttpResponse("No.", status=403)
if ppic_filepath.is_file():
return FileResponse(ppic_filepath.open('rb'))
else:
return FileResponse(b"", status=404)
# API for XHR requests # # API for XHR requests #
@login_required @login_required
@ -121,10 +148,10 @@ def api_order_drink(request):
if user.allow_order_with_negative_balance or user.balance > 0: if user.allow_order_with_negative_balance or user.balance > 0:
drinkID = int(request.POST["drinkID"]) drinkid = int(request.POST["drinkid"])
amount = int(request.POST["numberOfDrinks"]) amount = int(request.POST["numberofdrinks"])
drink = Drink.objects.get(pk=drinkID) drink = Drink.objects.get(pk=drinkid)
if ((drink.do_not_count and drink.available > 0) or (drink.available >= amount)) and not drink.deleted: if ((drink.do_not_count and drink.available > 0) or (drink.available >= amount)) and not drink.deleted:
Order.objects.create(drink=drink, user=user, amount=amount) Order.objects.create(drink=drink, user=user, amount=amount)
@ -132,7 +159,7 @@ def api_order_drink(request):
else: else:
return HttpResponse("notAvailable", status=400) return HttpResponse("notAvailable", status=400)
else: raise Exception("Balance below zero.") else: raise Exception("Unexpected input or missing privileges.")
except Exception as e: except Exception as e:
print(f"An exception occured while processing an order: User: {user.username} - Exception: {e}", file=sys.stderr) print(f"An exception occured while processing an order: User: {user.username} - Exception: {e}", file=sys.stderr)
@ -148,7 +175,7 @@ def api_deposit(request):
try: try:
amount = decimal.Decimal(request.POST["depositAmount"]) amount = decimal.Decimal(request.POST["depositamount"])
if 0.00 < amount < 9999.99: if 0.00 < amount < 9999.99:
# create transaction # create transaction
@ -163,5 +190,33 @@ def api_deposit(request):
else: raise Exception("Deposit amount too big or small.") else: raise Exception("Deposit amount too big or small.")
except Exception as e: except Exception as e:
print(f"An exception occured while processing an transaction: User: {user.username} - Exception: {e}", file=sys.stderr) print(f"An exception occured while processing a transaction: User: {user.username} - Exception: {e}", file=sys.stderr)
return HttpResponse(b"", status=500)
@login_required
def api_supply(request):
# check request -> supply
user = request.user
try:
price = decimal.Decimal(request.POST["supplyprice"])
description = str(request.POST["supplydescription"])
if 0.00 < price < 9999.99 and (user.allowed_to_supply or user.is_superuser):
# create transaction
RegisterTransaction.objects.create(
transaction_sum=-price,
comment=f"Supply: {description}",
is_user_deposit=False,
user=user
)
#
return HttpResponse("success", status=200)
else: raise Exception("Unexpected input or missing privileges.")
except Exception as e:
print(f"An exception occured while processing a supply transaction: User: {user.username} - Exception: {e}", file=sys.stderr)
return HttpResponse(b"", status=500) return HttpResponse(b"", status=500)

View file

@ -175,3 +175,5 @@ try:
CURRENCY_SUFFIX = os.environ["CURRENCY_SUFFIX"] CURRENCY_SUFFIX = os.environ["CURRENCY_SUFFIX"]
except KeyError: except KeyError:
CURRENCY_SUFFIX = "$" CURRENCY_SUFFIX = "$"
PROFILE_PICTURES = os.environ["PROFILE_PICTURES"]

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-06-13 19:01+0200\n" "POT-Creation-Date: 2022-10-15 19:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Julian Müller (W13R)\n" "Last-Translator: Julian Müller (W13R)\n"
"Language: DE\n" "Language: DE\n"
@ -17,221 +17,266 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: app/templates/admin/base_site.html:7 #: application/app/templates/admin/base_site.html:7
msgid "Django site admin" msgid "Django site admin"
msgstr "Django Administrator" msgstr "Django Administrator"
#: app/templates/admin/base_site.html:15 #: application/app/templates/admin/base_site.html:15
msgid "Django administration" msgid "Django administration"
msgstr "Django Administration" msgstr "Django Administration"
#: app/templates/baseLayout.html:41 #: application/app/templates/baselayout.html:41
msgid "An error occured. Please log out and log in again." msgid "An error occured. Please log out and log in again."
msgstr "Ein Fehler ist aufgetreten. Bitte ab- und wieder anmelden." msgstr "Ein Fehler ist aufgetreten. Bitte ab- und wieder anmelden."
#: app/templates/deposit.html:6 #: application/app/templates/deposit.html:6
msgid "Drinks - Deposit" msgid "Drinks - Deposit"
msgstr "Getränke - Einzahlen" msgstr "Getränke - Einzahlen"
#: app/templates/deposit.html:14 app/templates/userPanel.html:19 #: application/app/templates/deposit.html:14
#: application/app/templates/userpanel.html:23
msgid "Deposit" msgid "Deposit"
msgstr "Einzahlen" msgstr "Einzahlen"
#: app/templates/deposit.html:23 #: application/app/templates/deposit.html:23
msgid "Amount" msgid "Amount"
msgstr "Summe" msgstr "Summe"
#: app/templates/deposit.html:31 app/templates/order.html:72 #: application/app/templates/deposit.html:31
#: app/templates/registration/login.html:56 #: application/app/templates/order.html:72
#: application/app/templates/registration/login.html:57
#: application/app/templates/supply.html:41
msgid "cancel" msgid "cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
#: app/templates/deposit.html:32 #: application/app/templates/deposit.html:32
msgid "confirm" msgid "confirm"
msgstr "Bestätigen" msgstr "Bestätigen"
#: app/templates/history.html:6 #: application/app/templates/history.html:6
msgid "Drinks - History" msgid "Drinks - History"
msgstr "Getränke - Verlauf" msgstr "Getränke - Verlauf"
#: app/templates/history.html:14 app/templates/userPanel.html:25 #: application/app/templates/history.html:14
#: application/app/templates/userpanel.html:30
msgid "History" msgid "History"
msgstr "Verlauf" msgstr "Verlauf"
#: app/templates/history.html:22 #: application/app/templates/history.html:22
msgid "last 30 actions" msgid "last 30 actions"
msgstr "letzte 30 Vorgänge" msgstr "letzte 30 Vorgänge"
#: app/templates/history.html:33 app/templates/statistics.html:41 #: application/app/templates/history.html:33
#: app/templates/statistics.html:61 app/templates/statistics.html:81 #: application/app/templates/statistics.html:41
#: app/templates/statistics.html:101 app/templates/statistics.html:121 #: application/app/templates/statistics.html:61
#: app/templates/statistics.html:141 #: application/app/templates/statistics.html:81
#: application/app/templates/statistics.html:101
#: application/app/templates/statistics.html:121
#: application/app/templates/statistics.html:141
msgid "No history." msgid "No history."
msgstr "Kein Verlauf verfügbar." msgstr "Kein Verlauf verfügbar."
#: app/templates/index.html:6 #: application/app/templates/index.html:6
msgid "Drinks - Home" msgid "Drinks - Home"
msgstr "Getränke - Home" msgstr "Getränke - Home"
#: app/templates/index.html:14 #: application/app/templates/index.html:14
msgid "Available Drinks" msgid "Available Drinks"
msgstr "Verfügbare Getränke" msgstr "Verfügbare Getränke"
#: app/templates/index.html:27 app/templates/index.html:34 #: application/app/templates/index.html:27
#: application/app/templates/index.html:34
msgid "available" msgid "available"
msgstr "verfügbar" msgstr "verfügbar"
#: app/templates/index.html:43 #: application/app/templates/index.html:43
msgid "No drinks available." msgid "No drinks available."
msgstr "Es sind gerade keine Getränke verfügbar." msgstr "Es sind gerade keine Getränke verfügbar."
#: app/templates/order.html:7 #: application/app/templates/order.html:7
msgid "Drinks - Order" msgid "Drinks - Order"
msgstr "Getränke - Bestellen" msgstr "Getränke - Bestellen"
#: app/templates/order.html:16 #: application/app/templates/order.html:16
#: packages/django/forms/formsets.py:405 packages/django/forms/formsets.py:412
msgid "Order" msgid "Order"
msgstr "Bestellung" msgstr "Bestellung"
#: app/templates/order.html:29 #: application/app/templates/order.html:29
msgid "Drink" msgid "Drink"
msgstr "Getränk" msgstr "Getränk"
#: app/templates/order.html:34 #: application/app/templates/order.html:34
msgid "Price per Item" msgid "Price per Item"
msgstr "Preis pro Getränk" msgstr "Preis pro Getränk"
#: app/templates/order.html:40 #: application/app/templates/order.html:40
msgid "Available" msgid "Available"
msgstr "Verfügbar" msgstr "Verfügbar"
#: app/templates/order.html:46 #: application/app/templates/order.html:46
msgid "Count" msgid "Count"
msgstr "Anzahl" msgstr "Anzahl"
#: app/templates/order.html:63 #: application/app/templates/order.html:63
msgid "Sum" msgid "Sum"
msgstr "Summe" msgstr "Summe"
#: app/templates/order.html:73 #: application/app/templates/order.html:73
msgid "order" msgid "order"
msgstr "Bestellen" msgstr "Bestellen"
#: app/templates/order.html:85 #: application/app/templates/order.html:85
msgid "Your balance is too low to order a drink." msgid "Your balance is too low to order a drink."
msgstr "Dein Saldo ist zu niedrig um Getränke zu bestellen." msgstr "Dein Saldo ist zu niedrig um Getränke zu bestellen."
#: app/templates/order.html:86 app/templates/order.html:95 #: application/app/templates/order.html:86
#: application/app/templates/order.html:95
#: application/app/templates/supply.html:54
msgid "back" msgid "back"
msgstr "zurück" msgstr "zurück"
#: app/templates/order.html:94 #: application/app/templates/order.html:94
msgid "This drink is not available." msgid "This drink is not available."
msgstr "Dieses Getränk ist gerade nicht verfügbar." msgstr "Dieses Getränk ist gerade nicht verfügbar."
#: app/templates/registration/logged_out.html:7 #: application/app/templates/registration/logged_out.html:7
msgid "Drinks - Logged Out" msgid "Drinks - Logged Out"
msgstr "Getränke - Abgemeldet" msgstr "Getränke - Abgemeldet"
#: app/templates/registration/logged_out.html:17 #: application/app/templates/registration/logged_out.html:17
msgid "Logged out! You will be redirected shortly." msgid "Logged out! You will be redirected shortly."
msgstr "Du wurdest abgemeldet und wirst in Kürze weitergeleitet." msgstr "Du wurdest abgemeldet und wirst in Kürze weitergeleitet."
#: app/templates/registration/logged_out.html:19 #: application/app/templates/registration/logged_out.html:19
msgid "Click here if automatic redirection does not work." msgid "Click here if automatic redirection does not work."
msgstr "" msgstr ""
"Bitte klicke hier, wenn die automatische Weiterleitung nicht funktioniert." "Bitte klicke hier, wenn die automatische Weiterleitung nicht funktioniert."
#: app/templates/registration/login.html:7 #: application/app/templates/registration/login.html:8
msgid "Drinks - Login" msgid "Drinks - Login"
msgstr "Getränke - Anmeldung" msgstr "Getränke - Anmeldung"
#: app/templates/registration/login.html:26 #: application/app/templates/registration/login.html:27
msgid "Log in" msgid "Log in"
msgstr "Anmelden" msgstr "Anmelden"
#: app/templates/registration/login.html:28 #: application/app/templates/registration/login.html:29
msgid "Password/PIN" msgid "Password/PIN"
msgstr "Passwort/PIN" msgstr "Passwort/PIN"
#: app/templates/registration/login.html:57 #: application/app/templates/registration/login.html:58
msgid "login" msgid "login"
msgstr "Anmelden" msgstr "Anmelden"
#: app/templates/registration/login.html:65 #: application/app/templates/registration/login.html:66
msgid "Choose your account" msgid "Choose your account"
msgstr "Wähle deinen Account" msgstr "Wähle deinen Account"
#: app/templates/statistics.html:6 #: application/app/templates/statistics.html:6
msgid "Drinks - Statistics" msgid "Drinks - Statistics"
msgstr "Getränke - Statistiken" msgstr "Getränke - Statistiken"
#: app/templates/statistics.html:15 app/templates/userPanel.html:26 #: application/app/templates/statistics.html:15
#: application/app/templates/userpanel.html:31
msgid "Statistics" msgid "Statistics"
msgstr "Statistiken" msgstr "Statistiken"
#: app/templates/statistics.html:26 #: application/app/templates/statistics.html:26
msgid "Your orders per drink" msgid "Your orders per drink"
msgstr "Deine Bestellungen pro Getränk" msgstr "Deine Bestellungen pro Getränk"
#: app/templates/statistics.html:30 app/templates/statistics.html:50 #: application/app/templates/statistics.html:30
#: application/app/templates/statistics.html:50
msgid "drink" msgid "drink"
msgstr "Getränk" msgstr "Getränk"
#: app/templates/statistics.html:31 app/templates/statistics.html:51 #: application/app/templates/statistics.html:31
#: app/templates/statistics.html:71 app/templates/statistics.html:91 #: application/app/templates/statistics.html:51
#: app/templates/statistics.html:111 app/templates/statistics.html:131 #: application/app/templates/statistics.html:71
#: application/app/templates/statistics.html:91
#: application/app/templates/statistics.html:111
#: application/app/templates/statistics.html:131
msgid "count" msgid "count"
msgstr "Anzahl" msgstr "Anzahl"
#: app/templates/statistics.html:46 #: application/app/templates/statistics.html:46
msgid "All orders per drink" msgid "All orders per drink"
msgstr "Alle Bestellungen pro Getränk" msgstr "Alle Bestellungen pro Getränk"
#: app/templates/statistics.html:66 #: application/app/templates/statistics.html:66
msgid "Your orders per month (last 12 months)" msgid "Your orders per month (last 12 months)"
msgstr "Deine Bestellungen pro Monat (letzte 12 Monate)" msgstr "Deine Bestellungen pro Monat (letzte 12 Monate)"
#: app/templates/statistics.html:70 app/templates/statistics.html:90 #: application/app/templates/statistics.html:70
#: application/app/templates/statistics.html:90
msgid "month" msgid "month"
msgstr "Monat" msgstr "Monat"
#: app/templates/statistics.html:86 #: application/app/templates/statistics.html:86
msgid "All orders per month (last 12 months)" msgid "All orders per month (last 12 months)"
msgstr "Alle Bestellungen pro Monat (letzte 12 Monate)" msgstr "Alle Bestellungen pro Monat (letzte 12 Monate)"
#: app/templates/statistics.html:106 #: application/app/templates/statistics.html:106
msgid "Your orders per weekday" msgid "Your orders per weekday"
msgstr "Deine Bestellungen pro Wochentag" msgstr "Deine Bestellungen pro Wochentag"
#: app/templates/statistics.html:110 app/templates/statistics.html:130 #: application/app/templates/statistics.html:110
#: application/app/templates/statistics.html:130
msgid "day" msgid "day"
msgstr "Tag" msgstr "Tag"
#: app/templates/statistics.html:126 #: application/app/templates/statistics.html:126
msgid "All orders per weekday" msgid "All orders per weekday"
msgstr "Alle Bestellungen pro Wochentag" msgstr "Alle Bestellungen pro Wochentag"
#: app/templates/userPanel.html:6 app/templates/userPanel.html:8 #: application/app/templates/supply.html:7
msgid "Drinks - Supply"
msgstr "Getränke - Beschaffung"
#: application/app/templates/supply.html:16
#: application/app/templates/userpanel.html:36
msgid "Supply"
msgstr "Beschaffung"
#: application/app/templates/supply.html:27
msgid "Description"
msgstr "Beschreibung"
#: application/app/templates/supply.html:32
msgid "Price"
msgstr "Preis"
#: application/app/templates/supply.html:42
msgid "submit"
msgstr "Senden"
#: application/app/templates/supply.html:53
msgid "You are not allowed to view this site."
msgstr "Dir fehlt die Berechtigung, diese Seite anzuzeigen."
#: application/app/templates/userpanel.html:9
#: application/app/templates/userpanel.html:11
msgid "User" msgid "User"
msgstr "Benutzer" msgstr "Benutzer"
#: app/templates/userPanel.html:12 app/templates/userPanel.html:14 #: application/app/templates/userpanel.html:15
#: application/app/templates/userpanel.html:17
msgid "Balance" msgid "Balance"
msgstr "Saldo" msgstr "Saldo"
#: app/templates/userPanel.html:22 #: application/app/templates/userpanel.html:24
msgid "Account"
msgstr "Account"
#: app/templates/userPanel.html:30
msgid "Change Password"
msgstr "Passwort ändern"
#: app/templates/userPanel.html:31
msgid "Logout" msgid "Logout"
msgstr "Abmelden" msgstr "Abmelden"
#: app/views.py:47 #: application/app/templates/userpanel.html:27
msgid "Account"
msgstr "Account"
#: application/app/templates/userpanel.html:38
msgid "Change Password"
msgstr "Passwort ändern"
#: application/app/views.py:47
msgid "Invalid username or password." msgid "Invalid username or password."
msgstr "Benutzername oder Passwort ungültig." msgstr "Benutzername oder Passwort ungültig."

View file

@ -1,6 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export DJANGO_SK_ABS_FP="$(pwd)/config/secret_key.txt" export DJANGO_SK_ABS_FP="$(pwd)/config/secret_key.txt"
export PROFILE_PICTURES="$(pwd)/profilepictures/"
export STATIC_FILES="$(pwd)/static/" export STATIC_FILES="$(pwd)/static/"
export APP_VERSION="12" export APP_VERSION="13"
export PYTHONPATH="$(pwd)/packages/" export PYTHONPATH="$(pwd)/packages/"

View file

@ -59,10 +59,7 @@ if __name__ == "__main__":
# # # # # # # # # #
execute_sql_statement(cur, conn, """ log("Not deleting register_balance. You can delete it via the Admin Panel (Globals -> register_balance), as it is no more used.")
insert into app_global
values ('register_balance', 'This is the current balance of the register.', 0.0, '');
""")
execute_sql_statement(cur, conn, """ execute_sql_statement(cur, conn, """
insert into app_global insert into app_global

View file

@ -63,7 +63,6 @@ def check_database():
required_rows = [ required_rows = [
"global_message", "global_message",
"register_balance",
"admin_info" "admin_info"
] ]

View file

Before

Width:  |  Height:  |  Size: 740 B

After

Width:  |  Height:  |  Size: 740 B

Before After
Before After

61
static/css/appform.css Normal file
View file

@ -0,0 +1,61 @@
.appform {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: max-content;
font-size: 1.1rem;
}
.appform > .forminfo {
width: 100%;
text-align: left;
margin: .4rem 0;
}
.forminfo > span:first-child {
margin-right: 1rem;
}
.forminfo > span:last-child {
float: right;
}
.appform > .forminput {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin: .8rem 0;
gap: 1rem;
}
.appform > .statusinfo {
margin-top: .5rem;
}
.appform > .formbuttons {
border-top: 1px solid var(--glass-border-color);
padding-top: 1rem;
margin-top: 1rem;
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 1rem;
}
.formbuttons button, .formbuttons .button {
box-sizing: content-box;
font-size: 1rem;
width: fit-content;
}
.formheading {
text-align: left;
width: 100%;
margin-top: 0;
}
@media only screen and (max-width: 700px) {
.appform > .forminput {
flex-direction: column;
gap: .5rem;
}
.formheading {
text-align: center;
}
}

View file

@ -1,30 +1,29 @@
/* custom number input */ /* custom number input */
.customNumberInput { .customnumberinput {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
height: 2.2rem; height: 2.2rem;
width: 100% !important;
} }
.customNumberInput button { .customnumberinput button {
min-width: 2.5rem !important; min-width: 2.5rem !important;
width: 2.5rem !important; width: 2.5rem !important;
padding: 0; padding: 0;
margin: 0; margin: 0;
height: 100%; height: 100%;
} }
.customNumberInput-minus { .customnumberinput-minus {
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
z-index: 10; z-index: 10;
} }
.customNumberInput-plus { .customnumberinput-plus {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-top-left-radius: 0; border-top-left-radius: 0;
z-index: 10; z-index: 10;
} }
.customNumberInput input[type="number"] { .customnumberinput input[type="number"] {
max-height: 100%; max-height: 100%;
width: 5rem; width: 4rem;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-size: .9rem; font-size: .9rem;

View file

@ -1,3 +0,0 @@
#depositAmount {
width: 10rem;
}

View file

@ -9,7 +9,7 @@
padding-bottom: .4rem !important; padding-bottom: .4rem !important;
font-size: .95rem; font-size: .95rem;
} }
.history .historyDate { .history .historydate {
margin-left: auto; margin-left: auto;
text-align: right; text-align: right;
font-size: .8rem !important; font-size: .8rem !important;

View file

@ -1,4 +1,4 @@
.availableDrinksList { .availabledrinkslist {
width: 50%; width: 50%;
max-width: 45rem; max-width: 45rem;
list-style: none; list-style: none;
@ -9,13 +9,13 @@
justify-content: start; justify-content: start;
align-items: center; align-items: center;
} }
.availableDrinksList li { .availabledrinkslist li {
display: flex; display: flex;
width: 100%; width: 100%;
height: fit-content; height: fit-content;
margin-bottom: .6rem; margin-bottom: .6rem;
} }
.availableDrinksList li a { .availabledrinkslist li a {
display: flex; display: flex;
width: 100%; width: 100%;
align-items: center; align-items: center;
@ -25,20 +25,21 @@
text-decoration: none; text-decoration: none;
font-size: 1rem; font-size: 1rem;
} }
.availableDrinksList li a span:first-child { .availabledrinkslist li a span:first-child {
margin-right: 1rem !important; margin-right: 1rem !important;
text-align: left;
} }
.availableDrinksList li a span:last-child { .availabledrinkslist li a span:last-child {
margin-left: auto; margin-left: auto;
text-align: right; text-align: right;
font-size: 1rem; font-size: 1rem;
} }
/* mobile devices */ /* mobile devices */
@media only screen and (max-width: 700px) { @media only screen and (max-width: 700px) {
.availableDrinksList { .availabledrinkslist {
width: 95%; width: 95%;
} }
.availableDrinksList li a { .availabledrinkslist li a {
width: calc(100vw - (2 * .8rem)) !important; width: calc(100vw - (2 * .8rem)) !important;
padding: .8rem !important; padding: .8rem !important;
} }

View file

@ -1,15 +1,11 @@
/* login page */ /* login page */
body.overflowHidden {
overflow-y: hidden !important;
overflow-x: hidden !important;
}
main { main {
margin-top: 2vh; margin-top: 2vh;
} }
main > h1 { main > h1 {
display: none; display: none;
} }
.userlistContainer { .userlistcontainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -47,10 +43,10 @@ main > h1 {
text-align: center; text-align: center;
padding: .8rem 1.1rem; padding: .8rem 1.1rem;
} }
.userlistButton { .userlistbutton {
font-size: 1.1rem; font-size: 1.1rem;
} }
.passwordOverlayContainer { .passwordoverlaycontainer {
position: absolute; position: absolute;
top: 0; top: 0;
width: 100vw; width: 100vw;
@ -62,30 +58,41 @@ main > h1 {
background: var(--page-background); background: var(--page-background);
z-index: 40; z-index: 40;
} }
.passwordOverlay { .passwordoverlay {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: start; justify-content: start;
align-items: center; align-items: center;
} }
.passwordOverlay > form { .passwordoverlay > form {
min-width: unset; min-width: unset;
width: fit-content; width: fit-content;
} }
.passwordOverlay > form > h1 { .passwordoverlay > form > h1 {
margin-top: 2rem; margin-top: 2rem;
margin-bottom: 2rem; margin-bottom: 2rem;
} }
form input[type="password"], form input[type="text"] { /* loginform */
.loginform {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.loginform input[type="password"], form input[type="text"] {
width: 94%; width: 94%;
padding-top: .5rem; padding-top: .5rem;
padding-bottom: .5rem; padding-bottom: .5rem;
font-size: 1rem; font-size: 1rem;
margin: .1rem 0; margin: .1rem 0;
} }
form .horizontalButtonList { .loginform .horizontalbuttonlist {
margin-top: 1.5rem; margin-top: 1.5rem;
} }
.horizontalbuttonlist .button, .horizontalbuttonlist button {
font-size: 1rem;
}
/***/
.pinpad { .pinpad {
margin-top: 1.5rem; margin-top: 1.5rem;
margin-bottom: 0; margin-bottom: 0;
@ -109,7 +116,7 @@ form .horizontalButtonList {
margin: .2rem !important; margin: .2rem !important;
} }
@media only screen and (max-width: 700px) { @media only screen and (max-width: 700px) {
.userlistContainer { .userlistcontainer {
width: 95vw; width: 95vw;
} }
.userlist { .userlist {

View file

@ -6,8 +6,8 @@
--color: #fafafa; --color: #fafafa;
--color-error: rgb(255, 70, 70); --color-error: rgb(255, 70, 70);
/** glass **/ /** glass **/
--glass-bg-dropDown: #3a3b44ef; --glass-bg-dropdown: #3a3b44ef;
--glass-bg-dropDown-hover: #55565efa; --glass-bg-dropdown-hover: #55565efa;
--glass-bg-color1: #ffffff31; --glass-bg-color1: #ffffff31;
--glass-bg-color2: #ffffff1a; --glass-bg-color2: #ffffff1a;
--glass-bg-hover-color1: #ffffff46; --glass-bg-hover-color1: #ffffff46;
@ -16,18 +16,18 @@
--glass-border-color: #ffffff77; --glass-border-color: #ffffff77;
--glass-bg: linear-gradient(var(--glass-bg-color1), var(--glass-bg-color2)); --glass-bg: linear-gradient(var(--glass-bg-color1), var(--glass-bg-color2));
--glass-bg-hover: linear-gradient(var(--glass-bg-hover-color1), var(--glass-bg-hover-color2)); --glass-bg-hover: linear-gradient(var(--glass-bg-hover-color1), var(--glass-bg-hover-color2));
--glass-corner-radius: .3rem; --glass-corner-radius: .5rem;
/** page background **/ /** page background **/
--page-background-color1: #131d25; --page-background-color1: #131d25;
--page-background-color2: #311d30; --page-background-color2: #311d30;
--page-background: linear-gradient(-190deg, var(--page-background-color1), var(--page-background-color2)); --page-background: linear-gradient(-190deg, var(--page-background-color1), var(--page-background-color2));
/** global message banner **/ /** global message banner **/
--bg-globalMessage: linear-gradient(135deg, #4b351c, #411d52, #1c404b); --bg-globalmessage: linear-gradient(135deg, #4b351c, #411d52, #1c404b);
} }
@supports(backdrop-filter: blur(10px)) { @supports(backdrop-filter: blur(10px)) {
:root { :root {
--glass-bg-dropDown: #ffffff1a; --glass-bg-dropdown: #ffffff1a;
--glass-bg-dropDown-hover: #ffffff46; --glass-bg-dropdown-hover: #ffffff46;
--glass-blur: blur(18px); --glass-blur: blur(18px);
} }
} }
@ -42,7 +42,7 @@ body {
color: var(--color); color: var(--color);
overflow-x: hidden; overflow-x: hidden;
} }
.baseLayout { .baselayout {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: start; justify-content: start;
@ -60,7 +60,7 @@ main {
width: 100%; width: 100%;
margin-top: 5vh; margin-top: 5vh;
} }
.userPanel { .userpanel {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
@ -69,21 +69,21 @@ main {
font-size: 1rem; font-size: 1rem;
width: 94%; width: 94%;
} }
.userInfo > span { .userinfo > span {
font-size: 1.1rem; font-size: 1.1rem;
vertical-align: middle; vertical-align: middle;
} }
.userInfo > img { .userinfo > img {
vertical-align: middle; vertical-align: middle;
width: 1.8rem; width: 1.8rem;
height: 1.8rem; height: 1.8rem;
margin: .5rem; margin: .5rem;
} }
.userPanel > .horizontalButtonList { .userpanel > .horizontalbuttonlist {
margin-left: auto; margin-left: auto;
margin-right: 0; margin-right: 0;
} }
.userBalanceWarn { .userbalancewarn {
color: var(--color-error); color: var(--color-error);
font-weight: bold; font-weight: bold;
} }
@ -95,19 +95,16 @@ main {
width: 100%; width: 100%;
flex-grow: 1; flex-grow: 1;
} }
main > h1 { .globalmessage {
margin-top: 0;
}
.globalMessage {
width: 100vw; width: 100vw;
z-index: 999; z-index: 999;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: var(--bg-globalMessage); background: var(--bg-globalmessage);
padding: .3rem 0; padding: .3rem 0;
} }
.globalMessage div { .globalmessage div {
width: 96%; width: 96%;
text-align: center; text-align: center;
word-break: keep-all; word-break: keep-all;
@ -115,24 +112,24 @@ main > h1 {
box-sizing: border-box; box-sizing: border-box;
} }
/* DROP DOWN MENUS */ /* DROP DOWN MENUS */
.dropDownMenu { .dropdownmenu {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
border-radius: var(--glass-corner-radius); border-radius: var(--glass-corner-radius);
} }
.dropDownButton { .dropdownbutton {
width: fit-content; width: fit-content;
z-index: 190; z-index: 190;
box-shadow: none; box-shadow: none;
text-align: center; text-align: center;
justify-content: center; justify-content: center;
} }
.dropDownButton, .dropDownChoice { .dropdownbutton, .dropdownchoice {
font-size: 1rem; font-size: 1rem;
} }
.dropDownList { .dropdownlist {
position: absolute; position: absolute;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -144,35 +141,35 @@ main > h1 {
opacity: 0%; opacity: 0%;
transition: opacity 100ms; transition: opacity 100ms;
} }
.dropDownChoice { .dropdownchoice {
box-shadow: none; box-shadow: none;
border-radius: 0 !important; border-radius: 0 !important;
margin: 0; margin: 0;
margin-top: -1px; margin-top: -1px;
text-align: center; text-align: center;
justify-content: center; justify-content: center;
background: var(--glass-bg-dropDown) !important; background: var(--glass-bg-dropdown) !important;
backdrop-filter: none !important; backdrop-filter: none !important;
} }
.dropDownChoice:hover { .dropdownchoice:hover {
background: var(--glass-bg-dropDown-hover) !important; background: var(--glass-bg-dropdown-hover) !important;
} }
.dropDownList :first-child { .dropdownlist :first-child {
border-top-left-radius: var(--glass-corner-radius) !important; border-top-left-radius: var(--glass-corner-radius) !important;
border-top-right-radius: var(--glass-corner-radius) !important; border-top-right-radius: var(--glass-corner-radius) !important;
} }
.dropDownList :last-child { .dropdownlist :last-child {
border-bottom-left-radius: var(--glass-corner-radius) !important; border-bottom-left-radius: var(--glass-corner-radius) !important;
border-bottom-right-radius: var(--glass-corner-radius) !important; border-bottom-right-radius: var(--glass-corner-radius) !important;
} }
.dropDownVisible .dropDownList { .dropdownvisible .dropdownlist {
opacity: 100%; opacity: 100%;
visibility: visible; visibility: visible;
pointer-events: visible; pointer-events: visible;
} }
/* FOOTER */ /* FOOTER */
.footer { .footer {
z-index: 990; z-index: 900;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
@ -181,6 +178,7 @@ main > h1 {
margin-top: auto; margin-top: auto;
padding-top: 3rem; padding-top: 3rem;
padding-bottom: .3rem; padding-bottom: .3rem;
text-align: center;
} }
.footer div { .footer div {
font-size: .95rem; font-size: .95rem;
@ -236,47 +234,6 @@ th {
text-align: left; text-align: left;
border-bottom: 1px solid var(--color); border-bottom: 1px solid var(--color);
} }
/* FORMS */
form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 18rem;
height: max-content;
}
form .row {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: .2rem 0;
}
form .row .column {
display: flex;
flex-direction: row;
}
form h1 {
font-size: 1.6rem;
margin-bottom: 2rem;
}
form {
font-size: 1.1rem;
}
form .customNumberInput {
width: 100%;
}
form .statusInfo {
margin-top: .5rem;
}
form .horizontalButtonList {
margin-top: 2rem;
width: 100%;
}
form .button, form button {
font-size: 1rem;
}
/* BUTTONS & OTHER INPUT ELEMENTS */ /* BUTTONS & OTHER INPUT ELEMENTS */
.button, button { .button, button {
display: flex; display: flex;
@ -322,8 +279,8 @@ input[type="text"], input[type="password"], input[type="number"] {
border: none; border: none;
border-radius: var(--glass-corner-radius); border-radius: var(--glass-corner-radius);
} }
/**** CUSTOM CLASSES ****/ /**** OTHER CLASSES ****/
.centeringFlex { .centeringflex {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
@ -333,22 +290,25 @@ input[type="text"], input[type="password"], input[type="number"] {
text-align: center; text-align: center;
padding: 2rem 1rem; padding: 2rem 1rem;
} }
.horizontalButtonList { .horizontalbuttonlist {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: flex-end; align-items: flex-end;
justify-content: space-between; justify-content: space-between;
} }
.horizontalButtonList > .button, .horizontalButtonList > button, .horizontalButtonList > div { .horizontalbuttonlist > .button, .horizontalbuttonlist > button, .horizontalbuttonlist > div {
margin: 0 .5rem; margin: 0 .5rem;
} }
.errorText { .errortext {
margin-top: 1rem; margin-top: 1rem;
color: var(--color-error); color: var(--color-error);
} }
.nodisplay { .nodisplay {
display: none !important; display: none !important;
} }
.heading {
margin-top: 0;
}
/* MISC / GENERAL */ /* MISC / GENERAL */
h1 { h1 {
text-align: center; text-align: center;
@ -357,25 +317,25 @@ h1 {
/* MOBILE OPTIMIZATIONS */ /* MOBILE OPTIMIZATIONS */
@media only screen and (max-width: 700px) { @media only screen and (max-width: 700px) {
main { main {
margin-top: 1.5rem; margin-top: 2rem;
} }
.globalMessage span { .globalmessage span {
width: 90%; width: 90%;
} }
.userPanel { .userpanel {
flex-direction: column; flex-direction: column;
justify-content: start; justify-content: start;
align-items: center; align-items: center;
} }
.userPanel > .horizontalButtonList { .userpanel > .horizontalbuttonlist {
margin-right: 0; margin-right: 0;
margin-left: 0; margin-left: 0;
margin-top: .5rem; margin-top: .5rem;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
} }
.userPanel > .horizontalButtonList > .button, .userpanel > .horizontalbuttonlist > .button,
.userPanel > .horizontalButtonList > .dropDownMenu { .userpanel > .horizontalbuttonlist > .dropdownmenu {
margin: 0.25rem; margin: 0.25rem;
} }
} }

View file

@ -1,3 +0,0 @@
form {
width: 22rem;
}

View file

@ -1,15 +1,11 @@
.mainContainer { .maincontainer {
min-width: 70vw; min-width: 70vw;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: start; justify-content: start;
} }
.statsHeading { .tablescontainer {
min-width: max-content;
margin-top: 0;
}
.tablesContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -17,7 +13,7 @@
width: 95%; width: 95%;
margin-top: 2rem; margin-top: 2rem;
} }
.statisticsTable { .statisticstable {
margin-bottom: 2rem; margin-bottom: 2rem;
padding-bottom: 1rem; padding-bottom: 1rem;
display: flex; display: flex;
@ -26,38 +22,32 @@
align-items: center; align-items: center;
text-align: center; text-align: center;
} }
.statisticsTable h1 { .statisticstable h1 {
margin-top: 0; margin-top: 0;
font-size: 1.2rem; font-size: 1.2rem;
text-align: left; text-align: left;
min-width: 10rem; min-width: 10rem;
text-align: center; text-align: center;
} }
.statisticsTable table { .statisticstable table {
min-width: 20vw; min-width: 20vw;
width: fit-content; width: fit-content;
} }
.statisticsTable th:last-child { .statisticstable th:last-child {
text-align: right; text-align: right;
} }
.statisticsTable td:last-child { .statisticstable td:last-child {
text-align: right; text-align: right;
} }
@media only screen and (max-width: 700px) { @media only screen and (max-width: 700px) {
.statisticsTable h1 { .statisticstable h1 {
min-width: 90vw; min-width: 90vw;
} }
.statisticsTable table { .statisticstable table {
min-width: 80vw; min-width: 80vw;
} }
.statisticsTable { .statisticstable {
margin-bottom: 2rem; margin-bottom: 2rem;
padding-bottom: 1rem; padding-bottom: 1rem;
} }
.statsHeading {
margin-left: 0;
margin-right: 0;
padding-top: 1rem;
padding-bottom: 2rem;
}
} }

View file

@ -1,18 +1,18 @@
{ {
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
// get all customNumberInput Elements // get all customnumberinput Elements
let custom_number_inputs = document.getElementsByClassName("customNumberInput"); let customNumberInputElements = document.getElementsByClassName("customnumberinput");
// Add Event Handler to the elements of the customNumberInputs // Add Event Handler to the elements of the customnumberinputs
[...custom_number_inputs].forEach(element => { [...customNumberInputElements].forEach(element => {
// number input // number input
let numberFieldElement = element.getElementsByClassName("customNumberInputField")[0]; let numberFieldElement = element.getElementsByClassName("customnumberinput-field")[0];
// minus button // minus button
element.getElementsByClassName("customNumberInput-minus")[0].addEventListener("click", () => { element.getElementsByClassName("customnumberinput-minus")[0].addEventListener("click", () => {
alterCustomNumberField(numberFieldElement, -1) alterCustomNumberField(numberFieldElement, -1)
}); });
// plus button // plus button
element.getElementsByClassName("customNumberInput-plus")[0].addEventListener("click", () => { element.getElementsByClassName("customnumberinput-plus")[0].addEventListener("click", () => {
alterCustomNumberField(numberFieldElement, +1) alterCustomNumberField(numberFieldElement, +1)
}); });
}) })

View file

@ -2,21 +2,21 @@ document.addEventListener("DOMContentLoaded", () => {
// elements // elements
let deposit_form = document.getElementById("depositForm"); let depositForm = document.getElementById("depositform");
let status_info = document.getElementById("statusInfo"); let statusInfo = document.getElementById("statusinfo");
let deposit_submit_button = document.getElementById("depositSubmitBtn"); let depositSubmitButton = document.getElementById("depositsubmitbtn");
// event listener for deposit form // event listener for deposit form
// this implements a custom submit method // this implements a custom submit method
deposit_form.addEventListener("submit", (event) => { depositForm.addEventListener("submit", (event) => {
deposit_submit_button.disabled = true; depositSubmitButton.disabled = true;
event.preventDefault(); // Don't do the default submit action! event.preventDefault(); // Don't do the default submit action!
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
let formData = new FormData(deposit_form); let formData = new FormData(depositForm);
xhr.addEventListener("load", (event) => { xhr.addEventListener("load", (event) => {
@ -24,20 +24,20 @@ document.addEventListener("DOMContentLoaded", () => {
response_ = event.target.responseText; response_ = event.target.responseText;
if (status_ == 200 && response_ == "success") { if (status_ == 200 && response_ == "success") {
status_info.innerText = "Success. Redirecting soon."; statusInfo.innerText = "Success. Redirecting soon.";
window.location.replace("/"); window.location.replace("/");
} }
else { else {
status_info.classList.add("errorText"); statusInfo.classList.add("errortext");
status_info.innerText = "An error occured. Redirecting in 5 seconds..."; statusInfo.innerText = "An error occured. Redirecting in 5 seconds...";
window.setTimeout(() => { window.location.replace("/") }, 5000); window.setTimeout(() => { window.location.replace("/") }, 5000);
} }
}) })
xhr.addEventListener("error", (event) => { xhr.addEventListener("error", (event) => {
status_info.classList.add("errorText"); statusInfo.classList.add("errortext");
status_info.innerText = "An error occured. Redirecting in 5 seconds..."; statusInfo.innerText = "An error occured. Redirecting in 5 seconds...";
window.setTimeout(() => { window.location.replace("/") }, 5000); window.setTimeout(() => { window.location.replace("/") }, 5000);
}) })

View file

@ -2,14 +2,14 @@
// Define variables // Define variables
let username_input; let usernameInputElement;
let password_input; let passwordInputElement;
let submit_button; let submitButton;
let password_overlay; let passwordOverlayElement;
let pw_overlay_cancel; let pwOverlayCancelButton;
let userlist_buttons; let userlistButtons;
let pinpad_buttons; let pinpadButtons;
let userlist_container; let userlistContainerElement;
// Add event listeners after DOM Content loaded // Add event listeners after DOM Content loaded
@ -18,34 +18,34 @@
// elements // elements
username_input = document.getElementById("id_username"); usernameInputElement = document.getElementById("id_username");
password_input = document.getElementById("id_password"); passwordInputElement = document.getElementById("id_password");
submit_button = document.getElementById("submit_login"); submitButton = document.getElementById("submit_login");
password_overlay = document.getElementById("passwordOverlayContainer"); passwordOverlayElement = document.getElementById("passwordoverlaycontainer");
pw_overlay_cancel = document.getElementById("pwoCancel"); pwOverlayCancelButton = document.getElementById("pwocancel");
userlist_container = document.getElementById("userlistContainer"); userlistContainerElement = document.getElementById("userlistcontainer");
userlist_buttons = document.getElementsByClassName("userlistButton"); userlistButtons = document.getElementsByClassName("userlistbutton");
pinpad_buttons = document.getElementsByClassName("pinpadBtn"); pinpadButtons = document.getElementsByClassName("pinpadbtn");
// event listeners // event listeners
// [...<html-collection>] converts an html collection to an array // [...<html-collection>] converts an html collection to an array
[...userlist_buttons].forEach(element => { [...userlistButtons].forEach(element => {
element.addEventListener("click", () => { element.addEventListener("click", () => {
set_username(element.dataset.username); set_username(element.dataset.username);
show_password_overlay(); show_password_overlay();
}) })
}); });
[...pinpad_buttons].forEach(element => { [...pinpadButtons].forEach(element => {
element.addEventListener("click", () => { element.addEventListener("click", () => {
pinpad_press(element.dataset.btn); pinpad_press(element.dataset.btn);
}) })
}) })
pw_overlay_cancel.addEventListener("click", () => { pwOverlayCancelButton.addEventListener("click", () => {
hide_password_overlay(); hide_password_overlay();
}); });
@ -53,34 +53,34 @@
function set_username(username) { function set_username(username) {
username_input.value = username; usernameInputElement.value = username;
} }
function show_password_overlay() { function show_password_overlay() {
window.scrollTo(0, 0); window.scrollTo(0, 0);
password_overlay.classList.remove("nodisplay"); passwordOverlayElement.classList.remove("nodisplay");
userlist_container.classList.add("nodisplay"); userlistContainerElement.classList.add("nodisplay");
} }
function hide_password_overlay() { function hide_password_overlay() {
password_overlay.classList.add("nodisplay"); passwordOverlayElement.classList.add("nodisplay");
userlist_container.classList.remove("nodisplay"); userlistContainerElement.classList.remove("nodisplay");
password_input.value = ""; passwordInputElement.value = "";
} }
function pinpad_press(key) { function pinpad_press(key) {
if (key == "enter") { if (key == "enter") {
submit_button.click(); submitButton.click();
} }
else if (key == "x") { else if (key == "x") {
password_input.value = ""; passwordInputElement.value = "";
} }
else { else {
password_input.value += key; passwordInputElement.value += key;
} }
} }

View file

@ -1,17 +1,17 @@
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
let dropDownMenuElement = document.getElementById("dropDownMenu"); let dropdownmenuElement = document.getElementById("dropdownmenu");
let dropDownMenuButtonElement = document.getElementById("dropDownMenuButton"); let dropdownmenuButtonElement = document.getElementById("dropdownmenu-button");
if (dropDownMenuButtonElement != null) { if (dropdownmenuButtonElement != null) {
dropDownMenuButtonElement.addEventListener("click", () => { dropdownmenuButtonElement.addEventListener("click", () => {
if (dropDownMenuElement.classList.contains("dropDownVisible")) { if (dropdownmenuElement.classList.contains("dropdownvisible")) {
dropDownMenuElement.classList.remove("dropDownVisible"); dropdownmenuElement.classList.remove("dropdownvisible");
} }
else { else {
dropDownMenuElement.classList.add("dropDownVisible"); dropdownmenuElement.classList.add("dropdownvisible");
} }
}) })

View file

@ -2,54 +2,54 @@ document.addEventListener("DOMContentLoaded", () => {
// elements // elements
let order_number_of_drinks_input = document.getElementById("numberOfDrinks"); let orderNumberofdrinksInput = document.getElementById("numberofdrinks");
let order_number_of_drinks_btn_a = document.getElementById("numberOfDrinksBtnA"); let orderNumberofdrinksBtnA = document.getElementById("numberofdrinks-btn-minus");
let order_number_of_drinks_btn_b = document.getElementById("numberOfDrinksBtnB"); let orderNumberofdrinksBtnB = document.getElementById("numberofdrinks-btn-plus");
let order_sum_element = document.getElementById("orderCalculatedSum"); let orderSumElement = document.getElementById("ordercalculatedsum");
let order_form = document.getElementById("orderForm"); let orderFormElement = document.getElementById("orderform");
let status_info = document.getElementById("statusInfo"); let statusInfoElement = document.getElementById("statusinfo");
let order_submit_button = document.getElementById("orderSubmitBtn"); let orderSubmitButton = document.getElementById("ordersubmitbtn");
// calculate & display sum // calculate & display sum
let order_price_per_drink = parseFloat(document.getElementById("pricePerDrink").dataset.drinkPrice); let orderPricePerDrink = parseFloat(document.getElementById("priceperdrink").dataset.drinkPrice);
function calculate_and_display_sum() { function calculateAndDisplaySum() {
setTimeout(() => { setTimeout(() => {
let number_of_drinks = parseFloat(order_number_of_drinks_input.value); let numberOfDrinks = parseFloat(orderNumberofdrinksInput.value);
if (isNaN(number_of_drinks)) { if (isNaN(numberOfDrinks)) {
number_of_drinks = 1; numberOfDrinks = 1;
} }
let calculated_sum = order_price_per_drink * number_of_drinks; let calculated_sum = orderPricePerDrink * numberOfDrinks;
order_sum_element.innerText = new Intl.NumberFormat(undefined, {minimumFractionDigits: 2}).format(calculated_sum); orderSumElement.innerText = new Intl.NumberFormat(undefined, {minimumFractionDigits: 2}).format(calculated_sum);
}, 25); }, 25);
} }
order_number_of_drinks_input.addEventListener("input", calculate_and_display_sum); orderNumberofdrinksInput.addEventListener("input", calculateAndDisplaySum);
order_number_of_drinks_btn_a.addEventListener("click", calculate_and_display_sum); orderNumberofdrinksBtnA.addEventListener("click", calculateAndDisplaySum);
order_number_of_drinks_btn_b.addEventListener("click", calculate_and_display_sum); orderNumberofdrinksBtnB.addEventListener("click", calculateAndDisplaySum);
// custom submit method // custom submit method
order_form.addEventListener("submit", (event) => { orderFormElement.addEventListener("submit", (event) => {
order_submit_button.disabled = true; orderSubmitButton.disabled = true;
event.preventDefault(); // Don't do the default submit action! event.preventDefault(); // Don't do the default submit action!
if (isNaN(parseFloat(order_number_of_drinks_input.value))) { if (isNaN(parseFloat(orderNumberofdrinksInput.value))) {
order_number_of_drinks_input.value = 1; orderNumberofdrinksInput.value = 1;
} }
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
let formData = new FormData(order_form); let formData = new FormData(orderFormElement);
xhr.addEventListener("load", (event) => { xhr.addEventListener("load", (event) => {
@ -57,20 +57,20 @@ document.addEventListener("DOMContentLoaded", () => {
response_ = event.target.responseText; response_ = event.target.responseText;
if (status_ == 200 && response_ == "success") { if (status_ == 200 && response_ == "success") {
status_info.innerText = "Success."; statusInfoElement.innerText = "Success.";
window.location.replace("/"); window.location.replace("/");
} }
else { else {
status_info.classList.add("errorText"); statusInfoElement.classList.add("errortext");
status_info.innerText = "An error occured."; statusInfoElement.innerText = "An error occured.";
window.setTimeout(() => { window.location.reload() }, 5000); window.setTimeout(() => { window.location.reload() }, 5000);
} }
}) })
xhr.addEventListener("error", (event) => { xhr.addEventListener("error", (event) => {
status_info.classList.add("errorText"); statusInfoElement.classList.add("errortext");
status_info.innerText = "An error occured."; statusInfoElement.innerText = "An error occured.";
window.setTimeout(() => { window.location.reload() }, 5000); window.setTimeout(() => { window.location.reload() }, 5000);
}) })

56
static/js/supply.js Normal file
View file

@ -0,0 +1,56 @@
document.addEventListener("DOMContentLoaded", () => {
// elements
let supplyDescriptionElement = document.getElementById("supplydescription");
let supplyPriceElement = document.getElementById("supplyprice");
let supplyFormElement = document.getElementById("supplyform");
let statusInfoElement = document.getElementById("statusinfo");
let supplySubmitButton = document.getElementById("supplysubmitbtn");
// custom submit method
supplyFormElement.addEventListener("submit", (event) => {
supplySubmitButton.disabled = true;
event.preventDefault(); // Don't do the default submit action!
if (isNaN(parseFloat(supplyPriceElement.value)) || supplyDescriptionElement.value == "") {
statusInfoElement.innerText = "Please enter a description and price."
supplySubmitButton.disabled = false;
}
let xhr = new XMLHttpRequest();
let formData = new FormData(supplyFormElement);
xhr.addEventListener("load", (event) => {
status_ = event.target.status;
response_ = event.target.responseText;
if (status_ == 200 && response_ == "success") {
statusInfoElement.innerText = "Success.";
window.location.replace("/");
}
else {
statusInfoElement.classList.add("errortext");
statusInfoElement.innerText = "An error occured.";
window.setTimeout(() => { window.location.reload() }, 5000);
}
})
xhr.addEventListener("error", (event) => {
statusInfoElement.classList.add("errortext");
statusInfoElement.innerText = "An error occured.";
window.setTimeout(() => { window.location.reload() }, 5000);
})
xhr.open("POST", "/api/supply");
xhr.send(formData);
});
})