mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-28 10:53:11 +01:00
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):
|
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(
|
name = forms.CharField(
|
||||||
widget=forms.TextInput(attrs={'class': 'form-wontrol'}),
|
widget=forms.TextInput(attrs={'class': 'form-control'}),
|
||||||
required=False,
|
required=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Checklist
|
model = Checklist
|
||||||
fields = ('name',)
|
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):
|
def clean(self):
|
||||||
if not self.cleaned_data.get('checklist_template') and not self.cleaned_data.get('name'):
|
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'))
|
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'):
|
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'))
|
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
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
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')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('description', models.CharField(max_length=250, verbose_name='Description')),
|
('description', models.CharField(max_length=250, verbose_name='Description')),
|
||||||
('completion_date', models.DateTimeField(blank=True, null=True, verbose_name='Completion Date')),
|
('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')),
|
('checklist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='helpdesk.checklist', verbose_name='Checklist')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Checklist Task',
|
'verbose_name': 'Checklist Task',
|
||||||
'verbose_name_plural': 'Checklist Tasks',
|
'verbose_name_plural': 'Checklist Tasks',
|
||||||
|
'ordering': ('position',),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -2030,8 +2030,8 @@ class ChecklistTemplate(models.Model):
|
|||||||
|
|
||||||
def create_checklist_for_ticket(self, ticket):
|
def create_checklist_for_ticket(self, ticket):
|
||||||
checklist = ticket.checklists.create(name=self.name)
|
checklist = ticket.checklists.create(name=self.name)
|
||||||
for task in self.task_list:
|
for position, task in enumerate(self.task_list):
|
||||||
checklist.tasks.create(description=task)
|
checklist.tasks.create(description=task, position=position)
|
||||||
return checklist
|
return checklist
|
||||||
|
|
||||||
|
|
||||||
@ -2079,12 +2079,17 @@ class ChecklistTask(models.Model):
|
|||||||
null=True,
|
null=True,
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
position = models.PositiveSmallIntegerField(
|
||||||
|
verbose_name=_('Position'),
|
||||||
|
db_index=True
|
||||||
|
)
|
||||||
|
|
||||||
objects = ChecklistTaskQuerySet.as_manager()
|
objects = ChecklistTaskQuerySet.as_manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Checklist Task')
|
verbose_name = _('Checklist Task')
|
||||||
verbose_name_plural = _('Checklist Tasks')
|
verbose_name_plural = _('Checklist Tasks')
|
||||||
|
ordering = ('position',)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.description
|
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_title %}{{ ticket.queue.slug }}-{{ ticket.id }} : {% trans "View Ticket Details" %}{% endblock %}
|
||||||
|
|
||||||
{% block helpdesk_head %}
|
{% block helpdesk_head %}
|
||||||
|
<style>
|
||||||
|
.checklist {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block h1_title %}{{ ticket.ticket_for_url }}{% endblock %}
|
{% block h1_title %}{{ ticket.ticket_for_url }}{% endblock %}
|
||||||
|
@ -178,14 +178,17 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
{% for checklist in ticket.checklists.all %}
|
{% for checklist in ticket.checklists.all %}
|
||||||
<div class="col-sm-4 col-xs-12">
|
<div class="col-sm-4 col-xs-12">
|
||||||
<div class="card">
|
<div class="card checklist">
|
||||||
<div class="card-header" data-toggle="collapse" data-target="#checklist{{ checklist.id }}" onclick="$(this).find('i').toggleClass('fa-caret-down fa-caret-up')">
|
<div class="card-header">
|
||||||
<h4>
|
<h5>
|
||||||
{{ checklist }}
|
{{ 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>
|
<i class="fas fa-caret-down"></i>
|
||||||
</button>
|
</button>
|
||||||
</h4>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body collapse" id="checklist{{ checklist.id }}">
|
<div class="card-body collapse" id="checklist{{ checklist.id }}">
|
||||||
<div class="list-group">
|
<div class="list-group">
|
||||||
@ -214,9 +217,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="col-sm-4 col-xs-12">
|
<div class="col-sm-4 col-xs-12">
|
||||||
<div class="card">
|
<div class="card checklist">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h4>Add a checklist</h4>
|
<h5>Add a checklist</h5>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
|
@ -93,6 +93,16 @@ urlpatterns = [
|
|||||||
staff.attachment_del,
|
staff.attachment_del,
|
||||||
name="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"),
|
re_path(r"^raw/(?P<type>\w+)/$", staff.raw_details, name="raw"),
|
||||||
path("rss/", staff.rss_list, name="rss_index"),
|
path("rss/", staff.rss_list, name="rss_index"),
|
||||||
path("reports/", staff.report_index, name="report_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.handlers.wsgi import WSGIRequest
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django.forms import inlineformset_factory, TextInput, NumberInput
|
||||||
from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
|
from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
@ -49,7 +50,9 @@ from helpdesk.forms import (
|
|||||||
TicketDependencyForm,
|
TicketDependencyForm,
|
||||||
TicketForm,
|
TicketForm,
|
||||||
UserSettingsForm,
|
UserSettingsForm,
|
||||||
ChecklistForm
|
CreateChecklistForm,
|
||||||
|
ChecklistForm,
|
||||||
|
FormControlDeleteFormSet
|
||||||
)
|
)
|
||||||
from helpdesk.lib import process_attachments, queue_template_context, safe_template_context
|
from helpdesk.lib import process_attachments, queue_template_context, safe_template_context
|
||||||
from helpdesk.models import (
|
from helpdesk.models import (
|
||||||
@ -65,7 +68,9 @@ from helpdesk.models import (
|
|||||||
TicketChange,
|
TicketChange,
|
||||||
TicketCustomFieldValue,
|
TicketCustomFieldValue,
|
||||||
TicketDependency,
|
TicketDependency,
|
||||||
UserSettings
|
UserSettings,
|
||||||
|
Checklist,
|
||||||
|
ChecklistTask
|
||||||
)
|
)
|
||||||
from helpdesk.query import get_query_class, query_from_base64, query_to_base64
|
from helpdesk.query import get_query_class, query_from_base64, query_to_base64
|
||||||
from helpdesk.user import HelpdeskUser
|
from helpdesk.user import HelpdeskUser
|
||||||
@ -407,7 +412,7 @@ def view_ticket(request, ticket_id):
|
|||||||
else:
|
else:
|
||||||
submitter_userprofile_url = None
|
submitter_userprofile_url = None
|
||||||
|
|
||||||
checklist_form = ChecklistForm(request.POST or None)
|
checklist_form = CreateChecklistForm(request.POST or None)
|
||||||
if checklist_form.is_valid():
|
if checklist_form.is_valid():
|
||||||
checklist_template = checklist_form.cleaned_data.get('checklist_template')
|
checklist_template = checklist_form.cleaned_data.get('checklist_template')
|
||||||
if 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):
|
def return_ticketccstring_and_show_subscribe(user, ticket):
|
||||||
"""used in view_ticket() and followup_edit()"""
|
"""used in view_ticket() and followup_edit()"""
|
||||||
# create the ticketcc_string and check whether current user is already
|
# create the ticketcc_string and check whether current user is already
|
||||||
|
Loading…
Reference in New Issue
Block a user