Add db table for drink types
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
.env
|
||||
test.db
|
||||
test.db*
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||
|
||||
117
db/models.py
@@ -7,7 +7,6 @@ transaction logging, drink recording, and utility functions for drink statistics
|
||||
|
||||
Constants:
|
||||
DRINK_COST (int): Cost of a drink in cents.
|
||||
AVAILABLE_DRINKS (list): List of available drink types.
|
||||
|
||||
Functions:
|
||||
_log_transaction(user_id, user_is_postpaid, ...): Log a user's money transaction.
|
||||
@@ -37,7 +36,7 @@ import os
|
||||
import secrets
|
||||
import datetime
|
||||
import random
|
||||
from sqlalchemy import create_engine, text, select
|
||||
from sqlalchemy import create_engine, text
|
||||
from fastapi import HTTPException
|
||||
|
||||
from dotenv import load_dotenv
|
||||
@@ -49,7 +48,6 @@ DATABASE_URL = "sqlite:///" + str(DATABASE_FILE)
|
||||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
|
||||
DRINK_COST = 100 # cent
|
||||
AVAILABLE_DRINKS = ["Paulaner Spezi", "Mio Mate", "Club Mate", "Eistee Pfirsisch"]
|
||||
|
||||
with engine.connect() as conn:
|
||||
# Create a table for postpaid users
|
||||
@@ -85,9 +83,10 @@ with engine.connect() as conn:
|
||||
postpaid_user_id INTEGER,
|
||||
prepaid_user_id INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
drink_type TEXT,
|
||||
drink_type INTEGER DEFAULT 1,
|
||||
FOREIGN KEY (postpaid_user_id) REFERENCES users_postpaid(id),
|
||||
FOREIGN KEY (prepaid_user_id) REFERENCES users_prepaid(id)
|
||||
FOREIGN KEY (prepaid_user_id) REFERENCES users_prepaid(id),
|
||||
FOREIGN KEY (drink_type) REFERENCES drink_types(id)
|
||||
)
|
||||
"""))
|
||||
|
||||
@@ -106,6 +105,27 @@ with engine.connect() as conn:
|
||||
FOREIGN KEY (prepaid_user_id) REFERENCES users_prepaid(id)
|
||||
)
|
||||
"""))
|
||||
|
||||
# create a table for drink types
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS drink_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
drink_name TEXT NOT NULL UNIQUE,
|
||||
icon TEXT NOT NULL,
|
||||
quantity INT DEFAULT 0
|
||||
)
|
||||
"""))
|
||||
conn.execute(text("""
|
||||
INSERT OR IGNORE INTO drink_types (id, drink_name, icon, quantity) VALUES
|
||||
(1, 'Sonstiges', 'sonstiges.png', 0),
|
||||
(2, 'Paulaner Spezi', 'paulaner_spezi.png', 0),
|
||||
(3, 'Paulaner Limo Orange', 'paulaner_limo_orange.png', 0),
|
||||
(4, 'Paulaner Limo Zitrone', 'paulaner_limo_zitrone.png', 0),
|
||||
(5, 'Mio Mate Original', 'mio_mate_original.png', 0),
|
||||
(6, 'Mio Mate Ginger', 'mio_mate_ginger.png', 0),
|
||||
(7, 'Mio Mate Pomegranate', 'mio_mate_pomegranate.png', 0),
|
||||
(8, 'Club Mate', 'club_mate.png', 0)
|
||||
"""))
|
||||
conn.commit()
|
||||
|
||||
|
||||
@@ -293,7 +313,7 @@ def set_postpaid_user_money(user_id: int, money: float):
|
||||
connection.commit()
|
||||
return result.rowcount
|
||||
|
||||
def drink_postpaid_user(user_id: int, drink_type: str = ""):
|
||||
def drink_postpaid_user(user_id: int, drink_type: int = 1):
|
||||
"""
|
||||
Deducts 100 units from the specified postpaid user's balance and records a drink entry.
|
||||
Args:
|
||||
@@ -635,7 +655,7 @@ def get_last_drink(user_id: int, user_is_postpaid: bool, max_since_seconds: int
|
||||
result = connection.execute(t, {"user_id": user_id}).fetchone()
|
||||
if not result:
|
||||
return None
|
||||
drink_id, timestamp, drink_type = result
|
||||
drink_id, timestamp, drink_type_id = result
|
||||
|
||||
if timestamp:
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
@@ -645,8 +665,13 @@ def get_last_drink(user_id: int, user_is_postpaid: bool, max_since_seconds: int
|
||||
last_drink_time = last_drink_time.replace(tzinfo=datetime.timezone.utc)
|
||||
if (now - last_drink_time).total_seconds() > max_since_seconds:
|
||||
return None
|
||||
drink_obj = {"id": drink_id, "timestamp": timestamp, "drink_type": drink_type}
|
||||
drink_obj = {"id": drink_id, "timestamp": timestamp, "drink_type_id": drink_type_id}
|
||||
if drink_type_id:
|
||||
drink_type_name, drink_type_icon = get_drink_type(drink_type_id)
|
||||
drink_obj["drink_type_name"] = drink_type_name
|
||||
drink_obj["drink_type_icon"] = drink_type_icon
|
||||
return drink_obj
|
||||
return None
|
||||
|
||||
def revert_last_drink(user_id: int, user_is_postpaid: bool, drink_id: int, drink_cost: int = DRINK_COST):
|
||||
"""
|
||||
@@ -696,7 +721,7 @@ def revert_last_drink(user_id: int, user_is_postpaid: bool, drink_id: int, drink
|
||||
description="Reverted last drink"
|
||||
)
|
||||
|
||||
def update_drink_type(user_id: int, user_is_postpaid: bool, drink_id, drink_type: str):
|
||||
def update_drink_type(user_id: int, user_is_postpaid: bool, drink_id, drink_type_id: int):
|
||||
"""
|
||||
Updates the drink type for a specific drink associated with a user.
|
||||
Depending on whether the user is postpaid or prepaid, the function updates the `drink_type`
|
||||
@@ -715,14 +740,52 @@ def update_drink_type(user_id: int, user_is_postpaid: bool, drink_id, drink_type
|
||||
t = text("UPDATE drinks SET drink_type = :drink_type WHERE postpaid_user_id = :user_id AND id = :drink_id")
|
||||
else:
|
||||
t = text("UPDATE drinks SET drink_type = :drink_type WHERE prepaid_user_id = :user_id AND id = :drink_id")
|
||||
|
||||
t_update_quantity = text("UPDATE drink_types SET quantity = quantity - 1 WHERE id = :drink_type_id")
|
||||
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t, {"user_id": user_id, "drink_id": drink_id, "drink_type": drink_type})
|
||||
result = connection.execute(t, {"user_id": user_id, "drink_id": drink_id, "drink_type": drink_type_id})
|
||||
if result.rowcount == 0:
|
||||
raise HTTPException(status_code=404, detail="Drink not found")
|
||||
|
||||
result_quantity = connection.execute(t_update_quantity, {"drink_type_id": drink_type_id})
|
||||
if result_quantity.rowcount != 1:
|
||||
raise HTTPException(status_code=404, detail="Drink type not found")
|
||||
connection.commit()
|
||||
return result.rowcount
|
||||
|
||||
def get_drink_type(drink_id: int):
|
||||
t = text("SELECT drink_name, icon FROM drink_types WHERE id = :drink_id")
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t, {"drink_id": drink_id}).fetchone()
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail="Drink type not found")
|
||||
return {"drink_name": result[0], "icon": result[1]}
|
||||
|
||||
def get_drink_type_by_name(drink_name: str):
|
||||
t = text("SELECT id, icon FROM drink_types WHERE drink_name = :drink_name")
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t, {"drink_name": drink_name}).fetchone()
|
||||
if not result:
|
||||
raise HTTPException(status_code=404, detail="Drink type not found")
|
||||
return {"drink_type_id": result[0], "icon": result[1]}
|
||||
|
||||
def add_drink_type(drink_name: str, icon: str, quantity: int = 0):
|
||||
t = text("INSERT INTO drink_types (drink_name, icon, quantity) VALUES (:drink_name, :icon, :quantity)")
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t, {"drink_name": drink_name, "icon": icon, "quantity": quantity})
|
||||
if result.rowcount == 0:
|
||||
raise HTTPException(status_code=500, detail="Failed to add drink type")
|
||||
connection.commit()
|
||||
|
||||
def set_drink_type_quantity(drink_type_id: int, quantity: int):
|
||||
t = text("UPDATE drink_types SET quantity = :quantity WHERE id = :drink_type_id")
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t, {"drink_type_id": drink_type_id, "quantity": quantity})
|
||||
if result.rowcount == 0:
|
||||
raise HTTPException(status_code=404, detail="Drink type not found")
|
||||
connection.commit()
|
||||
|
||||
def get_most_used_drinks(user_id: int, user_is_postpaid: bool, limit: int = 4):
|
||||
"""
|
||||
Return up to `limit` most used drinks for a user, filling with random drinks if needed.
|
||||
@@ -734,19 +797,34 @@ def get_most_used_drinks(user_id: int, user_is_postpaid: bool, limit: int = 4):
|
||||
list[dict]: Each dict has 'drink_type' and 'count'.
|
||||
"""
|
||||
if user_is_postpaid:
|
||||
t = text("SELECT drink_type, count(drink_type) as count FROM drinks WHERE postpaid_user_id = :user_id AND drink_type IS NOT NULL AND drink_type != 'Sonstiges' AND drink_type != 'None' GROUP BY drink_type ORDER BY count DESC LIMIT :limit")
|
||||
t = text("SELECT drink_type, count(drink_type) as count FROM drinks WHERE postpaid_user_id = :user_id AND drink_type != 1 GROUP BY drink_type ORDER BY count DESC LIMIT :limit")
|
||||
else:
|
||||
t = text("SELECT drink_type, count(drink_type) as count FROM drinks WHERE prepaid_user_id = :user_id AND drink_type IS NOT NULL AND drink_type != 'Sonstiges' AND drink_type != 'None' GROUP BY drink_type ORDER BY count DESC LIMIT :limit")
|
||||
t = text("SELECT drink_type, count(drink_type) as count FROM drinks WHERE prepaid_user_id = :user_id AND drink_type != 1 GROUP BY drink_type ORDER BY count DESC LIMIT :limit")
|
||||
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t, {"user_id": user_id, "limit": limit}).fetchall()
|
||||
drinks = [{"drink_type": row[0], "count": row[1]} for row in result]
|
||||
drinks = [{"drink_type_id": row[0], "count": row[1]} for row in result]
|
||||
|
||||
available_drink_ids_text = text("SELECT id FROM drink_types")
|
||||
available_drinks = connection.execute(available_drink_ids_text).fetchall()
|
||||
available_drink_ids = [row[0] for row in available_drinks]
|
||||
if 1 in available_drink_ids:
|
||||
available_drink_ids.remove(1)
|
||||
|
||||
while len(drinks) < limit:
|
||||
random_drink = random.choice(AVAILABLE_DRINKS)
|
||||
if any(drink["drink_type"] == random_drink for drink in drinks):
|
||||
if not available_drink_ids:
|
||||
print("No more available drink types to fill up the drinks list")
|
||||
break
|
||||
random_drink = random.choice(available_drink_ids)
|
||||
if any(drink["drink_type_id"] == random_drink for drink in drinks):
|
||||
available_drink_ids.remove(random_drink)
|
||||
continue
|
||||
drinks.append({"drink_type": random_drink, "count": 0})
|
||||
drinks.append({"drink_type_id": random_drink, "count": 0})
|
||||
|
||||
for drink in drinks:
|
||||
drink_type_info = get_drink_type(drink["drink_type_id"])
|
||||
drink["drink_type"] = drink_type_info["drink_name"]
|
||||
drink["icon"] = drink_type_info["icon"]
|
||||
|
||||
return drinks
|
||||
|
||||
@@ -765,6 +843,11 @@ def get_stats_drink_types():
|
||||
result = connection.execute(t).fetchall()
|
||||
if not result:
|
||||
return []
|
||||
drinks = [{"drink_type": row[0], "count": row[1]} for row in result]
|
||||
drinks = [{"drink_type_id": row[0], "count": row[1]} for row in result]
|
||||
|
||||
for drink in drinks:
|
||||
drink_type_info = get_drink_type(drink["drink_type_id"])
|
||||
drink["drink_type"] = drink_type_info["drink_name"]
|
||||
drink["icon"] = drink_type_info["icon"]
|
||||
|
||||
return drinks
|
||||
|
||||
55
main.py
@@ -47,6 +47,7 @@ def home(request: Request):
|
||||
# if user is Admin, load all postpaid users
|
||||
users = None
|
||||
db_users_prepaid = None
|
||||
drink_types = []
|
||||
if ADMIN_GROUP in user_authentik["groups"]:
|
||||
with engine.connect() as conn:
|
||||
t = text("SELECT id FROM users_postpaid")
|
||||
@@ -58,6 +59,9 @@ def home(request: Request):
|
||||
if user_db:
|
||||
users.append(user_db)
|
||||
|
||||
t2 = text("SELECT id, drink_name, icon, quantity FROM drink_types")
|
||||
drink_types = conn.execute(t2).fetchall()
|
||||
|
||||
# if user is in Fachschaft, load all prepaid users
|
||||
prepaid_users_from_curr_user = []
|
||||
if FS_GROUP in user_authentik["groups"]:
|
||||
@@ -91,7 +95,7 @@ def home(request: Request):
|
||||
last_drink = db.models.get_last_drink(user_db_id, user_is_postpaid, 60)
|
||||
|
||||
most_used_drinks = db.models.get_most_used_drinks(user_db_id, user_is_postpaid, 3)
|
||||
most_used_drinks.append({"drink_type": "Sonstiges", "count": 0}) # ensure "Sonstiges" is in
|
||||
most_used_drinks.append({"drink_type_id": 1, "drink_type": "Sonstiges", "count": 0, "icon": "sonstiges.png"}) # ensure "Sonstiges" is in
|
||||
|
||||
return templates.TemplateResponse("index.html", {
|
||||
"request": request,
|
||||
@@ -103,6 +107,7 @@ def home(request: Request):
|
||||
"prepaid_users_from_curr_user": prepaid_users_from_curr_user,
|
||||
"last_drink": last_drink,
|
||||
"avail_drink_types": most_used_drinks,
|
||||
"drink_types": drink_types,
|
||||
})
|
||||
|
||||
@app.get("/login", response_class=HTMLResponse)
|
||||
@@ -465,7 +470,9 @@ def update_drink_post(request: Request, drink_type: str = Form(...)):
|
||||
if not drink_type:
|
||||
raise HTTPException(status_code=400, detail="Drink type is empty")
|
||||
|
||||
db.models.update_drink_type(user_db_id, get_is_postpaid(user_authentik), last_drink["id"], drink_type)
|
||||
drink_type_id = db.models.get_drink_type_by_name(drink_type)["drink_type_id"]
|
||||
|
||||
db.models.update_drink_type(user_db_id, get_is_postpaid(user_authentik), last_drink["id"], drink_type_id)
|
||||
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
|
||||
@@ -496,6 +503,50 @@ def stats(request: Request):
|
||||
"user_db_id": user_db_id,
|
||||
"stats_drink_types": drink_types,
|
||||
})
|
||||
|
||||
@app.post("/add_drink_type")
|
||||
def add_drink_type(request: Request, drink_type: str = Form(...), icon: str = Form(...)):
|
||||
user_authentik = request.session.get("user_authentik")
|
||||
if not user_authentik or ADMIN_GROUP not in user_authentik["groups"]:
|
||||
raise HTTPException(status_code=403, detail="Not allowed")
|
||||
user_db_id = request.session.get("user_db_id")
|
||||
if not user_db_id:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if not drink_type:
|
||||
raise HTTPException(status_code=400, detail="Drink type is empty")
|
||||
if not icon:
|
||||
raise HTTPException(status_code=400, detail="Icon is empty")
|
||||
if len(drink_type) < 3 or len(drink_type) > 20:
|
||||
raise HTTPException(status_code=400, detail="Drink type must be between 3 and 20 characters")
|
||||
if len(icon) < 3 or len(icon) > 20:
|
||||
raise HTTPException(status_code=400, detail="Icon must be between 3 and 20 characters")
|
||||
if not icon.endswith(".png"):
|
||||
raise HTTPException(status_code=400, detail="Icon must be a .png file")
|
||||
if db.models.get_drink_type_by_name(drink_type):
|
||||
raise HTTPException(status_code=400, detail="Drink type already exists")
|
||||
|
||||
db.models.add_drink_type(drink_type, icon)
|
||||
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
|
||||
@app.post("/set_drink_type_quantity")
|
||||
def drink_type_set_quantity(request: Request, drink_type_name: str = Form(...), quantity: int = Form(...)):
|
||||
user_authentik = request.session.get("user_authentik")
|
||||
if not user_authentik or ADMIN_GROUP not in user_authentik["groups"]:
|
||||
raise HTTPException(status_code=403, detail="Not allowed")
|
||||
user_db_id = request.session.get("user_db_id")
|
||||
if not user_db_id:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
if not drink_type_name or not quantity:
|
||||
raise HTTPException(status_code=400, detail="Drink type name and quantity are required")
|
||||
if quantity < 0 or quantity > 10000:
|
||||
raise HTTPException(status_code=400, detail="Quantity must be between 0 and 10000")
|
||||
|
||||
drink_type_id = db.models.get_drink_type_by_name(drink_type_name)["drink_type_id"]
|
||||
|
||||
db.models.set_drink_type_quantity(drink_type_id, quantity)
|
||||
|
||||
return RedirectResponse(url="/", status_code=303)
|
||||
|
||||
|
||||
def get_is_postpaid(user_authentik: dict) -> bool:
|
||||
|
||||
BIN
static/drinks/mio_mate_ginger.png
Normal file
|
After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
BIN
static/drinks/mio_mate_pomegranate.png
Normal file
|
After Width: | Height: | Size: 217 KiB |
BIN
static/drinks/paulaner_limo_orange.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
static/drinks/paulaner_limo_zitrone.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
static/drinks/rosbacher_iso.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
@@ -81,7 +81,7 @@ content %}
|
||||
<input type="hidden" name="drink_type" value="{{ drink.drink_type }}">
|
||||
<button type="submit"
|
||||
style="display: flex; flex-direction: column; align-items: center; background-color: var(--goetheblau); color: #fff; border: none; border-radius: 8px; padding: 0.7em 1.2em; cursor: pointer; min-width: 120px;">
|
||||
<img src="/static/drinks/{{ drink.drink_type|lower|replace(' ', '_') }}.png" alt="{{ drink.drink_type }}" style="width:48px; height:48px; object-fit:contain; margin-bottom:0.5em;">
|
||||
<img src="/static/drinks/{{ drink.icon }}" alt="{{ drink.drink_type }}" style="width:48px; height:48px; object-fit:contain; margin-bottom:0.5em;">
|
||||
<span>{{ drink.drink_type }}</span>
|
||||
{% if drink.count > 0 %}
|
||||
<span style="font-size:0.9em; color:#eee;">x{{ drink.count }}</span>
|
||||
@@ -155,14 +155,8 @@ content %}
|
||||
ID {{ db_user.postpaid_user_id }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="popup" style="display:none; position:fixed; top:25%; left:50%; transform:translate(-50%, -25%);
|
||||
background:#fff; border:2px solid #00618F; border-radius:12px; padding:1em; box-shadow:0 0 20px rgba(0,0,0,0.3); z-index:1000; max-width:90%; text-align:center;">
|
||||
<h2>Wähle dein Getränk oder warte bitte</h2>
|
||||
<div id="popup-getraenke" style="margin: 1em 0;"></div>
|
||||
<div id="progress-container" style="height:10px; background:#ccc; border-radius:5px; overflow:hidden;">
|
||||
<div id="progress-bar" style="height:10px; background:#00618F; width:100%; transition: width 0.1s linear;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% if user %}
|
||||
{% if 'Getraenkeliste Postpaid' in user.groups %}
|
||||
@@ -327,6 +321,53 @@ content %}
|
||||
<td colspan="7" style="text-align: center">No users in prepaid database</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<h3>Getränkesorten</h3>
|
||||
<p>Verfügbare Getränkesorten:</p>
|
||||
{% if drink_types %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding: 0.5em 1em">ID</th>
|
||||
<th style="padding: 0.5em 1em">Name</th>
|
||||
<th style="padding: 0.5em 1em">Icon</th>
|
||||
<th style="padding: 0.5em 1em">Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for drink_type in drink_types %}
|
||||
<td style="padding: 0.5em 1em">{{ drink_type[0] }}</td>
|
||||
<td style="padding: 0.5em 1em">{{ drink_type[1] }}</td>
|
||||
<td style="padding: 0.5em 1em">{{ drink_type[2] }}</td>
|
||||
<td style="padding: 0.5em 1em">{{ drink_type[3] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>Keine Getränkesorten vorhanden.</p>
|
||||
{% endif %}
|
||||
<p>Füge Getränkesorte hinzu:</p>
|
||||
<form method="post" action="/add_drink_type" style="display: flex; gap: 1em; align-items: center; margin-bottom: 1em; background: var(--hellgrau); padding: 1em; border-radius: 8px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); max-width: 600px;">
|
||||
<label for="drink_type_name" style="margin: 0 0.5em 0 0; font-weight: bold">Name:</label>
|
||||
<input id="drink_type_name" type="text" name="drink_type_name" placeholder="Drink Type Name" required style="padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; width: 100%;" />
|
||||
<label for="drink_type_icon" style="margin: 0 0.5em 0 0; font-weight: bold">Icon:</label>
|
||||
<input id="drink_type_icon" type="text" name="drink_type_icon" placeholder="Drink Type Icon" required style="padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; width: 100%;" />
|
||||
<button type="submit" style="padding: 0.5em 1em; background: rgb(0, 97, 143); color: #fff; border: none; border-radius: 4px; cursor: pointer;">Add Drink Type</button>
|
||||
</form>
|
||||
<p>Setze Menge der Getränkesorte:</p>
|
||||
<form method="post" action="/set_drink_type_quantity" style="display: flex; gap: 1em; align-items: center; margin-bottom: 1em; background: var(--hellgrau); padding: 1em; border-radius: 8px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); max-width: 600px;">
|
||||
<label for="drink_type_select" style="margin: 0 0.5em 0 0; font-weight: bold">Getränkesorte:</label>
|
||||
<select id="drink_type_select" name="drink_type_name" required style="padding: 0.5em; border: 1px solid #ccc; border-radius: 4px;">
|
||||
{% for drink_type in drink_types %}
|
||||
<option value="{{ drink_type[1] }}">{{ drink_type[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="drink_type_quantity" style="margin: 0 0.5em 0 0; font-weight: bold">Menge:</label>
|
||||
<input id="drink_type_quantity" type="number" name="quantity" min="0" step="1" required style="padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; width: 100px;" />
|
||||
<button type="submit" style="padding: 0.5em 1em; background: rgb(0, 97, 143); color: #fff; border: none; border-radius: 4px; cursor: pointer;">Setzen</button>
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||