Merge pull request #1431 from gurupratap-matharu/hotfix/followup-card-colors

Hotfix/followup card colors
This commit is contained in:
Christopher Broderick
2026-09-15 09:36:39 +01:00
committed by GitHub
23 changed files with 800 additions and 714 deletions

View File

@@ -819,4 +819,4 @@ class CreateChecklistForm(ChecklistForm):
class FormControlDeleteFormSet(forms.BaseInlineFormSet):
deletion_widget = forms.CheckboxInput(attrs={"class": "form-control"})
deletion_widget = forms.CheckboxInput(attrs={"class": "form-check-input"})

View File

@@ -6,7 +6,6 @@ a:not(.btn) {
text-decoration: none;
}
/* Bootstrap 5 removed the breadcrumb background; restore the BS4 grey */
.breadcrumb {
padding: 0.75rem 1rem;
border-radius: 0.25rem;
@@ -82,58 +81,6 @@ table .tickettitle {
min-width: 18rem;
}
/* Follow-ups are color coded by who can see them and which way they went, so
staff can tell an internal note from a reply to the submitter at a glance.
The color is backed up by the badge text in each follow-up header, so it is
never the only thing carrying the meaning. */
.followup-item {
border-left: 4px solid transparent;
}
/* Private, staff-only notes keep the plain background. */
.followup-item-internal {
border-left-color: #adb5bd;
}
.followup-item-internal .followup-item-badge {
background-color: #e9ecef;
color: #495057;
}
/* Sent out to the submitter. */
.followup-item-outbound {
background-color: #eaf6ec;
border-left-color: #198754;
}
.followup-item-outbound .followup-item-badge {
background-color: #d1e7dd;
color: #0f5132;
}
/* Received from the submitter. */
.followup-item-inbound {
background-color: #fdf6e3;
border-left-color: #ffc107;
}
.followup-item-inbound .followup-item-badge {
background-color: #fff3cd;
color: #664d03;
}
/* list-group-item-action repaints the background on hover, which would undo
the color coding, so darken each tint slightly instead. */
.followup-item-outbound.list-group-item-action:hover,
.followup-item-outbound.list-group-item-action:focus {
background-color: #d9eddf;
}
.followup-item-inbound.list-group-item-action:hover,
.followup-item-inbound.list-group-item-action:focus {
background-color: #faedcd;
}
.handle {
cursor: grab;
}

View File

@@ -1,10 +1,12 @@
.kanban-drop-zone {
transition: background 0.15s;
}
.kanban-drop-zone.drag-over {
background: rgba(var(--bs-secondary-bg-rgb), 0.5);
outline: 2px dashed rgba(255, 255, 255, 0.3);
background: rgb(var(--bs-secondary-bg-rgb), 0.5);
border: 2px dashed rgb(var(--bs-secondary-color-rgb), 0.3);
}
.kanban-card {
cursor: grab;
transition:

View File

@@ -2,102 +2,121 @@
{% load i18n %}
{% block helpdesk_title %}{% translate "Edit Checklist" %}{% endblock %}
{% block helpdesk_title %}
{% translate "Edit Checklist" %}
{% endblock helpdesk_title %}
{% block helpdesk_breadcrumb %}
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:list' %}">{% translate "Tickets" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:list' %}{{ ticket.id }}/">{{ ticket.queue.slug }}-{{ ticket.id }}</a>
</li>
<li class="breadcrumb-item active">{{ checklist.name }}</li>
{% endblock %}
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:list' %}">{% translate "Tickets" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:list' %}{{ ticket.id }}/">{{ ticket.queue.slug }}-{{ ticket.id }}</a>
</li>
<li class="breadcrumb-item active">{{ checklist.name }}</li>
{% endblock helpdesk_breadcrumb %}
{% block helpdesk_body %}
<div class="card">
<div class="card-header">
<h2>
{% translate "Edit Checklist" %}
<a class="btn btn-danger float-end" href='{% url "helpdesk:delete_ticket_checklist" ticket.id checklist.id %}'>
<i class="fas fa-trash"></i>
{% translate "Delete checklist" %}
<div class="row">
<div class="col-12 col-md-8 col-lg-6 mx-auto">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h1 class="fs-5 mb-0">{% translate "Edit Checklist" %}</h1>
<div class="dropdown">
<a class="btn bg-body-tertiary border btn-sm"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">
<i class="fa-solid fa-ellipsis-vertical"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item link-danger"
href='{% url "helpdesk:delete_ticket_checklist" ticket.id checklist.id %}'>
<i class="fa-regular fa-trash-can"></i>
{% translate "Delete" %}
</a>
</h2>
</li>
</ul>
</div>
</div>
<div class="card-body">
{% if form.non_field_errors %}
<p class="text-danger">
{{ form.non_field_errors }}
</p>
{% endif %}
<form method='post'>
{% csrf_token %}
<div class="row">
<div class="col-sm-6 col-xs-12 mb-3">
{{ form.name.label_tag }}
{{ form.name }}
{{ form.name.errors }}
</div>
</div>
<h3>Tasks</h3>
{{ formset.management_form }}
<table class="table">
<thead>
<tr>
<th class="text-end">{% translate "Position" %}</th>
<th>{% translate "Description" %}</th>
<th class="text-center">{% translate "Delete?" %}</th>
</tr>
</thead>
<tbody id="tasks">
{% for form in formset %}
{% include 'helpdesk/include/task_form_row.html' %}
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-secondary" id="addRow">
<i class="fas fa-plus"></i>
{% translate "Add task" %}
</button>
<div class='buttons mb-3 text-center'>
<a class="btn btn-link" href='{{ ticket.get_absolute_url }}'>
<i class="fas fa-times"></i>
{% translate "Cancel Changes" %}
</a>
<button type='submit' class="btn btn-primary">
<i class="fas fa-save"></i>
{% translate "Save Changes" %}
</button>
</div>
</form>
{% if form.non_field_errors %}
<p class="text-danger">{{ form.non_field_errors }}</p>
{% endif %}
<form method="post">
{% csrf_token %}
{% for field in form.visible_fields %}
<div class="mb-3">
<label class="form-label" for="{{ field.auto_id }}">{{ field.label }}{{ field.label_suffix }}</label>
{{ field }}
{% if field.errors %}<span class="text-danger">{{ field.errors }}</span>{% endif %}
{% if field.help_text %}
<div class="form-text" id="{{ field.auto_id }}_helptext">{{ field.help_text|safe }}</div>
{% endif %}
</div>
{% endfor %}
<h2 class="fs-6 mb-1">{% translate "Tasks" %}</h2>
<small class="form-text">{% translate "Click and drag the position icon to reorder the task items." %}</small>
{{ formset.management_form }}
<table class="table align-middle my-3">
<thead class="text-uppercase small">
<tr>
<th><span class="text-body-tertiary">{% translate "Position" %}</span></th>
<th><span class="text-body-tertiary">{% translate "Description" %}</span></th>
<th><span class="text-body-tertiary">{% translate "Delete?" %}</span></th>
</tr>
</thead>
<tbody id="tasks">
{% for form in formset %}
{% include "helpdesk/include/task_form_row.html" %}
{% endfor %}
</tbody>
</table>
<div class="d-flex">
<button type="button"
class="btn bg-primary-subtle text-primary-emphasis border-primary-subtle me-auto"
id="addRow">
<i class="fas fa-plus"></i>
{% translate "Add task" %}
</button>
<a class="btn bg-body-tertiary border me-3"
href="{{ ticket.get_absolute_url }}">{% translate "Cancel" %}</a>
<button type="submit" class="btn btn-success">{% translate "Save" %}</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
</div>
{% endblock helpdesk_body %}
{% block helpdesk_js %}
<script src="https://unpkg.com/sortablejs-make/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js"></script>
<script>
const updatePosition = () => {
$('#tasks tr').each((index, taskRow) => {
$(taskRow).find('input[id$=position]').val(index + 1)
})
}
<script src="https://unpkg.com/sortablejs-make/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js"></script>
<script>
const updatePosition = () => {
$('#tasks tr').each((index, taskRow) => {
$(taskRow).find('input[id$=position]').val(index + 1)
})
}
$(() => {
const idTasksTotalForms = $('#id_tasks-TOTAL_FORMS')
$('#addRow').on('click', function() {
const formCount = idTasksTotalForms.val()
$('#tasks').append(`{% include 'helpdesk/include/task_form_row.html' with form=formset.empty_form %}`.replace(/__prefix__/g, formCount))
idTasksTotalForms.val(parseInt(formCount) + 1);
updatePosition()
});
$(() => {
const idTasksTotalForms = $('#id_tasks-TOTAL_FORMS')
$('#addRow').on('click', function() {
const formCount = idTasksTotalForms.val()
$('#tasks').append(`{% include 'helpdesk/include/task_form_row.html' with form=formset.empty_form %}`.replace(/__prefix__/g, formCount))
idTasksTotalForms.val(parseInt(formCount) + 1);
updatePosition()
});
$('#tasks').sortable({
handle: '.handle',
onChange: updatePosition
})
})
</script>
{% endblock %}
$('#tasks').sortable({
handle: '.handle',
onChange: updatePosition
})
})
</script>
{% endblock helpdesk_js %}

View File

@@ -2,118 +2,147 @@
{% load i18n %}
{% block helpdesk_title %}{% translate "Checklist Templates" %}{% endblock %}
{% block helpdesk_title %}
{% translate "Checklist Templates" %}
{% endblock helpdesk_title %}
{% block helpdesk_breadcrumb %}
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:system_settings' %}">{% translate "System Settings" %}</a>
</li>
<li class="breadcrumb-item active">{% translate "Checklist Templates" %}</li>
{% endblock %}
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:system_settings' %}">{% translate "System Settings" %}</a>
</li>
<li class="breadcrumb-item active">{% translate "Checklist Templates" %}</li>
{% endblock helpdesk_breadcrumb %}
{% block helpdesk_body %}
<h2>{% translate "Maintain checklist templates" %}</h2>
<div class="row mt-4">
<div class="col-sm-6 col-xs-12">
<div class="card">
<div class="card-header">
{% if checklist_template %}
{% translate "Edit checklist template" %}
{% else %}
{% translate "Create new checklist template" %}
{% endif %}
</div>
<form method="post">
<div class="card-body">
{% csrf_token %}
{{ form.as_p }}
<table class="table">
<thead>
<tr>
<th></th>
<th>Task</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody id="tasks">
{% if checklist_template %}
{% for value in checklist_template.task_list %}
{% include 'helpdesk/include/template_task_form_row.html' %}
{% endfor %}
{% else %}
{% include 'helpdesk/include/template_task_form_row.html' %}
{% endif %}
</tbody>
</table>
<button type="button" id="addTask" class="btn btn-sm btn-secondary">
<i class="fas fa-plus"></i>
{% translate "Add another task" %}
</button>
</div>
<div class="card-footer text-center">
{% if checklist_template %}
<a href="{% url 'helpdesk:checklist_templates' %}" class="btn btn-secondary">
<i class="fas fa-times"></i>
{% translate "Cancel" %}
</a>
{% endif %}
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i>
{% translate "Save checklist template" %}
</button>
</div>
</form>
<h1 class="fs-3">{% translate "Manage checklist templates" %}</h1>
<p>{% translate "Create new or edit existing checklist templates." %}</p>
<div class="row">
<div class="col-12 col-lg-6">
<div class="list-group">
{% for checklist in checklists %}
<div class="list-group-item d-flex justify-content-between align-items-center{% if checklist_template.id == checklist.id %}
active{% endif %}">
<span class="fs-6 me-2">{{ checklist.name }}</span>
<span class="badge bg-primary-subtle text-primary-emphasis border-primary-subtle">{{ checklist.task_list|length }} {% translate "tasks" %}</span>
<div class="dropdown ms-auto">
<a class="btn bg-body-tertiary border btn-sm"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">
<i class="fa-solid fa-ellipsis-vertical"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end">
{% if checklist_template.id != checklist.id %}
<li>
<a class="dropdown-item" href="{% url "helpdesk:edit_checklist_template" checklist.id %}">
<i class="fas fa-edit me-2"></i>
{% translate "Edit" %}
</a>
</li>
{% endif %}
<li>
<a class="dropdown-item link-danger"
href="{% url "helpdesk:delete_checklist_template" checklist.id %}">
<i class="fa-regular fa-trash-can me-2"></i>
{% translate "Delete" %}
</a>
</li>
</ul>
</div>
</div>
<div class="col-sm-6 col-xs-12">
<div class="list-group">
{% for checklist in checklists %}
<div class="list-group-item d-flex justify-content-between align-items-center{% if checklist_template.id == checklist.id %} active{% endif %}">
<span>
{{ checklist.name }}
{% if checklist_template.id != checklist.id %}
<a class="btn btn-secondary btn-sm" href="{% url 'helpdesk:edit_checklist_template' checklist.id %}">
<i class="fas fa-edit"></i>
</a>
{% endif %}
<a class="btn btn-sm btn-danger" href="{% url 'helpdesk:delete_checklist_template' checklist.id %}">
<i class="fas fa-trash"></i>
</a>
</span>
<span class="badge text-bg-secondary rounded-pill">{{ checklist.task_list|length }} {% translate "tasks" %}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
<div class="col-12 col-lg-6">
<div class="card">
<div class="card-header">
{% if checklist_template %}
{% translate "Edit checklist template" %}
{% else %}
{% translate "Create new checklist template" %}
{% endif %}
</div>
<form method="post">
<div class="card-body">
{% csrf_token %}
{% for field in form.visible_fields %}
<div class="mb-3">
<label class="form-label" for="{{ field.auto_id }}">{{ field.label }}{{ field.label_suffix }}</label>
{{ field }}
{% if field.errors %}<span class="text-danger">{{ field.errors }}</span>{% endif %}
{% if field.help_text %}
<div class="form-text" id="{{ field.auto_id }}_helptext">{{ field.help_text|safe }}</div>
{% endif %}
</div>
{% endfor %}
<h2 class="fs-6 mb-1">{% translate "Tasks" %}</h2>
<small class="form-text">{% translate "Click and drag the position icon to reorder the task items." %}</small>
<table class="table align-middle my-3">
<thead class="text-uppercase small">
<tr>
<th><span class="text-body-tertiary">{% translate "Position" %}</span></th>
<th><span class="text-body-tertiary">{% translate "Description" %}</span></th>
<th><span class="text-body-tertiary">{% translate "Delete?" %}</span></th>
</tr>
</thead>
<tbody id="tasks">
{% if checklist_template %}
{% for value in checklist_template.task_list %}
{% include "helpdesk/include/template_task_form_row.html" %}
{% endfor %}
{% else %}
{% include "helpdesk/include/template_task_form_row.html" %}
{% endif %}
</tbody>
</table>
<div class="d-flex">
<button type="button"
id="addTask"
class="btn bg-primary-subtle text-primary-emphasis border-primary-subtle me-auto">
<i class="fas fa-plus"></i>
{% translate "Add task" %}
</button>
{% if checklist_template %}
<a href="{% url 'helpdesk:checklist_templates' %}" class="btn bg-body-tertiary border me-3">
{% translate "Cancel" %}
</a>
{% endif %}
<button type="submit" class="btn btn-success">{% translate "Save" %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
{% endblock helpdesk_body %}
{% block helpdesk_js %}
<script src="https://unpkg.com/sortablejs-make/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js"></script>
<script>
const updateTemplateTaskList = () => {
let tasks = []
$('#tasks .taskInput').each((index, taskInput) => {
tasks.push($(taskInput).val())
})
$('#id_task_list').val(JSON.stringify(tasks))
}
<script src="https://unpkg.com/sortablejs-make/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-sortablejs@latest/jquery-sortable.js"></script>
<script>
const updateTemplateTaskList = () => {
let tasks = []
$('#tasks .taskInput').each((index, taskInput) => {
tasks.push($(taskInput).val())
})
$('#id_task_list').val(JSON.stringify(tasks))
}
const removeTask = (btn) => {
$(btn).parents('tr').remove()
updateTemplateTaskList()
}
const removeTask = (btn) => {
$(btn).parents('tr').remove()
updateTemplateTaskList()
}
$(() => {
$('#addTask').on('click', () => {
$('#tasks').append(`{% include 'helpdesk/include/template_task_form_row.html' %}`)
})
$(() => {
$('#addTask').on('click', () => {
$('#tasks').append(`{% include 'helpdesk/include/template_task_form_row.html' %}`)
})
$('#tasks').sortable({
handle: '.handle',
onChange: updateTemplateTaskList
})
})
</script>
{% endblock %}
$('#tasks').sortable({
handle: '.handle',
onChange: updateTemplateTaskList
})
})
</script>
{% endblock %}

View File

@@ -1,7 +1,7 @@
{% load i18n humanize %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseDates"
@@ -11,11 +11,19 @@
<div class="collapse row gx-3 my-3" id="collapseDates">
<div class="col">
<label class="form-label" for="id_date_from">{% translate "Date (From)" %}</label>
<input class="form-control" type="text" name="date_from" value="{{ query_params.filtering.created__gte }}" id="id_date_from" />
<input class="form-control"
type="text"
name="date_from"
value="{{ query_params.filtering.created__gte }}"
id="id_date_from" />
</div>
<div class="col">
<label class="form-label" for="id_date_to">{% translate "Date (To)" %}</label>
<input class="form-control" type="text" name="date_to" value="{{ query_params.filtering.created__lte }}" id="id_date_to" />
<input class="form-control"
type="text"
name="date_to"
value="{{ query_params.filtering.created__lte }}"
id="id_date_to" />
</div>
<div class="form-text">{% translate "Use YYYY-MM-DD date format, eg 2018-01-30" %}</div>
</div>

View File

@@ -1,7 +1,7 @@
{% load static i18n humanize in_list %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseKbitems"

View File

@@ -1,7 +1,7 @@
{% load i18n humanize %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseKeywords"

View File

@@ -1,7 +1,7 @@
{% load static i18n humanize in_list %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseOwner"

View File

@@ -1,7 +1,7 @@
{% load i18n humanize static in_list %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapsePriority"

View File

@@ -1,7 +1,7 @@
{% load static i18n humanize in_list %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseQueue"

View File

@@ -1,7 +1,7 @@
{% load i18n humanize static %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseSorting"
@@ -11,40 +11,34 @@
<div class="collapse show my-3" id="collapseSorting">
<label id="sort-by" class="form-label" for="id_sort">{% translate "Sorting" %}</label>
<select class="form-select" id="id_sort" name="sort">
<option value='id'{% if query_params.sorting == "id" %} selected='selected'{% endif %}>
{% translate "Ticket" %}
</option>
<option value='created'{% if query_params.sorting == "created" %} selected='selected'{% endif %}>
<option value='id'{% if query_params.sorting == "id" %} selected{% endif %}>{% translate "Ticket" %}</option>
<option value='created'{% if query_params.sorting == "created" %} selected{% endif %}>
{% translate "Created" %}
</option>
<option value='last_followup'{% if query_params.sorting == "last_followup" %} selected='selected'{% endif %}>
<option value='last_followup'{% if query_params.sorting == "last_followup" %} selected{% endif %}>
{% translate "Last Followup" %}
</option>
<option value='due_date'{% if query_params.sorting == "due_date" %} selected='selected'{% endif %}>
<option value='due_date'{% if query_params.sorting == "due_date" %} selected{% endif %}>
{% translate "Due Date" %}
</option>
<option value='title'{% if query_params.sorting == "title" %} selected='selected'{% endif %}>
{% translate "Title" %}
</option>
<option value='queue'{% if query_params.sorting == "queue" %} selected='selected'{% endif %}>
{% translate "Queue" %}
</option>
<option value='status'{% if query_params.sorting == "status" %} selected='selected'{% endif %}>
{% translate "Status" %}
</option>
<option value='priority'{% if query_params.sorting == "priority" %} selected='selected'{% endif %}>
<option value='title'{% if query_params.sorting == "title" %} selected{% endif %}>{% translate "Title" %}</option>
<option value='queue'{% if query_params.sorting == "queue" %} selected{% endif %}>{% translate "Queue" %}</option>
<option value='status'
{% if query_params.sorting == "status" %}
selected{% endif %}>{% translate "Status" %}</option>
<option value='priority'{% if query_params.sorting == "priority" %} selected{% endif %}>
{% translate "Priority" %}
</option>
<option value='assigned_to'{% if query_params.sorting == "assigned_to" %} selected='selected'{% endif %}>
<option value='assigned_to'{% if query_params.sorting == "assigned_to" %} selected{% endif %}>
{% translate "Owner" %}
</option>
<option value='submitter_email'{% if query_params.sorting == "submitter_email" %} selected='selected'{% endif %}>
<option value='submitter_email'{% if query_params.sorting == "submitter_email" %} selected{% endif %}>
{% translate "Submitter E-Mail" %}
</option>
{% if helpdesk_settings.HELPDESK_KB_ENABLED %}
<option value='kbitem'{% if query_params.sorting == "kbitem" %} selected='selected'{% endif %}>
{% translate "KB Item" %}
</option>
<option value='kbitem'
{% if query_params.sorting == "kbitem" %}
selected{% endif %}>{% translate "KB Item" %}</option>
{% endif %}
</select>
<div class="form-check mt-3">

View File

@@ -1,7 +1,7 @@
{% load static i18n humanize in_list %}
<div class="d-grid">
<button class="btn btn-light"
<button class="btn bg-secondary-subtle text-secondary-emphasis"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseStatus"

View File

@@ -1,16 +1,16 @@
<tr>
{{ form.id }}
<td title="Drag & Drop" class="text-end handle">
<i class="fas fa-grip-vertical"></i>
{{ form.position }}
{{ form.position.errors }}
</td>
<td>
{{ form.description }}
{{ form.description.errors }}
</td>
<td>
{{ form.DELETE }}
{{ form.DELETE.errors }}
</td>
</tr>
{{ form.id }}
<td title="Drag & Drop" class="handle">
<i class="fas fa-grip-vertical text-body-secondary"></i>
{{ form.position }}
{{ form.position.errors }}
</td>
<td>
{{ form.description }}
{{ form.description.errors }}
</td>
<td>
<div class="form-check">{{ form.DELETE }}</div>
{{ form.DELETE.errors }}
</td>
</tr>

View File

@@ -1,13 +1,13 @@
<tr>
<td title="Drag & Drop" class="text-center handle">
<i class="fas fa-grip-vertical"></i>
</td>
<td>
<input class="form-control taskInput" oninput="updateTemplateTaskList()" required value="{{ value }}">
</td>
<td class="text-center">
<button type="button" class="btn btn-sm btn-danger" onclick="removeTask(this)">
<i class="fas fa-times"></i>
</button>
</td>
</tr>
<td title="Drag & Drop" class="handle ms-3">
<i class="fas fa-grip-vertical text-body-secondary"></i>
</td>
<td>
<input class="form-control taskInput" oninput="updateTemplateTaskList()" required value="{{ value }}" />
</td>
<td>
<button type="button" class="btn btn-sm bg-body-tertiary border" onclick="removeTask(this)">
<i class="fas fa-times"></i>
</button>
</td>
</tr>

View File

@@ -7,14 +7,14 @@
{% if category.queue %}
{% if iframe %}
<a href="{% url 'helpdesk:submit_iframe' %}?queue={{ category.queue.pk }}&_readonly_fields_=queue&{{ query_param_string }}"
class="btn btn-light ms-auto">
class="btn bg-body-tertiary border ms-auto">
{% translate "Contact a human" %}
<i class="fa fa-envelope ms-2"></i>
</a>
{% else %}
{# Create ticket link #}
<a href="{% url 'helpdesk:submit' %}?queue={{ category.queue.pk }}&_readonly_fields_=queue&{{ query_param_string }}"
class="btn btn-light ms-auto">
class="btn bg-body-tertiary border ms-auto">
{% translate "Create Ticket" %}
<i class="fa fa-plus ms-2"></i>
</a>
@@ -73,13 +73,13 @@
{% endif %}
{% if iframe %}
<a href="{% url 'helpdesk:submit_iframe' %}?{% if category.queue %}queue={{ category.queue.pk }}&_readonly_fields_=queue&{% endif %}kbitem={{ item.id }}&{{ query_param_string }}"
class="btn btn-light">
class="btn bg-body-tertiary border">
{% translate "Contact a human" %}
<i class="fa fa-envelope ms-1"></i>
</a>
{% else %}
<a href="{% url 'helpdesk:submit' %}?{% if category.queue %}queue={{ category.queue.pk }}&_readonly_fields_=queue&{% endif %}kbitem={{ item.id }}&{{ query_param_string }}"
class="btn btn-light">
class="btn bg-body-tertiary border">
{% translate "Create New Ticket" %}
<i class="fa fa-plus ms-1"></i>
</a>

View File

@@ -1,7 +1,7 @@
{% load i18n static helpdesk_staff %}
{% include "helpdesk/custom_navigation_header.html" %}
<nav class="navbar navbar-expand-lg bg-body-tertiary sticky-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary border-bottom sticky-top">
<div class="container-fluid">
<div class="d-flex align-items-center">
<a class="navbar-brand" href="{% url 'helpdesk:home' %}">
@@ -36,7 +36,7 @@
<input type='hidden' name='status' value='2' />
<input type='hidden' name='status' value='3' />
<input type='hidden' name='search_type' value='header' />
<button class="btn btn-outline-secondary" type="submit">
<button class="btn bg-body-tertiary border" type="submit">
<i class="fas fa-search"></i> {% translate "Go" %}
</button>
</div>

View File

@@ -17,7 +17,7 @@
<div class="card-body m-3">
<h5 class="text-center py-3">{% translate "Logout successful" %}</h5>
<div class="d-grid gap-3 py-3">
<a class="btn btn-outline-primary" href="{% url 'helpdesk:login' %}">
<a class="btn btn-primary" href="{% url 'helpdesk:login' %}">
<i class="fas fa-fw fa-sign-in-alt"></i>
{% translate "Log In" %}
</a>

View File

@@ -32,350 +32,409 @@
</div>
{% else %}
{# Nav Tabs #}
<div class="d-flex justify-content-between align-items-center me-3">
<ul class="nav nav-underline ms-3" id="ticketTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active"
id="followup-tab"
data-bs-toggle="tab"
data-bs-target="#followup-tab-pane"
type="button"
role="tab"
aria-controls="followup-tab-pane"
aria-selected="true">{% translate "Follow ups" %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link"
id="respond-tab"
data-bs-toggle="tab"
data-bs-target="#respond-tab-pane"
type="button"
role="tab"
aria-controls="respond-tab-pane"
aria-selected="false">{% translate "Respond" %}</button>
</li>
</ul>
{% if followups %}
<button type="button"
id="followup-sort-toggle"
class="btn bg-light-subtle text-light-emphasis btn-sm"
data-order="{% if helpdesk_settings.HELPDESK_FOLLOWUP_NEWEST_FIRST %}newest_first{% else %}oldest_first{% endif %}">
<i class="fa-solid fa-arrow-down-short-wide me-1"></i>
<span id="followup-sort-toggle-label">
{% if helpdesk_settings.HELPDESK_FOLLOWUP_NEWEST_FIRST %}
{% translate "Newest first" %}
{% else %}
{% translate "Oldest first" %}
{% endif %}
</span>
</button>
{% endif %}
</div>
<div class="tab-content" id="myTabContent">
{# Followup Pane #}
{% if followups %}
<div class="card">
<div class="card-header bg-body-tertiary pb-0">
<ul class="nav nav-underline" id="ticketTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active"
id="followup-tab"
data-bs-toggle="tab"
data-bs-target="#followup-tab-pane"
type="button"
role="tab"
aria-controls="followup-tab-pane"
aria-selected="true">{% translate "Follow ups" %}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link"
id="respond-tab"
data-bs-toggle="tab"
data-bs-target="#respond-tab-pane"
type="button"
role="tab"
aria-controls="respond-tab-pane"
aria-selected="false">{% translate "Respond" %}</button>
</li>
</ul>
</div>
<div class="card-body px-0">
<div class="tab-content" id="myTabContent">
{% if followups %}
{% load ticket_to_link followup_permissions %}
{% load ticket_to_link followup_permissions %}
<div class="tab-pane fade show active" id="followup-tab-pane" role="tabpanel" aria-labelledby="followup-tab" tabindex="0">
<div class="list-group list-group-flush my-3" id="followup-list">
{% for followup in followups %}
<div class="list-group-item list-group-item-action followup-item followup-item-{{ followup.direction }}">
<div class="d-flex align-items-center">
<h6 class="mb-0">{{ followup.title|escape|num_to_link }}</h6>
<span class="text-body-secondary ms-auto">
{% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET and followup.time_spent_formated %}
<i class="fa-regular fa-clock fa-sm"></i>
<small>{{ followup.time_spent_formated }} ·</small>
{% endif %}
{# Name the audience as well as coloring the row, so the #}
{# distinction survives for color blind users and in print. #}
<span class="badge followup-item-badge">
{% if followup.direction == 'internal' %}
<i class="fa-solid fa-lock fa-sm me-1"></i>{% translate "Private" %}
{% elif followup.direction == 'outbound' %}
<i class="fa-solid fa-arrow-right fa-sm me-1"></i>{% translate "To submitter" %}
{% else %}
<i class="fa-solid fa-arrow-left fa-sm me-1"></i>{% translate "From submitter" %}
{% endif %}
</span>
</span>
{# Skip the menu entirely rather than open an empty one: most #}
{# follow-ups offer no action to most staff members. #}
{% if followup|can_edit_followup:request.user or followup|can_delete_followup:request.user %}
<div class="dropdown ms-2">
<a class="btn bg-body-tertiary btn-sm" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa-solid fa-ellipsis-vertical"></i>
</a>
<ul class="dropdown-menu">
{% if followup|can_edit_followup:request.user %}
<li>
<a href="{% url 'helpdesk:followup_edit' ticket.id followup.id %}" class="dropdown-item">
<i class="fa-regular fa-edit me-1"></i>
{% translate "Edit" %}
</a>
</li>
{% endif %}
{% if followup|can_delete_followup:request.user %}
<li>
<a href="{% url 'helpdesk:followup_delete' ticket.id followup.id %}" class="dropdown-item link-danger">
<i class="fa-regular fa-trash-can me-1"></i>
{% translate "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
</div>
<small class="text-secondary">
{{ followup.date|naturaltime }} {% if followup.user %}· {{ followup.user }}{% endif %}
</small>
{% if followup.email_recipients is not None %}
<small class="text-secondary d-block">
<i class="fa-regular fa-envelope fa-sm me-1"></i>
{% if followup.email_recipients %}
{% blocktranslate with recipients=followup.email_recipients|join:", " %}Emailed: {{ recipients }}{% endblocktranslate %}
{# Followup Pane #}
<div class="tab-pane fade show active"
id="followup-tab-pane"
role="tabpanel"
aria-labelledby="followup-tab"
tabindex="0">
{# sort button #}
<div class="d-flex justify-content-end mb-2">
<button type="button"
id="followup-sort-toggle"
class="btn bg-light-subtle text-light-emphasis btn-sm me-3"
data-order="{% if helpdesk_settings.HELPDESK_FOLLOWUP_NEWEST_FIRST %}newest_first{% else %}oldest_first{% endif %}">
<i class="fa-solid fa-arrow-down-short-wide me-1"></i>
<span id="followup-sort-toggle-label">
{% if helpdesk_settings.HELPDESK_FOLLOWUP_NEWEST_FIRST %}
{% translate "Newest first" %}
{% else %}
{% translate "Emailed: no one" %}
{% translate "Oldest first" %}
{% endif %}
</small>
{% endif %}
{# Ticket comments #}
{% if followup.comment %}
<div class="mt-3">{{ followup.get_markdown|urlizetrunc:50|num_to_link }}</div>
{% endif %}
{# Changes to ticket #}
{% if followup.ticketchange_set.all %}
<ul class="list-unstyled mt-3">
{% for change in followup.ticketchange_set.all %}
<li>
{% blocktranslate with change.field as field and change.old_value as old_value and change.new_value as new_value %}
Changed {{ field }} from {{ old_value }} to {{ new_value }}.
{% endblocktranslate %}
</li>
{% endfor %}
</ul>
{% endif %}
{# Attachments #}
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS and followup.followupattachment_set.all %}
<h6 class="mt-3">{% translate "Attachments" %}</h6>
<ul>
{% for attachment in followup.followupattachment_set.all %}
<li>
<a href="{{ attachment.file.url }}" target="_blank">{{ attachment.filename }}</a>
<small class="text-secondary">({{ attachment.mime_type }}, {{ attachment.size|filesizeformat }})</small>
{% if followup.user and request.user == followup.user %}
<a href="{% url 'helpdesk:attachment_del' ticket.id attachment.id %}" class="text-secondary">
<i class="fa-regular fa-trash-can fa-sm ms-2"></i>
</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# Respond Pane #}
<div class="tab-pane fade" id="respond-tab-pane" role="tabpanel" aria-labelledby="respond-tab" tabindex="0">
<div class="card my-3 border-0">
<div class="card-body">
<form method="post" action="{% url 'helpdesk:update' ticket.id %}" enctype="multipart/form-data">
{% csrf_token %}
{% if preset_replies %}
<div class="mb-3">
<label class="form-label" for="id_preset">{% translate "Use a Pre-set Reply (Optional)" %}</label>
<select class="form-select" name="preset" id="id_preset">
<option value=''>------</option>
{% for preset in preset_replies %}<option value="{{ preset.id }}">{{ preset.name }}</option>{% endfor %}
</select>
<span class="form-text">
{% translate "Selecting a pre-set reply will over-write your comment below. You can then modify the pre-set reply to your liking before saving this update." %}
</span>
</div>
{% endif %}
{# Comment text box #}
<div class="mb-3">
<label class="form-label" for="commentBox">{% translate "Comment / Resolution" %}</label>
<textarea class="form-control" rows="3" name="comment" id="commentBox">{{ xform.comment }}</textarea>
{% url "helpdesk:help_context" as context_help_url %}
{% blocktranslate %}
<p class="form-text">You can insert ticket and queue details in your message. For more information, see the <a href="{{ context_help_url }}" target="_blank">context help page.</a></p>
{% endblocktranslate %}
</button>
</div>
{# Status radio buttons #}
<fieldset class="mb-3">
<legend class="form-label">{% translate "New Status" %}</legend>
{% for status_choice in ticket.get_allowed_status_flow %}
<div class="form-check">
<input class="form-check-input"
type="radio"
name="new_status"
value="{{ status_choice.0 }}"
id="st_{{ status_choice.1|lower }}"
{% if not ticket.can_be_resolved %}
disabled='disabled'{% endif %}
{% if ticket.status == status_choice.0 %}
checked="checked"{% endif %} />
<label for="st_{{ status_choice.1|lower }}"
class="form-check-label
{% if ticket.status == status_choice.0 %}active
{% endif %}radio-inline">{{ status_choice.1 }}</label>
</div>
{% endfor %}
{% if not ticket.can_be_resolved %}
<div class="form-text">{% translate "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}</div>
{% endif %}
</fieldset>
{# Public update checkbox #}
{% if helpdesk_settings.HELPDESK_UPDATE_PUBLIC_DEFAULT %}
<input type="hidden" name="public" value="1" />
{% else %}
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="public" value="1" id="id_public" checked="checked" />
<label class="form-label" for="id_public">{% translate "Is this update public? (Optional)" %}</label>
<div class="form-text">{% translate "If this is public, the submitter will be e-mailed your comment or resolution." %}</div>
</div>
{% endif %}
{# Time spent widget #}
{% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET and user.is_staff %}
<div class="w-25 mb-3">
<label class="form-label" for="id_time_spent">{% translate "Time spent (Optional)" %}</label>
<input class="form-control" name="time_spent" type="time" value="{% if form.errors %}{{ xform.time_spent }}{% endif %}" />
</div>
{% endif %}
{# Ticket Details #}
<p>
<a class="btn btn-light"
data-bs-toggle="collapse"
href="#collapseDetails"
role="button"
aria-expanded="false"
aria-controls="collapseDetails">{% translate "Update ticket details" %}</a>
</p>
<div class="collapse" id="collapseDetails">
{# Extra Ticket Details form #}
<div class="row">
<div class="col-12 col-lg-8">
<div class="row g-3 mb-3">
<div class="col-12">
<label class="form-label" for="id_title">{% translate "Title" %}</label>
<input class="form-control" name="title" value="{{ ticket.title|escape }}" />
</div>
<div class="col-md-3">
<label class="form-label" for="id_owner">{% translate "Owner" %}</label>
<select class="form-select" id="id_owner" name="owner">
<option value="0">{% translate "Unassigned" %}</option>
{% for u in assignable_users %}
<option value="{{ u.id }}" {% if u.id == ticket.assigned_to.id %} selected{% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="id_priority">{% translate "Priority" %}</label>
<select class="form-select" id="id_priority" name="priority">
{% for p in priorities %}
{% if p.0 == ticket.priority %}
<option value="{{ p.0 }}" selected="selected">{{ p.1 }}</option>
{% else %}
<option value="{{ p.0 }}">{{ p.1 }}</option>
{% endif %}
{% endfor %}
</select>
</div>
{% if queues|length > 1 %}
<div class="col-md-3">
<label class="form-label" for="id_queue">{% translate "Queue" %}</label>
<select class="form-select" id="id_queue" name="queue">
{% for queue_id, queue_name in queues %}
<option value="{{ queue_id }}" {% if queue_id == ticket.queue.id %} selected{% endif %}>{{ queue_name }}</option>
{% endfor %}
</select>
</div>
{% else %}
<input type="hidden" id="id_queue" name="queue" value="{{ ticket.queue.id }}" />
<ul id="followup-list" class="list-group list-group-flush mb-3">
{% for followup in followups %}
<li class="list-group-item list-group-item-action">
<div class="d-flex align-items-center">
{% if followup.direction == "outbound" %}
<span class="p-1 me-2 bg-success border border-2 border-success-subtle rounded-circle"></span>
{% elif followup.direction == "inbound" %}
<span class="p-1 me-2 bg-warning border border-2 border-warning-subtle rounded-circle"></span>
{% endif %}
<div class="col-md-3">
<label class="form-label" for="id_due_date">{% translate "Due on" %}</label>
{{ form.due_date }}
</div>
<div class="col-6">
{# ticket merge form #}
{{ customfields_form }}
<h6 class="mb-0">{{ followup.title|escape|num_to_link }}</h6>
<span class="text-body-secondary ms-auto">
{% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET and followup.time_spent_formated %}
<i class="fa-regular fa-clock fa-sm"></i>
<small>{{ followup.time_spent_formated }} ·</small>
{% endif %}
{% if followup.direction == "internal" %}
<span class="badge bg-light-subtle text-light-emphasis">
<i class="fa-solid fa-lock fa-sm me-1"></i>{% translate "Private" %}
</span>
{% elif followup.direction == "outbound" %}
<span class="badge bg-success-subtle text-success-emphasis">
<i class="fa-solid fa-arrow-right fa-sm me-1"></i>{% translate "To submitter" %}
</span>
{% else %}
<span class="badge bg-warning-subtle text-warning-emphasis">
<i class="fa-solid fa-arrow-left fa-sm me-1"></i>{% translate "From submitter" %}
</span>
{% endif %}
</span>
{# Quick actions dropdown #}
{% if followup|can_edit_followup:request.user or followup|can_delete_followup:request.user %}
<div class="dropdown ms-2">
<a class="btn bg-body-tertiary btn-sm"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false">
<i class="fa-solid fa-ellipsis-vertical"></i>
</a>
<ul class="dropdown-menu">
{% if followup|can_edit_followup:request.user %}
<li>
<a href="{% url 'helpdesk:followup_edit' ticket.id followup.id %}"
class="dropdown-item small">
<i class="fa-regular fa-edit me-1"></i>
{% translate "Edit" %}
</a>
</li>
{% endif %}
{% if followup|can_delete_followup:request.user %}
<li>
<a href="{% url 'helpdesk:followup_delete' ticket.id followup.id %}"
class="dropdown-item link-danger text-sm">
<i class="fa-regular fa-trash-can me-1"></i>
{% translate "Delete" %}
</a>
</li>
{% endif %}
</ul>
</div>
{% endif %}
</div>
<small class="text-body-secondary">
{{ followup.date|naturaltime }} {% if followup.user %}· {{ followup.user }}{% endif %}
</small>
{% if followup.email_recipients is not None %}
<small class="text-body-secondary d-block">
<i class="fa-regular fa-envelope fa-sm me-1"></i>
{% if followup.email_recipients %}
{% blocktranslate with recipients=followup.email_recipients|join:", " %}Emailed: {{ recipients }}{% endblocktranslate %}
{% else %}
{% translate "Emailed: no one" %}
{% endif %}
</small>
{% endif %}
{# Ticket comments #}
{% if followup.comment %}
<div class="mt-3">{{ followup.get_markdown|urlizetrunc:50|num_to_link }}</div>
{% endif %}
{# Changes to ticket #}
{% if followup.ticketchange_set.all %}
<ul class="mt-3">
{% for change in followup.ticketchange_set.all %}
<li class="small">
{% blocktranslate with change.field as field and change.old_value as old_value and change.new_value as new_value %}
Changed {{ field }} from {{ old_value }} to {{ new_value }}.
{% endblocktranslate %}
</li>
{% endfor %}
</ul>
{% endif %}
{# Attachments #}
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS and followup.followupattachment_set.all %}
<h6 class="mt-3">{% translate "Attachments" %}</h6>
<ul>
{% for attachment in followup.followupattachment_set.all %}
<li>
<a href="{{ attachment.file.url }}" target="_blank">{{ attachment.filename }}</a>
<small class="text-body-secondary">({{ attachment.mime_type }}, {{ attachment.size|filesizeformat }})</small>
{% if followup.user and request.user == followup.user %}
<a href="{% url 'helpdesk:attachment_del' ticket.id attachment.id %}"
class="text-body-secondary">
<i class="fa-regular fa-trash-can fa-sm ms-2"></i>
</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{# Respond Pane #}
<div class="tab-pane fade" id="respond-tab-pane" role="tabpanel" aria-labelledby="respond-tab" tabindex="0">
<div class="card border-0 mb-3">
<div class="card-body">
<form method="post" action="{% url 'helpdesk:update' ticket.id %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="row">
<div class="col-12 col-md-8 col-lg-5">
{% if preset_replies %}
<div class="mb-3">
<label class="form-label"
for="id_preset">{% translate "Use a Pre-set Reply (Optional)" %}</label>
<select class="form-select" name="preset" id="id_preset">
<option value=''>------</option>
{% for preset in preset_replies %}<option value="{{ preset.id }}">{{ preset.name }}</option>{% endfor %}
</select>
<span class="form-text">
{% translate "Selecting a pre-set reply will over-write your comment below. You can then modify the pre-set reply to your liking before saving this update." %}
</span>
</div>
{% endif %}
{# Comment text box #}
<div class="mb-3">
<label class="form-label" for="commentBox">{% translate "Comment / Resolution" %}</label>
<textarea class="form-control"
rows="3"
name="comment"
id="commentBox">{{ xform.comment }}</textarea>
{% url "helpdesk:help_context" as context_help_url %}
{% blocktranslate %}
<p class="form-text">
You can insert ticket and queue details in your message.
For more information, see the <a href="{{ context_help_url }}" target="_blank">context help page.</a>
</p>
{% endblocktranslate %}
</div>
{# Status radio buttons #}
<fieldset class="mb-3">
<span class="form-label">{% translate "New Status" %}</span>
{% for status_choice in ticket.get_allowed_status_flow %}
<div class="form-check">
<input class="form-check-input"
type="radio"
name="new_status"
value="{{ status_choice.0 }}"
id="st_{{ status_choice.1|lower }}"
{% if not ticket.can_be_resolved %}disabled{% endif %}
{% if ticket.status == status_choice.0 %}checked{% endif %} />
{# djlint:off #}
<label for="st_{{ status_choice.1|lower }}"
class="form-check-label radio-inline {% if ticket.status == status_choice.0 %}active {% endif %}">
{{ status_choice.1 }}
</label>
{# djlint: on #}
</div>
{% endfor %}
{% if not ticket.can_be_resolved %}
<div class="form-text">{% translate "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}</div>
{% endif %}
</fieldset>
{# Public update checkbox #}
{% if helpdesk_settings.HELPDESK_UPDATE_PUBLIC_DEFAULT %}
<input type="hidden" name="public" value="1" />
{% else %}
<div class="form-check form-switch mb-3">
<input class="form-check-input"
type="checkbox"
name="public"
value="1"
id="id_public"
switch
checked />
<label class="form-label"
for="id_public">{% translate "Is this update public? (Optional)" %}</label>
<div class="form-text">{% translate "If this is public, the submitter will be e-mailed your comment or resolution." %}</div>
</div>
{% endif %}
{# Time spent widget #}
{% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET and user.is_staff %}
<div class="mb-3">
<label class="form-label" for="id_time_spent">{% translate "Time spent (Optional)" %}</label>
<input class="form-control"
name="time_spent"
type="time"
value="{% if form.errors %}{{ xform.time_spent }}{% endif %}" />
</div>
{% endif %}
</div>
</div>
</div>
</div>
{# Checklists #}
{% if ticket.checklists.exists %}
<p>
<a class="btn btn-light"
data-bs-toggle="collapse"
href="#collapseChecklists"
role="button"
aria-expanded="false"
aria-controls="collapseChecklists">{% translate "Update checklists" %}</a>
</p>
<div class="collapse" id="collapseChecklists">
<div class="row">
{% for checklist in ticket.checklists.all %}
<div class="col-12 col-md-4">
<div class="card border-0 mb-3">
<h6 class="card-title">{{ checklist }}</h6>
<div class="list-group list-group-flush">
{% for task in checklist.tasks.all %}
<div class="list-group-item"
{% if task.completion_date %}
title="{% translate "Completed on" %} {{ task.completion_date }}"
{% endif %}>
<div class="form-check">
<input class="form-check-input"
type="checkbox"
id="task-{{ task.id }}"
name="checklist-{{ checklist.id }}"
value="{{ task.id }}"
{% if task.completion_date %}
checked{% endif %} />
<label class="form-check-label" for="task-{{ task.id }}">{{ task }}</label>
</div>
{# Optional form content #}
{# Three toggle buttons #}
<div class="d-grid gap-2 d-md-block mb-3">
<a class="btn bg-body-tertiary border"
data-bs-toggle="collapse"
href="#collapseDetails"
role="button"
aria-expanded="false"
aria-controls="collapseDetails">{% translate "Update ticket details" %}</a>
{% if ticket.checklists.exists %}
<a class="btn bg-body-tertiary border"
data-bs-toggle="collapse"
href="#collapseChecklists"
role="button"
aria-expanded="false"
aria-controls="collapseChecklists">{% translate "Update checklists" %}</a>
{% endif %}
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS %}
<a class="btn bg-body-tertiary border"
data-bs-toggle="collapse"
href="#collapseAttachments"
role="button"
aria-expanded="false"
aria-controls="collapseAttachments">{% translate "Attach File(s)" %}</a>
{% endif %}
</div>
{# Extra Ticket Details form #}
<div class="collapse my-3" id="collapseDetails">
<div class="row">
<div class="col-12 col-lg-8">
<div class="card card-body border-0 bg-body-tertiary">
<div class="row g-3 mb-3">
<div class="col-12">
<label class="form-label" for="id_title">{% translate "Title" %}</label>
<input class="form-control" name="title" value="{{ ticket.title|escape }}" />
</div>
<div class="col-md-3">
<label class="form-label" for="id_owner">{% translate "Owner" %}</label>
<select class="form-select" id="id_owner" name="owner">
<option value="0">{% trans "Unassigned" %}</option>
{% for u in assignable_users %}
<option value="{{ u.id }}"
{% if u.id == ticket.assigned_to.id %}
selected{% endif %}>{{ u }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="id_priority">{% translate "Priority" %}</label>
<select class="form-select" id="id_priority" name="priority">
{% for p in priorities %}
{% if p.0 == ticket.priority %}
<option value="{{ p.0 }}" selected="selected">{{ p.1 }}</option>
{% else %}
<option value="{{ p.0 }}">{{ p.1 }}</option>
{% endif %}
{% endfor %}
</select>
</div>
{% if queues|length > 1 %}
<div class="col-md-3">
<label class="form-label" for="id_queue">{% translate "Queue" %}</label>
<select class="form-select" id="id_queue" name="queue">
{% for queue_id, queue_name in queues %}
<option value="{{ queue_id }}"
{% if queue_id == ticket.queue.id %}
selected{% endif %}>{{ queue_name }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
{% else %}
<input type="hidden" id="id_queue" name="queue" value="{{ ticket.queue.id }}" />
{% endif %}
<div class="col-md-3">
<label class="form-label" for="id_due_date">{% translate "Due on" %}</label>
{{ form.due_date }}
</div>
<div class="col-6">
{# ticket merge form #}
{{ customfields_form }}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# Attachments #}
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS %}
{% if form.errors.attachment %}<small class="error">{{ form.errors.attachment }}</small>{% endif %}
<p>
<a class="btn btn-light"
data-bs-toggle="collapse"
href="#collapseAttachments"
role="button"
aria-expanded="false"
aria-controls="collapseAttachments">{% translate "Attach File(s)" %}</a>
</p>
<div class="collapse mb-3" id="collapseAttachments">
<div class="add_file_fields_wrap">
<div class="mb-3">
<label class="form-label" for="file0">{% translate "Browse..." %}</label>
<input class="form-control" type="file" name="attachment" id="file0" multiple />
</div>
</div>
<button class="add_file_field_button btn btn-outline-primary btn-sm">{% translate "Add Another File" %}</button>
</div>
{% endif %}
<button class="btn btn-primary" type="submit">{% translate "Update This Ticket" %}</button>
</form>
{# Checklists #}
{% if ticket.checklists.exists %}
<div class="collapse" id="collapseChecklists">
<div class="row">
{% for checklist in ticket.checklists.all %}
<div class="col-12 col-md-4">
<div class="card border-0 mb-3">
<ul class="list-group">
<li class="list-group-item">
<h6 class="mb-0">{{ checklist }}</h6>
</li>
{% for task in checklist.tasks.all %}
<li class="list-group-item"
{% if task.completion_date %}
title="{% translate "Completed on" %} {{ task.completion_date }}"
{% endif %}>
<div class="form-check">
<input class="form-check-input"
type="checkbox"
id="task-{{ task.id }}"
name="checklist-{{ checklist.id }}"
value="{{ task.id }}"
{% if task.completion_date %}
checked{% endif %} />
<label class="form-check-label" for="task-{{ task.id }}">{{ task }}</label>
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# Attachments #}
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS %}
{% if form.errors.attachment %}<small class="error">{{ form.errors.attachment }}</small>{% endif %}
<div class="collapse mb-3" id="collapseAttachments">
<div class="row">
<div class="col-12 col-md-8 col-lg-5">
<div class="add_file_fields_wrap">
<div class="mb-3">
<label class="form-label" for="file0">{% translate "Browse..." %}</label>
<input class="form-control" type="file" name="attachment" id="file0" multiple />
</div>
</div>
<button class="add_file_field_button btn btn-link btn-sm"
type="button">{% translate "Add Another File" %}</button>
</div>
</div>
</div>
{% endif %}
<button class="btn btn-success" type="submit">{% translate "Save" %}</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade"
@@ -398,7 +457,8 @@
{{ checklist_form }}
</div>
<div class="modal-footer">
<a class="btn btn-secondary" href="{% url 'helpdesk:checklist_templates' %}">{% translate "Manage templates" %}</a>
<a class="btn btn-secondary"
href="{% url 'helpdesk:checklist_templates' %}">{% translate "Manage templates" %}</a>
<button type="submit" class="btn btn-primary">{% translate "Add" %}</button>
</div>
</form>
@@ -409,7 +469,7 @@
{% endblock %}
{% block helpdesk_js %}
<script type='text/javascript' language='javascript'>
<script language='javascript'>
$(function() {
$("#id_due_date").datepicker({
dateFormat: 'yy-mm-dd'
@@ -417,7 +477,7 @@
});
</script>
<script type='text/javascript' language='javascript'>
<script language='javascript'>
$(document).ready(function() {
$('#id_preset').change(function() {
preset = $('#id_preset').val();
@@ -458,7 +518,6 @@
$button.data('order', newOrder);
$('#followup-sort-toggle-label').text(label);
});
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS %}
// lists for file input change events, then updates the associated text label
// with the file name selected
@@ -475,7 +534,6 @@
});
{% endif %}
});
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS %}
// this function listens for changes on any file input, and
// emits the appropriate event to update the input's text.

View File

@@ -1,12 +1,12 @@
{% load i18n static humanize ticket_to_link helpdesk_util %}
<div class="card border-0">
<div class="card-body">
<div class="card border-0 mb-3">
<div class="card-body pt-0">
{# Heading & description #}
<div class="d-flex">
<h2>{{ ticket.title }} <span class="badge rounded-pill bg-light-subtle text-light-emphasis">#{{ ticket.id }}</span> </h2>
<h2>{{ ticket.title }} <span class="badge bg-body-tertiary text-secondary-emphasis border">#{{ ticket.id }}</span> </h2>
<div class="dropdown ms-auto">
<button class="btn bg-light-subtle text-light-emphasis dropdown-toggle"
<button class="btn bg-body-tertiary border dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-expanded="false">{% translate "Actions" %}</button>
@@ -53,7 +53,8 @@
<hr />
<p class="mb-0">
{% if "Resolved" == ticket.get_status_display %}
<a href="?close" class="alert-link">{% translate "Accept and Close" %}<i class="fa-solid fa-arrow-right ms-2"></i></a>
<a href="?close"
class="alert-link">{% translate "Accept and Close" %}<i class="fa-solid fa-arrow-right ms-2"></i></a>
{% endif %}
</p>
</div>
@@ -61,24 +62,27 @@
{# Ticket description #}
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-3">
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Due Date" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Due Date" %}</small>
<p>
{{ ticket.due_date|date:"DATETIME_FORMAT" }}
{% if ticket.due_date %}
<a class="link-secondary" href="#" data-bs-toggle="tooltip" data-bs-title="{{ ticket.due_date|naturaltime }}"><i class="fa-regular fa-hourglass ms-1"></i></a>
<a class="link-secondary"
href="#"
data-bs-toggle="tooltip"
data-bs-title="{{ ticket.due_date|naturaltime }}"><i class="fa-regular fa-hourglass ms-1"></i></a>
{% endif %}
</p>
</div>
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Priority" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Priority" %}</small>
<p>{{ ticket.get_priority_display }}</p>
</div>
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Status" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Status" %}</small>
<p>{{ ticket.get_status }}</p>
</div>
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Assigned to" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Assigned to" %}</small>
<form method="post" action="{% url 'helpdesk:update' ticket.id %}">
{% csrf_token %}
<input type="hidden" name="ticket_id" value="{{ ticket.id }}" />
@@ -90,17 +94,23 @@
<div class="d-flex align-items-center gap-2">
<div class="input-group input-group-sm">
<select name="owner" class="form-select">
<option value="0" {% if not ticket.assigned_to %} disabled selected{% endif %}>{% translate "Unassigned" %}</option>
<option value="0"
{% if not ticket.assigned_to %}
disabled selected{% endif %}>{% translate "Unassigned" %}</option>
{% for user in assignable_users %}
<option value="{{ user.id }}" {% if ticket.assigned_to and ticket.assigned_to.id == user.id %}selected{% endif %}>
<option value="{{ user.id }}"
{% if ticket.assigned_to and ticket.assigned_to.id == user.id %}selected{% endif %}>
{{ user.get_full_name|default:user.username }}
</option>
{% endfor %}
</select>
<button class="btn btn-outline-primary" type="submit" data-bs-toggle="tooltip" title="{% translate 'Save ticket assignment' %}">
<button class="btn bg-body-tertiary border"
type="submit"
data-bs-toggle="tooltip"
title="{% translate 'Save ticket assignment' %}">
<i class="fas fa-user-check"></i>
</button>
<a class="btn btn-outline-primary"
<a class="btn bg-body-tertiary border"
data-bs-toggle="tooltip"
href="?take"
title="{% translate 'Assign this ticket to ' %}{{ request.user.email }}">
@@ -111,25 +121,29 @@
</form>
</div>
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Submitted by" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Submitted by" %}</small>
<div class="input-group input-group-sm">
<input type="text" class="form-control" value="{{ ticket.submitter_email }}" aria-label="Submitter email" disabled />
<input type="text"
class="form-control"
value="{{ ticket.submitter_email }}"
aria-label="Submitter email"
readonly />
{% if user.is_superuser %}
{% if submitter_userprofile_url %}
<a class="btn btn-outline-primary"
<a class="btn bg-body-tertiary border"
data-bs-toggle="tooltip"
href="{{ submitter_userprofile_url }}"
title="{% translate "Edit " %}{{ ticket.submitter_email }}{% translate " user profile" %}">
<i class="fas fa-address-book"></i>
</a>
{% endif %}
<a class="btn btn-outline-primary"
<a class="btn bg-body-tertiary border"
data-bs-toggle="tooltip"
href="{% url 'helpdesk:list' %}?q={{ ticket.submitter_email }}"
title='{% translate "Display tickets filtered for " %}{{ ticket.submitter_email }}{% translate " as a keyword" %}'>
<i class="fas fa-search"></i>
</a>
<a class="btn btn-outline-primary"
<a class="btn bg-body-tertiary border"
data-bs-toggle="tooltip"
href='{% url 'helpdesk:email_ignore_add' %}?email={{ ticket.submitter_email }}'
title='{% translate "Add email address for the ticket system to ignore." %}'>
@@ -140,27 +154,27 @@
</div>
{% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Time spent" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Time spent" %}</small>
<p>{{ ticket.time_spent_formated }}</p>
</div>
{% endif %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Queue" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Queue" %}</small>
<p>{{ ticket.queue }}</p>
</div>
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Copies to" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Copies to" %}</small>
<div class="input-group input-group-sm">
{% if ticket.queue.enable_notifications_on_email_events %}
<input type="text" class="form-control" value="{{ ticketcc_string }}" disabled />
<a class="btn btn-outline-primary btn-sm"
<input type="text" class="form-control" value="{{ ticketcc_string }}" readonly />
<a class="btn bg-body-tertiary border btn-sm"
data-bs-toggle='tooltip'
href='{% url 'helpdesk:ticket_cc' ticket.id %}'
title='{% translate "Click here to add / remove people who should receive an e-mail whenever this ticket is updated." %}'>
<i class="fa fa-share"></i>
</a>
{% if SHOW_SUBSCRIBE %}
<a class="btn btn-outline-primary btn-sm"
<a class="btn bg-body-tertiary border btn-sm"
data-bs-toggle='tooltip'
href='?subscribe'
title='{% translate "Click here to subscribe yourself to this ticket, if you want to receive an e-mail whenever this ticket is updated." %}'>
@@ -178,7 +192,7 @@
</div>
{% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET != False %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Resolves" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Resolves" %}</small>
<a class="link-secondary"
data-bs-toggle="tooltip"
href="{% url 'helpdesk:ticket_resolves_add' ticket.id %}"
@@ -202,7 +216,7 @@
</ul>
</div>
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Depends on" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Depends on" %}</small>
<a class="link-secondary ms-1"
data-bs-toggle="tooltip"
href="{% url 'helpdesk:ticket_dependency_add' ticket.id %}"
@@ -226,13 +240,13 @@
{% endif %}
{% if ticket.kbitem %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Knowledgebase item" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Knowledgebase item" %}</small>
<p><a href="{{ ticket.kbitem.query_url }}">{{ ticket.kbitem|truncatechars:30 }}</a></p>
</div>
{% endif %}
{% if ticket.ticketcustomfieldvalue_set.exists %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Additional Info" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Additional Info" %}</small>
<ul class="list-unstyled">
{% for customfield in ticket.ticketcustomfieldvalue_set.all %}
<li>
@@ -257,7 +271,7 @@
{% endif %}
{% if helpdesk_settings.HELPDESK_ENABLE_ATTACHMENTS %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Attachments" %}</small>
<small class="text-uppercase text-body-secondary">{% translate "Attachments" %}</small>
{% with recent_attachments=ticket_attachments|slice:":3" older_attachments=ticket_attachments|slice:"3:" %}
<ul class="list-group list-group-flush">
{% for attachment in recent_attachments %}
@@ -289,8 +303,8 @@
</div>
{% endif %}
<div class="col">
<small class="text-uppercase text-secondary">{% translate "Checklists" %}</small>
<a class="ms-2" data-bs-toggle="modal" data-bs-target="#createChecklistModal" href="">
<small class="text-uppercase text-body-secondary">{% translate "Checklists" %}</small>
<a class="link-secondary ms-2" data-bs-toggle="modal" data-bs-target="#createChecklistModal" href="">
<i class="fas fa-plus"></i>
<span class="visually-hidden">{% translate "Create" %}</span>
</a>
@@ -307,27 +321,32 @@
</span>
</p>
<div class="btn-group btn-group-sm" role="group" aria-label="Checklist actions">
<button class="btn btn-light"
<button class="btn bg-body-tertiary border"
type="button"
data-bs-toggle="collapse"
data-bs-target="#checklist{{ checklist.id }}"
onclick="$(this).children('i').toggleClass('fa-caret-down fa-caret-up')">
<i class="fas fa-caret-down"></i>
</button>
<a class="btn btn-light" href="{% url 'helpdesk:edit_ticket_checklist' ticket.id checklist.id %}">
<a class="btn bg-body-tertiary border"
href="{% url 'helpdesk:edit_ticket_checklist' ticket.id checklist.id %}">
<i class="fa-regular fa-edit"></i>
</a>
</div>
</div>
<div class="collapse" id="checklist{{ checklist.id }}">
<ul class="list-unstyled">
<div class="collapse mt-3" id="checklist{{ checklist.id }}">
<ul class="list-unstyled pe-none">
{% for task in checklist.tasks.all %}
<li class="mb-1" {% if task.completion_date %} title="{% translate "Completed on" %} {{ task.completion_date }}" {% endif %}>
<li class="mb-1"
{% if task.completion_date %}
title="{% translate "Completed on" %} {{ task.completion_date }}"
{% endif %}>
<div class="form-check">
<input class="form-check-input"
type="checkbox"
id="task-view-{{ task.id }}"
disabled{% if task.completion_date %}
checked{% endif %} />
{% if task.completion_date %}
checked{% endif %} />
<label class="form-check-label" for="task-view-{{ task.id }}">{{ task }}</label>
</div>
</li>

View File

@@ -9,7 +9,9 @@
{% block helpdesk_head %}
{# Timeline 3 CSS #}
{% if helpdesk_settings.HELPDESK_USE_CDN %}
<link title="timeline-styles" rel="stylesheet" href="https://cdn.knightlab.com/libs/timeline3/latest/css/timeline.css" />
<link title="timeline-styles"
rel="stylesheet"
href="https://cdn.knightlab.com/libs/timeline3/latest/css/timeline.css" />
{% else %}
<link title="timeline-styles" rel="stylesheet" href="{% static 'helpdesk/vendor/timeline3/css/timeline.css' %}" />
{% endif %}
@@ -64,7 +66,10 @@
</ul>
{% endif %}
<div class="tab-content" id="tableTabContent">
<div class="tab-pane fade show active" id="datatabletabcontents" role="tabpanel" aria-labelledby="datatabletabcontents-tab">
<div class="tab-pane fade show active"
id="datatabletabcontents"
role="tabpanel"
aria-labelledby="datatabletabcontents-tab">
<div class="card border-0">
<div class="card-body">
<form method="post" action="{% url 'helpdesk:mass_update' %}" id="ticket_mass_update">
@@ -93,8 +98,8 @@
</thead>
</table>
{# Actions dropdown #}
<div class="dropdown dropend" id="actions-dropdown">
<button class="btn btn-outline-secondary border-0 dropdown-toggle"
<div class="dropdown" id="actions-dropdown">
<button class="btn bg-body-tertiary border dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
data-bs-auto-close="outside"
@@ -145,7 +150,7 @@
</select>
</li>
<li class="d-grid p-3">
<button type="submit" class="btn btn-outline-primary">
<button type="submit" class="btn bg-body-tertiary border">
{% translate "Go" %}
<i class="fa-solid fa-arrow-right"></i>
</button>
@@ -161,7 +166,8 @@
{% if saved_query.user == user %}
<p>
{% blocktranslate with saved_query.title as query_name %}You are currently viewing saved query <strong>"{{ query_name }}"</strong>.{% endblocktranslate %}
<a href="{% url 'helpdesk:delete_query' saved_query.id %}" class="alert-link text-decoration-underline link-offset-2">{% translate "Delete Saved Query" %}</a>
<a href="{% url 'helpdesk:delete_query' saved_query.id %}"
class="alert-link text-decoration-underline link-offset-2">{% translate "Delete Saved Query" %}</a>
</p>
{% endif %}
<hr />
@@ -182,7 +188,7 @@
{% endif %}
</div>
{# Filters Offcanvas #}
<a class="btn btn-outline-secondary border-0"
<a class="btn bg-body-tertiary border"
id="advanced-filters"
data-bs-toggle="offcanvas"
href="#offcanvasFilters"
@@ -211,14 +217,16 @@
</ul>
</form>
</div>
<div class="offcanvas-footer">
<div class="d-grid m-3">
<button class="btn btn-outline-primary" type="submit" form="filtersForm">{% translate "Apply Filters" %}</button>
</div>
<div class="offcanvas-footer p-3 d-flex justify-content-end">
<button type="button"
class="btn bg-body-tertiary border me-3"
data-bs-dismiss="offcanvas"
aria-label="Close">{% translate "Cancel" %}</button>
<button class="btn btn-success" type="submit" form="filtersForm">{% translate "Apply Filters" %}</button>
</div>
</div>
{# Queries Offcanvas #}
<button class="btn btn-outline-secondary border-0"
<button class="btn bg-body-tertiary border"
id="queries"
type="button"
data-bs-toggle="offcanvas"
@@ -273,12 +281,15 @@
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" name="shared" id="id_shared" />
<label class="form-label mb-0" for="id_shared">{% translate "Share this query with other users." %}</label>
<label class="form-label mb-0"
for="id_shared">{% translate "Share this query with other users." %}</label>
<div class="form-text">{% translate "If you share this query, it will be visible by <em>all</em> other logged-in users." %}</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-outline-primary">{% translate "Save Query" %}<i class="fa-solid fa-arrow-right ms-2"></i></button>
</div>
<button type="submit" class="btn btn-success me-2">{% translate "Save" %}</button>
<button class="btn bg-body-tertiary border"
type="button"
data-bs-dismiss="offcanvas"
aria-label="Close">{% translate "Cancel" %}</button>
</form>
</div>
{# Use Saved query pane #}
@@ -302,9 +313,10 @@
{% endfor %}
</select>
</div>
<div class="d-grid">
<button class="btn btn-outline-primary" type="submit">{% translate "Run Query" %}<i class="fa-solid fa-arrow-right ms-2"></i></button>
</div>
<button class="btn btn-success me-2" type="submit">{% translate "Run Query" %}</button>
<button class="btn bg-body-tertiary border" type="button" data-bs-dismiss="offcanvas" aria-label="Close">
{% translate "Cancel" %}
</button>
</form>
</div>
{% endif %}
@@ -339,7 +351,7 @@
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
const renderDateTime = (data, type, row, meta) => `<span class="small text-secondary">${data}</span>`
const renderDateTime = (data, type, row, meta) => `<span class="small text-body-secondary">${data}</span>`
$(document).ready(function() {
// Ticket DataTable Initialization
@@ -372,7 +384,7 @@
},
'buttons': [{
extend: "colvis",
className: "btn btn-outline-secondary border-0",
className: "btn bg-body-tertiary border",
id: "column-visibility",
text: "Columns"
}],
@@ -416,7 +428,7 @@
}, {
data: "queue",
render: function(data, type, row, meta) {
return `<span class="badge text-bg-light text-secondary">${data.title}</span>`
return `<span class="badge bg-light-subtle text-light-emphasis border border-light-subtle">${data.title}</span>`
},
visible: false,
}, {
@@ -436,19 +448,22 @@
data: "assigned_to",
render: function(data, type, row, meta) {
if (data !== "None") {
return data;
return `<span class="small text-body-secondary">${data}</span>`
}
return "";
}
}, {
data: "submitter"
data: "submitter",
render: function(data, type, row, meta) {
return `<span class="small text-body-secondary">${data}</span>`
}
}, {
data: "last_followup",
render: function(data, type, row) {
let locale = navigator.language || navigator.userLanguage || window.helpdesk_settings.LANGUAGE_CODE;
if (isNaN(Date.parse(data))) {
return `<span class="small text-secondary">${window.helpdesk_settings.NO_FOLLOWUP_TEXT}</span>`
return `<span class="small text-body-secondary">${window.helpdesk_settings.NO_FOLLOWUP_TEXT}</span>`
}
let date = new Date(data);
@@ -461,7 +476,7 @@
minute: "2-digit",
hour12: true
});
return `<span class="small text-secondary">${lastFollowupDate}</span>`
return `<span class="small text-body-secondary">${lastFollowupDate}</span>`
}
},
{% if helpdesk_settings.HELPDESK_ENABLE_TIME_SPENT_ON_TICKET %}
@@ -478,7 +493,6 @@
],
order: []
});
{# Shortcuts to select/unselect multiple tickets #}
$("#select_all_btn").click(function() {
$(".ticket_multi_select").prop('checked', true);
@@ -494,7 +508,6 @@
$(".buttons-colvis").removeClass('dt-button')
$("#dt-search-0").removeClass('form-control-sm')
{# Timeline initialization when tab is displayed #}
// The TL.Timeline constructor takes at least two arguments:
// the id of the Timeline container (no '#'), and

View File

@@ -85,7 +85,6 @@ class FollowUpDirectionRenderingTests(TestCase):
r = self.client.get(self.url)
self.assertContains(r, "followup-item-internal")
self.assertContains(r, "Private")
def test_outbound_followup_is_labeled_to_submitter(self):
@@ -95,7 +94,6 @@ class FollowUpDirectionRenderingTests(TestCase):
r = self.client.get(self.url)
self.assertContains(r, "followup-item-outbound")
self.assertContains(r, "To submitter")
def test_inbound_followup_is_labeled_from_submitter(self):
@@ -108,5 +106,4 @@ class FollowUpDirectionRenderingTests(TestCase):
r = self.client.get(self.url)
self.assertContains(r, "followup-item-inbound")
self.assertContains(r, "From submitter")

View File

@@ -113,7 +113,7 @@ class StaffTicketListViewTests(TestCase):
self.assertEqual(r.context["query_params"]["sorting"], "last_followup")
self.assertContains(
r,
"<option value='last_followup' selected='selected'>",
"<option value='last_followup' selected>",
html=False,
)