src/CompanyGroupBundle/Resources/views/pages/tickets/view_ticket.html.twig line 1

Open in your IDE?
  1. {% set page_title = (ticket.ticketNumber ?? 'Ticket') ~ ' | HoneyBee' %}
  2. {% include '@Application/inc/central_header.html.twig' %}
  3. {% include '@CompanyGroup/pages/admin/_sidebar.html.twig' %}
  4. <style>
  5.     .ticket-view-section { padding: 2rem 0 4rem; }
  6.     /* Breadcrumb */
  7.     .breadcrumb-bar { font-size: 0.82rem; color: #888; margin-bottom: 1.5rem; }
  8.     .breadcrumb-bar a { color: #1d5b9e; text-decoration: none; }
  9.     .breadcrumb-bar a:hover { text-decoration: underline; }
  10.     /* Header card */
  11.     .ticket-header-card {
  12.         background: #fff;
  13.         border: 1px solid #e3eaf3;
  14.         border-radius: 14px;
  15.         padding: 1.5rem 1.75rem;
  16.         margin-bottom: 1.5rem;
  17.     }
  18.     .tk-number {
  19.         font-size: 0.8rem; font-weight: 700;
  20.         color: #1d5b9e; background: #e8f0fb;
  21.         border-radius: 6px; padding: 3px 10px;
  22.         display: inline-block; margin-bottom: 0.6rem;
  23.     }
  24.     .tk-title {
  25.         font-size: 1.4rem; font-weight: 700; color: #1a1a2e; margin-bottom: 0.5rem;
  26.     }
  27.     /* Badges */
  28.     .badge-pill { font-size: 0.72rem; font-weight: 600; border-radius: 50px; padding: 3px 10px; display: inline-block; }
  29.     .badge-status-1  { background: #e3f0ff; color: #1d5b9e; }
  30.     .badge-status-2  { background: #fff3cd; color: #856404; }
  31.     .badge-status-3  { background: #d1f7e7; color: #1a7a4a; }
  32.     .badge-status-4  { background: #e9ecef; color: #6c757d; }
  33.     .badge-priority-1 { background: #fde8e8; color: #c0392b; }
  34.     .badge-priority-2 { background: #fff3cd; color: #856404; }
  35.     .badge-priority-3 { background: #e9ecef; color: #555; }
  36.     .badge-source-system { background: #f0e6ff; color: #6f42c1; }
  37.     .badge-source-manual { background: #e3f0ff; color: #1d5b9e; }
  38.     /* Info grid */
  39.     .info-grid {
  40.         display: grid;
  41.         grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  42.         gap: 1rem;
  43.         margin-top: 1.25rem;
  44.     }
  45.     .info-item label {
  46.         font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  47.         letter-spacing: 0.06em; color: #aaa; display: block; margin-bottom: 3px;
  48.     }
  49.     .info-item span { font-size: 0.9rem; color: #333; font-weight: 500; }
  50.     /* Detail card */
  51.     .detail-card {
  52.         background: #fff;
  53.         border: 1px solid #e3eaf3;
  54.         border-radius: 14px;
  55.         padding: 1.5rem 1.75rem;
  56.         margin-bottom: 1.5rem;
  57.     }
  58.     .detail-card h5 {
  59.         font-size: 0.88rem; font-weight: 700; text-transform: uppercase;
  60.         letter-spacing: 0.05em; color: #1d5b9e; margin-bottom: 1rem;
  61.         padding-bottom: 0.5rem; border-bottom: 1px solid #e3eaf3;
  62.     }
  63.     .description-body {
  64.         font-size: 0.92rem; color: #444; line-height: 1.7;
  65.         white-space: pre-wrap;
  66.     }
  67.     /* System tracking card */
  68.     .system-card {
  69.         background: #fcfaff;
  70.         border: 1px solid #e0d4f7;
  71.         border-radius: 14px;
  72.         padding: 1.5rem 1.75rem;
  73.         margin-bottom: 1.5rem;
  74.     }
  75.     .system-card h5 {
  76.         font-size: 0.88rem; font-weight: 700; text-transform: uppercase;
  77.         letter-spacing: 0.05em; color: #6f42c1; margin-bottom: 1rem;
  78.         padding-bottom: 0.5rem; border-bottom: 1px solid #e0d4f7;
  79.     }
  80.     .hash-code {
  81.         font-family: monospace; font-size: 0.8rem;
  82.         background: #f4eeff; color: #6f42c1;
  83.         border-radius: 6px; padding: 4px 10px;
  84.         word-break: break-all;
  85.     }
  86.     .payload-pre {
  87.         background: #1a1a2e; color: #a8d8a8;
  88.         border-radius: 8px; padding: 1rem;
  89.         font-size: 0.78rem; overflow-x: auto;
  90.         max-height: 300px; font-family: monospace;
  91.     }
  92.     /* Timeline */
  93.     .timeline { position: relative; padding-left: 1.75rem; margin-top: 0.5rem; }
  94.     .timeline::before {
  95.         content: ''; position: absolute;
  96.         left: 7px; top: 0; bottom: 0;
  97.         width: 2px; background: #e3eaf3;
  98.     }
  99.     .tl-item { position: relative; margin-bottom: 1.25rem; }
  100.     .tl-dot {
  101.         position: absolute; left: -1.75rem; top: 3px;
  102.         width: 14px; height: 14px; border-radius: 50%;
  103.         background: #1d5b9e; border: 2px solid #fff;
  104.         box-shadow: 0 0 0 2px #1d5b9e;
  105.     }
  106.     .tl-dot.error { background: #c0392b; box-shadow: 0 0 0 2px #c0392b; }
  107.     .tl-time { font-size: 0.75rem; color: #aaa; margin-bottom: 2px; }
  108.     .tl-server { font-size: 0.75rem; color: #888; }
  109.     .tl-message { font-size: 0.875rem; color: #333; font-weight: 500; }
  110.     .tl-code {
  111.         display: inline-block; font-size: 0.72rem; font-weight: 600;
  112.         background: #fde8e8; color: #c0392b;
  113.         border-radius: 4px; padding: 1px 7px; margin-top: 3px;
  114.     }
  115.     .tl-payload-toggle {
  116.         font-size: 0.75rem; color: #1d5b9e;
  117.         cursor: pointer; text-decoration: underline;
  118.         margin-top: 4px; display: inline-block;
  119.     }
  120.     .tl-payload {
  121.         display: none; background: #1a1a2e; color: #a8d8a8;
  122.         border-radius: 6px; padding: 0.75rem; font-size: 0.75rem;
  123.         font-family: monospace; margin-top: 6px; overflow-x: auto;
  124.     }
  125.     /* Action bar */
  126.     .action-bar {
  127.         background: #fff; border: 1px solid #e3eaf3;
  128.         border-radius: 12px; padding: 1rem 1.5rem;
  129.         margin-bottom: 1.5rem;
  130.         display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;
  131.     }
  132.     .btn-hb {
  133.         border-radius: 50px; font-size: 0.82rem; font-weight: 600;
  134.         padding: 0.4rem 1.1rem; border: none; cursor: pointer;
  135.         text-decoration: none; display: inline-block;
  136.     }
  137.     .btn-hb-primary { background: #1d5b9e; color: #fff; }
  138.     .btn-hb-primary:hover { background: #174f8a; color: #fff; }
  139.     .btn-hb-success { background: #1a7a4a; color: #fff; }
  140.     .btn-hb-success:hover { background: #155d39; color: #fff; }
  141.     .btn-hb-danger  { background: #c0392b; color: #fff; }
  142.     .btn-hb-danger:hover  { background: #a93226; color: #fff; }
  143.     .btn-hb-outline { background: transparent; color: #1d5b9e; border: 1.5px solid #1d5b9e; }
  144.     .btn-hb-outline:hover { background: #e8f0fb; }
  145.     .count-chip {
  146.         display: inline-flex; align-items: center; gap: 4px;
  147.         font-size: 0.78rem; color: #c0392b; background: #fde8e8;
  148.         border-radius: 50px; padding: 3px 10px; font-weight: 600;
  149.     }
  150. </style>
  151. <div class="hb-admin-layout">
  152. <section class="ticket-view-section">
  153.     <div class="container">
  154.         {# ── Breadcrumb ── #}
  155.         <div class="breadcrumb-bar">
  156.             <a href="{{ path('ticket_list') }}"><i class="fa fa-ticket-alt fa-xs me-1"></i>Tickets</a>
  157.             &nbsp;/&nbsp;
  158.             <span>{{ ticket.ticketNumber ?? 'View' }}</span>
  159.         </div>
  160.         {# ── Action bar ── #}
  161.         <div class="action-bar">
  162.             <a href="{{ path('ticket_edit', {id: ticket.id}) }}" class="btn-hb btn-hb-outline">
  163.                 <i class="fa fa-edit-alt me-1"></i>Edit
  164.             </a>
  165.             {% if ticket.status == 1 or ticket.status == 2 %}
  166.                 <a href="{{ path('ticket_resolve', {id: ticket.id}) }}" class="btn-hb btn-hb-success">
  167.                     <i class="fa fa-check-circle me-1"></i>Mark Resolved
  168.                 </a>
  169.                 <a href="{{ path('ticket_close', {id: ticket.id}) }}" class="btn-hb btn-hb-danger">
  170.                     <i class="fa fa-times-circle me-1"></i>Close
  171.                 </a>
  172.             {% endif %}
  173.             {% if ticket.status == 1 %}
  174.                 <a href="{{ path('ticket_assign', {id: ticket.id}) }}" class="btn-hb btn-hb-primary">
  175.                     <i class="fa fa-user-check me-1"></i>Assign
  176.                 </a>
  177.             {% endif %}
  178.             <a href="{{ path('ticket_list') }}" class="btn-hb btn-hb-outline ms-auto">
  179.                 <i class="fa fa-arrow-left me-1"></i>Back to List
  180.             </a>
  181.         </div>
  182.         {# ── Header card ── #}
  183.         <div class="ticket-header-card">
  184.             <div class="tk-number">{{ ticket.ticketNumber }}</div>
  185.             <div class="tk-title">{{ ticket.title ?? '(No title)' }}</div>
  186.             <div class="d-flex gap-2 flex-wrap align-items-center">
  187.                 {% set statusLabel = {1: 'Open', 2: 'In Progress', 3: 'Resolved', 4: 'Closed'} %}
  188.                 {% set priorityLabel = {1: '🔴 Emergency', 2: '🟡 Important', 3: '🟢 Normal'} %}
  189.                 <span class="badge-pill badge-status-{{ ticket.status ?? 1 }}">
  190.                     {{ statusLabel[ticket.status] ?? 'Unknown' }}
  191.                 </span>
  192.                 <span class="badge-pill badge-priority-{{ ticket.urgency ?? 3 }}">
  193.                     {{ priorityLabel[ticket.urgency] ?? 'Normal' }}
  194.                 </span>
  195.                 {% if ticket.systemCreated %}
  196.                     <span class="badge-pill badge-source-system">
  197.                         <i class="fa fa-robot fa-xs me-1"></i>System Generated
  198.                     </span>
  199.                 {% else %}
  200.                     <span class="badge-pill badge-source-manual">
  201.                         <i class="fa fa-user fa-xs me-1"></i>Manual
  202.                     </span>
  203.                 {% endif %}
  204.                 {% if ticket.reportCount > 1 %}
  205.                     <span class="count-chip">
  206.                         <i class="fa fa-redo fa-xs"></i>
  207.                         Reported {{ ticket.reportCount }} times
  208.                     </span>
  209.                 {% endif %}
  210.             </div>
  211.             <div class="info-grid">
  212.                 <div class="info-item">
  213.                     <label>Created</label>
  214.                     <span>{{ ticket.createdAt ? ticket.createdAt|date('d M Y, H:i') : '—' }}</span>
  215.                 </div>
  216.                 <div class="info-item">
  217.                     <label>First Reported</label>
  218.                     <span>{{ ticket.firstReportedAt ? ticket.firstReportedAt|date('d M Y, H:i') : '—' }}</span>
  219.                 </div>
  220.                 <div class="info-item">
  221.                     <label>Last Reported</label>
  222.                     <span>{{ ticket.lastReportedAt ? ticket.lastReportedAt|date('d M Y, H:i') : '—' }}</span>
  223.                 </div>
  224.                 {% if ticket.deadlineDate %}
  225.                 <div class="info-item">
  226.                     <label>Deadline</label>
  227.                     <span>{{ ticket.deadlineDate|date('d M Y') }}</span>
  228.                 </div>
  229.                 {% endif %}
  230.                 {% if ticket.resolvedAt %}
  231.                 <div class="info-item">
  232.                     <label>Resolved At</label>
  233.                     <span style="color:#1a7a4a">{{ ticket.resolvedAt|date('d M Y, H:i') }}</span>
  234.                 </div>
  235.                 {% endif %}
  236.                 {% if ticket.closedAt %}
  237.                 <div class="info-item">
  238.                     <label>Closed At</label>
  239.                     <span style="color:#6c757d">{{ ticket.closedAt|date('d M Y, H:i') }}</span>
  240.                 </div>
  241.                 {% endif %}
  242.                 <div class="info-item">
  243.                     <label>Reported By</label>
  244.                     <span>{{ ticket.name ?? ('User #' ~ (ticket.userId ?? '—')) }}</span>
  245.                 </div>
  246.                 <div class="info-item">
  247.                     <label>Assigned To</label>
  248.                     <span>{{ ticket.assignedToUserId ? 'User #' ~ ticket.assignedToUserId : '—' }}</span>
  249.                 </div>
  250.                 {% if ticket.serverIdentifier %}
  251.                 <div class="info-item">
  252.                     <label>Server</label>
  253.                     <span><i class="fa fa-server fa-xs me-1"></i>{{ ticket.serverIdentifier }}</span>
  254.                 </div>
  255.                 {% endif %}
  256.             </div>
  257.         </div>
  258.         <div class="row">
  259.             <div class="col-lg-8">
  260.                 {# ── Description ── #}
  261.                 <div class="detail-card">
  262.                     <h5><i class="fa fa-align-left me-2"></i>Description</h5>
  263.                     <div class="description-body">
  264.                         {{ ticket.ticketBody ?? 'No description provided.' }}
  265.                     </div>
  266.                 </div>
  267.                 {# ── Internal note ── #}
  268.                 {% if ticket.note %}
  269.                 <div class="detail-card">
  270.                     <h5><i class="fa fa-sticky-note me-2"></i>Internal Note</h5>
  271.                     <div class="description-body">{{ ticket.note }}</div>
  272.                 </div>
  273.                 {% endif %}
  274.                 {# ── System payload (last) ── #}
  275.                 {% if ticket.lastPayloadJson %}
  276.                 <div class="system-card">
  277.                     <h5><i class="fa fa-code me-2"></i>Last System Payload</h5>
  278.                     <pre class="payload-pre">{{ ticket.lastPayloadJson }}</pre>
  279.                 </div>
  280.                 {% endif %}
  281.                 {# ── Occurrence timeline ── #}
  282.                 {% if reports is defined and reports|length > 0 %}
  283.                 <div class="detail-card">
  284.                     <h5>
  285.                         <i class="fa fa-history me-2"></i>Occurrence Log
  286.                         <span class="count-chip ms-2">{{ reports|length }} entries</span>
  287.                     </h5>
  288.                     <div class="timeline">
  289.                         {% for report in reports %}
  290.                             <div class="tl-item">
  291.                                 <div class="tl-dot {{ report.errorCode ? 'error' }}"></div>
  292.                                 <div class="tl-time">
  293.                                     <i class="fa fa-clock fa-xs me-1"></i>
  294.                                     {{ report.reportedAt ? report.reportedAt|date('d M Y, H:i:s') : '—' }}
  295.                                 </div>
  296.                                 {% if report.serverIdentifier %}
  297.                                     <div class="tl-server">
  298.                                         <i class="fa fa-server fa-xs me-1"></i>{{ report.serverIdentifier }}
  299.                                     </div>
  300.                                 {% endif %}
  301.                                 <div class="tl-message">{{ report.message ?? '(No message)' }}</div>
  302.                                 {% if report.errorCode %}
  303.                                     <span class="tl-code">{{ report.errorCode }}</span>
  304.                                 {% endif %}
  305.                                 {% if report.payloadJson %}
  306.                                     <span class="tl-payload-toggle"
  307.                                           onclick="togglePayload(this)">
  308.                                         <i class="fa fa-code fa-xs me-1"></i>Show payload
  309.                                     </span>
  310.                                     <pre class="tl-payload">{{ report.payloadJson }}</pre>
  311.                                 {% endif %}
  312.                             </div>
  313.                         {% endfor %}
  314.                     </div>
  315.                 </div>
  316.                 {% endif %}
  317.             </div>
  318.             <div class="col-lg-4">
  319.                 {# ── System tracking card ── #}
  320.                 {% if ticket.problemHash %}
  321.                 <div class="system-card">
  322.                     <h5><i class="fa fa-fingerprint me-2"></i>Problem Hash</h5>
  323.                     <div class="hash-code">{{ ticket.problemHash }}</div>
  324.                     <div class="mt-2" style="font-size:0.78rem;color:#888">
  325.                         Used for deduplication — tickets with the same hash
  326.                         reported within 1 hour are merged into this record.
  327.                     </div>
  328.                 </div>
  329.                 {% endif %}
  330.                 {# ── Contact info ── #}
  331.                 {% if ticket.email or ticket.phone or ticket.name %}
  332.                 <div class="detail-card">
  333.                     <h5><i class="fa fa-address-card me-2"></i>Reporter Contact</h5>
  334.                     {% if ticket.name %}
  335.                         <div class="info-item mb-2">
  336.                             <label>Name</label>
  337.                             <span>{{ ticket.name }}</span>
  338.                         </div>
  339.                     {% endif %}
  340.                     {% if ticket.email %}
  341.                         <div class="info-item mb-2">
  342.                             <label>Email</label>
  343.                             <span><a href="mailto:{{ ticket.email }}">{{ ticket.email }}</a></span>
  344.                         </div>
  345.                     {% endif %}
  346.                     {% if ticket.phone %}
  347.                         <div class="info-item mb-2">
  348.                             <label>Phone</label>
  349.                             <span>{{ ticket.phone }}</span>
  350.                         </div>
  351.                     {% endif %}
  352.                     {% if ticket.preferredContactMethod %}
  353.                         <div class="info-item">
  354.                             <label>Preferred Contact</label>
  355.                             <span>{{ ticket.preferredContactMethod == 1 ? 'Email' : 'Phone' }}</span>
  356.                         </div>
  357.                     {% endif %}
  358.                 </div>
  359.                 {% endif %}
  360.                 {# ── Feedback ── #}
  361.                 {% if ticket.feedback %}
  362.                 <div class="detail-card">
  363.                     <h5><i class="fa fa-comment me-2"></i>Feedback</h5>
  364.                     <div style="font-size:0.9rem; color:#444;">{{ ticket.feedback }}</div>
  365.                 </div>
  366.                 {% endif %}
  367.             </div>
  368.         </div>
  369.     </div>
  370. </section>
  371. <script>
  372.     function togglePayload(el) {
  373.         var pre = el.nextElementSibling;
  374.         if (pre.style.display === 'block') {
  375.             pre.style.display = 'none';
  376.             el.textContent = '⌄ Show payload';
  377.         } else {
  378.             pre.style.display = 'block';
  379.             el.innerHTML = '<i class="fa fa-code fa-xs me-1"></i>Hide payload';
  380.         }
  381.     }
  382. </script>
  383. </div>{# /hb-admin-layout #}
  384. {% include '@Application/footer/central_footer.html.twig' %}