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

@@ -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"))