40096-vm/core/templates/core/ticket_detail.html
2026-05-26 15:54:52 +00:00

148 lines
6.8 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ ticket.ticket_number }} · RelayDesk{% endblock %}
{% block meta_description %}Threaded replies and status tracking for {{ ticket.ticket_number }}.{% endblock %}
{% block content %}
<section class="page-hero py-5">
<div class="container">
<div class="row align-items-start g-4">
<div class="col-lg-8">
<div class="d-flex flex-wrap align-items-center gap-2 mb-3">
<span class="eyebrow">{{ ticket.ticket_number }}</span>
<span class="status-badge status-{{ ticket.status }}">{{ ticket.get_status_display }}</span>
<span class="priority-pill priority-{{ ticket.priority }}">{{ ticket.get_priority_display }}</span>
</div>
<h1 class="display-6 fw-bold">{{ ticket.subject }}</h1>
<p class="lead text-muted mb-0">Submitted by {{ ticket.requester_name }} in {{ ticket.get_category_display }} · Updated {{ ticket.updated_at|timesince }} ago</p>
</div>
<div class="col-lg-4 text-lg-end">
<a class="btn btn-outline-dark" href="{% url 'ticket_list' %}">Back to queue</a>
</div>
</div>
</div>
</section>
<section class="container mb-5">
<div class="row g-4">
<div class="col-lg-8">
<article class="support-card mb-4">
<span class="section-kicker">Original request</span>
<p class="ticket-description mb-0">{{ ticket.description|linebreaksbr }}</p>
</article>
<div class="support-card mb-4">
<div class="d-flex justify-content-between align-items-center gap-3 mb-4">
<div>
<span class="section-kicker">Threaded replies</span>
<h2 class="h3 mb-0">Conversation</h2>
</div>
<span class="reply-count">{{ ticket.replies.count }} repl{{ ticket.replies.count|pluralize:"y,ies" }}</span>
</div>
<div class="timeline">
<div class="timeline-item requester">
<div class="timeline-dot" aria-hidden="true"></div>
<div class="reply-card">
<div class="reply-meta">
<strong>{{ ticket.requester_name }}</strong>
<span>opened this ticket · {{ ticket.created_at|date:"M j, Y g:i A" }}</span>
</div>
<p class="mb-0">{{ ticket.description|linebreaksbr }}</p>
</div>
</div>
{% for reply in ticket.replies.all %}
<div class="timeline-item {% if reply.is_staff_reply %}staff{% else %}requester{% endif %}">
<div class="timeline-dot" aria-hidden="true"></div>
<div class="reply-card">
<div class="reply-meta">
<strong>{{ reply.author_name }}</strong>
<span>{% if reply.is_staff_reply %}support agent{% else %}requester{% endif %} · {{ reply.created_at|date:"M j, Y g:i A" }}</span>
</div>
<p class="mb-0">{{ reply.body|linebreaksbr }}</p>
</div>
</div>
{% empty %}
<div class="empty-thread">No replies yet. Add the first update below.</div>
{% endfor %}
</div>
</div>
<div class="support-card">
<span class="section-kicker">Add update</span>
<h2 class="h3">Post a reply</h2>
<form method="post" novalidate>
{% csrf_token %}
<input type="hidden" name="reply_submit" value="1">
{% include "core/partials/form_errors.html" with form=reply_form %}
<div class="row g-3">
<div class="col-md-6">
<label class="form-label" for="{{ reply_form.author_name.id_for_label }}">{{ reply_form.author_name.label }}</label>
{{ reply_form.author_name }}
{% include "core/partials/field_errors.html" with field=reply_form.author_name %}
</div>
<div class="col-md-6">
<label class="form-label" for="{{ reply_form.author_email.id_for_label }}">{{ reply_form.author_email.label }}</label>
{{ reply_form.author_email }}
{% include "core/partials/field_errors.html" with field=reply_form.author_email %}
</div>
<div class="col-12">
<label class="form-label" for="{{ reply_form.body.id_for_label }}">{{ reply_form.body.label }}</label>
{{ reply_form.body }}
{% include "core/partials/field_errors.html" with field=reply_form.body %}
</div>
</div>
<button class="btn btn-primary mt-4" type="submit">Add reply</button>
</form>
</div>
</div>
<aside class="col-lg-4">
<div class="support-card sticky-summary mb-4">
<span class="section-kicker">Status tracking</span>
<dl class="ticket-facts">
<div><dt>Status</dt><dd><span class="status-badge status-{{ ticket.status }}">{{ ticket.get_status_display }}</span></dd></div>
<div><dt>Priority</dt><dd>{{ ticket.get_priority_display }}</dd></div>
<div><dt>Category</dt><dd>{{ ticket.get_category_display }}</dd></div>
<div><dt>Assigned</dt><dd>{% if ticket.assigned_to %}{{ ticket.assigned_to.get_username }}{% else %}Unassigned{% endif %}</dd></div>
<div><dt>Requester</dt><dd>{{ ticket.requester_name }}<br><a href="mailto:{{ ticket.requester_email }}">{{ ticket.requester_email }}</a></dd></div>
</dl>
</div>
{% if user.is_staff %}
<div class="support-card">
<span class="section-kicker">Agent controls</span>
<h2 class="h4">Triage ticket</h2>
<form method="post" novalidate>
{% csrf_token %}
<input type="hidden" name="triage_submit" value="1">
{% include "core/partials/form_errors.html" with form=triage_form %}
<div class="mb-3">
<label class="form-label" for="{{ triage_form.status.id_for_label }}">Status</label>
{{ triage_form.status }}
{% include "core/partials/field_errors.html" with field=triage_form.status %}
</div>
<div class="mb-3">
<label class="form-label" for="{{ triage_form.priority.id_for_label }}">Priority</label>
{{ triage_form.priority }}
{% include "core/partials/field_errors.html" with field=triage_form.priority %}
</div>
<div class="mb-3">
<label class="form-label" for="{{ triage_form.assigned_to.id_for_label }}">Assigned to</label>
{{ triage_form.assigned_to }}
{% include "core/partials/field_errors.html" with field=triage_form.assigned_to %}
</div>
<button class="btn btn-secondary w-100" type="submit">Update triage</button>
</form>
</div>
{% else %}
<div class="mini-panel">
<strong>Agent controls</strong>
<p class="mb-0">Staff can sign in through Admin to assign owners and update ticket status.</p>
</div>
{% endif %}
</aside>
</div>
</section>
{% endblock %}