- 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>
132 lines
5.8 KiB
HTML
132 lines
5.8 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}#{{ ticket.number }} · EPI-Support{% endblock %}
|
||
{% block content %}
|
||
|
||
<p><a class="back" href="{{ url_for('index') }}">← zurück zur Übersicht</a></p>
|
||
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<h2>#{{ ticket.number }} – {{ ticket.title }}</h2>
|
||
<div class="head-tools">
|
||
{% if ticket.archived %}
|
||
<form method="post" action="{{ url_for('unarchive_ticket', ticket_id=ticket.id) }}">
|
||
<button class="btn-ghost" type="submit">↩ Wieder einblenden</button>
|
||
</form>
|
||
{% else %}
|
||
<form method="post" action="{{ url_for('archive_ticket', ticket_id=ticket.id) }}">
|
||
<button class="btn-ghost" type="submit" title="Als wirklich erledigt ablegen und ausblenden">✓ Wirklich erledigt</button>
|
||
</form>
|
||
{% endif %}
|
||
<a class="btn-sm" href="{{ zammad_url }}/#ticket/zoom/{{ ticket.id }}"
|
||
target="_blank" rel="noopener">in Zammad öffnen ↗</a>
|
||
</div>
|
||
</div>
|
||
<dl class="meta">
|
||
<div><dt>Status</dt><dd><span class="state state-{{ ticket.state }}">{{ ticket.state }}</span></dd></div>
|
||
<div><dt>Priorität</dt><dd>{{ ticket.priority }}</dd></div>
|
||
<div><dt>Bearbeiter</dt><dd>{{ ticket.owner if ticket.owner and ticket.owner != '-' else '—' }}</dd></div>
|
||
<div><dt>Gruppe</dt><dd>{{ ticket.group }}</dd></div>
|
||
<div><dt>Artikel</dt><dd>{{ ticket.article_count }}</dd></div>
|
||
<div><dt>erstellt</dt><dd>{{ ticket.zammad_created_at|dt }}</dd></div>
|
||
<div><dt>zuletzt geändert</dt><dd>{{ ticket.zammad_updated_at|dt }}</dd></div>
|
||
<div><dt>geschlossen</dt><dd>{{ ticket.last_close_at|dt }}</dd></div>
|
||
</dl>
|
||
<form method="post" action="{{ url_for('set_state', ticket_id=ticket.id) }}" class="state-form">
|
||
<label for="state_id">Status ändern:</label>
|
||
<select id="state_id" name="state_id">
|
||
{% for s in settable_states %}
|
||
<option value="{{ s.id }}" {{ 'selected' if s.id == ticket.state_id }}>{{ s.label }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<button class="btn-sm" type="submit">setzen</button>
|
||
<span class="hint-inline">wirkt live in Zammad</span>
|
||
</form>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<div class="panel-head">
|
||
<h2>Konversation{% if conversation %} ({{ conversation|length }}){% endif %}</h2>
|
||
<form method="post" action="{{ url_for('refresh_conversation', ticket_id=ticket.id) }}">
|
||
<button class="btn-sm" type="submit">↻ Aktualisieren</button>
|
||
</form>
|
||
</div>
|
||
{% if conv_error %}
|
||
<p class="muted">Konversation konnte nicht geladen werden: {{ conv_error }}</p>
|
||
{% elif conversation %}
|
||
<div class="thread">
|
||
{% for a in conversation %}
|
||
<article class="msg msg-{{ a.sender|lower if a.sender else 'system' }}">
|
||
<header class="msg-head">
|
||
<span class="who">
|
||
<span class="role role-{{ a.sender|lower if a.sender else 'system' }}">{{ a.sender or 'System' }}</span>
|
||
{{ a.frm }}
|
||
</span>
|
||
<time>{{ a.created_at|dt }}</time>
|
||
</header>
|
||
{% if a.subject %}<div class="msg-subj">{{ a.subject }}</div>{% endif %}
|
||
<div class="msg-body">{{ a.body }}</div>
|
||
{% if a.attachments %}
|
||
{% set imgs = a.attachments|selectattr('is_image')|list %}
|
||
{% set files = a.attachments|rejectattr('is_image')|list %}
|
||
{% if imgs %}
|
||
<div class="msg-imgs">
|
||
{% for att in imgs %}
|
||
<a href="{{ url_for('attachment', ticket_id=ticket.id, article_id=a.id, att_id=att.id) }}" target="_blank" rel="noopener">
|
||
<img class="msg-img" loading="lazy" alt="{{ att.filename }}"
|
||
src="{{ url_for('attachment', ticket_id=ticket.id, article_id=a.id, att_id=att.id) }}">
|
||
</a>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% if files %}
|
||
<div class="msg-att">
|
||
{% for att in files %}
|
||
📎 {% if att.id %}<a href="{{ url_for('attachment', ticket_id=ticket.id, article_id=a.id, att_id=att.id) }}" target="_blank" rel="noopener">{{ att.filename }}</a>{% else %}{{ att.filename }}{% endif %}
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% endif %}
|
||
</article>
|
||
{% endfor %}
|
||
</div>
|
||
{% else %}
|
||
<p class="muted">Keine Nachrichten vorhanden.</p>
|
||
{% endif %}
|
||
|
||
{% if ticket.state != 'closed' %}
|
||
<form method="post" action="{{ url_for('reply_preview', ticket_id=ticket.id) }}"
|
||
class="reply-form attach-form" enctype="multipart/form-data">
|
||
<textarea name="body" rows="4" required data-draft
|
||
placeholder="Antwort an den EPI-Support verfassen… (Screenshot mit Strg+V einfügen)"></textarea>
|
||
{% include "_attach.html" %}
|
||
<div class="form-actions">
|
||
<span class="hint-inline">Geht nach Bestätigung live an EPI.</span>
|
||
<button class="btn-sm" type="submit">Antworten →</button>
|
||
</div>
|
||
</form>
|
||
{% else %}
|
||
<p class="muted">Ticket ist geschlossen – zum Antworten ggf. ein neues Ticket anlegen.</p>
|
||
{% endif %}
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<div class="panel-head"><h2>Verlauf (von uns erfasst)</h2></div>
|
||
{% if timeline %}
|
||
<ul class="timeline">
|
||
{% for c in timeline %}
|
||
<li>
|
||
<span class="tag tag-{{ c.field }}">{{ c.label }}</span>
|
||
<div class="chg">{{ c.old_value or '–' }} → <b>{{ c.new_value or '–' }}</b>
|
||
{% if c.detail %}<div class="detail">{{ c.detail }}</div>{% endif %}
|
||
</div>
|
||
<time>{{ c.detected_at|dt }}</time>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% else %}
|
||
<p class="muted">Seit Beobachtungsbeginn keine Änderung erfasst.</p>
|
||
{% endif %}
|
||
</section>
|
||
{% endblock %}
|
||
{% block scripts %}<script src="{{ url_for('static', filename='upload.js') }}"></script>{% endblock %}
|