53 lines
16 KiB
HTML
53 lines
16 KiB
HTML
{% extends 'base.html' %}{% load static jalali %}
|
||
{% block title %}{{ page_title }}{% endblock %}
|
||
{% block content %}
|
||
<div class="ticket-workspace staff-ticket-workspace" data-conversation-workspace data-read-url="{% url 'staff_tickets:read_shares' ticket.public_id %}" data-poll-url="{% url 'staff_tickets:poll' ticket.public_id %}" data-poll-version="{{ poll_version }}" data-poll-cursor="{{ poll_cursor }}" data-draft-key="ticket-{{ ticket.public_id }}-staff" data-reply-result="{{ request.GET.reply_result|default:'' }}">
|
||
<header class="ticket-workspace-head">
|
||
<div class="ticket-title"><a class="workspace-back" href="{% url 'staff_tickets:list' %}" aria-label="بازگشت">→</a><div><span class="ticket-number" dir="ltr">{{ ticket.ticket_number }}</span><h1>{{ ticket.subject }}</h1></div></div>
|
||
<div class="ticket-head-actions"><span class="badge" data-ticket-status>{{ ticket.get_status_display }}</span>{% include 'tickets/_voip_call_button.html' with customer=ticket.customer %}{% if can_claim %}<form method="post" action="{% url 'staff_tickets:claim' ticket.public_id %}">{% csrf_token %}<button type="submit">اختصاص به من</button></form>{% endif %}<button type="button" class="icon-button" data-toggle-panel="ticket-actions" aria-label="عملیات تیکت">⚙</button><button type="button" class="icon-button" data-toggle-panel="customer-info" aria-label="اطلاعات مشتری">◉</button></div>
|
||
</header>
|
||
{% if ticket.group_dispatch %}
|
||
<section class="ticket-dispatch-context" aria-label="اطلاعات ارسال گروهی">
|
||
<div><span aria-hidden="true">⇶</span><p><small>{{ ticket.group_dispatch.get_operation_type_display }}</small><b>{{ ticket.group_dispatch.subject }}</b><em>{{ ticket.group_dispatch.contact_group.name }}{% if ticket.group_dispatch.response_control.mode == 'SEALED' and not ticket.group_dispatch.response_control.opened_at %} — پاسخها تا بازگشایی محرمانهاند{% endif %}</em></p></div>
|
||
<div class="ticket-dispatch-context-actions">{% if can_view_dispatch %}<a href="{% url 'staff_tickets:dispatch_detail' ticket.group_dispatch.public_id %}">مشاهده گزارش گروه</a>{% endif %}{% if can_resolve_dispatch and group_open_count %}<form method="post" action="{% url 'staff_tickets:dispatch_resolve' ticket.group_dispatch.public_id %}" onsubmit="return confirm('هر {{ group_open_count }} تیکت باز این ارسال گروهی حلشده میشوند. آیا مطمئن هستید؟')">{% csrf_token %}<input type="hidden" name="return_ticket" value="{{ ticket.public_id }}"><button type="submit">✓ حلشدن تمامی تیکتهای گروه ({{ group_open_count }})</button></form>{% endif %}</div>
|
||
<button type="button" class="ticket-dispatch-context-close" data-dismiss-dispatch-context aria-label="بستن پیام ارسال گروهی" title="بستن">×</button>
|
||
</section>
|
||
{% endif %}
|
||
<div class="workspace-grid staff-conversation-grid">
|
||
<aside class="workspace-side actions-side" id="ticket-actions" data-workspace-panel>
|
||
<button type="button" class="panel-close" data-close-panel>×</button>
|
||
<section class="info-card"><p class="panel-index">وضعیت تیکت</p><div class="info-list"><div class="info-row"><span>وضعیت</span><b>{{ ticket.get_status_display }}</b></div><div class="info-row"><span>اولویت</span><b>{{ ticket.get_priority_display }}</b></div><div class="info-row"><span>دسته</span><b>{{ ticket.category }}</b></div><div class="info-row"><span>مالک</span><b>{{ ticket.assigned_expert_name }}</b></div>{% if ticket.first_response_due_at %}<div class="info-row"><span>مهلت پاسخ</span><b>{{ ticket.first_response_due_at|jalali_datetime }}</b></div>{% endif %}</div></section>
|
||
{% if sms_jobs %}<section class="info-card sms-delivery-card"><div class="sms-delivery-heading"><div><h3>وضعیت پیامکهای مشتری</h3><small>خلاصه ارسال و تحویل</small></div><span aria-hidden="true">ⓘ</span></div><div class="sms-delivery-list">{% for sms_job in sms_jobs %}<details class="sms-delivery-item"><summary><span class="sms-delivery-event">{% if sms_job.event_type == 'STAFF_TICKET_CREATED' %}ثبت تیکت توسط کارشناس{% elif sms_job.event_type == 'CUSTOMER_TICKET_CREATED' %}ثبت تیکت مشتری{% elif sms_job.event_type == 'STAFF_REPLY' %}اعلان پاسخ کارشناس{% else %}پیامک سیستمی{% endif %}</span><b>{% if sms_job.delivery_status %}{{ sms_job.get_delivery_status_display }}{% else %}{{ sms_job.get_status_display }}{% endif %}</b><span class="sms-delivery-info" aria-label="نمایش جزئیات زمانبندی پیامک" title="نمایش جزئیات">i</span></summary><ol class="sms-delivery-timeline"><li class="is-complete"><span></span><div><b>ثبت درخواست ارسال</b><time datetime="{{ sms_job.created_at|date:'c' }}">{{ sms_job.created_at|jalali_datetime }}</time></div></li><li class="{% if sms_job.sent_at %}is-complete{% endif %}"><span></span><div><b>پذیرش توسط درگاه پیامک</b>{% if sms_job.sent_at %}<time datetime="{{ sms_job.sent_at|date:'c' }}">{{ sms_job.sent_at|jalali_datetime }}</time>{% else %}<small>{% if sms_job.status == 'FAILED' %}ناموفق{% elif sms_job.status == 'CANCELLED' %}لغوشده پیش از ارسال{% else %}در انتظار ارسال{% endif %}</small>{% endif %}</div></li><li class="{% if sms_job.delivery_status == 'DELIVERED' %}is-complete{% endif %}"><span></span><div><b>تأیید رسیدن به گوشی</b>{% if sms_job.delivery_status == 'DELIVERED' and sms_job.delivery_checked_at %}<time datetime="{{ sms_job.delivery_checked_at|date:'c' }}">{{ sms_job.delivery_checked_at|jalali_datetime }}</time>{% elif sms_job.delivery_checked_at %}<small>آخرین استعلام: {{ sms_job.delivery_checked_at|jalali_datetime }}</small>{% else %}<small>{% if sms_job.sent_at %}در انتظار نتیجه اپراتور{% else %}هنوز ارسال نشده{% endif %}</small>{% endif %}</div></li></ol>{% if sms_job.delivery_description %}<p class="sms-delivery-description">{{ sms_job.delivery_description }}</p>{% endif %}{% if sms_job.last_error %}<p class="sms-delivery-error">علت دقیق: {{ sms_job.last_error }}</p>{% endif %}{% if sms_job.provider_message_id %}<small class="sms-delivery-tracking" dir="ltr">شناسه رهگیری: {{ sms_job.provider_message_id }}</small>{% endif %}{% if sms_job.sms_retryable %}<form method="post" action="{% url 'staff_tickets:sms_retry' ticket.public_id sms_job.pk %}" class="inline-retry-form">{% csrf_token %}<button type="submit" aria-label="تلاش مجدد برای ارسال این پیامک" title="تلاش مجدد"><span aria-hidden="true">↻</span> تلاش مجدد</button></form>{% endif %}</details>{% endfor %}</div></section>{% endif %}
|
||
{% include 'tickets/_immediate_announcement.html' %}
|
||
{% include 'tickets/_announcement_status.html' %}
|
||
<section class="info-card ticket-operations"><h3>عملیات تیکت</h3>{% if can_share %}<form method="post" action="{% url 'staff_tickets:status' ticket.public_id %}">{% csrf_token %}<label>وضعیت<select name="status">{% for value,label in statuses %}<option value="{{ value }}" {% if ticket.status == value %}selected{% endif %}>{{ label }}</option>{% endfor %}</select></label><button>ثبت وضعیت</button></form>{% endif %}{% if can_share %}<form method="post" action="{% url 'staff_tickets:priority' ticket.public_id %}">{% csrf_token %}<label>اولویت<select name="priority">{% for value,label in priorities %}<option value="{{ value }}" {% if ticket.priority == value %}selected{% endif %}>{{ label }}</option>{% endfor %}</select></label><button class="secondary">ثبت اولویت</button></form>{% endif %}{% if can_transfer %}<details><summary>انتقال تیکت</summary><form method="post" action="{% url 'staff_tickets:transfer' ticket.public_id %}">{% csrf_token %}{{ transfer_form.as_p }}<button>انتقال</button></form></details>{% endif %}{% include "tickets/_staff_ticket_share.html" %}</section>
|
||
{% include 'tickets/_staff_sharing_audit.html' %}
|
||
{% if can_delete %}<section class="info-card danger-zone"><h3>حذف کامل تیکت</h3><p class="muted">تمام پیامها، فایلها و سوابق تیکت برای همیشه حذف میشوند.</p><form method="post" action="{% url 'staff_tickets:delete' ticket.public_id %}" onsubmit="return confirm('آیا از حذف کامل و غیرقابلبازگشت این تیکت مطمئن هستید؟')">{% csrf_token %}<button type="submit" class="danger-button">حذف کامل تیکت</button></form></section>{% endif %}
|
||
</aside>
|
||
<main class="conversation-pane correspondence-pane">
|
||
<section class="conversation-toolbar"><div><b>سوابق رسیدگی و مکاتبات</b></div><small><span data-conversation-count>{{ conversation|length }}</span> مکاتبه</small></section>
|
||
<section class="conversation" data-conversation role="log" aria-live="polite">{% if has_older %}<button class="load-older" type="button" data-load-older hx-get="{% url 'staff_tickets:messages' ticket.public_id %}?before={{ older_cursor }}" hx-target="this" hx-swap="outerHTML">↑ نمایش مکاتبات قدیمیتر</button>{% endif %}{% include 'tickets/_staff_messages.html' %}<div class="conversation-end" data-conversation-end aria-hidden="true"></div></section>
|
||
<button class="new-message-jump" type="button" data-jump-latest hidden>↓ رفتن به آخرین مکاتبه</button>
|
||
{% if not limited_share %}<section class="composer staff-composer {% if request.GET.reply_result != 'error' %}is-collapsed{% endif %}" x-data="{mode:'reply'}" :class="mode==='note'&&'note-mode'" data-composer-shell>
|
||
<button type="button" class="composer-launch" data-composer-toggle aria-expanded="false"><span class="composer-launch-icon">+</span><span><b>ثبت اقدام جدید در تیکت</b><small>پاسخ رسمی یا یادداشت کارشناسی ثبت کنید</small></span><span class="composer-launch-arrow">↑</span></button>
|
||
<div class="mode" role="tablist"><button type="button" role="tab" data-composer-mode="reply" @click="mode='reply'" :class="mode==='reply'&&'active'">پاسخ رسمی به مشتری</button>{% if perms.messaging.add_internal_note %}<button type="button" role="tab" data-composer-mode="note" @click="mode='note'" :class="mode==='note'&&'active note-button'">🔒 یادداشت کارشناسی</button>{% endif %}</div>
|
||
<form method="post" enctype="multipart/form-data" data-ticket-composer data-chat-coach data-chat-coach-key="ticket-{{ ticket.public_id }}-staff-{{ user.public_id }}" :action="mode==='note'?'{% url 'staff_tickets:note' ticket.public_id %}':'{% url 'staff_tickets:reply' ticket.public_id %}'">{% csrf_token %}
|
||
<div class="composer-context"><div><b x-text="mode==='note'?'ثبت یادداشت کارشناسی در پرونده':'ثبت پاسخ رسمی در تیکت'"></b><small x-text="mode==='note'?'این یادداشت فقط برای تیم کارشناسی قابل مشاهده است.':'پاسخ برای مشتری ارسال میشود و اعلان خواهد شد.'"></small></div></div>
|
||
<div class="composer-input-row"><div class="composer-editor">{{ form.body }}{{ form.body.errors }}<span class="composer-draft-status" data-draft-status></span></div><span class="composer-send-icon" role="button" tabindex="0" data-scroll-to-submit aria-label="رفتن به دکمه ثبت پاسخ" title="رفتن به دکمه ثبت پاسخ"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 20.5 21 12 3 3.5v6.6l12 1.9-12 1.9z"/></svg></span></div>
|
||
<div class="reply-media-picker"><div class="media-picker-actions"><label class="media-picker-button gallery-button" for="staff-reply-gallery"><span class="media-picker-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M4 5h3l1.2-1.5h7.6L17 5h3a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2Zm8 3.2a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8Z"/></svg></span><span>انتخاب عکس از گالری</span></label><label class="media-picker-button" for="staff-reply-file"><span class="media-picker-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M8.5 12.8 14.8 6.5a3 3 0 1 1 4.2 4.2l-8.5 8.5a5 5 0 0 1-7.1-7.1l8-8 1.4 1.4-8 8a3 3 0 1 0 4.3 4.3l8.5-8.5a1 1 0 0 0-1.4-1.4l-6.4 6.3a1 1 0 0 0 1.4 1.5l5.7-5.7 1.4 1.4-5.7 5.7a3 3 0 1 1-4.2-4.3Z"/></svg></span><span>انتخاب فایل</span></label></div><input id="staff-reply-gallery" type="file" accept="image/*" multiple data-gallery-input><input id="staff-reply-file" type="file" multiple data-document-input><span class="canonical-files">{{ form.files }}</span>{{ form.files.errors }}<small class="compression-note" data-compression-status>تصاویر بزرگ پیش از ارسال بهینه میشوند.</small></div><div class="file-preview" data-file-preview></div>
|
||
<div class="composer-extra" hidden data-composer-extra>{{ form.mentioned_experts.label_tag }}{{ form.mentioned_experts }}</div>
|
||
<div class="voice-recorder compact-recorder" data-voice-recorder hidden><div class="voice-head"><b>افزودن توضیح صوتی</b><span class="badge" data-timer>00:00</span></div><div class="voice-actions"><button type="button" data-record>● شروع ضبط</button><button type="button" data-pause disabled>مکث</button><button type="button" data-stop disabled>توقف</button><button type="button" class="ghost" data-delete hidden>حذف</button></div><audio controls hidden></audio></div>
|
||
<div class="composer-bar"><div class="composer-tools"><button class="tool-button" type="button" data-toggle-recorder>●<span>توضیح صوتی</span></button><button class="tool-button" type="button" data-toggle-extra>@<span>اطلاع به کارشناس</span></button><button class="tool-button composer-close" type="button" data-composer-close>بستن</button></div><small class="composer-hint">پیش از ثبت، متن و پیوستها را بازبینی کنید.</small><button class="send-button" type="submit" data-primary-submit><span x-text="mode==='note'?'ثبت یادداشت در پرونده':'ثبت پاسخ در تیکت'"></span> ←</button></div>
|
||
</form>
|
||
</section>
|
||
{% endif %}
|
||
</main>
|
||
<aside class="workspace-side customer-side" id="customer-info" data-workspace-panel><button type="button" class="panel-close" data-close-panel>×</button><section class="info-card"><p class="panel-index">مشتری</p><h3>{{ ticket.customer.customer_profile|default:'بدون نام' }}</h3><p class="mt-1 text-sm font-bold" dir="ltr">{{ ticket.customer.mobile }}</p><div class="info-list"><div class="info-row"><span>نوع مشتری</span><b>{{ ticket.customer.customer_profile.get_customer_type_display|default:'ثبت نشده' }}</b></div><div class="info-row"><span>موقعیت تیکت</span><b>{% if ticket.city %}{{ ticket.city }}، {{ ticket.province }}{% elif ticket.province %}{{ ticket.province }}{% else %}ثبت نشده{% endif %}</b></div><div class="info-row"><span>تعداد تیکت</span><b>{{ ticket.customer.customer_tickets.count }}</b></div></div>{% include 'payments/_ticket_notices.html' with customer=ticket.customer %}</section><section class="info-card"><h3>سابقه اخیر</h3>{% for old in customer_history %}<div class="history-item"><b>{{ old.subject }}</b><span class="ticket-number">{{ old.ticket_number }}</span>{% if perms.tickets.view_all_tickets or old.owner == user or old.selected_expert == user %}<a class="back-link" href="{% url 'staff_tickets:detail' old.public_id %}">مشاهده ←</a>{% else %}<small class="muted">محتوای خصوصی</small>{% endif %}</div>{% empty %}<p class="muted mt-3">تیکت دیگری ثبت نشده است.</p>{% endfor %}</section></aside>
|
||
</div>
|
||
</div>
|
||
{% include 'tickets/_image_viewer.html' %}
|
||
<script src="{% static 'js/conversation-read.js' %}?v={{ static_asset_version }}"></script>
|
||
<link rel="stylesheet" href="{% static 'css/staff-sharing.css' %}?v={{ static_asset_version }}">
|
||
<script src="{% static 'vendor/compressor.min.js' %}?v={{ static_asset_version }}"></script><script src="{% static 'js/attachment-uploader.js' %}?v={{ static_asset_version }}"></script><script src="{% static 'js/image-viewer.js' %}?v={{ static_asset_version }}"></script><script src="{% static 'js/conversation.js' %}?v={{ static_asset_version }}"></script><script src="{% static 'js/voip-call.js' %}?v={{ static_asset_version }}"></script>
|
||
{% endblock %}
|