Color-code drink buttons by category

Add a Drink.category field (beer, alc_free_beer, radler, alc_free_radler,
soft, water) and apply per-category gradient backgrounds to the booking
buttons so users can recognize drinks at a glance. Sort buttons by
category, shrink them to a 3:2 aspect ratio, and switch labels to more
verbose brand names (Sternburg Export, Ur-Krostitzer, etc.).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 22:56:27 +02:00
parent 2b46c7cd54
commit b10e434d0c
7 changed files with 132 additions and 20 deletions
@@ -18,18 +18,18 @@ PAYMENTS = [
]
DRINKS = [
# name, crates, btl/crate, size, price/crate, deposit/crate, sale/btl
("Sterni", 12, 20, 0.5, 10.99, 3.10, 2.00),
("Krosti", 5, 20, 0.5, 16.49, 3.10, 2.50),
("Buddi", 5, 20, 0.5, 20.99, 3.10, 2.50),
("Helles", 5, 20, 0.5, 15.99, 4.50, 2.50),
("Radler", 2, 20, 0.5, 14.99, 3.10, 2.50),
("Lübzer 0,0", 1, 20, 0.5, 17.99, 3.10, 2.50),
("Freiberger 0,0", 4, 20, 0.5, 15.49, 3.10, 2.50),
("Mate", 2, 20, 0.5, 17.49, 4.50, 2.50),
("Vita Cola", 2, 12, 1.0, 10.99, 3.30, 2.50),
("Spezi", 2, 20, 0.5, 17.99, 3.10, 2.50),
("Wasser", 10, 12, 1.0, 5.99, 3.30, 1.50),
# name, category, crates, btl/crate, size, price/crate, deposit/crate, sale/btl
("Sternburg Export", "beer", 12, 20, 0.5, 10.99, 3.10, 2.00),
("Ur-Krostitzer", "beer", 5, 20, 0.5, 16.49, 3.10, 2.50),
("Budweiser", "beer", 5, 20, 0.5, 20.99, 3.10, 2.50),
("Altenburger Helles", "beer", 5, 20, 0.5, 15.99, 4.50, 2.50),
("Feldschl. Radler", "radler", 2, 20, 0.5, 14.99, 3.10, 2.50),
("Lübzer Grapef. 0,0", "alc_free_radler", 1, 20, 0.5, 17.99, 3.10, 2.50),
("Freiberger 0,0", "alc_free_beer", 4, 20, 0.5, 15.49, 3.10, 2.50),
("Club Mate", "soft", 2, 20, 0.5, 17.49, 4.50, 2.50),
("Vita Cola", "soft", 2, 12, 1.0, 10.99, 3.30, 2.50),
("Paulaner Spezi", "soft", 2, 20, 0.5, 17.99, 3.10, 2.50),
("Wasser", "water", 10, 12, 1.0, 5.99, 3.30, 1.50),
]
@@ -46,11 +46,12 @@ class Command(BaseCommand):
)
self.stdout.write(f"{'created' if created else 'updated'}: {obj.purpose} ({obj.date})")
for name, crates, btl, size, price, deposit, sale in DRINKS:
for name, category, crates, btl, size, price, deposit, sale in DRINKS:
obj, created = Drink.objects.update_or_create(
name=name,
year=2026,
defaults={
"category": category,
"crates_ordered": crates,
"crates_purchased": crates,
"crates_returned": 0,
@@ -0,0 +1,18 @@
# Generated by Django 6.0.5 on 2026-05-14 20:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gaehsnitz', '0006_user_payment'),
]
operations = [
migrations.AddField(
model_name='drink',
name='category',
field=models.CharField(choices=[('beer', 'Bier'), ('radler', 'Radler'), ('alc_free_beer', 'Bier alkoholfrei'), ('soft', 'Softdrink'), ('water', 'Wasser')], default='beer', max_length=16, verbose_name='Kategorie'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 6.0.5 on 2026-05-14 20:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gaehsnitz', '0007_drink_category'),
]
operations = [
migrations.AlterField(
model_name='drink',
name='category',
field=models.CharField(choices=[('beer', 'Bier'), ('radler', 'Radler'), ('alc_free_beer', 'Bier alkoholfrei'), ('alc_free_radler', 'Radler alkoholfrei'), ('soft', 'Softdrink'), ('water', 'Wasser')], default='beer', max_length=16, verbose_name='Kategorie'),
),
]
+14
View File
@@ -83,8 +83,22 @@ class Payment(models.Model):
class Drink(models.Model):
class Category(models.TextChoices):
beer = "beer", "Bier"
radler = "radler", "Radler"
alc_free_beer = "alc_free_beer", "Bier alkoholfrei"
alc_free_radler = "alc_free_radler", "Radler alkoholfrei"
soft = "soft", "Softdrink"
water = "water", "Wasser"
name = models.CharField("Name", max_length=32)
year = models.PositiveSmallIntegerField("Jahr", default=2024)
category = models.CharField(
"Kategorie",
max_length=16,
choices=Category.choices,
default=Category.beer,
)
crates_ordered = models.PositiveSmallIntegerField(
"Kästen bestellt",
help_text="nur zur Info, wie gut wir geplant haben — nicht die tatsächlich konsumierten/bezahlten Flaschen",
+48 -4
View File
@@ -203,14 +203,58 @@ section {
.drink-btn {
width: 100%;
aspect-ratio: 4 / 3;
aspect-ratio: 3 / 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
padding: 12px 8px;
gap: 2px;
padding: 8px;
line-height: 1.2;
color: #161616;
text-shadow: none;
}
.drink-btn-beer {
background: linear-gradient(180deg, #f0c878 0%, #b8731a 100%);
}
.drink-btn-beer:hover, .drink-btn-beer:focus {
background: linear-gradient(180deg, #f7d699 0%, #cc8520 100%);
}
.drink-btn-radler {
background: linear-gradient(180deg, #f7e36b 0%, #d8a02a 100%);
}
.drink-btn-radler:hover, .drink-btn-radler:focus {
background: linear-gradient(180deg, #fff08c 0%, #e8b034 100%);
}
.drink-btn-alc_free_beer {
background: linear-gradient(180deg, #f0c878 0%, #5e8bbf 100%);
}
.drink-btn-alc_free_beer:hover, .drink-btn-alc_free_beer:focus {
background: linear-gradient(180deg, #f7d699 0%, #7aa3d1 100%);
}
.drink-btn-alc_free_radler {
background: linear-gradient(180deg, #f7e36b 0%, #5e8bbf 100%);
}
.drink-btn-alc_free_radler:hover, .drink-btn-alc_free_radler:focus {
background: linear-gradient(180deg, #fff08c 0%, #7aa3d1 100%);
}
.drink-btn-soft {
background: linear-gradient(180deg, #f0a35e 0%, #a85a22 100%);
}
.drink-btn-soft:hover, .drink-btn-soft:focus {
background: linear-gradient(180deg, #f7b878 0%, #c06a2c 100%);
}
.drink-btn-water {
background: linear-gradient(180deg, #d5e8f4 0%, #95c2dc 100%);
}
.drink-btn-water:hover, .drink-btn-water:focus {
background: linear-gradient(180deg, #e4f0f8 0%, #aad0e6 100%);
}
.drink-plus {
@@ -228,7 +272,7 @@ section {
.drink-price {
font-size: 0.85rem;
font-weight: normal;
opacity: 0.8;
opacity: 0.75;
}
.history {
+19 -2
View File
@@ -6,7 +6,7 @@ from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth import get_user_model
from django.db.models import F, Sum
from django.db.models import Case, F, IntegerField, Sum, Value, When
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
@@ -173,7 +173,24 @@ def pin_view(request):
@require_http_methods(["GET"])
def me_view(request):
phase = _require_open(request)
drinks = Drink.objects.filter(year=current_year()).order_by("name") if phase == "booking" else Drink.objects.none()
drinks = (
Drink.objects.filter(year=current_year())
.annotate(
category_order=Case(
When(category="beer", then=Value(0)),
When(category="alc_free_beer", then=Value(1)),
When(category="radler", then=Value(2)),
When(category="alc_free_radler", then=Value(3)),
When(category="soft", then=Value(4)),
When(category="water", then=Value(5)),
default=Value(99),
output_field=IntegerField(),
)
)
.order_by("category_order", "name")
if phase == "booking"
else Drink.objects.none()
)
booked_drink = None
booked_id = request.GET.get("booked")
if booked_id:
+1 -1
View File
@@ -42,7 +42,7 @@
<form method="post" action="{% url 'suff:book' %}">
{% csrf_token %}
<input type="hidden" name="drink_id" value="{{ drink.id }}" />
<button type="submit" class="drink-btn">
<button type="submit" class="drink-btn drink-btn-{{ drink.category }}">
<span class="drink-plus">+1</span>
<span class="drink-name">{{ drink.name }}</span>
<span class="drink-price">{{ drink.sale_price_per_bottle|floatformat:2 }} €</span>