default migrations
This commit is contained in:
parent
f630e00c21
commit
a59baefd8f
@ -1,8 +1,8 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
|
||||
from .models import User
|
||||
from .models import User, Department
|
||||
|
||||
admin.site.register(User, UserAdmin)
|
||||
admin.site.register(User)
|
||||
admin.site.register(Department)
|
||||
|
@ -5,14 +5,15 @@ Copyright (c) 2019 - present AppSeed.us
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import User
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
email = forms.EmailField(
|
||||
widget=forms.EmailInput(
|
||||
attrs={
|
||||
"placeholder": "Username",
|
||||
"placeholder": "Email Address",
|
||||
"class": "form-control"
|
||||
}
|
||||
))
|
||||
@ -26,10 +27,17 @@ class LoginForm(forms.Form):
|
||||
|
||||
|
||||
class SignUpForm(UserCreationForm):
|
||||
username = forms.CharField(
|
||||
forename = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"placeholder": "Username",
|
||||
"placeholder": "Forename",
|
||||
"class": "form-control"
|
||||
}
|
||||
))
|
||||
surname = forms.CharField(
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"placeholder": "Surname",
|
||||
"class": "form-control"
|
||||
}
|
||||
))
|
||||
@ -50,11 +58,11 @@ class SignUpForm(UserCreationForm):
|
||||
password2 = forms.CharField(
|
||||
widget=forms.PasswordInput(
|
||||
attrs={
|
||||
"placeholder": "Password check",
|
||||
"placeholder": "Password (Again)",
|
||||
"class": "form-control"
|
||||
}
|
||||
))
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'email', 'password1', 'password2')
|
||||
fields = ('forename', 'surname', 'email', 'password1', 'password2')
|
||||
|
@ -1,8 +1,7 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-04 14:05
|
||||
# Generated by Django 3.2.16 on 2024-01-05 12:04
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import uuid
|
||||
|
||||
@ -16,30 +15,36 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Department',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=150)),
|
||||
('icon', models.CharField(blank=True, max_length=32, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('icon', models.ImageField(default='users/default.webp', upload_to='users', verbose_name='profile picture')),
|
||||
('email', models.EmailField(error_messages={'unique': 'A user with this email address already exists.'}, max_length=254, unique=True, verbose_name='email address')),
|
||||
('forename', models.CharField(help_text='This should be your real first name.', max_length=150, verbose_name='first name')),
|
||||
('surname', models.CharField(help_text='This should be your real last name.', max_length=150, verbose_name='last name')),
|
||||
('create_timestamp', models.DateTimeField(default=django.utils.timezone.now, help_text='When the user was created.', verbose_name='Creation Date')),
|
||||
('edit_timestamp', models.DateTimeField(default=django.utils.timezone.now, help_text='When the user was last edited.', verbose_name='Last Edited')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Use as a "soft delete" rather than deleting the user.', verbose_name='active status')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates whether the user has unrestricted site control.', verbose_name='superuser status')),
|
||||
('department', models.ForeignKey(blank=True, help_text='Which department does this user belong to?', null=True, on_delete=django.db.models.deletion.SET_NULL, to='authentication.department', verbose_name='department')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-04 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='icon',
|
||||
field=models.ImageField(default='users/default.webp', upload_to='users'),
|
||||
),
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-04 14:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0002_user_icon'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='name',
|
||||
field=models.CharField(default='Corban-Lee Jones', max_length=150),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, unique=True),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-04 22:15
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0003_auto_20240104_1447'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name='user',
|
||||
managers=[
|
||||
],
|
||||
),
|
||||
]
|
35
apps/authentication/migrations/default_departments.py
Normal file
35
apps/authentication/migrations/default_departments.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-05 10:44
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.apps import apps
|
||||
import uuid
|
||||
|
||||
|
||||
def create_departments(app, schema_editor):
|
||||
|
||||
Department = apps.get_model("authentication", "Department")
|
||||
|
||||
data = [
|
||||
{"title": "Development", "icon": None},
|
||||
{"title": "Sales", "icon": None},
|
||||
{"title": "Marketing", "icon": None},
|
||||
{"title": "Management", "icon": None},
|
||||
{"title": "Business Strategy", "icon": None},
|
||||
]
|
||||
|
||||
for item in data:
|
||||
priority = Department.objects.create(title=item["title"], icon=item["icon"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentication", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_departments)
|
||||
]
|
@ -3,23 +3,39 @@
|
||||
import uuid
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, BaseUserManager
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class Department(models.Model):
|
||||
title = models.CharField(max_length=150)
|
||||
icon = models.CharField(max_length=32, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return {
|
||||
"title": self.title,
|
||||
"icon": self.icon
|
||||
}
|
||||
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
|
||||
def create_user(self, email: str, name: str, password: str=None, **extra_fields):
|
||||
def create_user(self, email: str, forename: str, surname: str, password: str=None, **extra_fields):
|
||||
if not email:
|
||||
raise ValueError("Please provide an email address")
|
||||
|
||||
email = self.normalize_email(email)
|
||||
user = self.model(email=email, name=name, **extra_fields)
|
||||
user = self.model(email=email, forename=forename, surname=surname, **extra_fields)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
return user
|
||||
|
||||
def create_superuser(self, email: str, name: str, password: str, **extra_fields):
|
||||
def create_superuser(self, email: str, forename: str, surname: str, password: str, **extra_fields):
|
||||
extra_fields.setdefault("is_staff", True)
|
||||
extra_fields.setdefault("is_superuser", True)
|
||||
extra_fields.setdefault("is_active", True)
|
||||
@ -30,27 +46,108 @@ class UserManager(BaseUserManager):
|
||||
if not extra_fields.get("is_superuser"):
|
||||
raise ValueError("Superuser must have is_superuser=True")
|
||||
|
||||
return self.create_user(email, name, password, **extra_fields)
|
||||
return self.create_user(email, forename, surname, password, **extra_fields)
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
class User(AbstractBaseUser, PermissionsMixin):
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
icon = models.ImageField(upload_to="users", default="users/default.webp")
|
||||
name = models.CharField(max_length=150)
|
||||
email = models.EmailField(unique=True)
|
||||
|
||||
icon = models.ImageField(_("profile picture"), upload_to="users", default="users/default.webp")
|
||||
email = models.EmailField(
|
||||
_("email address"),
|
||||
unique=True,
|
||||
error_messages = {
|
||||
"unique": _("A user with this email address already exists.")
|
||||
}
|
||||
)
|
||||
forename = models.CharField(
|
||||
_("first name"),
|
||||
max_length=150,
|
||||
help_text=_("This should be your real first name.")
|
||||
)
|
||||
surname = models.CharField(
|
||||
_("last name"),
|
||||
max_length=150,
|
||||
help_text=_("This should be your real last name.")
|
||||
)
|
||||
|
||||
department = models.ForeignKey(
|
||||
Department,
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_("department"),
|
||||
help_text=_("Which department does this user belong to?")
|
||||
)
|
||||
|
||||
create_timestamp = models.DateTimeField(
|
||||
_("Creation Date"),
|
||||
editable=True,
|
||||
default=timezone.now,
|
||||
help_text=_("When the user was created.")
|
||||
)
|
||||
edit_timestamp = models.DateTimeField(
|
||||
_("Last Edited"),
|
||||
editable=True,
|
||||
default=timezone.now,
|
||||
help_text=_("When the user was last edited.")
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(
|
||||
_("active status"),
|
||||
default=True,
|
||||
help_text=_('Use as a "soft delete" rather than deleting the user.')
|
||||
)
|
||||
is_staff = models.BooleanField(
|
||||
_("staff status"),
|
||||
default=False,
|
||||
help_text=_("Designates whether the user can log into this admin site.")
|
||||
)
|
||||
is_superuser = models.BooleanField(
|
||||
_("superuser status"),
|
||||
default=False,
|
||||
help_text=_("Designates whether the user has unrestricted site control.")
|
||||
)
|
||||
|
||||
USERNAME_FIELD = "email"
|
||||
REQUIRED_FIELDS = ["name"]
|
||||
EMAIL_FIELD = "email"
|
||||
REQUIRED_FIELDS = ["forename", "surname"]
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('user')
|
||||
verbose_name_plural = _('users')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return f"{self.id} • {self.email} • {self.formal_fullname}"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.edit_timestamp = timezone.now()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
self.email = self.__class__.objects.normalize_email(self.email)
|
||||
|
||||
@property
|
||||
def fullname(self) -> str:
|
||||
return f"{self.forename} {self.surname}"
|
||||
|
||||
@property
|
||||
def formal_fullname(self) -> str:
|
||||
return f"{self.surname}, {self.forename}"
|
||||
|
||||
def serialize(self) -> dict:
|
||||
department = self.department.serialize() if self.department else None
|
||||
|
||||
return {
|
||||
"id": self.id,
|
||||
"icon": self.icon.url,
|
||||
"name": self.name,
|
||||
"email": self.email
|
||||
"email": self.email,
|
||||
"forename": self.forename,
|
||||
"surname": self.surname,
|
||||
"department": department,
|
||||
"create_timestamp": self.create_timestamp,
|
||||
"edit_timestamp": self.edit_timestamp
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ def login_view(request):
|
||||
if request.method == "POST":
|
||||
|
||||
if form.is_valid():
|
||||
username = form.cleaned_data.get("username")
|
||||
email = form.cleaned_data.get("email")
|
||||
password = form.cleaned_data.get("password")
|
||||
user = authenticate(username=username, password=password)
|
||||
user = authenticate(username=email, password=password)
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
return redirect("/")
|
||||
@ -36,10 +36,16 @@ def register_user(request):
|
||||
if request.method == "POST":
|
||||
form = SignUpForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
username = form.cleaned_data.get("username")
|
||||
user = form.save(commit=False)
|
||||
|
||||
# Develepment, give all new users admin
|
||||
user.is_staff = True
|
||||
user.is_superuser = True
|
||||
user.save()
|
||||
|
||||
email = form.cleaned_data.get("email")
|
||||
raw_password = form.cleaned_data.get("password1")
|
||||
user = authenticate(username=username, password=raw_password)
|
||||
user = authenticate(username=email, password=raw_password)
|
||||
|
||||
msg = 'User created successfully.'
|
||||
success = True
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Ticket
|
||||
from .models import Ticket, TicketPriority, TicketTag
|
||||
|
||||
admin.site.register(Ticket)
|
||||
admin.site.register(TicketPriority)
|
||||
admin.site.register(TicketTag)
|
||||
|
@ -1,9 +1,10 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-04 14:05
|
||||
# Generated by Django 3.2.16 on 2024-01-05 10:44
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
@ -34,12 +35,14 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='Ticket',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=60)),
|
||||
('description', models.TextField(max_length=650)),
|
||||
('create_timestamp', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('edit_timestamp', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('title', models.CharField(help_text='An extremely short summary of the ticket subject.', max_length=100, verbose_name='title')),
|
||||
('description', models.TextField(help_text='Detailed description of the ticket subject.', max_length=650, verbose_name='description')),
|
||||
('create_timestamp', models.DateTimeField(default=django.utils.timezone.now, help_text='When the user was created.', verbose_name='Creation Date')),
|
||||
('edit_timestamp', models.DateTimeField(default=django.utils.timezone.now, help_text='When the user was last edited.', verbose_name='Last Edited')),
|
||||
('author', models.ForeignKey(help_text='The creator of the ticket.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author')),
|
||||
('priority', models.ForeignKey(help_text='The importance level of this ticket.', on_delete=django.db.models.deletion.CASCADE, to='home.ticketpriority', verbose_name='priority')),
|
||||
('tags', models.ManyToManyField(blank=True, help_text='Categories of the ticket.', to='home.TicketTag', verbose_name='tags')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-04 23:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
34
apps/home/migrations/default_priorities.py
Normal file
34
apps/home/migrations/default_priorities.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-05 10:44
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.apps import apps
|
||||
import uuid
|
||||
|
||||
|
||||
def create_priorities(app, schema_editor):
|
||||
|
||||
TicketPriority = apps.get_model("home", "TicketPriority")
|
||||
|
||||
data = [
|
||||
{"title": "Urgent", "colour": "#FF0000"},
|
||||
{"title": "High", "colour": "#FFA500"},
|
||||
{"title": "Normal", "colour": "#FFFF00"},
|
||||
{"title": "Low", "colour": "#008000"}
|
||||
]
|
||||
|
||||
for item in data:
|
||||
priority = TicketPriority.objects.create(title=item["title"], colour=item["colour"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("home", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_priorities)
|
||||
]
|
36
apps/home/migrations/default_tags.py
Normal file
36
apps/home/migrations/default_tags.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Generated by Django 3.2.16 on 2024-01-05 10:44
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.apps import apps
|
||||
import uuid
|
||||
|
||||
|
||||
def create_tags(app, schema_editor):
|
||||
|
||||
TicketTag = apps.get_model("home", "TicketTag")
|
||||
|
||||
data = [
|
||||
{"title": "Network", "colour": "#0000FF"},
|
||||
{"title": "Software", "colour": "#FFA500"},
|
||||
{"title": "Hardware", "colour": "#808080"},
|
||||
{"title": "Question", "colour": "#FFFF00"},
|
||||
{"title": "Require's Help", "colour": "#00FF00"},
|
||||
{"title": "Issue", "colour": "#FF0000"}
|
||||
]
|
||||
|
||||
for item in data:
|
||||
priority = TicketTag.objects.create(title=item["title"], colour=item["colour"])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("home", "default_priorities"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_tags)
|
||||
]
|
@ -7,28 +7,86 @@ from datetime import timedelta, datetime
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class TicketPriority(models.Model):
|
||||
title = models.CharField(max_length=32)
|
||||
colour = models.CharField(max_length=7)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return {
|
||||
"title": self.title,
|
||||
"colour": self.colour
|
||||
}
|
||||
|
||||
|
||||
class TicketTag(models.Model):
|
||||
title = models.CharField(max_length=32)
|
||||
colour = models.CharField(max_length=7)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def serialize(self) -> dict:
|
||||
return {
|
||||
"title": self.title,
|
||||
"colour": self.colour
|
||||
}
|
||||
|
||||
|
||||
class Ticket(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
title = models.CharField(max_length=60)
|
||||
description = models.TextField(max_length=650)
|
||||
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
create_timestamp = models.DateTimeField(editable=True, default=timezone.now)
|
||||
edit_timestamp = models.DateTimeField(editable=True, default=timezone.now)
|
||||
|
||||
title = models.CharField(
|
||||
_("title"),
|
||||
max_length=100,
|
||||
help_text=_("An extremely short summary of the ticket subject.")
|
||||
)
|
||||
description = models.TextField(
|
||||
_("description"),
|
||||
max_length=650,
|
||||
help_text=_("Detailed description of the ticket subject.")
|
||||
)
|
||||
author = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_("author"),
|
||||
on_delete=models.CASCADE,
|
||||
help_text=_("The creator of the ticket.")
|
||||
)
|
||||
|
||||
priority = models.ForeignKey(
|
||||
TicketPriority,
|
||||
verbose_name=_("priority"),
|
||||
on_delete=models.CASCADE,
|
||||
help_text=_("The importance level of this ticket.")
|
||||
)
|
||||
tags = models.ManyToManyField(
|
||||
TicketTag,
|
||||
verbose_name=_("tags"),
|
||||
blank=True,
|
||||
help_text=_("Categories of the ticket.")
|
||||
|
||||
)
|
||||
|
||||
create_timestamp = models.DateTimeField(
|
||||
_("Creation Date"),
|
||||
editable=True,
|
||||
default=timezone.now,
|
||||
help_text=_("When the user was created.")
|
||||
)
|
||||
edit_timestamp = models.DateTimeField(
|
||||
_("Last Edited"),
|
||||
editable=True,
|
||||
default=timezone.now,
|
||||
help_text=_("When the user was last edited.")
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"#{self.id} • {self.title} • {self.author}"
|
||||
return f"#{self.id} • {self.title} •{f' {self.author.department.title} •' if self.author.department else ''} {self.author.formal_fullname}"
|
||||
|
||||
def clean_description(self):
|
||||
cleaned_description = bleach.clean(
|
||||
@ -40,6 +98,7 @@ class Ticket(models.Model):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.description = self.clean_description()
|
||||
self.edit_timestamp = timezone.now()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
@ -91,5 +150,7 @@ class Ticket(models.Model):
|
||||
"edit_timestamp": self.edit_timestamp,
|
||||
"is_edited": self.is_edited,
|
||||
"is_older_than_day": self.is_older_than_day,
|
||||
"timestamp": self.timestamp
|
||||
"timestamp": self.timestamp,
|
||||
"priority": self.priority.serialize(),
|
||||
"tags": [tag.serialize() for tag in self.tags.all()]
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from django.forms.models import model_to_dict
|
||||
|
||||
from .models import Ticket
|
||||
from ..authentication.models import Department
|
||||
from .models import Ticket, TicketPriority, TicketTag
|
||||
|
||||
|
||||
@login_required()
|
||||
@ -22,8 +23,15 @@ def dashboard(request):
|
||||
@login_required()
|
||||
def tickets(request):
|
||||
tickets = Ticket.objects.all().order_by("-create_timestamp")
|
||||
priorities = TicketPriority.objects.all()
|
||||
tags = TicketTag.objects.all()
|
||||
departments = Department.objects.all()
|
||||
|
||||
context = {
|
||||
"tickets": tickets,
|
||||
"priorities": priorities,
|
||||
"tags": tags,
|
||||
"departments": departments,
|
||||
"dayago": datetime.now() - timedelta(hours=24)
|
||||
}
|
||||
|
||||
|
@ -32,10 +32,10 @@
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-normal text-dark form-label">Username</label>
|
||||
{{ form.username }}
|
||||
<label class="text-normal text-dark form-label">Email Address</label>
|
||||
{{ form.email }}
|
||||
</div>
|
||||
<span class="text-error">{{ form.username.errors }}</span>
|
||||
<span class="text-error">{{ form.email.errors }}</span>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="text-normal text-dark form-label">Password</label>
|
||||
|
@ -40,10 +40,16 @@
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" class="text-normal text-dark">Username</label>
|
||||
{{ form.username }}
|
||||
<label class="form-label" class="text-normal text-dark">Forename</label>
|
||||
{{ form.forename }}
|
||||
</div>
|
||||
<span class="text-error">{{ form.username.errors }}</span>
|
||||
<span class="text-error">{{ form.forename.errors }}</span>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" class="text-normal text-dark">Surname</label>
|
||||
{{ form.surname }}
|
||||
</div>
|
||||
<span class="text-error">{{ form.surname.errors }}</span>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" class="text-normal text-dark">Email Address</label>
|
||||
@ -58,7 +64,7 @@
|
||||
<span class="text-error">{{ form.password1.errors }}</span>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" class="text-normal text-dark">Password Check</label>
|
||||
<label class="form-label" class="text-normal text-dark">Password (Again)</label>
|
||||
{{ form.password2 }}
|
||||
</div>
|
||||
<span class="text-error">{{ form.password2.errors }}</span>
|
||||
|
@ -22,39 +22,43 @@
|
||||
<li class="nav-item">
|
||||
<h6>Filters</h6>
|
||||
</li>
|
||||
<li class="nav-item ps-3 mT-15">
|
||||
<h6 class="small">Priority</h6>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="priority1" class="form-check-input">
|
||||
<label for="priority1" class="form-check-label">High</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="priority2" class="form-check-input">
|
||||
<label for="priority2" class="form-check-label">Medium</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="priority3" class="form-check-input">
|
||||
<label for="priority3" class="form-check-label">Low</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item ps-3 mT-15">
|
||||
<h6 class="small">Department</h6>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="sales" class="form-check-input">
|
||||
<label for="sales" class="form-check-label">Sales</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="strategy" class="form-check-input">
|
||||
<label for="strategy" class="form-check-label">Strategy</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="marketing" class="form-check-input">
|
||||
<label for="marketing" class="form-check-label">Marketing</label>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item ps-3 mT-15">
|
||||
<h6 class="small">Tags</h6>
|
||||
</li>
|
||||
|
||||
{% if priorities %}
|
||||
<li class="nav-item ps-3 mT-15">
|
||||
<h6 class="small">Priority</h6>
|
||||
{% for priority in priorities %}
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="priority-{{ priority.id }}" class="form-check-input">
|
||||
<label for="priority-{{ priority.id }}" class="form-check-label">{{ priority.title }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if departments %}
|
||||
<li class="nav-item ps-3 mT-15">
|
||||
<h6 class="small">Department</h6>
|
||||
{% for department in departments %}
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="department-{{ department.id }}" class="form-check-input">
|
||||
<label for="department-{{ department.id }}" class="form-check-label">{{ department.title }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if tags %}
|
||||
<li class="nav-item ps-3 mT-15">
|
||||
<h6 class="small">Tags</h6>
|
||||
{% for tag in tags %}
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="tag-{{ tag.id }}" class="form-check-input">
|
||||
<label for="tag-{{ tag.id }}" class="form-check-label">{{ tag.title }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<!-- <li class="nav-item mt-5">
|
||||
<a href="javascript:void(0)" class="nav-link c-grey-800 cH-blue-500 actived">
|
||||
<div class="peers ai-c jc-sb">
|
||||
@ -209,7 +213,7 @@
|
||||
<div class="peer peer-greed ov-h">
|
||||
<div class="peers ai-c">
|
||||
<div class="peer peer-greed">
|
||||
<h6 class="ticket-author mb-0">{{ ticket.author }}</h6>
|
||||
<h6 class="ticket-author mb-0">{{ ticket.author.fullname }}</h6>
|
||||
</div>
|
||||
<div class="peer">
|
||||
<small class="ticket-timestamp">
|
||||
@ -394,7 +398,7 @@
|
||||
$("#ticketAuthorImg").prop("src", "");
|
||||
$("#ticketTimestamp").text("");
|
||||
$("#btnGroupDrop2").hide();
|
||||
$("#ticketBadges").hide();
|
||||
$("#ticketBadges").empty().hide();
|
||||
|
||||
if (displayedTicketID === ticketID) {
|
||||
displayedTicketID = -1;
|
||||
@ -411,19 +415,31 @@
|
||||
csrfmiddlewaretoken: '{{ csrf_token }}'
|
||||
},
|
||||
success: function (data) {
|
||||
alert(JSON.stringify(data, null, 4));
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
|
||||
var ticket = data.ticket;
|
||||
var author = ticket.author;
|
||||
var department = author.department;
|
||||
var priority = ticket.priority;
|
||||
|
||||
$("#ticketTitle").text(ticket.title);
|
||||
$("#ticketDesc").append($(ticket.description));
|
||||
$("#ticketAuthor").text(author.name);
|
||||
$("#ticketDesc").append($(`<div>${ticket.description}</div>`));
|
||||
$("#ticketAuthor").text(`${author.forename} ${author.surname}`);
|
||||
$("#ticketAuthorImg").show();
|
||||
$("#ticketAuthorImg").prop("src", author.icon);
|
||||
$("#btnGroupDrop2").show();
|
||||
$("#ticketBadges").show();
|
||||
|
||||
$("#ticketBadges").append($(`<div class="badge me-1" style="background-color: ${priority.colour};">${priority.title}</div>`));
|
||||
|
||||
if (department != null) {
|
||||
$("#ticketBadges").append($(`<div class="badge bgc-deep-purple-500 me-1">${department.title}</div>`));
|
||||
}
|
||||
|
||||
ticket.tags.forEach(function(tag) {
|
||||
$("#ticketBadges").append($(`<div class="badge me-1" style="background-color: ${tag.colour};">${tag.title}</div>`));
|
||||
});
|
||||
|
||||
// timestamp
|
||||
var timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime;
|
||||
|
@ -184,7 +184,7 @@
|
||||
<img class="w-2r bdrs-50p" src="{{ request.user.icon.url }}" alt="">
|
||||
</div>
|
||||
<div class="peer">
|
||||
<span class="fsz-sm c-grey-900">{{ request.user }}</span>
|
||||
<span class="fsz-sm c-grey-900">{{ request.user.formal_fullname }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<ul class="dropdown-menu fsz-sm">
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
BIN
media/users/download.png
Normal file
BIN
media/users/download.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
media/users/download_kprVYJv.png
Normal file
BIN
media/users/download_kprVYJv.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Loading…
x
Reference in New Issue
Block a user