new changes.
parent
81d65b4236
commit
71108fe3ec
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,11 @@
|
|||||||
|
from django.urls import path, include
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# ADD
|
||||||
|
path('ticket/', views.customer_add_ticket, name='customeraddticket'),
|
||||||
|
path('customer/ticketupdate/<int:ticket_id>/', views.customer_add_ticket_update, name='customeraddticketupdate'),
|
||||||
|
path('ticketupdatereaction/<str:reaction_type>/<int:ticketupdate_id>/', views.customer_add_ticket_update_reaction, name='customeraddticketupdatereaction'),
|
||||||
|
|
||||||
|
]
|
@ -0,0 +1,175 @@
|
|||||||
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
from customercore.decorators import *
|
||||||
|
from customercore.models import *
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.utils.html import strip_tags
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
@customer_login_required
|
||||||
|
def customer_add_ticket(request, *args, **kwargs):
|
||||||
|
customer_orders = Order.objects.filter(customer=request.user.customerprofile)
|
||||||
|
customer_orders_with_last_status = customer_orders.annotate(max_status_date=Max('orderstatus__date'))
|
||||||
|
customer_orders_completed = customer_orders_with_last_status.filter(orderstatus__status='Completed',orderstatus__date=F('max_status_date'))
|
||||||
|
customer_products = OrderItem.objects.filter(active__in=[True, None], item__type = 'Product', order__customer = request.user.customerprofile, order__in=customer_orders_completed )
|
||||||
|
customer_projects = Project.objects.filter(customer=request.user.customerprofile)
|
||||||
|
support_department = get_object_or_404(Department, name='Support')
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
project = None
|
||||||
|
product = None
|
||||||
|
departments = [support_department]
|
||||||
|
regarding = 'General/Account/Billing'
|
||||||
|
if request.POST.get('project') and request.POST.get('regarding') == 'Project':
|
||||||
|
project = get_object_or_404(Project, id=request.POST.get('project'))
|
||||||
|
project_types = project.project_type.all()
|
||||||
|
departments = [project_type.department for project_type in project_types]
|
||||||
|
regarding = 'Project/Product'
|
||||||
|
elif request.POST.get('product') and request.POST.get('regarding') == 'Product':
|
||||||
|
product = get_object_or_404(Item, id=request.POST.get('product'))
|
||||||
|
departments = [product.item_type.department]
|
||||||
|
regarding = 'Project/Product'
|
||||||
|
ticket = Ticket(
|
||||||
|
status = 'Open',
|
||||||
|
customer = request.user.customerprofile,
|
||||||
|
title = request.POST.get('title'),
|
||||||
|
description = request.POST.get('description'),
|
||||||
|
regarding = regarding,
|
||||||
|
project = project,
|
||||||
|
product = product,
|
||||||
|
opened_by = request.user,
|
||||||
|
opened_date = datetime.now()
|
||||||
|
)
|
||||||
|
ticket.save()
|
||||||
|
ticket.departments.set(departments)
|
||||||
|
|
||||||
|
ticket_status = TicketStatus(
|
||||||
|
ticket = ticket,
|
||||||
|
status = 'Open',
|
||||||
|
added_by = request.user,
|
||||||
|
date_added = datetime.now()
|
||||||
|
)
|
||||||
|
ticket_status.save()
|
||||||
|
for file in request.FILES.getlist('files'):
|
||||||
|
ticket_attachment = TicketAttachment(
|
||||||
|
ticket=ticket,
|
||||||
|
file=file
|
||||||
|
)
|
||||||
|
ticket_attachment.save()
|
||||||
|
|
||||||
|
|
||||||
|
department_ids = [dept.id for dept in departments]
|
||||||
|
staff_profiles = StaffProfile.objects.filter(staff_position__department__id__in=department_ids, active=True)
|
||||||
|
print(staff_profiles)
|
||||||
|
for staff in staff_profiles:
|
||||||
|
subject = f"New Ticket Opened: {ticket.title}"
|
||||||
|
html_message = render_to_string('email_templates/new_ticket.html', {
|
||||||
|
'ticket': ticket,
|
||||||
|
'staff_first_name': staff.user.first_name
|
||||||
|
})
|
||||||
|
plain_message = strip_tags(html_message)
|
||||||
|
from_email = settings.DEFAULT_FROM_EMAIL
|
||||||
|
to_email = staff.user.email
|
||||||
|
|
||||||
|
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
|
||||||
|
return redirect('customerticketdetails', ticket_number=ticket.ticket_number)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'customer_products': customer_products,
|
||||||
|
'customer_projects': customer_projects,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'add_templates/customer-add-ticket.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@customer_login_required
|
||||||
|
def customer_add_ticket_update(request, ticket_id):
|
||||||
|
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
ticket_update = TicketUpdate(
|
||||||
|
ticket = ticket,
|
||||||
|
description = request.POST.get('description'),
|
||||||
|
added_by = request.user,
|
||||||
|
date_added = datetime.now(),
|
||||||
|
)
|
||||||
|
ticket_update.save()
|
||||||
|
for file in request.FILES.getlist('files'):
|
||||||
|
ticket_attachment = TicketAttachment(
|
||||||
|
ticket_update=ticket_update,
|
||||||
|
file=file
|
||||||
|
)
|
||||||
|
ticket_attachment.save()
|
||||||
|
|
||||||
|
|
||||||
|
if ticket.ticket_members.exists():
|
||||||
|
members_ids = ticket.ticket_members.values_list('id', flat=True)
|
||||||
|
staff_profiles = StaffProfile.objects.filter(id__in=members_ids)
|
||||||
|
else:
|
||||||
|
department_ids = ticket.departments.values_list('id', flat=True)
|
||||||
|
staff_profiles = StaffProfile.objects.filter(staff_position__department__id__in=department_ids, active=True)
|
||||||
|
|
||||||
|
|
||||||
|
for staff in staff_profiles:
|
||||||
|
subject = f"New Ticket Update: {ticket.title}"
|
||||||
|
html_message = render_to_string('email_templates/new_ticket_update.html', {
|
||||||
|
'ticket_update': ticket_update,
|
||||||
|
'staff_first_name': staff.user.first_name
|
||||||
|
})
|
||||||
|
plain_message = strip_tags(html_message)
|
||||||
|
from_email = settings.DEFAULT_FROM_EMAIL
|
||||||
|
to_email = staff.user.email
|
||||||
|
|
||||||
|
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
|
||||||
|
|
||||||
|
|
||||||
|
return redirect('customerticketdetails', ticket_number=ticket.ticket_number)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'ticket': ticket,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'add_templates/customer-add-ticket.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@customer_login_required
|
||||||
|
def customer_add_ticket_update_reaction(request, reaction_type, ticketupdate_id):
|
||||||
|
ticket_update = get_object_or_404(TicketUpdate, id=ticketupdate_id)
|
||||||
|
|
||||||
|
existing_reaction = TicketUpdateReaction.objects.filter(ticket_update=ticket_update, customer=request.user).first()
|
||||||
|
|
||||||
|
if existing_reaction:
|
||||||
|
# If the existing reaction type is equal to the new reaction, delete it
|
||||||
|
if existing_reaction.reaction == reaction_type:
|
||||||
|
existing_reaction.delete()
|
||||||
|
else:
|
||||||
|
# If not, delete all previous reactions and add a new one
|
||||||
|
TicketUpdateReaction.objects.filter(ticket_update=ticket_update, customer=request.user).delete()
|
||||||
|
reaction = TicketUpdateReaction.objects.create(
|
||||||
|
ticket_update=ticket_update,
|
||||||
|
reaction=reaction_type,
|
||||||
|
customer=request.user
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# If there's no existing reaction, simply add the new one
|
||||||
|
reaction = TicketUpdateReaction.objects.create(
|
||||||
|
ticket_update=ticket_update,
|
||||||
|
reaction=reaction_type,
|
||||||
|
customer=request.user
|
||||||
|
)
|
||||||
|
|
||||||
|
return redirect('customerticketdetails', ticket_number=ticket_update.ticket.ticket_number)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-05-22 12:17
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('customercore', '0016_rename_ticketstatusupdate_ticketstatus'),
|
||||||
|
('osinacore', '0081_status_task'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TicketTask',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.task')),
|
||||||
|
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customercore.ticketupdate')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,80 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const fileInput = document.querySelector("#uploader");
|
||||||
|
const progressArea = document.querySelector(".progress-area");
|
||||||
|
const uploadedArea = document.querySelector(".uploaded-area");
|
||||||
|
|
||||||
|
const uploadTrigger = fileInput.closest('div').querySelector('p');
|
||||||
|
|
||||||
|
// Add event listener to the upload trigger element
|
||||||
|
uploadTrigger.addEventListener("click", () => {
|
||||||
|
fileInput.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.onchange = ({ target }) => {
|
||||||
|
let file = target.files[0];
|
||||||
|
if (file) {
|
||||||
|
let fileName = file.name;
|
||||||
|
if (fileName.length >= 12) {
|
||||||
|
let splitName = fileName.split('.');
|
||||||
|
fileName = splitName[0].substring(0, 13) + "... ." + splitName[1];
|
||||||
|
}
|
||||||
|
// Clear previous progress and uploaded file information
|
||||||
|
progressArea.innerHTML = "";
|
||||||
|
uploadedArea.innerHTML = "";
|
||||||
|
uploadFile(fileName, file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function uploadFile(name, file) {
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "upload"); // Change URL to your upload endpoint
|
||||||
|
xhr.upload.addEventListener("progress", ({ loaded, total }) => {
|
||||||
|
let fileLoaded = Math.floor((loaded / total) * 100);
|
||||||
|
let fileTotal = Math.floor(total / 1000);
|
||||||
|
let fileSize;
|
||||||
|
(fileTotal < 1024) ? fileSize = fileTotal + " KB" : fileSize = (loaded / (1024 * 1024)).toFixed(2) + " MB";
|
||||||
|
let progressHTML = `<li class="flex items-center mb-4">
|
||||||
|
<i class="fas fa-file-alt text-blue-500 mr-2"></i>
|
||||||
|
<div class="flex-grow">
|
||||||
|
<div class="flex justify-between text-gray-500">
|
||||||
|
<span>${name} • Uploading</span>
|
||||||
|
<span>${fileLoaded}%</span>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-300 h-2 rounded-lg mt-1">
|
||||||
|
<div class="bg-secondosiblue h-2 rounded-lg" style="width: ${fileLoaded}%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>`;
|
||||||
|
uploadedArea.classList.add("onprogress");
|
||||||
|
progressArea.innerHTML = progressHTML;
|
||||||
|
if (loaded === total) {
|
||||||
|
progressArea.innerHTML = "";
|
||||||
|
let uploadedHTML = `<div class="w-full px-3 py-3 bg-secondosiblue bg-opacity-70 rounded-md flex justify-between items-center gap-3 mt-3">
|
||||||
|
<div class="flex justify-start items-center gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="text-white h-6 w-6" color="#000000" fill="none">
|
||||||
|
<path d="M16 17L9 17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path d="M16 13L13 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
<path d="M20.5 14C20.5 17.7712 20.5 19.6569 19.2552 20.8284C18.0104 22 16.0069 22 12 22H11.2273C7.96607 22 6.33546 22 5.20307 21.2022C4.87862 20.9736 4.59058 20.7025 4.3477 20.3971C3.5 19.3313 3.5 17.7966 3.5 14.7273V12.1818C3.5 9.21865 3.5 7.73706 3.96894 6.55375C4.72281 4.65142 6.31714 3.15088 8.33836 2.44135C9.59563 2 11.1698 2 14.3182 2C16.1173 2 17.0168 2 17.7352 2.2522C18.8902 2.65765 19.8012 3.5151 20.232 4.60214C20.5 5.27832 20.5 6.12494 20.5 7.81818V14Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
||||||
|
<path d="M3.5 12C3.5 10.1591 4.99238 8.66667 6.83333 8.66667C7.49912 8.66667 8.28404 8.78333 8.93137 8.60988C9.50652 8.45576 9.95576 8.00652 10.1099 7.43136C10.2833 6.78404 10.1667 5.99912 10.1667 5.33333C10.1667 3.49238 11.6591 2 13.5 2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
<p class="text-white">${name}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-end items-center gap-2">
|
||||||
|
<p class="text-white">${fileSize}</p>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="text-white h-6 w-6" fill="none">
|
||||||
|
<path d="M5 14.5C5 14.5 6.5 14.5 8.5 18C8.5 18 14.0588 8.83333 19 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>`;
|
||||||
|
uploadedArea.classList.remove("onprogress");
|
||||||
|
uploadedArea.innerHTML = uploadedHTML;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
xhr.send(formData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue