tangled
alpha
login
or
join now
aottr.dev
/
paw
0
fork
atom
Free and open source ticket system written in python
0
fork
atom
overview
issues
pulls
pipelines
📦 NEW: Add follow-up ticket feature
Dustin Kroeger
2 years ago
8d6b97e3
ab7ba8fb
+66
-9
6 changed files
expand all
collapse all
unified
split
paw
__init__.py
templates
ticketing
create_ticket.html
ticketing
forms.py
migrations
0009_ticket_follow_up_to_alter_category_team.py
models.py
views.py
+1
-1
paw/__init__.py
···
1
1
from django import get_version
2
2
3
3
-
VERSION = (0, 1, 0, "beta", 7)
3
3
+
VERSION = (0, 1, 0, "beta", 8)
4
4
5
5
__version__ = get_version(VERSION)
+14
-3
paw/templates/ticketing/create_ticket.html
···
7
7
{% csrf_token %}
8
8
{{ form.non_field_errors }}
9
9
<label class="form-control mb-2">
10
10
+
{{ form.category.errors }}
11
11
+
<div class="label">
12
12
+
<span for="{{ form.category.id_for_label }}" class="label-text font-semibold text-base-content">{% trans 'Category' %}</span>
13
13
+
</div>
14
14
+
{{ form.category }}
15
15
+
</label>
16
16
+
<label class="form-control mb-2">
10
17
{{ form.title.errors }}
11
18
<div class="label">
12
19
<span for="{{ form.title.id_for_label }}" class="label-text font-semibold text-base-content">{% trans 'Title' %}</span>
···
20
27
</div>
21
28
{{ form.description }}
22
29
</label>
30
30
+
31
31
+
{% if has_closed_tickets %}
23
32
<label class="form-control mb-2">
24
24
-
{{ form.category.errors }}
33
33
+
{{ form.follow_up_to.errors }}
25
34
<div class="label">
26
26
-
<span for="{{ form.category.id_for_label }}" class="label-text font-semibold text-base-content">{% trans 'Category' %}</span>
35
35
+
<span for="{{ form.follow_up_to.id_for_label }}" class="label-text font-semibold text-base-content">{% trans 'Create as follow-up to a closed ticket' %}</span>
27
36
</div>
28
28
-
{{ form.category }}
37
37
+
{{ form.follow_up_to }}
29
38
</label>
39
39
+
{% endif %}
40
40
+
30
41
<div class="flex justify-end items-center mt-4">
31
42
<button type="submit" class="btn btn-success">{% trans 'Create Ticket' %}</button>
32
43
</div>
+6
-2
ticketing/forms.py
···
13
13
class TicketForm(forms.ModelForm):
14
14
class Meta:
15
15
model = Ticket
16
16
-
fields = ['title', 'description', 'category']
16
16
+
fields = ['title', 'description', 'category', 'follow_up_to']
17
17
widgets = {
18
18
'title': forms.TextInput(attrs={'class': 'input input-bordered w-full', 'placeholder': _('Please enter a title'), 'aria-label': _('Title')}),
19
19
'description': forms.Textarea(attrs={'class': 'textarea textarea-bordered h-32 w-full', 'placeholder': _('Please describe your issue'), 'aria-label': _('Description')}),
20
20
'category': forms.Select(attrs={'class': 'select select-bordered w-full'}),
21
21
+
'follow_up_to': forms.Select(attrs={'class': 'select select-bordered w-full'}),
21
22
}
22
23
23
23
-
def __init__(self, *args, **kwargs):
24
24
+
def __init__(self, user, *args, **kwargs):
24
25
super(TicketForm, self).__init__(*args, **kwargs)
25
26
self.fields['category'].empty_label = _('General')
27
27
+
self.fields['follow_up_to'].empty_label = _('No Follow-up')
28
28
+
self.fields['follow_up_to'].queryset = Ticket.objects.filter(
29
29
+
status=Ticket.Status.CLOSED, user=user)
26
30
27
31
28
32
class TemplateForm(forms.Form):
+36
ticketing/migrations/0009_ticket_follow_up_to_alter_category_team.py
···
1
1
+
# Generated by Django 5.0.3 on 2024-03-09 22:59
2
2
+
3
3
+
import django.db.models.deletion
4
4
+
from django.db import migrations, models
5
5
+
6
6
+
7
7
+
class Migration(migrations.Migration):
8
8
+
9
9
+
dependencies = [
10
10
+
("ticketing", "0008_remove_ticket_assigned_group_team_category_team_and_more"),
11
11
+
]
12
12
+
13
13
+
operations = [
14
14
+
migrations.AddField(
15
15
+
model_name="ticket",
16
16
+
name="follow_up_to",
17
17
+
field=models.ForeignKey(
18
18
+
blank=True,
19
19
+
null=True,
20
20
+
on_delete=django.db.models.deletion.CASCADE,
21
21
+
related_name="follow_ups",
22
22
+
to="ticketing.ticket",
23
23
+
),
24
24
+
),
25
25
+
migrations.AlterField(
26
26
+
model_name="category",
27
27
+
name="team",
28
28
+
field=models.ForeignKey(
29
29
+
blank=True,
30
30
+
help_text="If a team is selected, new tickets will automatically assigned to this team.",
31
31
+
null=True,
32
32
+
on_delete=django.db.models.deletion.CASCADE,
33
33
+
to="ticketing.team",
34
34
+
),
35
35
+
),
36
36
+
]
+2
ticketing/models.py
···
51
51
PawUser, on_delete=models.CASCADE, related_name='assigned_to_user', null=True, blank=True)
52
52
assigned_team = models.ForeignKey(
53
53
Team, on_delete=models.CASCADE, related_name='assigned_to_team', null=True, blank=True)
54
54
+
follow_up_to = models.ForeignKey(
55
55
+
"self", on_delete=models.CASCADE, null=True, blank=True, related_name='follow_ups')
54
56
55
57
class Meta:
56
58
indexes = [
+7
-3
ticketing/views.py
···
85
85
86
86
@login_required
87
87
def create_ticket(request):
88
88
+
89
89
+
has_closed_tickets = Ticket.objects.filter(
90
90
+
user=request.user, status=Ticket.Status.CLOSED).exists()
91
91
+
88
92
if request.method == "POST":
89
89
-
form = TicketForm(request.POST)
93
93
+
form = TicketForm(request.user, request.POST)
90
94
if form.is_valid():
91
95
ticket = form.save(commit=False)
92
96
ticket.user = request.user
93
97
ticket.save()
94
98
return redirect("ticket_detail", ticket_id=ticket.id)
95
99
else:
96
96
-
form = TicketForm()
97
97
-
return render(request, "ticketing/create_ticket.html", {"form": form})
100
100
+
form = TicketForm(request.user)
101
101
+
return render(request, "ticketing/create_ticket.html", {"form": form, "has_closed_tickets": has_closed_tickets})
98
102
99
103
100
104
@login_required