feat: add shared task assignments and quick win sorting

This commit is contained in:
2026-04-15 13:18:50 +02:00
parent f8f3641811
commit 4233175067
18 changed files with 414 additions and 55 deletions

View File

@@ -16,7 +16,11 @@
<link rel="apple-touch-icon" href="{{ url_for('static', filename='images/apple-touch-icon.png') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css', v=asset_version('css/style.css')) }}">
</head>
<body data-push-key="{{ config['VAPID_PUBLIC_KEY'] if current_user.is_authenticated else '' }}">
<body
data-push-key="{{ config['VAPID_PUBLIC_KEY'] if current_user.is_authenticated else '' }}"
data-current-user-id="{{ current_user.id if current_user.is_authenticated else '' }}"
data-current-user-name="{{ current_user.name if current_user.is_authenticated else '' }}"
>
{% from "partials/macros.html" import nav_icon %}
<div class="app-shell {% if not current_user.is_authenticated %}app-shell--auth{% endif %}">
{% if current_user.is_authenticated %}
@@ -119,10 +123,7 @@
<p class="eyebrow">Punkte fair verbuchen</p>
<h2>Wer hat diese Aufgabe erledigt?</h2>
<p id="completeDialogText">Bitte wähle aus, wem die Punkte gutgeschrieben werden sollen.</p>
<div class="choice-grid">
<button type="button" class="button button--secondary" data-complete-choice="assigned">Zugewiesene Person</button>
<button type="button" class="button" data-complete-choice="me">Ich</button>
</div>
<div class="choice-grid" id="completeDialogChoices"></div>
<button type="button" class="button button--ghost" id="completeDialogClose">Abbrechen</button>
</form>
</dialog>

View File

@@ -48,7 +48,7 @@
<div>
<div class="chip-row">
{{ status_badge(task) }}
<span class="point-pill">{{ task.points_awarded }} Punkte</span>
<span class="point-pill">{{ task.points_awarded }} Punkte{% if task.is_shared_assignment %} / Person{% endif %}</span>
</div>
<h3>{{ task.title }}</h3>
</div>
@@ -68,7 +68,7 @@
</div>
<div>
<dt>Zuständig</dt>
<dd>{{ task.assigned_user.name if task.assigned_user else 'Unverteilt' }}</dd>
<dd>{{ task.assignee_label }}</dd>
</div>
<div>
<dt>Rhythmus</dt>
@@ -84,18 +84,26 @@
<div class="task-card__footer">
<div class="task-assignee">
{{ avatar(task.assigned_user) }}
<span>{{ task.assigned_user.name if task.assigned_user else 'Ohne Person' }}</span>
<span class="task-assignee__avatars">
{% for assigned_user in task.assigned_users %}
{{ avatar(assigned_user) }}
{% endfor %}
</span>
<span>{{ task.assignee_label }}</span>
</div>
{% if not task.completed_at %}
{% if task.assigned_user_id and task.assigned_user_id != current_user.id %}
{% if task.assigned_users and current_user.id not in task.assigned_user_ids %}
<button
type="button"
class="button"
data-complete-action="{{ url_for('tasks.complete', task_id=task.id) }}"
data-complete-title="{{ task.title }}"
data-complete-assigned="{{ task.assigned_user.name if task.assigned_user else 'Zugewiesene Person' }}"
data-complete-assigned="{{ task.assignee_label }}"
data-assigned-primary-id="{{ task.assigned_user.id if task.assigned_user else '' }}"
data-assigned-primary-name="{{ task.assigned_user.name if task.assigned_user else '' }}"
data-assigned-secondary-id="{{ task.assigned_user_secondary.id if task.assigned_user_secondary else '' }}"
data-assigned-secondary-name="{{ task.assigned_user_secondary.name if task.assigned_user_secondary else '' }}"
>
{{ nav_icon('check') }}
<span>Erledigen</span>

View File

@@ -36,11 +36,17 @@
<article class="panel">
<p class="eyebrow">Direkt sichtbar</p>
<h2>Aktive Quick-Wins bearbeiten</h2>
<div class="quick-win-list">
<p class="muted">Per Drag & Drop kannst du die Reihenfolge festlegen, die später auch bei den Quick-Win-Chips erscheint.</p>
<input type="hidden" id="quickWinSortToken" value="{{ csrf_token() }}">
<div class="quick-win-list" data-quick-win-sort-list>
{% for quick_win in quick_wins %}
<article class="quick-win-manage-card">
<article class="quick-win-manage-card" draggable="true" data-quick-win-sort-item="{{ quick_win.id }}">
<form method="post" action="{{ url_for('settings.update_quick_win', quick_win_id=quick_win.id) }}" class="quick-win-manage-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div class="quick-win-manage-card__drag">
{{ nav_icon('list') }}
<span>Ziehen zum Sortieren</span>
</div>
<div class="field">
<label for="quick-win-title-{{ quick_win.id }}">Titel</label>
<input id="quick-win-title-{{ quick_win.id }}" type="text" name="title" value="{{ quick_win.title }}" minlength="2" maxlength="160" required>

View File

@@ -11,6 +11,9 @@
<option value="all" {% if filters.status == 'all' %}selected{% endif %}>Alle</option>
<option value="open" {% if filters.status == 'open' %}selected{% endif %}>Offen</option>
<option value="soon" {% if filters.status == 'soon' %}selected{% endif %}>Bald fällig</option>
<option value="today" {% if filters.status == 'today' %}selected{% endif %}>Heute fällig</option>
<option value="tomorrow" {% if filters.status == 'tomorrow' %}selected{% endif %}>Morgen fällig</option>
<option value="day_after_tomorrow" {% if filters.status == 'day_after_tomorrow' %}selected{% endif %}>Übermorgen fällig</option>
<option value="overdue" {% if filters.status == 'overdue' %}selected{% endif %}>Überfällig</option>
<option value="completed" {% if filters.status == 'completed' %}selected{% endif %}>Erledigt</option>
</select>
@@ -48,4 +51,3 @@
{% endfor %}
</section>
{% endblock %}

View File

@@ -75,8 +75,8 @@
<small class="calendar-task__person" lang="de">
{% if task.completed_by_user %}
{{ task.completed_by_user.name|hyphenate_de }}
{% elif task.assigned_user %}
{{ task.assigned_user.name|hyphenate_de }}
{% elif task.assigned_users %}
{{ task.assignee_label|hyphenate_de }}
{% else %}
{{ 'Ohne Zuweisung'|hyphenate_de }}
{% endif %}
@@ -108,8 +108,8 @@
<small class="calendar-task__person" lang="de">
{% if task.completed_by_user %}
{{ task.completed_by_user.name|hyphenate_de }}
{% elif task.assigned_user %}
{{ task.assigned_user.name|hyphenate_de }}
{% elif task.assigned_users %}
{{ task.assignee_label|hyphenate_de }}
{% else %}
{{ 'Ohne Zuweisung'|hyphenate_de }}
{% endif %}

View File

@@ -46,13 +46,41 @@
<section class="stack">
<div class="section-heading">
<h2>Bald fällig</h2>
<span class="section-heading__count">{{ sections.soon|length }}</span>
<span class="section-heading__count">{{ sections.due_today|length }}</span>
</div>
<div class="task-grid">
{% for task in sections.soon %}
{% for task in sections.due_today %}
{{ task_card(task, current_user) }}
{% else %}
<div class="empty-state">Gerade nichts, was in den nächsten Tagen drängt.</div>
<div class="empty-state">Heute ist gerade nichts mehr auf Kante.</div>
{% endfor %}
</div>
</section>
<section class="stack">
<div class="section-heading">
<h2>Morgen fällig</h2>
<span class="section-heading__count">{{ sections.due_tomorrow|length }}</span>
</div>
<div class="task-grid">
{% for task in sections.due_tomorrow %}
{{ task_card(task, current_user) }}
{% else %}
<div class="empty-state">Für morgen sieht es gerade entspannt aus.</div>
{% endfor %}
</div>
</section>
<section class="stack">
<div class="section-heading">
<h2>Übermorgen fällig</h2>
<span class="section-heading__count">{{ sections.due_day_after_tomorrow|length }}</span>
</div>
<div class="task-grid">
{% for task in sections.due_day_after_tomorrow %}
{{ task_card(task, current_user) }}
{% else %}
<div class="empty-state">Auch übermorgen ist noch nichts Drängendes drin.</div>
{% endfor %}
</div>
</section>
@@ -85,4 +113,3 @@
</div>
</section>
{% endblock %}

View File

@@ -35,6 +35,13 @@
{% for error in form.assigned_user_id.errors %}<small class="error">{{ error }}</small>{% endfor %}
</div>
<div class="field">
{{ form.assigned_user_secondary_id.label }}
{{ form.assigned_user_secondary_id() }}
<small class="muted">Wenn du hier noch jemanden auswählst, zählen die Punkte pro Person halbiert.</small>
{% for error in form.assigned_user_secondary_id.errors %}<small class="error">{{ error }}</small>{% endfor %}
</div>
<div class="field">
{{ form.due_date.label }}
{{ form.due_date() }}