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(
|
statement = select(TeamRegistration).where(
|
||||||
TeamRegistration.person_name == person, TeamRegistration.work_type == work_type
|
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:
|
if session.exec(statement).one_or_none() is None:
|
||||||
registration = TeamRegistration(
|
registration = TeamRegistration(
|
||||||
person_name=person,
|
person_name=person,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import typing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Literal
|
|
||||||
|
|
||||||
from sqlmodel import Field, Relationship, SQLModel, String
|
from sqlmodel import Field, Relationship, SQLModel, String
|
||||||
|
|
||||||
|
WorkTypes = typing.Literal["cooking", "dishes", "tables"]
|
||||||
|
|
||||||
|
|
||||||
class Event(SQLModel, table=True):
|
class Event(SQLModel, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=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_min: int = 3
|
||||||
team_dishes_max: int = 5
|
team_dishes_max: int = 5
|
||||||
|
|
||||||
|
# Todo: Rename to "table"
|
||||||
team_prep_min: int = 1
|
team_prep_min: int = 1
|
||||||
team_prep_max: int = 1
|
team_prep_max: int = 1
|
||||||
|
|
||||||
registrations: list["Registration"] = Relationship()
|
registrations: list["Registration"] = Relationship()
|
||||||
team: list["TeamRegistration"] = 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):
|
class TeamRegistration(SQLModel, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
event_id: int | None = Field(default=None, foreign_key="event.id")
|
event_id: int | None = Field(default=None, foreign_key="event.id")
|
||||||
person_name: str = Field(nullable=False)
|
person_name: str = Field(nullable=False)
|
||||||
work_type: Literal["cooking", "dishes", "tables"] = Field(
|
work_type: WorkTypes = Field(nullable=False, sa_type=String)
|
||||||
nullable=False, sa_type=String
|
|
||||||
)
|
|
||||||
comment: str | None
|
comment: str | None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,12 @@
|
|||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Allmende Essen</title>
|
<title>Allmende Essen</title>
|
||||||
<link href="/static/css/bootstrap.min.css" rel="stylesheet"
|
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
|
||||||
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB">
|
|
||||||
<link href="/static/icons/bootstrap-icons.min.css" rel="stylesheet">
|
<link href="/static/icons/bootstrap-icons.min.css" rel="stylesheet">
|
||||||
</head>
|
</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">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="/">
|
<a class="navbar-brand" href="/">
|
||||||
@@ -38,7 +37,6 @@
|
|||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/bootstrap.bundle.min.js"
|
<script src="/static/js/bootstrap.bundle.min.js"></script>
|
||||||
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -18,21 +18,19 @@
|
|||||||
<hr class="hr"/>
|
<hr class="hr"/>
|
||||||
<p class="h3">Anmeldungen</p>
|
<p class="h3">Anmeldungen</p>
|
||||||
<div class="container">
|
<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">
|
<div class="col-md-4 d-flex justify-content-center align-items-center flex-column">
|
||||||
<!-- Button trigger modal -->
|
<!-- 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
|
Anmeldung hinzufügen
|
||||||
</button>
|
</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
|
Dienst übernehmen
|
||||||
</button>
|
</button>
|
||||||
{% if event.recipe_link %}
|
{% if event.recipe_link %}
|
||||||
<div class="mb-3">
|
<a href="{{ event.recipe_link }}" class="btn btn-outline-primary mb-2 w-100" target="_blank">
|
||||||
<a href="{{ event.recipe_link }}" class="btn btn-outline-primary" target="_blank">
|
|
||||||
<i class="bi bi-book"></i> Original Rezept ansehen
|
<i class="bi bi-book"></i> Original Rezept ansehen
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
@@ -41,7 +39,7 @@
|
|||||||
<h5 class="card-title">Dienste</h5>
|
<h5 class="card-title">Dienste</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<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>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
{{ teamEntries(event, "cooking") }}
|
{{ teamEntries(event, "cooking") }}
|
||||||
@@ -50,7 +48,7 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<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>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
{{ teamEntries(event, "dishes") }}
|
{{ teamEntries(event, "dishes") }}
|
||||||
@@ -59,7 +57,7 @@
|
|||||||
<hr/>
|
<hr/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<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>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
{{ teamEntries(event, "tables") }}
|
{{ teamEntries(event, "tables") }}
|
||||||
@@ -95,7 +93,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table">
|
<!-- Desktop table view -->
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Haushalt</th>
|
<th scope="col">Haushalt</th>
|
||||||
@@ -116,7 +116,38 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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>
|
</div>
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal fade" id="registration" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
|
<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="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label for="InputAdults" class="form-label">Anzahl Erwachsene</label>
|
<label for="InputAdults" class="form-label">Anzahl Erwachsene</label>
|
||||||
<input name="numAdults" id="InputAdults" type="text" class="form-control"
|
<input name="numAdults" id="InputAdults" type="number" class="form-control"
|
||||||
aria-label="Anzahl Erwachsene">
|
aria-label="Anzahl Erwachsene" min="0" step="1" inputmode="numeric">
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label for="InputKids" class="form-label">Anzahl Kinder >7 </label>
|
<label for="InputKids" class="form-label">Anzahl Kinder >7 </label>
|
||||||
<input name="numKids" id="InputKids" type="text" class="form-control"
|
<input name="numKids" id="InputKids" type="number" class="form-control"
|
||||||
aria-label="Anzahl Kinder >7">
|
aria-label="Anzahl Kinder >7" min="0" step="1" inputmode="numeric">
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<label for="InputSmallKids" class="form-label">Anzahl Kinder <7 </label>
|
<label for="InputSmallKids" class="form-label">Anzahl Kinder <7 </label>
|
||||||
<input name="numSmallKids" id="InputSmallKids" type="text" class="form-control"
|
<input name="numSmallKids" id="InputSmallKids" type="number" class="form-control"
|
||||||
aria-label="Anzahl Kinder <7">
|
aria-label="Anzahl Kinder <7" min="0" step="1" inputmode="numeric">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -188,9 +219,9 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="workType" class="form-label">Dienst-Art</label>
|
<label for="workType" class="form-label">Dienst-Art</label>
|
||||||
<select id="workType" name="workType" class="form-select" aria-label="Multiple select example">
|
<select id="workType" name="workType" class="form-select" aria-label="Multiple select example">
|
||||||
<option value="cooking">Kochen</option>
|
{% if not event.team_max_reached("cooking") %}<option value="cooking">Kochen</option>{% endif %}
|
||||||
<option value="dishes">Spülen</option>
|
{% if not event.team_max_reached("dishes") %}<option value="dishes">Spülen</option>{% endif %}
|
||||||
<option value="tables">Tische vorbereiten</option>
|
{% if not event.team_max_reached("tables") %}<option value="tables">Tische vorbereiten</option>{% endif %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,22 +8,20 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
|
||||||
{% for event in events %}
|
{% for event in events %}
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card p-3 m-3">
|
<div class="card h-100 shadow-sm">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<h5 class="card-title mb-1">{{ event.title }}</h5>
|
||||||
<h5 class="card-title">{{ event.title }}</h5>
|
<p class="text-muted mb-3"><i class="bi bi-calendar"></i> {{ event.event_time.strftime('%A, %d.%m.%Y')
|
||||||
<h5 class="text-muted"><i class="bi bi-calendar"></i> {{ event.event_time.strftime('%A, %d.%m.%Y')
|
|
||||||
}}
|
}}
|
||||||
</h5>
|
</p>
|
||||||
</div>
|
|
||||||
<p class="card-text">{{ event.description }}</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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user