{% extends "base.html" %} {% block title %}Ticket #{{ provider_thread_id or ticket.id }}{% endblock %} {% block content %}
{# ============== Left: conversation column ============== #}
{# -- Sticky header -- #}
{# Row 1: back, subject, ID, external link … refresh #}
arrow_back

{{ ticket.thread.subject }}

#{{ provider_thread_id or ticket.id }} {% if provider_thread_id %} open_in_new {% endif %}
{# Row 1b: product icon + name + customer website #} {% if ticket.thread.product_name or ticket.thread.customer_website %}
{% if product and product.product_type == 'business_care' %}
BC
{% elif product and product.pico_url %} {% endif %} {% if ticket.thread.product_name %} {{ ticket.thread.product_name }} {% endif %} {% if ticket.thread.customer_website %} {% set ws_href = ticket.thread.customer_website if ticket.thread.customer_website.startswith('http') else 'https://' + ticket.thread.customer_website %} {% if site_check and site_check.is_online == true %} {% set tip = 'Online' %} {% if site_check.error_message %}{% set tip = 'Online (' ~ site_check.http_status ~ ' — ' ~ site_check.error_message ~ ')' %} {% elif site_check.http_status %}{% set tip = 'Online (' ~ site_check.http_status ~ ', ' ~ (site_check.response_time_ms or '?') ~ 'ms)' %}{% endif %} {% elif site_check and site_check.is_online == false %} {% set tip = 'Offline' %} {% if site_check.error_message %}{% set tip = 'Offline — ' ~ site_check.error_message %}{% endif %} {% endif %} {{ ticket.thread.customer_website | truncate(32) }} {% endif %}
{% endif %} {# -- Second row: controls + info -- #}
{# Support contract status #} {% if ticket.thread.is_support_expired %} error Support Expired {% elif ticket.thread.is_support_expired is not none %} check_circle Support Active {% endif %} {# Intent / sentiment badge from latest AI classification #} {% if suggestions %} {% set _latest_s = suggestions|sort(attribute='created_at', reverse=True)|first %} {% set _intent = (_latest_s.classification_json|fromjson).get('intent_category', '') %} {% if _intent %} {% set _intent_map = { 'angry': ['red', 'sentiment_very_dissatisfied'], 'escalation': ['red', 'priority_high'], 'complaint': ['orange', 'feedback'], 'refund': ['orange', 'currency_exchange'], 'bug': ['amber', 'bug_report'], 'question': ['blue', 'help'], 'config': ['blue', 'settings'], 'help': ['blue', 'support_agent'], 'feature_request': ['purple', 'lightbulb'], 'thanks_solved': ['green', 'check_circle'] } %} {% set _style = _intent_map.get(_intent, ['gray', 'label']) %} {{ _style[1] }} {{ _intent | replace('_', ' ') | title }} {% endif %} {% endif %} {# Customer language — manual override (any language, not limited to what support knows) #}
{% if not ticket.is_read %} {% endif %} {% if not ticket.first_response_at %} {% endif %}
{# -- Thread Summary / TL;DR -- shown when no segments, otherwise integrated into topic overlay #} {% if thread_summary and segments|length <= 1 %}
summarize Thread Summary
{{ thread_summary }}
{% endif %} {# -- Review request banner -- #} {% if is_review_request and ticket.status == 'closed' and not ticket.is_read %}
sentiment_very_satisfied

Happy customer — time for a review request!

This customer sent a thank-you on a closed ticket. Reply to clear the Addons "unanswered" counter and earn a positive rating.

{% endif %} {# -- Conversation thread -- #}
{# Floating topic overlay — single pill, updated via JS #} {% if segments|length > 1 %}
{# Expandable per-topic summary panel under the pill #}
{% endif %} {% set ns = namespace(prev_seg=-1) %} {% for msg in messages %} {% set cur_seg = message_segment_map.get(msg.id, -1) %} {% if cur_seg != ns.prev_seg and cur_seg >= 0 %} {# Inline topic marker (not sticky — observed by JS) #}
topic {{ segments[cur_seg].label if cur_seg < segments|length else 'Topic' }}
{% set ns.prev_seg = cur_seg %} {% endif %} {% if scroll_to_message_id and msg.id == scroll_to_message_id %}
Your last reply
{% endif %} {% set outbound = msg.direction == 'outbound' %}
{# Avatar #}
{{ 'C' if not outbound else 'A' }}
{# Bubble + hover actions #}
{# Bubble #}
{{ msg.author_display }} {{ msg.created_at.strftime('%Y-%m-%d %H:%M') }}
{% if msg.body_raw %}{{ msg.body_raw | safe }}{% else %}{{ msg.body_text | e | replace('\n', '
') | safe }}{% endif %}
{# Translation result area — pre-populated from cache when available #} {% set cached_tr = cached_translations.get(msg.id) %}
{% if cached_tr %}{{ cached_tr | safe }}{% endif %}
{# Attachments #} {% if msg.attachments %}
{% for att in msg.attachments %} {% set ext = att.filename.rsplit('.', 1)[-1].lower() if '.' in att.filename else '' %} {% if ext in ('jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg') %} {% set att_icon = 'image' %} {% set att_color = 'bg-blue-50 hover:bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-500/10 dark:hover:bg-blue-500/20 dark:text-blue-400 dark:border-blue-500/30' %} {% set att_label = 'Image' %} {% elif ext == 'pdf' %} {% set att_icon = 'picture_as_pdf' %} {% set att_color = 'bg-red-50 hover:bg-red-100 text-red-700 border-red-200 dark:bg-red-500/10 dark:hover:bg-red-500/20 dark:text-red-400 dark:border-red-500/30' %} {% set att_label = 'PDF' %} {% elif ext in ('zip', 'rar', '7z', 'tar', 'gz') %} {% set att_icon = 'folder_zip' %} {% set att_color = 'bg-amber-50 hover:bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-500/10 dark:hover:bg-amber-500/20 dark:text-amber-400 dark:border-amber-500/30' %} {% set att_label = ext.upper() %} {% elif ext in ('doc', 'docx', 'odt', 'txt') %} {% set att_icon = 'description' %} {% set att_color = 'bg-indigo-50 hover:bg-indigo-100 text-indigo-700 border-indigo-200 dark:bg-indigo-500/10 dark:hover:bg-indigo-500/20 dark:text-indigo-400 dark:border-indigo-500/30' %} {% set att_label = ext.upper() %} {% else %} {% set att_icon = 'attach_file' %} {% set att_color = 'bg-gray-100 hover:bg-gray-200 text-gray-700 border-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 dark:text-gray-300 dark:border-gray-600' %} {% set att_label = ext.upper() if ext else 'File' %} {% endif %} {{ att_icon }} {{ att_label }} .{{ ext }} {% endfor %}
{% endif %}
{# Hover action bar #}
{% if known_languages | length > 1 %}
{% endif %}
{% endfor %}
{# -- Reply bar -- #}
{# Minimize/expand toggle #} {# Tool strip — compact icon buttons that expand panels on click #}
{% if id_product %} {% endif %}
{# Collapsible: Quick Reply panel #} {% if id_product %} {% endif %} {# Collapsible: Product URL builder panel #} {# Editor — full width #}
{# Draft translation result panel #}
{# Action bar — horizontal below editor #} {% set _translate_lang = customer_language or preferred_language %}
{# Attach #} {# Translate — with dropdown #}
{% set _all_common_langs = ['ar','ca','cs','da','de','el','en','es','fi','fr','he','hr','hu','id','it','ja','ko','nl','no','pl','pt','ro','ru','sk','sv','tr','uk','zh'] %}
{# Send — right-aligned #}
{# Quill.js WYSIWYG Editor #} {# Paste image confirmation dialog #}
{# ============== Right sidebar ============== #}
{% if id_product %} {% endif %} {% endblock %}