feat: add shared quick wins workflow

This commit is contained in:
2026-04-15 11:49:46 +02:00
parent 07ab0461e9
commit 4aa4447c01
14 changed files with 437 additions and 137 deletions

View File

@@ -10,6 +10,7 @@ from config import Config
from .cli import register_cli, seed_badges
from .extensions import csrf, db, login_manager
from .forms import QuickTaskForm
from .models import QuickWin
from .routes import auth, main, scoreboard, settings, tasks
from .routes.main import load_icon_svg
from .services.app_settings import get_quick_task_config
@@ -64,6 +65,7 @@ def create_app(config_class: type[Config] = Config) -> Flask:
(key, values["label"])
for key, values in quick_task_config.items()
]
quick_wins = QuickWin.query.filter_by(active=True).order_by(QuickWin.id.asc()).all()
def asset_version(filename: str) -> int:
path = Path(app.static_folder) / filename
try:
@@ -94,6 +96,7 @@ def create_app(config_class: type[Config] = Config) -> Flask:
"now_local": local_now(),
"quick_task_form": quick_task_form,
"quick_task_config": quick_task_config,
"quick_wins": quick_wins,
}
@app.template_filter("date_de")

View File

@@ -121,7 +121,7 @@ class QuickTaskForm(FlaskForm):
choices=[(key, key) for key, _, _ in QUICK_TASK_EFFORTS],
validators=[DataRequired()],
)
submit = SubmitField("Schnellaufgabe speichern")
submit = SubmitField("Quick-Win speichern")
class QuickTaskConfigForm(FlaskForm):
@@ -133,4 +133,16 @@ class QuickTaskConfigForm(FlaskForm):
medium_points = IntegerField("Dauert etwas", validators=[DataRequired(), NumberRange(min=1, max=500)])
heavy_label = StringField("Bezeichnung", validators=[DataRequired(), Length(min=2, max=60)])
heavy_points = IntegerField("Aufwendig", validators=[DataRequired(), NumberRange(min=1, max=500)])
super_heavy_label = StringField("Bezeichnung", validators=[DataRequired(), Length(min=2, max=60)])
super_heavy_points = IntegerField("Super aufwendig", validators=[DataRequired(), NumberRange(min=1, max=500)])
submit = SubmitField("Aufwand speichern")
class QuickWinForm(FlaskForm):
title = StringField("Quick-Win", validators=[DataRequired(), Length(min=2, max=160)])
effort = SelectField(
"Aufwand",
choices=[(key, key) for key, _, _ in QUICK_TASK_EFFORTS],
validators=[DataRequired()],
)
submit = SubmitField("Quick-Win speichern")

View File

@@ -48,6 +48,7 @@ class User(UserMixin, TimestampMixin, db.Model):
lazy=True,
)
subscriptions = db.relationship("PushSubscription", backref="user", lazy=True, cascade="all, delete-orphan")
created_quick_wins = db.relationship("QuickWin", backref="created_by_user", lazy=True)
awarded_badges = db.relationship(
"UserBadge",
backref="user",
@@ -106,6 +107,14 @@ class TaskTemplate(TimestampMixin, db.Model):
return f"Alle {self.recurrence_interval_value} {unit_label}"
class QuickWin(TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(160), nullable=False, index=True)
effort = db.Column(db.String(40), nullable=False, index=True)
active = db.Column(db.Boolean, nullable=False, default=True)
created_by_user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False, index=True)
class TaskInstance(TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
task_template_id = db.Column(db.Integer, db.ForeignKey("task_template.id"), nullable=False, index=True)

View File

@@ -8,8 +8,8 @@ from flask_login import current_user, login_required
from werkzeug.utils import secure_filename
from ..extensions import csrf, db
from ..forms import AdminUserForm, QuickTaskConfigForm, SettingsProfileForm
from ..models import BadgeDefinition, MonthlyScoreSnapshot, NotificationLog, PushSubscription, TaskInstance, TaskTemplate, User
from ..forms import AdminUserForm, QuickTaskConfigForm, QuickWinForm, SettingsProfileForm
from ..models import BadgeDefinition, MonthlyScoreSnapshot, NotificationLog, PushSubscription, QuickWin, TaskInstance, TaskTemplate, User
from ..services.app_settings import get_quick_task_config, set_setting_int, set_setting_str
from ..services.badges import earned_badges_for_user
from ..services.notifications import push_enabled
@@ -26,7 +26,10 @@ def _require_admin():
def _settings_tabs():
tabs = [("settings.index", "Profil & Team", "gear")]
tabs = [
("settings.index", "Profil & Team", "gear"),
("settings.quick_wins", "Quick-Wins", "plus"),
]
if current_user.is_admin:
tabs.append(("settings.badges", "Badges", "award"))
return tabs
@@ -48,17 +51,6 @@ def index():
current_user.ensure_calendar_feed_token()
form = SettingsProfileForm(original_email=current_user.email, obj=current_user)
admin_form = AdminUserForm(prefix="admin")
quick_task_config_form = QuickTaskConfigForm(prefix="quickconfig")
quick_task_config = get_quick_task_config()
if request.method == "GET":
quick_task_config_form.fast_label.data = quick_task_config["fast"]["label"]
quick_task_config_form.fast_points.data = quick_task_config["fast"]["points"]
quick_task_config_form.normal_label.data = quick_task_config["normal"]["label"]
quick_task_config_form.normal_points.data = quick_task_config["normal"]["points"]
quick_task_config_form.medium_label.data = quick_task_config["medium"]["label"]
quick_task_config_form.medium_points.data = quick_task_config["medium"]["points"]
quick_task_config_form.heavy_label.data = quick_task_config["heavy"]["label"]
quick_task_config_form.heavy_points.data = quick_task_config["heavy"]["points"]
if form.validate_on_submit():
current_user.name = form.name.data.strip()
current_user.email = form.email.data.lower().strip()
@@ -77,8 +69,6 @@ def index():
"settings/index.html",
form=form,
admin_form=admin_form,
quick_task_config_form=quick_task_config_form,
quick_task_config=quick_task_config,
users=User.query.order_by(User.is_admin.desc(), User.name.asc()).all(),
earned_badges=earned_badges_for_user(current_user.id),
calendar_feed_url=url_for("main.calendar_feed", token=current_user.calendar_feed_token, _external=True),
@@ -89,6 +79,53 @@ def index():
)
@bp.route("/quick-wins", methods=["GET", "POST"])
@login_required
def quick_wins():
quick_win_form = QuickWinForm(prefix="quickwin")
quick_task_config_form = QuickTaskConfigForm(prefix="quickconfig")
quick_task_config = get_quick_task_config()
quick_win_form.effort.choices = [(key, values["label"]) for key, values in quick_task_config.items()]
quick_task_config_form.fast_label.data = quick_task_config["fast"]["label"]
quick_task_config_form.fast_points.data = quick_task_config["fast"]["points"]
quick_task_config_form.normal_label.data = quick_task_config["normal"]["label"]
quick_task_config_form.normal_points.data = quick_task_config["normal"]["points"]
quick_task_config_form.medium_label.data = quick_task_config["medium"]["label"]
quick_task_config_form.medium_points.data = quick_task_config["medium"]["points"]
quick_task_config_form.heavy_label.data = quick_task_config["heavy"]["label"]
quick_task_config_form.heavy_points.data = quick_task_config["heavy"]["points"]
quick_task_config_form.super_heavy_label.data = quick_task_config["super_heavy"]["label"]
quick_task_config_form.super_heavy_points.data = quick_task_config["super_heavy"]["points"]
if quick_win_form.validate_on_submit():
existing_quick_win = QuickWin.query.filter_by(title=quick_win_form.title.data.strip(), active=True).first()
if existing_quick_win:
flash("Diesen Quick-Win gibt es bereits.", "error")
return redirect(url_for("settings.quick_wins"))
quick_win = QuickWin(
title=quick_win_form.title.data.strip(),
effort=quick_win_form.effort.data,
active=True,
created_by_user_id=current_user.id,
)
db.session.add(quick_win)
db.session.commit()
flash(f"Quick-Win „{quick_win.title}“ wurde gespeichert.", "success")
return redirect(url_for("settings.quick_wins"))
return render_template(
"settings/quick_wins.html",
quick_win_form=quick_win_form,
quick_task_config_form=quick_task_config_form,
quick_task_config=quick_task_config,
quick_wins=QuickWin.query.filter_by(active=True).order_by(QuickWin.id.asc()).all(),
settings_tabs=_settings_tabs(),
active_settings_tab="settings.quick_wins",
)
@bp.route("/calendar-feed/regenerate", methods=["POST"])
@login_required
def regenerate_calendar_feed():
@@ -174,9 +211,25 @@ def update_quick_task_config():
set_setting_int("quick_task_points_medium", form.medium_points.data)
set_setting_str("quick_task_label_heavy", form.heavy_label.data)
set_setting_int("quick_task_points_heavy", form.heavy_points.data)
set_setting_str("quick_task_label_super_heavy", form.super_heavy_label.data)
set_setting_int("quick_task_points_super_heavy", form.super_heavy_points.data)
db.session.commit()
flash("Schnellaufgaben-Aufwand und Punkte wurden aktualisiert.", "success")
return redirect(url_for("settings.index"))
flash("Quick-Win-Aufwand und Punkte wurden aktualisiert.", "success")
return redirect(url_for("settings.quick_wins"))
@bp.route("/quick-wins/<int:quick_win_id>/delete", methods=["POST"])
@login_required
def delete_quick_win(quick_win_id: int):
quick_win = QuickWin.query.get_or_404(quick_win_id)
if quick_win.created_by_user_id != current_user.id and not current_user.is_admin:
flash("Diesen Quick-Win kannst du nicht entfernen.", "error")
return redirect(url_for("settings.quick_wins"))
quick_win.active = False
db.session.commit()
flash("Quick-Win wurde ausgeblendet.", "success")
return redirect(url_for("settings.quick_wins"))
@bp.route("/users/<int:user_id>/toggle-admin", methods=["POST"])

View File

@@ -8,7 +8,7 @@ from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from ..forms import QuickTaskForm, TaskForm
from ..models import TaskInstance, User
from ..models import QuickWin, TaskInstance, User
from ..services.app_settings import get_quick_task_config
from ..services.dates import month_label, today_local
from ..services.tasks import (
@@ -109,26 +109,34 @@ def create():
@bp.route("/tasks/quick", methods=["POST"])
@login_required
def quick_create():
form = QuickTaskForm(prefix="quick")
config = get_quick_task_config()
form.effort.choices = [
(key, values["label"])
for key, values in config.items()
]
if not form.validate_on_submit():
for field_errors in form.errors.values():
for error in field_errors:
flash(error, "error")
return redirect(request.referrer or url_for("tasks.my_tasks"))
quick_action = request.form.get("quick_action", "save")
task = create_quick_task(form.title.data, form.effort.data, current_user)
quick_mode = request.form.get("quick_mode", "preset")
if quick_mode == "preset":
quick_win = QuickWin.query.filter_by(id=request.form.get("quick_win_id", type=int), active=True).first()
if not quick_win:
flash("Dieser Quick-Win ist nicht mehr verfügbar.", "error")
return redirect(request.referrer or url_for("tasks.my_tasks"))
title = quick_win.title
effort = quick_win.effort
else:
form = QuickTaskForm(prefix="quick")
form.effort.choices = [(key, values["label"]) for key, values in config.items()]
if not form.validate_on_submit():
for field_errors in form.errors.values():
for error in field_errors:
flash(error, "error")
return redirect(request.referrer or url_for("tasks.my_tasks"))
title = form.title.data
effort = form.effort.data
task = create_quick_task(title, effort, current_user, description="Quick-Win")
if quick_action == "complete":
complete_task(task, current_user.id)
flash(f"Schnellaufgabe{task.title}“ wurde direkt als erledigt gespeichert.", "success")
flash(f"Quick-Win{task.title}“ wurde direkt als erledigt gespeichert.", "success")
else:
flash(f"Schnellaufgabe{task.title}“ wurde für dich angelegt.", "success")
flash(f"Quick-Win{task.title}“ wurde für dich angelegt.", "success")
return redirect(request.referrer or url_for("tasks.my_tasks"))

View File

@@ -9,10 +9,12 @@ QUICK_TASK_DEFAULTS = {
"quick_task_label_normal": "Normal",
"quick_task_label_medium": "Dauert etwas",
"quick_task_label_heavy": "Aufwendig",
"quick_task_label_super_heavy": "Super aufwendig",
"quick_task_points_fast": 4,
"quick_task_points_normal": 8,
"quick_task_points_medium": 12,
"quick_task_points_heavy": 18,
"quick_task_points_super_heavy": 28,
}
QUICK_TASK_EFFORTS = [
@@ -20,6 +22,7 @@ QUICK_TASK_EFFORTS = [
("normal", "quick_task_label_normal", "quick_task_points_normal"),
("medium", "quick_task_label_medium", "quick_task_points_medium"),
("heavy", "quick_task_label_heavy", "quick_task_points_heavy"),
("super_heavy", "quick_task_label_super_heavy", "quick_task_points_super_heavy"),
]

View File

@@ -5,7 +5,7 @@ import os
from sqlalchemy import inspect, text
from ..extensions import db
from ..models import User
from ..models import QuickWin, User
from .app_settings import ensure_app_settings
@@ -30,17 +30,48 @@ def ensure_schema_and_admins() -> None:
db.session.commit()
admin_exists = User.query.filter_by(is_admin=True).first()
if admin_exists:
return
default_quick_win_user = admin_exists
preferred_admin_email = os.getenv("DEFAULT_ADMIN_EMAIL", "mail@hnz.io").lower().strip()
preferred_user = User.query.filter(User.email.ilike(preferred_admin_email)).first()
if preferred_user:
if preferred_user and not admin_exists:
preferred_user.is_admin = True
db.session.commit()
return
default_quick_win_user = preferred_user
first_user = User.query.order_by(User.id.asc()).first()
if first_user:
if first_user and not User.query.filter_by(is_admin=True).first():
first_user.is_admin = True
db.session.commit()
default_quick_win_user = first_user
_ensure_default_quick_wins(default_quick_win_user or User.query.order_by(User.id.asc()).first())
def _ensure_default_quick_wins(default_user: User | None) -> None:
if not default_user:
return
defaults = [
("Schnell Aufräumen", "fast"),
("Spülmaschine ausräumen", "normal"),
("Bett machen", "normal"),
("Lüften", "fast"),
("Wäsche zusammenlegen", "medium"),
]
existing_titles = {quick_win.title for quick_win in QuickWin.query.all()}
created = False
for title, effort in defaults:
if title not in existing_titles:
db.session.add(
QuickWin(
title=title,
effort=effort,
active=True,
created_by_user_id=default_user.id,
)
)
created = True
if created:
db.session.commit()

View File

@@ -129,12 +129,12 @@ def complete_task(task: TaskInstance, completed_by_user_id: int) -> TaskInstance
return task
def create_quick_task(title: str, effort: str, creator: User) -> TaskInstance:
def create_quick_task(title: str, effort: str, creator: User, description: str = "Quick-Win") -> TaskInstance:
config = get_quick_task_config()
effort_config = config[effort]
template = TaskTemplate(
title=title.strip(),
description="Schnellaufgabe",
description=description,
default_points=effort_config["points"],
default_assigned_user_id=creator.id,
recurrence_interval_value=None,
@@ -147,7 +147,7 @@ def create_quick_task(title: str, effort: str, creator: User) -> TaskInstance:
task = TaskInstance(
task_template_id=template.id,
title=template.title,
description="Schnellaufgabe",
description=description,
assigned_user_id=creator.id,
due_date=today_local(),
points_awarded=template.default_points,

View File

@@ -1030,6 +1030,71 @@ p {
margin: 0;
}
.quick-win-list {
display: grid;
gap: 12px;
}
.quick-win-manage-card,
.quick-win-card {
display: grid;
gap: 12px;
padding: 18px;
border-radius: var(--radius-md);
background: var(--surface-soft);
border: 1px solid rgba(132, 152, 190, 0.22);
}
.quick-win-manage-card {
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
}
.quick-win-grid {
display: grid;
gap: 12px;
}
.quick-win-card__actions,
.quick-win-custom__body {
display: grid;
gap: 10px;
}
.quick-win-card__actions .button,
.quick-win-custom__body .button {
width: 100%;
}
.quick-win-card p,
.quick-win-manage-card p {
color: var(--muted);
}
.quick-win-card--custom {
cursor: pointer;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
list-style: none;
}
.quick-win-card--custom::-webkit-details-marker {
display: none;
}
.quick-win-custom {
display: grid;
gap: 10px;
}
.quick-win-custom[open] .quick-win-card--custom {
border-color: rgba(37, 99, 235, 0.24);
}
.quick-win-custom__body {
padding: 4px 2px 0;
}
.push-box__state {
align-items: flex-start;
padding: 16px;

View File

@@ -105,7 +105,7 @@
{% endfor %}
</nav>
<button type="button" class="fab-quick-task" id="quickTaskOpen" aria-label="Schnellaufgabe anlegen">
<button type="button" class="fab-quick-task" id="quickTaskOpen" aria-label="Quick-Wins öffnen">
{{ nav_icon('plus') }}
</button>
@@ -123,25 +123,54 @@
</dialog>
<dialog class="complete-dialog" id="quickTaskDialog">
<form method="post" action="{{ url_for('tasks.quick_create') }}" class="complete-dialog__surface complete-dialog__surface--task">
{{ quick_task_form.hidden_tag() }}
<p class="eyebrow">Schnellaufgabe</p>
<h2>Direkt etwas für dich anlegen</h2>
<p class="muted">Titel und Aufwand reichen. Die Aufgabe wird automatisch dir zugewiesen und auf heute gesetzt.</p>
<div class="field">
{{ quick_task_form.title.label }}
{{ quick_task_form.title(placeholder="Zum Beispiel: Küche kurz aufräumen") }}
<div class="complete-dialog__surface complete-dialog__surface--task">
<p class="eyebrow">Quick-Wins</p>
<h2>Schnell etwas abhaken</h2>
<p class="muted">Alle Quick-Wins sind für das ganze Team sichtbar. Für „Sonstiges“ kannst du Titel und Aufwand frei wählen.</p>
<div class="quick-win-grid">
{% for quick_win in quick_wins %}
<article class="quick-win-card">
<div>
<strong>{{ quick_win.title }}</strong>
<p>{{ quick_task_config[quick_win.effort].label }}</p>
</div>
<form method="post" action="{{ url_for('tasks.quick_create') }}" class="quick-win-card__actions">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="quick_mode" value="preset">
<input type="hidden" name="quick_win_id" value="{{ quick_win.id }}">
<button type="submit" class="button button--secondary" name="quick_action" value="save">Speichern</button>
<button type="submit" class="button" name="quick_action" value="complete">Direkt erledigt</button>
</form>
</article>
{% endfor %}
<details class="quick-win-custom">
<summary class="quick-win-card quick-win-card--custom">
<div>
<strong>Sonstiges (bitte auch nutzen)</strong>
<p>Eigener Titel und freier Aufwand</p>
</div>
{{ nav_icon('plus') }}
</summary>
<form method="post" action="{{ url_for('tasks.quick_create') }}" class="quick-win-custom__body">
{{ quick_task_form.hidden_tag() }}
<input type="hidden" name="quick_mode" value="custom">
<div class="field">
{{ quick_task_form.title.label }}
{{ quick_task_form.title(placeholder="Zum Beispiel: Flur kurz aufräumen") }}
</div>
<div class="field">
{{ quick_task_form.effort.label }}
{{ quick_task_form.effort() }}
</div>
<div class="dialog-actions">
<button type="submit" class="button" name="quick_action" value="save">Aufgabe speichern</button>
<button type="submit" class="button button--secondary" name="quick_action" value="complete">Aufgabe als erledigt speichern</button>
</div>
</form>
</details>
</div>
<div class="field">
{{ quick_task_form.effort.label }}
{{ quick_task_form.effort() }}
</div>
<div class="dialog-actions">
<button type="submit" class="button" name="quick_action" value="save">Aufgabe speichern</button>
<button type="submit" class="button button--secondary" name="quick_action" value="complete">Aufgabe als erledigt speichern</button>
<button type="button" class="button button--ghost" id="quickTaskClose">Abbrechen</button>
</div>
</form>
<button type="button" class="button button--ghost" id="quickTaskClose">Abbrechen</button>
</div>
</dialog>
<form method="post" class="sr-only" id="completeDialogForm">

View File

@@ -182,73 +182,5 @@
{% endfor %}
</div>
</section>
<section class="panel">
<p class="eyebrow">Admin</p>
<h2>Schnellaufgabe-Aufwand</h2>
<p class="muted">Hier definierst du die sichtbaren Aufwand-Stufen und die dazugehörigen Punkte. Im Schnellaufgaben-Dialog wird nur die Bezeichnung angezeigt.</p>
<form method="post" action="{{ url_for('settings.update_quick_task_config') }}" class="badge-settings">
{{ quick_task_config_form.hidden_tag() }}
<div class="badge-setting-card">
<div>
<strong>Slot 1</strong>
<p class="muted">Kleine Sache für zwischendurch.</p>
</div>
<div class="field">
{{ quick_task_config_form.fast_label.label }}
{{ quick_task_config_form.fast_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.fast_points.id }}">Punkte</label>
{{ quick_task_config_form.fast_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 2</strong>
<p class="muted">Typische Alltagssache.</p>
</div>
<div class="field">
{{ quick_task_config_form.normal_label.label }}
{{ quick_task_config_form.normal_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.normal_points.id }}">Punkte</label>
{{ quick_task_config_form.normal_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 3</strong>
<p class="muted">Braucht etwas mehr Zeit oder Konzentration.</p>
</div>
<div class="field">
{{ quick_task_config_form.medium_label.label }}
{{ quick_task_config_form.medium_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.medium_points.id }}">Punkte</label>
{{ quick_task_config_form.medium_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 4</strong>
<p class="muted">Spürbarer Aufwand mit mehr Punkten.</p>
</div>
<div class="field">
{{ quick_task_config_form.heavy_label.label }}
{{ quick_task_config_form.heavy_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.heavy_points.id }}">Punkte</label>
{{ quick_task_config_form.heavy_points() }}
</div>
</div>
<div class="field field--full">
{{ quick_task_config_form.submit(class_='button button--secondary') }}
</div>
</form>
</section>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,143 @@
{% extends "base.html" %}
{% from "partials/macros.html" import nav_icon %}
{% block title %}Quick-Wins · Putzliga{% endblock %}
{% block page_title %}Quick-Wins{% endblock %}
{% block content %}
<section class="settings-tabs">
{% for endpoint, label, icon in settings_tabs %}
<a href="{{ url_for(endpoint) }}" class="settings-tab {% if active_settings_tab == endpoint %}is-active{% endif %}">
{{ nav_icon(icon) }}
<span>{{ label }}</span>
</a>
{% endfor %}
</section>
<section class="two-column">
<article class="panel">
<p class="eyebrow">Gemeinsame Vorlagen</p>
<h2>Quick-Win anlegen</h2>
<p class="muted">Alle hier angelegten Quick-Wins sind direkt für das ganze Team im Plus-Menü verfügbar.</p>
<form method="post" class="form-grid">
{{ quick_win_form.hidden_tag() }}
<div class="field">
{{ quick_win_form.title.label }}
{{ quick_win_form.title(placeholder="Zum Beispiel: Müllbeutel wechseln") }}
{% for error in quick_win_form.title.errors %}<small class="error">{{ error }}</small>{% endfor %}
</div>
<div class="field">
{{ quick_win_form.effort.label }}
{{ quick_win_form.effort() }}
{% for error in quick_win_form.effort.errors %}<small class="error">{{ error }}</small>{% endfor %}
</div>
{{ quick_win_form.submit(class_='button') }}
</form>
</article>
<article class="panel">
<p class="eyebrow">Direkt sichtbar</p>
<h2>Aktive Quick-Wins</h2>
<div class="quick-win-list">
{% for quick_win in quick_wins %}
<article class="quick-win-manage-card">
<div>
<strong>{{ quick_win.title }}</strong>
<p class="muted">{{ quick_task_config[quick_win.effort].label }} · von {{ quick_win.created_by_user.name }}</p>
</div>
{% if quick_win.created_by_user_id == current_user.id or current_user.is_admin %}
<form method="post" action="{{ url_for('settings.delete_quick_win', quick_win_id=quick_win.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="button button--ghost">Entfernen</button>
</form>
{% endif %}
</article>
{% else %}
<div class="empty-state">Noch keine Quick-Wins angelegt. Der erste steht gleich oben bereit.</div>
{% endfor %}
</div>
</article>
</section>
{% if current_user.is_admin %}
<section class="panel">
<p class="eyebrow">Admin</p>
<h2>Quick-Win-Aufwand</h2>
<p class="muted">Hier definierst du die sichtbaren Aufwand-Stufen und die dazugehörigen Punkte. Im Quick-Wins-Dialog wird nur die Bezeichnung angezeigt.</p>
<form method="post" action="{{ url_for('settings.update_quick_task_config') }}" class="badge-settings">
{{ quick_task_config_form.hidden_tag() }}
<div class="badge-setting-card">
<div>
<strong>Slot 1</strong>
<p class="muted">Kleine Sache für zwischendurch.</p>
</div>
<div class="field">
{{ quick_task_config_form.fast_label.label }}
{{ quick_task_config_form.fast_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.fast_points.id }}">Punkte</label>
{{ quick_task_config_form.fast_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 2</strong>
<p class="muted">Typische Alltagssache.</p>
</div>
<div class="field">
{{ quick_task_config_form.normal_label.label }}
{{ quick_task_config_form.normal_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.normal_points.id }}">Punkte</label>
{{ quick_task_config_form.normal_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 3</strong>
<p class="muted">Braucht etwas mehr Zeit oder Konzentration.</p>
</div>
<div class="field">
{{ quick_task_config_form.medium_label.label }}
{{ quick_task_config_form.medium_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.medium_points.id }}">Punkte</label>
{{ quick_task_config_form.medium_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 4</strong>
<p class="muted">Spürbarer Aufwand mit mehr Punkten.</p>
</div>
<div class="field">
{{ quick_task_config_form.heavy_label.label }}
{{ quick_task_config_form.heavy_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.heavy_points.id }}">Punkte</label>
{{ quick_task_config_form.heavy_points() }}
</div>
</div>
<div class="badge-setting-card">
<div>
<strong>Slot 5</strong>
<p class="muted">Extra große Aufgabe für echte Kraftakte.</p>
</div>
<div class="field">
{{ quick_task_config_form.super_heavy_label.label }}
{{ quick_task_config_form.super_heavy_label() }}
</div>
<div class="field">
<label for="{{ quick_task_config_form.super_heavy_points.id }}">Punkte</label>
{{ quick_task_config_form.super_heavy_points() }}
</div>
</div>
<div class="field field--full">
{{ quick_task_config_form.submit(class_='button button--secondary') }}
</div>
</form>
</section>
{% endif %}
{% endblock %}