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

Open in your IDE?
  1. {% set page_title = 'New Ticket | HoneyBee' %}
  2. {% include '@Application/inc/central_header.html.twig' %}
  3. {% include '@CompanyGroup/pages/admin/_sidebar.html.twig' %}
  4. <style>
  5. /* ── Design tokens ─────────────────────────────────────────────────────────── */
  6. :root {
  7.     --n-cream:     #F7F5F0;
  8.     --n-cream-2:   #F0EDE5;
  9.     --n-white:     #FFFFFF;
  10.     --n-dark:      #1A1D2E;
  11.     --n-dark-2:    #252840;
  12.     --n-amber:     #C07D2A;
  13.     --n-amber-lt:  #D4954A;
  14.     --n-amber-dim: rgba(192,125,42,.10);
  15.     --n-sage:      #3D6B52;
  16.     --n-sage-dim:  rgba(61,107,82,.09);
  17.     --n-muted:     #6B6E7F;
  18.     --n-muted-2:   #9395A5;
  19.     --n-border:    rgba(26,29,46,.07);
  20.     --n-border-md: rgba(26,29,46,.12);
  21.     --n-shadow-sm: 0 2px 12px rgba(26,29,46,.07);
  22.     --n-shadow-md: 0 8px 32px rgba(26,29,46,.09);
  23.     --n-radius:    12px;
  24.     --n-radius-sm: 8px;
  25.     --n-font:      'DM Sans', 'Poppins', system-ui, sans-serif;
  26. }
  27. *, *::before, *::after { box-sizing: border-box; }
  28. body { background: var(--n-cream); font-family: var(--n-font); color: var(--n-dark); text-align: left; }
  29. /* ── Layout ────────────────────────────────────────────────────────────────── */
  30. .tc-wrap { max-width: 1100px; margin: 0 auto; padding: 0 28px; }
  31. .tc-page { padding: 40px 0 80px; }
  32. /* ── Breadcrumb ────────────────────────────────────────────────────────────── */
  33. .tc-breadcrumb {
  34.     font-size: .8rem; color: var(--n-muted-2); margin-bottom: 20px;
  35.     display: flex; align-items: center; gap: 6px;
  36. }
  37. .tc-breadcrumb a { color: var(--n-muted); text-decoration: none; font-weight: 500; }
  38. .tc-breadcrumb a:hover { color: var(--n-amber); }
  39. .tc-breadcrumb .sep { color: var(--n-border-md); }
  40. .tc-breadcrumb .current { color: var(--n-dark); font-weight: 600; }
  41. /* ── Page header ───────────────────────────────────────────────────────────── */
  42. .tc-page-title {
  43.     font-family: 'Montserrat', sans-serif; font-size: clamp(1.4rem,2.5vw,1.9rem);
  44.     font-weight: 800; color: var(--n-dark); letter-spacing: -.02em; margin: 0 0 5px;
  45. }
  46. .tc-page-sub { font-size: .88rem; color: var(--n-muted); margin: 0 0 28px; }
  47. .required-star { color: #c0392b; }
  48. /* ── Form card ─────────────────────────────────────────────────────────────── */
  49. .tc-card {
  50.     background: var(--n-white); border: 1px solid var(--n-border-md);
  51.     border-radius: var(--n-radius); padding: 28px 28px 24px;
  52.     margin-bottom: 16px; box-shadow: var(--n-shadow-sm);
  53. }
  54. .tc-card-title {
  55.     font-family: monospace; font-size: .72rem; font-weight: 700;
  56.     letter-spacing: .1em; text-transform: uppercase; color: var(--n-amber);
  57.     margin-bottom: 20px; padding-bottom: 12px;
  58.     border-bottom: 1px solid var(--n-border); display: flex;
  59.     align-items: center; justify-content: space-between; gap: 8px;
  60. }
  61. .tc-card-title i { margin-right: 6px; }
  62. /* ── Form elements ─────────────────────────────────────────────────────────── */
  63. .tc-label {
  64.     display: block; font-size: .8rem; font-weight: 600;
  65.     color: var(--n-muted); margin-bottom: 5px;
  66. }
  67. .tc-input,
  68. .tc-select,
  69. .tc-textarea {
  70.     width: 100%; border-radius: var(--n-radius-sm);
  71.     border: 1.5px solid var(--n-border-md); background: var(--n-cream);
  72.     color: var(--n-dark); font-family: var(--n-font); font-size: .9rem;
  73.     padding: 9px 13px; transition: border-color .15s, box-shadow .15s;
  74.     appearance: none;
  75. }
  76. .tc-textarea { resize: vertical; min-height: 130px; }
  77. .tc-input:focus, .tc-select:focus, .tc-textarea:focus {
  78.     outline: none; border-color: var(--n-amber);
  79.     box-shadow: 0 0 0 3px var(--n-amber-dim);
  80. }
  81. .tc-select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='7'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%236B6E7F' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 13px center; padding-right: 36px; }
  82. .tc-char-count { font-size: .73rem; color: var(--n-muted-2); text-align: right; margin-top: 4px; font-family: monospace; }
  83. .tc-hint { font-size: .75rem; color: var(--n-muted-2); margin-top: 4px; }
  84. /* ── Priority grid ─────────────────────────────────────────────────────────── */
  85. .tc-priority-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }
  86. .tc-prio-input { display: none; }
  87. .tc-prio-label {
  88.     display: flex; flex-direction: column; align-items: center; justify-content: center;
  89.     gap: 4px; border: 1.5px solid var(--n-border-md); border-radius: var(--n-radius);
  90.     padding: 16px 8px; cursor: pointer; text-align: center; background: var(--n-cream);
  91.     transition: border-color .15s, background .15s;
  92. }
  93. .tc-prio-label:hover { border-color: var(--n-amber-lt); background: var(--n-amber-dim); }
  94. .tc-prio-input:checked + .tc-prio-label { border-color: var(--n-amber); background: var(--n-amber-dim); }
  95. .tc-prio-input[value="1"]:checked + .tc-prio-label { border-color: #c0392b; background: rgba(192,57,43,.07); }
  96. .tc-prio-input[value="2"]:checked + .tc-prio-label { border-color: var(--n-amber-lt); background: var(--n-amber-dim); }
  97. .tc-prio-icon { font-size: 1.4rem; }
  98. .tc-prio-name { font-size: .8rem; font-weight: 700; color: var(--n-dark); }
  99. .tc-prio-desc { font-size: .7rem; color: var(--n-muted-2); }
  100. /* ── Source toggle ─────────────────────────────────────────────────────────── */
  101. .tc-source-toggle {
  102.     display: flex; border: 1.5px solid var(--n-border-md);
  103.     border-radius: var(--n-radius-sm); overflow: hidden;
  104. }
  105. .tc-source-toggle input { display: none; }
  106. .tc-source-toggle label {
  107.     flex: 1; text-align: center; padding: 9px 8px;
  108.     font-size: .82rem; font-weight: 600; cursor: pointer;
  109.     color: var(--n-muted); background: var(--n-cream); transition: all .15s;
  110.     border-right: 1.5px solid var(--n-border-md);
  111. }
  112. .tc-source-toggle label:last-of-type { border-right: none; }
  113. .tc-source-toggle input:checked + label { background: var(--n-dark); color: #fff; }
  114. /* ── Advanced section (collapsible) ───────────────────────────────────────── */
  115. .tc-adv-toggle {
  116.     font-size: .78rem; font-weight: 700; color: var(--n-muted);
  117.     cursor: pointer; display: flex; align-items: center; gap: 5px;
  118.     font-family: monospace; text-transform: uppercase; letter-spacing: .08em;
  119. }
  120. .tc-adv-toggle:hover { color: var(--n-amber); }
  121. .tc-adv-badge {
  122.     background: rgba(111,66,193,.09); color: #6f42c1;
  123.     font-size: .65rem; font-weight: 700; padding: 2px 8px;
  124.     border-radius: 100px; font-family: monospace; letter-spacing: .06em;
  125. }
  126. .tc-adv-body { display: none; margin-top: 16px; }
  127. .tc-adv-body.open { display: block; }
  128. .tc-mono { font-family: monospace !important; font-size: .83rem !important; }
  129. /* ── Action bar ────────────────────────────────────────────────────────────── */
  130. .tc-action-bar {
  131.     display: flex; align-items: center; gap: 12px;
  132.     padding-top: 8px;
  133. }
  134. .tc-btn-submit {
  135.     display: inline-flex; align-items: center; gap: 8px;
  136.     padding: 12px 28px; background: var(--n-dark); color: #fff;
  137.     border: none; border-radius: var(--n-radius-sm);
  138.     font-family: var(--n-font); font-size: .92rem; font-weight: 700;
  139.     cursor: pointer; transition: background .18s, transform .15s; text-decoration: none;
  140. }
  141. .tc-btn-submit:hover { background: var(--n-amber); color: #fff; transform: translateY(-1px); }
  142. .tc-btn-cancel {
  143.     display: inline-flex; align-items: center; gap: 6px;
  144.     padding: 12px 20px; background: transparent; color: var(--n-muted);
  145.     border: 1.5px solid var(--n-border-md); border-radius: var(--n-radius-sm);
  146.     font-family: var(--n-font); font-size: .9rem; font-weight: 600;
  147.     cursor: pointer; text-decoration: none; transition: all .18s;
  148. }
  149. .tc-btn-cancel:hover { border-color: var(--n-dark); color: var(--n-dark); }
  150. </style>
  151. <div class="hb-admin-layout">
  152. <div class="tc-page">
  153.     <div class="tc-wrap">
  154.         {# ── Breadcrumb ── #}
  155.         <div class="tc-breadcrumb">
  156.             <a href="{{ path('ticket_list') }}"><i class="fa fa-ticket-alt fa-xs"></i> Tickets</a>
  157.             <span class="sep">/</span>
  158.             <span class="current">New Ticket</span>
  159.         </div>
  160.         <h1 class="tc-page-title">Create New Ticket</h1>
  161.         <p class="tc-page-sub">Fields marked <span class="required-star">*</span> are required.</p>
  162.         <form method="POST"
  163.               action="{{ path('ticket_create_submit') }}"
  164.               enctype="multipart/form-data"
  165.               id="createTicketForm">
  166.             <div class="row g-4">
  167.                 {# ── LEFT column ── #}
  168.                 <div class="col-lg-8">
  169.                     {# Core details #}
  170.                     <div class="tc-card">
  171.                         <div class="tc-card-title">
  172.                             <span><i class="fa fa-info-circle"></i>Ticket Details</span>
  173.                         </div>
  174.                         <div class="mb-3">
  175.                             <label class="tc-label">Title <span class="required-star">*</span></label>
  176.                             <input type="text" name="title" class="tc-input"
  177.                                    placeholder="Brief summary of the issue…"
  178.                                    value="{{ formData.title ?? '' }}"
  179.                                    maxlength="255" required>
  180.                         </div>
  181.                         <div class="mb-3">
  182.                             <label class="tc-label">Description <span class="required-star">*</span></label>
  183.                             <textarea name="description" class="tc-textarea" rows="6"
  184.                                       id="descriptionField"
  185.                                       placeholder="Describe the issue in detail…"
  186.                                       maxlength="5000"
  187.                                       required>{{ formData.description ?? '' }}</textarea>
  188.                             <div class="tc-char-count" id="charCount">0 / 5000</div>
  189.                         </div>
  190.                         <div class="mb-3">
  191.                             <label class="tc-label">Internal Note</label>
  192.                             <textarea name="note" class="tc-textarea" rows="3"
  193.                                       placeholder="Internal notes (not visible to the reporter)…">{{ formData.note ?? '' }}</textarea>
  194.                         </div>
  195.                         <div class="mb-0">
  196.                             <label class="tc-label">Attachments</label>
  197.                             <input type="file" name="attachments[]" class="tc-input"
  198.                                    multiple accept=".jpg,.jpeg,.png,.pdf,.txt,.log,.zip">
  199.                             <div class="tc-hint">JPG, PNG, PDF, TXT, LOG, ZIP — max 10 MB per file</div>
  200.                         </div>
  201.                     </div>
  202.                     {# Classification #}
  203.                     <div class="tc-card">
  204.                         <div class="tc-card-title">
  205.                             <span><i class="fa fa-sliders-h"></i>Classification</span>
  206.                         </div>
  207.                         <div class="mb-4">
  208.                             <label class="tc-label">Ticket Type</label>
  209.                             <select name="ticketType" class="tc-select">
  210.                                 <option value="">— Select type —</option>
  211.                                 <option value="1" {{ formData.ticketType == 1 ? 'selected' }}>Service Ticket</option>
  212.                                 <option value="2" {{ formData.ticketType == 2 ? 'selected' }}>To-Do</option>
  213.                                 <option value="3" {{ formData.ticketType == 3 ? 'selected' }}>To-Do Reply</option>
  214.                                 <option value="4" {{ formData.ticketType == 4 ? 'selected' }}>Query</option>
  215.                                 <option value="5" {{ formData.ticketType == 5 ? 'selected' }}>Development</option>
  216.                                 <option value="6" {{ formData.ticketType == 6 ? 'selected' }}>Supplier</option>
  217.                                 <option value="7" {{ formData.ticketType == 7 ? 'selected' }}>Client</option>
  218.                             </select>
  219.                         </div>
  220.                         <div class="mb-4">
  221.                             <label class="tc-label">Priority <span class="required-star">*</span></label>
  222.                             <div class="tc-priority-grid">
  223.                                 <div>
  224.                                     <input type="radio" name="priority" value="1" id="prio1" class="tc-prio-input"
  225.                                            {{ (formData.priority ?? 3) == 1 ? 'checked' }}>
  226.                                     <label for="prio1" class="tc-prio-label">
  227.                                         <span class="tc-prio-icon">🔴</span>
  228.                                         <span class="tc-prio-name">Emergency</span>
  229.                                         <span class="tc-prio-desc">Needs immediate action</span>
  230.                                     </label>
  231.                                 </div>
  232.                                 <div>
  233.                                     <input type="radio" name="priority" value="2" id="prio2" class="tc-prio-input"
  234.                                            {{ (formData.priority ?? 3) == 2 ? 'checked' }}>
  235.                                     <label for="prio2" class="tc-prio-label">
  236.                                         <span class="tc-prio-icon">🟡</span>
  237.                                         <span class="tc-prio-name">Important</span>
  238.                                         <span class="tc-prio-desc">Should be addressed soon</span>
  239.                                     </label>
  240.                                 </div>
  241.                                 <div>
  242.                                     <input type="radio" name="priority" value="3" id="prio3" class="tc-prio-input"
  243.                                            {{ (formData.priority ?? 3) == 3 ? 'checked' }}>
  244.                                     <label for="prio3" class="tc-prio-label">
  245.                                         <span class="tc-prio-icon">🟢</span>
  246.                                         <span class="tc-prio-name">Normal</span>
  247.                                         <span class="tc-prio-desc">Routine issue</span>
  248.                                     </label>
  249.                                 </div>
  250.                             </div>
  251.                         </div>
  252.                         <div class="mb-0">
  253.                             <label class="tc-label">Source</label>
  254.                             <div class="tc-source-toggle">
  255.                                 <input type="radio" name="source" value="1" id="src1"
  256.                                        {{ (formData.source ?? 1) == 1 ? 'checked' }}>
  257.                                 <label for="src1"><i class="fa fa-user fa-xs"></i> Manual</label>
  258.                                 <input type="radio" name="source" value="2" id="src2"
  259.                                        {{ (formData.source ?? 1) == 2 ? 'checked' }}>
  260.                                 <label for="src2"><i class="fa fa-robot fa-xs"></i> System</label>
  261.                                 <input type="radio" name="source" value="3" id="src3"
  262.                                        {{ (formData.source ?? 1) == 3 ? 'checked' }}>
  263.                                 <label for="src3"><i class="fa fa-code fa-xs"></i> API</label>
  264.                             </div>
  265.                         </div>
  266.                     </div>
  267.                     {# System Tracking — collapsible #}
  268.                     <div class="tc-card">
  269.                         <div class="tc-card-title">
  270.                             <span><i class="fa fa-fingerprint"></i>System Tracking</span>
  271.                             <div style="display:flex;align-items:center;gap:10px;">
  272.                                 <span class="tc-adv-badge">Advanced</span>
  273.                                 <span class="tc-adv-toggle" onclick="toggleSystemFields(this)" id="sysToggleBtn">
  274.                                     <i class="fa fa-chevron-down fa-xs"></i> Expand
  275.                                 </span>
  276.                             </div>
  277.                         </div>
  278.                         <div class="tc-adv-body" id="systemFieldsBlock">
  279.                             <div class="mb-3">
  280.                                 <label class="tc-label">Problem Hash</label>
  281.                                 <input type="text" name="problemHash" class="tc-input tc-mono"
  282.                                        placeholder="SHA256 — leave blank to auto-compute"
  283.                                        value="{{ formData.problemHash ?? '' }}">
  284.                                 <div class="tc-hint">SHA256 of (errorCode + fileLocation + exceptionClass + appId). Do not include timestamps or user IDs.</div>
  285.                             </div>
  286.                             <div class="row g-3 mb-3">
  287.                                 <div class="col-md-6">
  288.                                     <label class="tc-label">Error Code</label>
  289.                                     <input type="text" name="errorCode" class="tc-input"
  290.                                            placeholder="e.g. 500, DB_CONN_FAIL"
  291.                                            value="{{ formData.errorCode ?? '' }}">
  292.                                 </div>
  293.                                 <div class="col-md-6">
  294.                                     <label class="tc-label">Server Identifier</label>
  295.                                     <input type="text" name="serverIdentifier" class="tc-input"
  296.                                            placeholder="e.g. app-server-01"
  297.                                            value="{{ formData.serverIdentifier ?? '' }}">
  298.                                 </div>
  299.                             </div>
  300.                             <div class="mb-0">
  301.                                 <label class="tc-label">Payload JSON</label>
  302.                                 <textarea name="lastPayloadJson" class="tc-textarea tc-mono" rows="5"
  303.                                           placeholder='{"exception":"RuntimeException","file":"src/...","line":42,...}'>{{ formData.lastPayloadJson ?? '' }}</textarea>
  304.                             </div>
  305.                         </div>
  306.                     </div>
  307.                 </div>
  308.                 {# ── RIGHT column ── #}
  309.                 <div class="col-lg-4">
  310.                     {# Assignment #}
  311.                     <div class="tc-card">
  312.                         <div class="tc-card-title">
  313.                             <span><i class="fa fa-user-check"></i>Assignment</span>
  314.                         </div>
  315.                         <div class="mb-3">
  316.                             <label class="tc-label">Assign To (User ID)</label>
  317.                             <input type="number" name="assignedToUserId" class="tc-input"
  318.                                    placeholder="User ID…"
  319.                                    value="{{ formData.assignedToUserId ?? '' }}">
  320.                         </div>
  321.                         <div class="mb-3">
  322.                             <label class="tc-label">Deadline</label>
  323.                             <input type="date" name="deadlineDate" class="tc-input"
  324.                                    value="{{ formData.deadlineDate ?? '' }}">
  325.                         </div>
  326.                         <div class="mb-0">
  327.                             <label class="tc-label">Tagged User IDs</label>
  328.                             <input type="text" name="taggedUserIds" class="tc-input"
  329.                                    placeholder="Comma-separated, e.g. 12, 45, 78"
  330.                                    value="{{ formData.taggedUserIds ?? '' }}">
  331.                         </div>
  332.                     </div>
  333.                     {# Reporter contact #}
  334.                     <div class="tc-card">
  335.                         <div class="tc-card-title">
  336.                             <span><i class="fa fa-address-card"></i>Reporter Contact</span>
  337.                         </div>
  338.                         <div class="mb-3">
  339.                             <label class="tc-label">Name</label>
  340.                             <input type="text" name="name" class="tc-input"
  341.                                    placeholder="Reporter's name…"
  342.                                    value="{{ formData.name ?? '' }}">
  343.                         </div>
  344.                         <div class="mb-3">
  345.                             <label class="tc-label">Email</label>
  346.                             <input type="email" name="email" class="tc-input"
  347.                                    placeholder="reporter@example.com"
  348.                                    value="{{ formData.email ?? '' }}">
  349.                         </div>
  350.                         <div class="mb-3">
  351.                             <label class="tc-label">Phone</label>
  352.                             <input type="text" name="phone" class="tc-input"
  353.                                    placeholder="+49 …"
  354.                                    value="{{ formData.phone ?? '' }}">
  355.                         </div>
  356.                         <div class="mb-0">
  357.                             <label class="tc-label">Preferred Contact</label>
  358.                             <select name="preferredContactMethod" class="tc-select">
  359.                                 <option value="">— No preference —</option>
  360.                                 <option value="1" {{ formData.preferredContactMethod == 1 ? 'selected' }}>Email</option>
  361.                                 <option value="2" {{ formData.preferredContactMethod == 2 ? 'selected' }}>Phone</option>
  362.                             </select>
  363.                         </div>
  364.                     </div>
  365.                     {# Context #}
  366.                     <div class="tc-card">
  367.                         <div class="tc-card-title">
  368.                             <span><i class="fa fa-sitemap"></i>Context</span>
  369.                         </div>
  370.                         <div class="mb-3">
  371.                             <label class="tc-label">Company ID</label>
  372.                             <input type="number" name="companyId" class="tc-input"
  373.                                    value="{{ formData.companyId ?? '' }}">
  374.                         </div>
  375.                         <div class="mb-3">
  376.                             <label class="tc-label">App ID</label>
  377.                             <input type="number" name="appId" class="tc-input"
  378.                                    value="{{ formData.appId ?? '' }}">
  379.                         </div>
  380.                         <div class="mb-0">
  381.                             <label class="tc-label">Branch ID</label>
  382.                             <input type="number" name="branchId" class="tc-input"
  383.                                    value="{{ formData.branchId ?? '' }}">
  384.                         </div>
  385.                     </div>
  386.                 </div>
  387.             </div>
  388.             {# ── Submit bar ── #}
  389.             <div class="tc-action-bar">
  390.                 <button type="submit" class="tc-btn-submit">
  391.                     <i class="fa fa-paper-plane" style="font-size:.8rem"></i> Create Ticket
  392.                 </button>
  393.                 <a href="{{ path('ticket_list') }}" class="tc-btn-cancel">
  394.                     Cancel
  395.                 </a>
  396.             </div>
  397.         </form>
  398.     </div>
  399. </div>
  400. <script>
  401.     // Character counter
  402.     var descField = document.getElementById('descriptionField');
  403.     var charCount = document.getElementById('charCount');
  404.     if (descField) {
  405.         descField.addEventListener('input', function () {
  406.             charCount.textContent = this.value.length + ' / 5000';
  407.         });
  408.     }
  409.     // System fields toggle
  410.     function toggleSystemFields(el) {
  411.         var block = document.getElementById('systemFieldsBlock');
  412.         var isOpen = block.classList.contains('open');
  413.         if (isOpen) {
  414.             block.classList.remove('open');
  415.             el.innerHTML = '<i class="fa fa-chevron-down fa-xs"></i> Expand';
  416.         } else {
  417.             block.classList.add('open');
  418.             el.innerHTML = '<i class="fa fa-chevron-up fa-xs"></i> Collapse';
  419.         }
  420.     }
  421.     // Auto-expand if values are pre-filled
  422.     (function () {
  423.         var fields = ['[name="problemHash"]', '[name="serverIdentifier"]', '[name="lastPayloadJson"]'];
  424.         for (var i = 0; i < fields.length; i++) {
  425.             var el = document.querySelector(fields[i]);
  426.             if (el && el.value.trim() !== '') {
  427.                 var block = document.getElementById('systemFieldsBlock');
  428.                 block.classList.add('open');
  429.                 var btn = document.getElementById('sysToggleBtn');
  430.                 if (btn) btn.innerHTML = '<i class="fa fa-chevron-up fa-xs"></i> Collapse';
  431.                 break;
  432.             }
  433.         }
  434.     })();
  435. </script>
  436. </div>{# /hb-admin-layout #}
  437. {% include '@Application/footer/central_footer.html.twig' %}