Files
EpiSupport-Zammad-Scraper/templates/ticket.html
T
leopold.stroblandClaude Opus 4.8 1107a912bc Add ticket status setter and prevent draft text loss
- Set ticket state (Offen / In Bearbeitung / Warten auf Schließen /
  Geschlossen) from the ticket detail page via a live PUT to Zammad;
  pending-close gets a default pending time and the change is synced back
  so it isn't flagged as an external change
- Replace the disruptive meta-refresh with a JS auto-refresh that pauses
  while a text field has unsent content or focus
- Persist reply/new-ticket drafts to localStorage and restore them after
  a reload or accidental navigation

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 11:15:30 +02:00

121 lines
5.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% 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>
<a class="btn-sm" href="{{ zammad_url }}/#ticket/zoom/{{ ticket.id }}"
target="_blank" rel="noopener">in Zammad öffnen ↗</a>
</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 %}