Compare commits
3 Commits
4a470ae09e
...
847cac4bba
| Author | SHA1 | Date | |
|---|---|---|---|
| 847cac4bba | |||
| 102e03b546 | |||
| 1d29e954b8 |
@@ -164,6 +164,7 @@ async def add_team_registration(request: Request, event_id: int, session: Sessio
|
||||
statement = select(TeamRegistration).where(
|
||||
TeamRegistration.person_name == person, TeamRegistration.work_type == work_type
|
||||
)
|
||||
# if the person has already registered for the same work type, just ignore
|
||||
if session.exec(statement).one_or_none() is None:
|
||||
registration = TeamRegistration(
|
||||
person_name=person,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import typing
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from sqlmodel import Field, Relationship, SQLModel, String
|
||||
|
||||
WorkTypes = typing.Literal["cooking", "dishes", "tables"]
|
||||
|
||||
|
||||
class Event(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
@@ -19,20 +21,45 @@ class Event(SQLModel, table=True):
|
||||
team_dishes_min: int = 3
|
||||
team_dishes_max: int = 5
|
||||
|
||||
# Todo: Rename to "table"
|
||||
team_prep_min: int = 1
|
||||
team_prep_max: int = 1
|
||||
|
||||
registrations: list["Registration"] = Relationship()
|
||||
team: list["TeamRegistration"] = Relationship()
|
||||
|
||||
def team_min_reached(self, work_type: WorkTypes):
|
||||
threshold = {
|
||||
"cooking": self.team_cooking_min,
|
||||
"dishes": self.team_dishes_min,
|
||||
"tables": self.team_prep_min,
|
||||
}[work_type]
|
||||
return sum(1 for t in self.team if t.work_type == work_type) >= threshold
|
||||
|
||||
def team_max_reached(self, work_type: WorkTypes):
|
||||
threshold = {
|
||||
"cooking": self.team_cooking_max,
|
||||
"dishes": self.team_dishes_max,
|
||||
"tables": self.team_prep_max,
|
||||
}[work_type]
|
||||
return sum(1 for t in self.team if t.work_type == work_type) >= threshold
|
||||
|
||||
def all_teams_min(self):
|
||||
return all(
|
||||
self.team_min_reached(work_type) for work_type in typing.get_args(WorkTypes)
|
||||
)
|
||||
|
||||
def all_teams_max(self):
|
||||
return all(
|
||||
self.team_max_reached(work_type) for work_type in typing.get_args(WorkTypes)
|
||||
)
|
||||
|
||||
|
||||
class TeamRegistration(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
event_id: int | None = Field(default=None, foreign_key="event.id")
|
||||
person_name: str = Field(nullable=False)
|
||||
work_type: Literal["cooking", "dishes", "tables"] = Field(
|
||||
nullable=False, sa_type=String
|
||||
)
|
||||
work_type: WorkTypes = Field(nullable=False, sa_type=String)
|
||||
comment: str | None
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Allmende Essen</title>
|
||||
<link href="/static/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB">
|
||||
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/icons/bootstrap-icons.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="p-3 m-0 border-0 bd-example m-0 border-0">
|
||||
<body class="p-3 m-0 border-0 bd-example">
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
@@ -38,7 +37,6 @@
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="/static/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"></script>
|
||||
<script src="/static/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -18,21 +18,19 @@
|
||||
<hr class="hr"/>
|
||||
<p class="h3">Anmeldungen</p>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="row m-2">
|
||||
<div class="col-md-4 d-flex justify-content-center align-items-center flex-column">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-primary mb-2" data-bs-toggle="modal" data-bs-target="#registration">
|
||||
<button type="button" class="btn btn-primary mb-2 w-100" data-bs-toggle="modal" data-bs-target="#registration">
|
||||
Anmeldung hinzufügen
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary mb-2" data-bs-toggle="modal" data-bs-target="#teamRegistration">
|
||||
<button type="button" class="btn btn-primary mb-2 w-100" {% if event.all_teams_max() %}disabled{% endif %} data-bs-toggle="modal" data-bs-target="#teamRegistration">
|
||||
Dienst übernehmen
|
||||
</button>
|
||||
{% if event.recipe_link %}
|
||||
<div class="mb-3">
|
||||
<a href="{{ event.recipe_link }}" class="btn btn-outline-primary" target="_blank">
|
||||
<i class="bi bi-book"></i> Original Rezept ansehen
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{ event.recipe_link }}" class="btn btn-outline-primary mb-2 w-100" target="_blank">
|
||||
<i class="bi bi-book"></i> Original Rezept ansehen
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@@ -41,7 +39,7 @@
|
||||
<h5 class="card-title">Dienste</h5>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
Kochen: {% if event.team | selectattr("work_type", "equalto", "cooking") | list | count >= 3 %}✅{% endif %}
|
||||
Kochen: {% if event.team_min_reached("cooking") %}✅{% endif %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ teamEntries(event, "cooking") }}
|
||||
@@ -50,7 +48,7 @@
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
Spülen: {% if event.team | selectattr("work_type", "equalto", "dishes") | list | count >= 3 %}✅{% endif %}
|
||||
Spülen: {% if event.team_min_reached("dishes") %}✅{% endif %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ teamEntries(event, "dishes") }}
|
||||
@@ -59,7 +57,7 @@
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
Tische vorbereiten: {% if event.team | selectattr("work_type", "equalto", "tables") | list | count >= 1 %}✅{% endif %}
|
||||
Tische vorbereiten: {% if event.team_min_reached("tables") %}✅{% endif %}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{{ teamEntries(event, "tables") }}
|
||||
@@ -95,28 +93,61 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Haushalt</th>
|
||||
<th scope="col">Erwachsene</th>
|
||||
<th scope="col">Kinder</th>
|
||||
<th scope="col">Kleinkinder</th>
|
||||
<th scope="col">Löschen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for reg in event.registrations %}
|
||||
<tr>
|
||||
<td>{{ reg.household.name }}</td>
|
||||
<td>{{ reg.num_adult_meals }}</td>
|
||||
<td>{{ reg.num_children_meals }}</td>
|
||||
<td>{{ reg.num_small_children_meals }}</td>
|
||||
<td><a href="/event/{{event.id}}/registration/{{reg.household_id}}/delete"><i class="bi bi-trash"></i></a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Desktop table view -->
|
||||
<div class="d-none d-md-block">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Haushalt</th>
|
||||
<th scope="col">Erwachsene</th>
|
||||
<th scope="col">Kinder</th>
|
||||
<th scope="col">Kleinkinder</th>
|
||||
<th scope="col">Löschen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for reg in event.registrations %}
|
||||
<tr>
|
||||
<td>{{ reg.household.name }}</td>
|
||||
<td>{{ reg.num_adult_meals }}</td>
|
||||
<td>{{ reg.num_children_meals }}</td>
|
||||
<td>{{ reg.num_small_children_meals }}</td>
|
||||
<td><a href="/event/{{event.id}}/registration/{{reg.household_id}}/delete"><i class="bi bi-trash"></i></a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Mobile card view -->
|
||||
<div class="d-md-none">
|
||||
{% for reg in event.registrations %}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<h5 class="card-title mb-0">{{ reg.household.name }}</h5>
|
||||
<a href="/event/{{event.id}}/registration/{{reg.household_id}}/delete" class="text-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-4 text-center">
|
||||
<div class="text-muted small">Erwachsene</div>
|
||||
<div class="fw-bold">{{ reg.num_adult_meals }}</div>
|
||||
</div>
|
||||
<div class="col-4 text-center">
|
||||
<div class="text-muted small">Kinder</div>
|
||||
<div class="fw-bold">{{ reg.num_children_meals }}</div>
|
||||
</div>
|
||||
<div class="col-4 text-center">
|
||||
<div class="text-muted small">Kleinkinder</div>
|
||||
<div class="fw-bold">{{ reg.num_small_children_meals }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="registration" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
|
||||
@@ -143,18 +174,18 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label for="InputAdults" class="form-label">Anzahl Erwachsene</label>
|
||||
<input name="numAdults" id="InputAdults" type="text" class="form-control"
|
||||
aria-label="Anzahl Erwachsene">
|
||||
<input name="numAdults" id="InputAdults" type="number" class="form-control"
|
||||
aria-label="Anzahl Erwachsene" min="0" step="1" inputmode="numeric">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="InputKids" class="form-label">Anzahl Kinder >7 </label>
|
||||
<input name="numKids" id="InputKids" type="text" class="form-control"
|
||||
aria-label="Anzahl Kinder >7">
|
||||
<input name="numKids" id="InputKids" type="number" class="form-control"
|
||||
aria-label="Anzahl Kinder >7" min="0" step="1" inputmode="numeric">
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="InputSmallKids" class="form-label">Anzahl Kinder <7 </label>
|
||||
<input name="numSmallKids" id="InputSmallKids" type="text" class="form-control"
|
||||
aria-label="Anzahl Kinder <7">
|
||||
<input name="numSmallKids" id="InputSmallKids" type="number" class="form-control"
|
||||
aria-label="Anzahl Kinder <7" min="0" step="1" inputmode="numeric">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -188,9 +219,9 @@
|
||||
<div class="col-md-6">
|
||||
<label for="workType" class="form-label">Dienst-Art</label>
|
||||
<select id="workType" name="workType" class="form-select" aria-label="Multiple select example">
|
||||
<option value="cooking">Kochen</option>
|
||||
<option value="dishes">Spülen</option>
|
||||
<option value="tables">Tische vorbereiten</option>
|
||||
{% if not event.team_max_reached("cooking") %}<option value="cooking">Kochen</option>{% endif %}
|
||||
{% if not event.team_max_reached("dishes") %}<option value="dishes">Spülen</option>{% endif %}
|
||||
{% if not event.team_max_reached("tables") %}<option value="tables">Tische vorbereiten</option>{% endif %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,22 +8,20 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||
{% for event in events %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card p-3 m-3">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title">{{ event.title }}</h5>
|
||||
<h5 class="text-muted"><i class="bi bi-calendar"></i> {{ event.event_time.strftime('%A, %d.%m.%Y')
|
||||
<h5 class="card-title mb-1">{{ event.title }}</h5>
|
||||
<p class="text-muted mb-3"><i class="bi bi-calendar"></i> {{ event.event_time.strftime('%A, %d.%m.%Y')
|
||||
}}
|
||||
</h5>
|
||||
</div>
|
||||
</p>
|
||||
<p class="card-text">{{ event.description }}</p>
|
||||
<a href="event/{{ event.id }}" class="btn {% if event.registration_deadline > now %}btn-primary{% else %}btn-secondary{% endif %}">{% if event.registration_deadline > now %}Zur Anmeldung{% else %}Details ansehen{% endif %}</a>
|
||||
<a href="event/{{ event.id }}" class="btn btn-sm {% if event.registration_deadline > now %}btn-primary{% else %}btn-secondary{% endif %}">{% if event.registration_deadline > now %}Zur Anmeldung{% else %}Details ansehen{% endif %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user