Add "truly done" ticket archiving with auto-unhide

- Let the user mark closed tickets as really done and hide them, so
  tickets closed prematurely by an agent stay visible until confirmed
- User-side "archived" flag stored in the DB (independent of Zammad),
  added via a schema migration on existing databases
- Hide archived tickets from the overview by default with an
  "erledigte einblenden" toggle; archive/unarchive from the ticket
  detail page and via a per-row quick action
- Poller automatically un-hides an archived ticket when new activity is
  detected, so nothing is missed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 17:34:09 +02:00
co-authored by Claude Opus 4.8
parent 1107a912bc
commit 528d66b2b0
6 changed files with 94 additions and 15 deletions
+27 -7
View File
@@ -46,22 +46,30 @@
<section class="panel">
<div class="panel-head"><h2>Status-Filter</h2></div>
{% set arch = '1' if show_archived else none %}
<div class="chips">
<a class="chip {{ 'on' if not active_state }}" href="{{ url_for('index', sort=sort, dir=dir) }}">alle</a>
<a class="chip {{ 'on' if not active_state }}" href="{{ url_for('index', sort=sort, dir=dir, archived=arch) }}">alle</a>
<a class="chip {{ 'on' if active_state == 'not_closed' }}"
href="{{ url_for('index', state='not_closed', sort=sort, dir=dir) }}">nicht geschlossen</a>
href="{{ url_for('index', state='not_closed', sort=sort, dir=dir, archived=arch) }}">nicht geschlossen</a>
{% for s in states %}
<a class="chip {{ 'on' if active_state == s.state }}"
href="{{ url_for('index', state=s.state, sort=sort, dir=dir) }}">{{ s.state }} ({{ s.c }})</a>
href="{{ url_for('index', state=s.state, sort=sort, dir=dir, archived=arch) }}">{{ s.state }} ({{ s.c }})</a>
{% endfor %}
</div>
<div class="chips" style="margin-top:.5rem">
{% if show_archived %}
<a class="chip on" href="{{ url_for('index', state=active_state or none, sort=sort, dir=dir) }}">erledigte ausblenden</a>
{% else %}
<a class="chip" href="{{ url_for('index', state=active_state or none, sort=sort, dir=dir, archived='1') }}">erledigte einblenden{% if archived_count %} ({{ archived_count }}){% endif %}</a>
{% endif %}
</div>
</section>
</div>
{% macro th(col, label) %}
{% set nextdir = 'asc' if (sort == col and dir == 'desc') else 'desc' %}
<th class="{{ 'sorted' if sort == col }}">
<a href="{{ url_for('index', state=active_state or none, sort=col, dir=nextdir) }}">
<a href="{{ url_for('index', state=active_state or none, sort=col, dir=nextdir, archived=request.args.get('archived')) }}">
{{ label }}{% if sort == col %} <span class="arrow">{{ '▼' if dir == 'desc' else '▲' }}</span>{% endif %}
</a>
</th>
@@ -77,25 +85,37 @@
{{ th('number', '#') }}{{ th('title', 'Titel') }}{{ th('state', 'Status') }}
{{ th('priority', 'Priorität') }}{{ th('owner', 'Bearbeiter') }}
{{ th('articles', 'Artikel') }}{{ th('updated', 'zuletzt geändert') }}
<th></th>
</tr>
</thead>
<tbody>
{% for t in tickets %}
{% set n = unseen_map.get(t.id) %}
<tr class="{{ 'row-unseen' if n }}">
<tr class="{{ 'row-unseen' if n }}{{ ' row-archived' if t.archived }}">
<td>
{% if n %}<span class="badge" title="{{ n }} unbestätigte Änderung(en)">{{ n }}</span>{% endif %}
<a href="{{ url_for('ticket_detail', ticket_id=t.id) }}">{{ t.number }}</a>
</td>
<td class="title">{{ t.title }}</td>
<td class="title">{{ t.title }}{% if t.archived %} <span class="tag">erledigt</span>{% endif %}</td>
<td><span class="state state-{{ t.state }}">{{ t.state }}</span></td>
<td>{{ t.priority }}</td>
<td>{{ t.owner if t.owner and t.owner != '-' else '—' }}</td>
<td>{{ t.article_count }}</td>
<td><time>{{ t.zammad_updated_at|dt }}</time></td>
<td class="rowact">
{% if t.archived %}
<form method="post" action="{{ url_for('unarchive_ticket', ticket_id=t.id) }}">
<button type="submit" title="Wieder einblenden"></button>
</form>
{% else %}
<form method="post" action="{{ url_for('archive_ticket', ticket_id=t.id) }}">
<button type="submit" title="Als wirklich erledigt ablegen (ausblenden)"></button>
</form>
{% endif %}
</td>
</tr>
{% else %}
<tr><td colspan="7" class="muted">Keine Tickets.</td></tr>
<tr><td colspan="8" class="muted">Keine Tickets.</td></tr>
{% endfor %}
</tbody>
</table>