212 lines
9.9 KiB
HTML
212 lines
9.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}New Ticket{% endblock %}
|
|
{% block page_title %}Submit a New Ticket{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="row justify-content-center">
|
|
<div class="col-lg-8">
|
|
{% if templates %}
|
|
<div class="card mb-3">
|
|
<div class="card-header"><i class="bi bi-layout-text-window-reverse me-2"></i>Start from a Template</div>
|
|
<div class="card-body">
|
|
<p style="font-size:13px;color:var(--muted);margin-bottom:14px;">
|
|
Select a common request type to pre-fill the form, or fill it in manually below.
|
|
</p>
|
|
<div class="row g-2">
|
|
{% for t in templates %}
|
|
<div class="col-6 col-md-4">
|
|
<button type="button" class="btn btn-secondary w-100 text-start template-btn"
|
|
style="padding:10px 14px;font-size:13px;"
|
|
data-name="{{ t.name }}"
|
|
data-title="{{ t.title_hint }}"
|
|
data-category="{{ t.category }}"
|
|
data-priority="{{ t.priority }}"
|
|
data-description="{{ t.description | e }}">
|
|
<i class="bi {{ t.icon }} me-2" style="color:var(--accent);"></i>{{ t.name }}
|
|
</button>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Template preview panel -->
|
|
<div id="tmpl-preview" style="display:none;margin-top:14px;padding:14px 16px;
|
|
background:var(--surface2);border:1px solid var(--border);border-radius:8px;">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;">
|
|
<span style="font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;">Preview</span>
|
|
<button type="button" id="tmpl-apply-btn" class="btn btn-primary btn-sm">
|
|
<i class="bi bi-check2 me-1"></i>Apply Template
|
|
</button>
|
|
</div>
|
|
<div style="font-size:13px;font-weight:600;color:var(--text);margin-bottom:4px;" id="tmpl-prev-title">—</div>
|
|
<div style="display:flex;gap:6px;margin-bottom:8px;">
|
|
<span style="font-size:11px;padding:1px 8px;border-radius:4px;background:var(--border);color:var(--muted);" id="tmpl-prev-cat">—</span>
|
|
<span id="tmpl-prev-prio" class="badge">—</span>
|
|
</div>
|
|
<div style="font-size:12px;color:var(--muted);white-space:pre-wrap;line-height:1.55;max-height:100px;overflow-y:auto;" id="tmpl-prev-desc">—</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<i class="bi bi-plus-circle me-2"></i>New Support Request
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="POST" enctype="multipart/form-data">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
|
<div class="row g-3">
|
|
<div class="col-12">
|
|
<label class="form-label">Issue Title *</label>
|
|
<input type="text" class="form-control" name="title" required
|
|
placeholder="Brief description of the issue (e.g. 'Cannot connect to VPN')"/>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label">Category *</label>
|
|
<select class="form-select" name="category" required>
|
|
{% for cat in categories %}
|
|
<option value="{{ cat }}">{{ cat.replace('_',' ').title() }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label">Priority *</label>
|
|
<select class="form-select" name="priority" required>
|
|
{% for p in priorities %}
|
|
<option value="{{ p }}" {% if p == 'medium' %}selected{% endif %}>{{ p.upper() }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label">Location / Floor</label>
|
|
<input type="text" class="form-control" name="location" placeholder="e.g. 3rd Floor, Room 302"/>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label">Asset Tag / Device Serial</label>
|
|
<input type="text" class="form-control" name="asset_tag" placeholder="e.g. ASSET-1234"/>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<label class="form-label">Detailed Description *</label>
|
|
<textarea class="form-control" name="description" rows="6" required
|
|
placeholder="Please describe the issue in detail: - What were you trying to do? - What happened? - Any error messages? - When did it start?"></textarea>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<label class="form-label">Attachments (screenshots, logs, etc.)</label>
|
|
<input type="file" class="form-control" name="attachments" multiple
|
|
accept=".png,.jpg,.jpeg,.gif,.pdf,.doc,.docx,.txt,.zip,.log"/>
|
|
<div style="font-size:11px;color:var(--muted);margin-top:5px;">
|
|
Accepted: PNG, JPG, PDF, DOC, DOCX, TXT, ZIP, LOG. Max 16 MB per file.
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Priority guide -->
|
|
<div class="col-12">
|
|
<div style="background:var(--surface2);border:1px solid var(--border);border-radius:8px;padding:14px 16px;">
|
|
<div style="font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:10px;">Priority Guide</div>
|
|
<div class="row g-2" style="font-size:12px;">
|
|
<div class="col-sm-3"><span class="badge badge-low me-1">LOW</span> Minor inconvenience, no work stoppage</div>
|
|
<div class="col-sm-3"><span class="badge badge-medium me-1">MEDIUM</span> Impacting productivity, workaround available</div>
|
|
<div class="col-sm-3"><span class="badge badge-high me-1">HIGH</span> Significant impact, no workaround</div>
|
|
<div class="col-sm-3"><span class="badge badge-critical me-1">CRITICAL</span> Complete outage or security incident</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2 mt-4">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="bi bi-send me-2"></i>Submit Ticket
|
|
</button>
|
|
<a href="{{ url_for('tickets.dashboard') }}" class="btn btn-secondary">Cancel</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4 d-none d-lg-block">
|
|
<div class="card mb-3">
|
|
<div class="card-header"><i class="bi bi-robot me-2"></i>Prefer Chatting?</div>
|
|
<div class="card-body">
|
|
<p style="font-size:13px;color:var(--muted);">Let our AI assistant guide you through reporting your issue conversationally.</p>
|
|
<button class="btn btn-outline-primary w-100" onclick="toggleChat()">
|
|
<i class="bi bi-chat-dots me-2"></i>Open IT Assistant
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header"><i class="bi bi-lightbulb me-2"></i>Tips</div>
|
|
<div class="card-body" style="font-size:13px;color:var(--muted);">
|
|
<p>✅ Include error messages exactly as shown</p>
|
|
<p>✅ Mention what changed recently</p>
|
|
<p>✅ List affected users or devices</p>
|
|
<p>✅ Attach relevant screenshots or logs</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% block scripts %}
|
|
<script>
|
|
(function () {
|
|
const preview = document.getElementById('tmpl-preview');
|
|
const applyBtn = document.getElementById('tmpl-apply-btn');
|
|
const prevTitle = document.getElementById('tmpl-prev-title');
|
|
const prevCat = document.getElementById('tmpl-prev-cat');
|
|
const prevPrio = document.getElementById('tmpl-prev-prio');
|
|
const prevDesc = document.getElementById('tmpl-prev-desc');
|
|
|
|
let activeData = null;
|
|
|
|
function applyTemplate(data) {
|
|
if (!data) return;
|
|
if (data.title) document.querySelector('input[name=title]').value = data.title;
|
|
if (data.desc) document.querySelector('textarea[name=description]').value = data.desc;
|
|
const catSel = document.querySelector('select[name=category]');
|
|
const prioSel = document.querySelector('select[name=priority]');
|
|
if (catSel) { for (const o of catSel.options) { if (o.value === data.cat) { o.selected = true; break; } } }
|
|
if (prioSel) { for (const o of prioSel.options) { if (o.value === data.prio) { o.selected = true; break; } } }
|
|
document.querySelector('input[name=title]').scrollIntoView({behavior:'smooth', block:'center'});
|
|
document.querySelector('input[name=title]').focus();
|
|
// Highlight briefly
|
|
document.querySelector('input[name=title]').style.outline = '2px solid var(--accent)';
|
|
setTimeout(() => { document.querySelector('input[name=title]').style.outline = ''; }, 1200);
|
|
}
|
|
|
|
document.querySelectorAll('.template-btn').forEach(btn => {
|
|
function showPreview() {
|
|
activeData = {
|
|
name: btn.dataset.name,
|
|
title: btn.dataset.title,
|
|
cat: btn.dataset.category,
|
|
prio: btn.dataset.priority,
|
|
desc: btn.dataset.description,
|
|
};
|
|
prevTitle.textContent = activeData.title || activeData.name;
|
|
prevCat.textContent = (activeData.cat || '').replace(/_/g, ' ');
|
|
prevDesc.textContent = activeData.desc || '(no description)';
|
|
prevPrio.textContent = (activeData.prio || '').toUpperCase();
|
|
prevPrio.className = 'badge badge-' + (activeData.prio || 'medium');
|
|
preview.style.display = 'block';
|
|
}
|
|
|
|
btn.addEventListener('mouseenter', showPreview);
|
|
btn.addEventListener('focus', showPreview);
|
|
btn.addEventListener('click', function () {
|
|
showPreview();
|
|
applyTemplate(activeData);
|
|
});
|
|
});
|
|
|
|
if (applyBtn) {
|
|
applyBtn.addEventListener('click', () => applyTemplate(activeData));
|
|
}
|
|
})();
|
|
</script>
|
|
{% endblock %}
|
|
{% endblock %} |