forked from extern/django-helpdesk
Create a page to edit checklist name and tasks + another page for deletion
This commit is contained in:
parent
b8d06a0fb1
commit
af1ba5f205
@ -607,23 +607,34 @@ class MultipleTicketSelectForm(forms.Form):
|
||||
|
||||
|
||||
class ChecklistForm(forms.ModelForm):
|
||||
checklist_template = forms.ModelChoiceField(
|
||||
label=_("Template"),
|
||||
queryset=ChecklistTemplate.objects.all(),
|
||||
widget=forms.Select(attrs={'class': 'form-wontrol'}),
|
||||
required=False,
|
||||
)
|
||||
name = forms.CharField(
|
||||
widget=forms.TextInput(attrs={'class': 'form-wontrol'}),
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'class': 'form-control'}),
|
||||
required=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Checklist
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
class CreateChecklistForm(ChecklistForm):
|
||||
checklist_template = forms.ModelChoiceField(
|
||||
label=_("Template"),
|
||||
queryset=ChecklistTemplate.objects.all(),
|
||||
widget=forms.Select(attrs={'class': 'form-control'}),
|
||||
required=False,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['name'].required = False
|
||||
|
||||
def clean(self):
|
||||
if not self.cleaned_data.get('checklist_template') and not self.cleaned_data.get('name'):
|
||||
raise ValidationError(_('Please choose at least a name or a template for the new checklist'))
|
||||
if self.cleaned_data.get('checklist_template') and self.cleaned_data.get('name'):
|
||||
raise ValidationError(_('Please choose either a name or a template for the new checklist'))
|
||||
|
||||
|
||||
class FormControlDeleteFormSet(forms.BaseInlineFormSet):
|
||||
deletion_widget = forms.CheckboxInput(attrs={'class': 'form-control'})
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 4.2 on 2023-04-22 20:50
|
||||
# Generated by Django 4.2 on 2023-04-28 21:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@ -42,11 +42,13 @@ class Migration(migrations.Migration):
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('description', models.CharField(max_length=250, verbose_name='Description')),
|
||||
('completion_date', models.DateTimeField(blank=True, null=True, verbose_name='Completion Date')),
|
||||
('position', models.PositiveSmallIntegerField(db_index=True, verbose_name='Position')),
|
||||
('checklist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='helpdesk.checklist', verbose_name='Checklist')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Checklist Task',
|
||||
'verbose_name_plural': 'Checklist Tasks',
|
||||
'ordering': ('position',),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
@ -2030,8 +2030,8 @@ class ChecklistTemplate(models.Model):
|
||||
|
||||
def create_checklist_for_ticket(self, ticket):
|
||||
checklist = ticket.checklists.create(name=self.name)
|
||||
for task in self.task_list:
|
||||
checklist.tasks.create(description=task)
|
||||
for position, task in enumerate(self.task_list):
|
||||
checklist.tasks.create(description=task, position=position)
|
||||
return checklist
|
||||
|
||||
|
||||
@ -2079,12 +2079,17 @@ class ChecklistTask(models.Model):
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
position = models.PositiveSmallIntegerField(
|
||||
verbose_name=_('Position'),
|
||||
db_index=True
|
||||
)
|
||||
|
||||
objects = ChecklistTaskQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Checklist Task')
|
||||
verbose_name_plural = _('Checklist Tasks')
|
||||
ordering = ('position',)
|
||||
|
||||
def __str__(self):
|
||||
return self.description
|
||||
|
45
helpdesk/templates/helpdesk/checklist_confirm_delete.html
Normal file
45
helpdesk/templates/helpdesk/checklist_confirm_delete.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% extends "helpdesk/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block helpdesk_title %}{% trans "Delete Checklist" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_breadcrumb %}
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'helpdesk:list' %}">{% trans "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 %}
|
||||
|
||||
{% block helpdesk_body %}
|
||||
<div class="row">
|
||||
<div class="col-sm-6 offset-sm-3 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
{% trans "Delete Checklist" %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method='post'>
|
||||
{% csrf_token %}
|
||||
<p>{% trans "Are you sure your want to delete checklist" %} {{ checklist.name }} ?</p>
|
||||
<div class='buttons form-group text-center'>
|
||||
<a class="btn btn-secondary" href='{{ ticket.get_absolute_url }}'>
|
||||
<i class="fas fa-times"></i>
|
||||
{% trans "Don't Delete" %}
|
||||
</a>
|
||||
<button type='submit' class="btn btn-danger">
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Yes, I Understand - Delete" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
106
helpdesk/templates/helpdesk/checklist_form.html
Normal file
106
helpdesk/templates/helpdesk/checklist_form.html
Normal file
@ -0,0 +1,106 @@
|
||||
{% extends "helpdesk/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block helpdesk_title %}{% trans "Edit Checklist" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_breadcrumb %}
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{% url 'helpdesk:list' %}">{% trans "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 %}
|
||||
|
||||
{% block helpdesk_body %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
{% trans "Edit Checklist" %}
|
||||
<a class="btn btn-danger float-right" href='{% url "helpdesk:delete_ticket_checklist" ticket.id checklist.id %}'>
|
||||
<i class="fas fa-trash"></i>
|
||||
{% trans "Delete checklist" %}
|
||||
</a>
|
||||
</h2>
|
||||
</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 form-group">
|
||||
{{ form.name.label_tag }}
|
||||
{{ form.name }}
|
||||
{{ form.name.errors }}
|
||||
</div>
|
||||
</div>
|
||||
<h3>Tasks</h3>
|
||||
{{ formset.management_form }}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Description" %}</th>
|
||||
<th>{% trans "Position" %}</th>
|
||||
<th class="text-center">{% trans "Delete?" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tasks">
|
||||
{% for form in formset %}
|
||||
<tr>
|
||||
{{ form.id }}
|
||||
<td>
|
||||
{{ form.description }}
|
||||
{{ form.description.errors }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form.position }}
|
||||
{{ form.position.errors }}
|
||||
</td>
|
||||
<td>
|
||||
{{ form.DELETE }}
|
||||
{{ form.DELETE.errors }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<button type="button" class="btn btn-secondary" id="addRow">
|
||||
<i class="fas fa-plus"></i>
|
||||
{% trans "Add another task" %}
|
||||
</button>
|
||||
<div class='buttons form-group text-center'>
|
||||
<a class="btn btn-link" href='{{ ticket.get_absolute_url }}'>
|
||||
<i class="fas fa-times"></i>
|
||||
{% trans "Cancel Changes" %}
|
||||
</a>
|
||||
<button type='submit' class="btn btn-primary">
|
||||
<i class="fas fa-save"></i>
|
||||
{% trans "Save Changes" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block helpdesk_js %}
|
||||
<script>
|
||||
$(() => {
|
||||
const idTasksTotalForms = $('#id_tasks-TOTAL_FORMS')
|
||||
$('#addRow').on('click', function() {
|
||||
const formCount = idTasksTotalForms.val()
|
||||
const description = '{{ formset.empty_form.description|escapejs }}'.replace(/__prefix__/g, formCount);
|
||||
const position = '{{ formset.empty_form.position|escapejs }}'.replace(/__prefix__/g, formCount);
|
||||
const delete_checkbox = '{{ formset.empty_form.DELETE|escapejs }}'.replace(/__prefix__/g, formCount);
|
||||
$('#tasks').append(`<tr><td>${description}</td><td>${position}</td><td>${delete_checkbox}</td></tr>`)
|
||||
idTasksTotalForms.val(parseInt(formCount) + 1);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
@ -7,6 +7,11 @@
|
||||
{% block helpdesk_title %}{{ ticket.queue.slug }}-{{ ticket.id }} : {% trans "View Ticket Details" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_head %}
|
||||
<style>
|
||||
.checklist {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block h1_title %}{{ ticket.ticket_for_url }}{% endblock %}
|
||||
|
@ -178,14 +178,17 @@
|
||||
<div class="row">
|
||||
{% for checklist in ticket.checklists.all %}
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card-header" data-toggle="collapse" data-target="#checklist{{ checklist.id }}" onclick="$(this).find('i').toggleClass('fa-caret-down fa-caret-up')">
|
||||
<h4>
|
||||
<div class="card checklist">
|
||||
<div class="card-header">
|
||||
<h5>
|
||||
{{ checklist }}
|
||||
<button class="btn btn-link float-right">
|
||||
<a class="btn btn-link btn-sm" href="{% url 'helpdesk:edit_ticket_checklist' ticket.id checklist.id %}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<button class="btn btn-secondary btn-sm float-right" data-toggle="collapse" data-target="#checklist{{ checklist.id }}" onclick="$(this).find('i').toggleClass('fa-caret-down fa-caret-up')">
|
||||
<i class="fas fa-caret-down"></i>
|
||||
</button>
|
||||
</h4>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body collapse" id="checklist{{ checklist.id }}">
|
||||
<div class="list-group">
|
||||
@ -214,9 +217,9 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<div class="card">
|
||||
<div class="card checklist">
|
||||
<div class="card-header">
|
||||
<h4>Add a checklist</h4>
|
||||
<h5>Add a checklist</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
|
@ -93,6 +93,16 @@ urlpatterns = [
|
||||
staff.attachment_del,
|
||||
name="attachment_del",
|
||||
),
|
||||
path(
|
||||
"tickets/<int:ticket_id>/checklists/<int:checklist_id>/",
|
||||
staff.edit_ticket_checklist,
|
||||
name="edit_ticket_checklist"
|
||||
),
|
||||
path(
|
||||
"tickets/<int:ticket_id>/checklists/<int:checklist_id>/delete/",
|
||||
staff.delete_ticket_checklist,
|
||||
name="delete_ticket_checklist"
|
||||
),
|
||||
re_path(r"^raw/(?P<type>\w+)/$", staff.raw_details, name="raw"),
|
||||
path("rss/", staff.rss_list, name="rss_index"),
|
||||
path("reports/", staff.report_index, name="report_index"),
|
||||
|
@ -20,6 +20,7 @@ from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||
from django.db.models import Q
|
||||
from django.forms import inlineformset_factory, TextInput, NumberInput
|
||||
from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse, reverse_lazy
|
||||
@ -49,7 +50,9 @@ from helpdesk.forms import (
|
||||
TicketDependencyForm,
|
||||
TicketForm,
|
||||
UserSettingsForm,
|
||||
ChecklistForm
|
||||
CreateChecklistForm,
|
||||
ChecklistForm,
|
||||
FormControlDeleteFormSet
|
||||
)
|
||||
from helpdesk.lib import process_attachments, queue_template_context, safe_template_context
|
||||
from helpdesk.models import (
|
||||
@ -65,7 +68,9 @@ from helpdesk.models import (
|
||||
TicketChange,
|
||||
TicketCustomFieldValue,
|
||||
TicketDependency,
|
||||
UserSettings
|
||||
UserSettings,
|
||||
Checklist,
|
||||
ChecklistTask
|
||||
)
|
||||
from helpdesk.query import get_query_class, query_from_base64, query_to_base64
|
||||
from helpdesk.user import HelpdeskUser
|
||||
@ -407,7 +412,7 @@ def view_ticket(request, ticket_id):
|
||||
else:
|
||||
submitter_userprofile_url = None
|
||||
|
||||
checklist_form = ChecklistForm(request.POST or None)
|
||||
checklist_form = CreateChecklistForm(request.POST or None)
|
||||
if checklist_form.is_valid():
|
||||
checklist_template = checklist_form.cleaned_data.get('checklist_template')
|
||||
if checklist_template:
|
||||
@ -432,6 +437,55 @@ def view_ticket(request, ticket_id):
|
||||
})
|
||||
|
||||
|
||||
@helpdesk_staff_member_required
|
||||
def edit_ticket_checklist(request, ticket_id, checklist_id):
|
||||
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||
ticket_perm_check(request, ticket)
|
||||
checklist = get_object_or_404(ticket.checklists.all(), id=checklist_id)
|
||||
|
||||
form = ChecklistForm(request.POST or None, instance=checklist)
|
||||
TaskFormSet = inlineformset_factory(
|
||||
Checklist,
|
||||
ChecklistTask,
|
||||
formset=FormControlDeleteFormSet,
|
||||
fields=['description', 'position'],
|
||||
widgets={
|
||||
'description': TextInput(attrs={'class': 'form-control'}),
|
||||
'position': NumberInput(attrs={'class': 'form-control'}),
|
||||
},
|
||||
can_delete=True,
|
||||
extra=1
|
||||
)
|
||||
formset = TaskFormSet(request.POST or None, instance=checklist)
|
||||
if form.is_valid() and formset.is_valid():
|
||||
form.save()
|
||||
formset.save()
|
||||
return redirect(ticket)
|
||||
|
||||
return render(request, 'helpdesk/checklist_form.html', {
|
||||
'ticket': ticket,
|
||||
'checklist': checklist,
|
||||
'form': form,
|
||||
'formset': formset,
|
||||
})
|
||||
|
||||
|
||||
@helpdesk_staff_member_required
|
||||
def delete_ticket_checklist(request, ticket_id, checklist_id):
|
||||
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||
ticket_perm_check(request, ticket)
|
||||
checklist = get_object_or_404(ticket.checklists.all(), id=checklist_id)
|
||||
|
||||
if request.POST:
|
||||
checklist.delete()
|
||||
return redirect(ticket)
|
||||
|
||||
return render(request, 'helpdesk/checklist_confirm_delete.html', {
|
||||
'ticket': ticket,
|
||||
'checklist': checklist,
|
||||
})
|
||||
|
||||
|
||||
def return_ticketccstring_and_show_subscribe(user, ticket):
|
||||
"""used in view_ticket() and followup_edit()"""
|
||||
# create the ticketcc_string and check whether current user is already
|
||||
|
Loading…
Reference in New Issue
Block a user