[ADD] LEAVE: Comment section for leave allocation request and its group view

This commit is contained in:
Horilla
2024-01-16 14:21:53 +05:30
parent 17b5f877a2
commit eb5258b053
9 changed files with 348 additions and 1 deletions

View File

@@ -11,6 +11,7 @@ from .models import (
Holiday,
CompanyLeave,
LeaveAllocationRequest,
LeaveallocationrequestComment,
LeaverequestComment,
)
@@ -24,3 +25,4 @@ admin.site.register(CompanyLeave)
admin.site.register(LeaveAllocationRequest)
admin.site.register(LeaveRequestConditionApproval)
admin.site.register(LeaverequestComment)
admin.site.register(LeaveallocationrequestComment)

View File

@@ -19,6 +19,7 @@ from .models import (
Holiday,
CompanyLeave,
LeaveAllocationRequest,
LeaveallocationrequestComment,
LeaverequestComment,
)
from .methods import (
@@ -806,3 +807,17 @@ class LeaverequestcommentForm(ModelForm):
model = LeaverequestComment
fields = ('comment',)
class LeaveallocationrequestcommentForm(ModelForm):
"""
Leave Allocation Requestcomment form
"""
class Meta:
"""
Meta class for additional options
"""
model = LeaveallocationrequestComment
fields = ('comment',)

View File

@@ -695,6 +695,24 @@ class LeaveAllocationRequest(models.Model):
super().save(*args, **kwargs)
class LeaveallocationrequestComment(models.Model):
"""
LeaveallocationrequestComment Model
"""
request_id = models.ForeignKey(LeaveAllocationRequest, on_delete=models.CASCADE)
employee_id = models.ForeignKey(Employee, on_delete=models.CASCADE)
comment = models.TextField(null=True, verbose_name=_("Comment"))
created_at = models.DateTimeField(
auto_now_add=True,
verbose_name=_("Created At"),
null=True,
)
def __str__(self) -> str:
return f"{self.comment}"
class LeaveRequestConditionApproval(models.Model):
sequence = models.IntegerField()
is_approved = models.BooleanField(default=False)

View File

@@ -0,0 +1,27 @@
{% load i18n %}
{% if form.errors %}
<!-- form errors -->
<div class="oh-wrapper">
<div class="oh-alert-container">
{% for error in form.non_field_errors %}
<div class="oh-alert oh-alert--animated oh-alert--danger">
{{ error }}
</div>
{% endfor %}
</div>
</div>
{% endif %}
<form
hx-post="{% url 'allocation-request-add-comment' request_id %}"
hx-target="#shiftRequestCommentForm"
method="post"
hx-encoding="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button
type="submit"
class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp"
>
{% trans "Save" %}
</button>
</form>

View File

@@ -0,0 +1,56 @@
{% load basefilters %}
{% load i18n %}
{% if no_comments %}
<div class="oh-timeoff-modal__profile-content">
<div class="">
<div>
<span class="oh-timeoff-title fw-bold" style="display: block;">{% trans "There is no comments to show." %}</span>
<img style="display: block;width: 100px;margin: 20px auto ;" src="/static/images/ui/comment.png" class="">
</div>
</div>
</div>
{% else %}
{% for comment in comments %}
<div class="oh-timeoff-modal__profile-content">
<div class="oh-profile ms-4">
<div>
<span class="oh-timeoff-modal__stat-title fw-bold">{% trans "Comment" %}
<a href='{% url "leave-request-delete-comment" comment.id %}' title='Delete Comment'>
<ion-icon name="close-circle-outline" class="text-danger ms-3 mt-1" aria-label="close outline"></ion-icon></a>
</span>
<div>
<span class="oh-timeoff-title fw-bold">{{comment.comment}}</span>
</div>
</div>
</div>
</div>
<div class="oh-modal__dialog-body">
<div class="oh-timeoff-modal__stats-container">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title fw-bold">{% trans "By" %}</span>
<a style="text-decoration:none;"
href ="{% url 'employee-view-individual' comment.employee_id.id %}">
<span class="oh-timeoff-modal__stat-count">{{ comment.employee_id }}</span></a>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title fw-bold">{% trans "Date & Time" %}</span>
<span class="oh-timeoff-modal__stat-count">
{% trans "on" %} &nbsp<span class="dateformat_changer">{{ comment.created_at|date:"F j, Y" }}</span> &nbsp
{% trans "at" %} &nbsp <span class="timeformat_changer">{{ comment.created_at|time:"g:i A" }}</span>
</span>
</div>
</div>
<hr>
</div>
{% endfor %}
{% endif %}

View File

@@ -35,6 +35,7 @@
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=created_by" hx-target="#userRequest">{% trans "Created By" %}</div>
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=status" hx-target="#userRequest">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
@@ -68,6 +69,22 @@
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.created_by}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.get_status_display}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{ leave_allocation_request.description}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group" >
<button type="button" hx-get="{% url 'allocation-request-add-comment' leave_allocation_request.id %}" title="Add Comment" hx-target="#shiftRequestCommentForm"
hx-swap="innerHTML" class="oh-btn oh-btn--light" data-toggle="oh-modal-toggle" data-target="#shiftcommentModal"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
<button type="button" hx-get="{% url 'allocation-request-view-comment' leave_allocation_request.id %}" hx-target="#shiftRequestDetailTarget" data-toggle='oh-modal-toggle'
data-target = '#shiftRequestDetailModal' title="View Comment" class="oh-btn oh-btn--light" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="eye-outline" role="img" class="md hydrated" aria-label="eye outline"></ion-icon>
</button>
</div>
</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}" onclick="event.stopPropagation()">
<div class="oh-btn-group">
{% if leave_allocation_request.status == 'requested' %}
@@ -196,6 +213,7 @@
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=created_by" hx-target="#userRequest">{% trans "Created By" %}</div>
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=status" hx-target="#userRequest">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
<div class="oh-sticky-table__th">{% trans "Options" %}</div>
</div>
@@ -230,6 +248,22 @@
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.created_by}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.get_status_display}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{ leave_allocation_request.description}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group" >
<button type="button" hx-get="{% url 'allocation-request-add-comment' leave_allocation_request.id %}" title="Add Comment" hx-target="#shiftRequestCommentForm"
hx-swap="innerHTML" class="oh-btn oh-btn--light" data-toggle="oh-modal-toggle" data-target="#shiftcommentModal"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
<button type="button" hx-get="{% url 'allocation-request-view-comment' leave_allocation_request.id %}" hx-target="#shiftRequestDetailTarget" data-toggle='oh-modal-toggle'
data-target = '#shiftRequestDetailModal' title="View Comment" class="oh-btn oh-btn--light" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="eye-outline" role="img" class="md hydrated" aria-label="eye outline"></ion-icon>
</button>
</div>
</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}" onclick="event.stopPropagation()">
<div class="oh-btn-group">
{% if leave_allocation_request.status == 'requested' %}

View File

@@ -16,6 +16,7 @@
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=created_by" hx-target="#userRequest">{% trans "Created By" %}</div>
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=status" hx-target="#userRequest">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
@@ -49,6 +50,22 @@
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.created_by}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.get_status_display}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{ leave_allocation_request.description}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group" >
<button type="button" hx-get="{% url 'allocation-request-add-comment' leave_allocation_request.id %}" title="Add Comment" hx-target="#shiftRequestCommentForm"
hx-swap="innerHTML" class="oh-btn oh-btn--light" data-toggle="oh-modal-toggle" data-target="#shiftcommentModal"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
<button type="button" hx-get="{% url 'allocation-request-view-comment' leave_allocation_request.id %}" hx-target="#shiftRequestDetailTarget" data-toggle='oh-modal-toggle'
data-target = '#shiftRequestDetailModal' title="View Comment" class="oh-btn oh-btn--light" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="eye-outline" role="img" class="md hydrated" aria-label="eye outline"></ion-icon>
</button>
</div>
</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}" onclick="event.stopPropagation()">
<div class="oh-btn-group">
{% if leave_allocation_request.status == 'requested' %}
@@ -154,6 +171,7 @@
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=created_by" hx-target="#userRequest">{% trans "Created By" %}</div>
<div class="oh-sticky-table__th" hx-get="{% url 'leave-allocation-request-filter' %}?{{pd}}&sortby=status" hx-target="#userRequest">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Comment" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
<div class="oh-sticky-table__th">{% trans "Options" %}</div>
</div>
@@ -188,6 +206,22 @@
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.created_by}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{leave_allocation_request.get_status_display}}</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}">{{ leave_allocation_request.description}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group" >
<button type="button" hx-get="{% url 'allocation-request-add-comment' leave_allocation_request.id %}" title="Add Comment" hx-target="#shiftRequestCommentForm"
hx-swap="innerHTML" class="oh-btn oh-btn--light" data-toggle="oh-modal-toggle" data-target="#shiftcommentModal"
style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="newspaper-outline" role="img" class="md hydrated" aria-label="newspaper outline"></ion-icon>
</button>
<button type="button" hx-get="{% url 'allocation-request-view-comment' leave_allocation_request.id %}" hx-target="#shiftRequestDetailTarget" data-toggle='oh-modal-toggle'
data-target = '#shiftRequestDetailModal' title="View Comment" class="oh-btn oh-btn--light" style="flex: 1 0 auto; width:20px;height: 40.68px; padding: 0;">
<ion-icon name="eye-outline" role="img" class="md hydrated" aria-label="eye outline"></ion-icon>
</button>
</div>
</div>
<div class="oh-sticky-table__td {% if leave_allocation_request.status == 'rejected' %} diff-cell{% endif %}" onclick="event.stopPropagation()">
<div class="oh-btn-group">
{% if leave_allocation_request.status == 'requested' %}
@@ -315,7 +349,56 @@
</div>
{% endif %}
</div>
<!-- start of comment modal -->
<div
class="oh-modal"
id="shiftcommentModal"
role="dialog"
aria-labelledby="emptagModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="createModalTitle">
{% trans "Add Comment" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body" id="shiftRequestCommentForm"></div>
</div>
</div>
<!-- end of comment modal -->
<div
class="oh-modal" style="z-index: 60;"
id="shiftRequestDetailModal"
role="dialog"
aria-labelledby="shiftRequestDetailModal"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2
class="oh-modal__dialog-title"
id=""
>
{% trans "Details" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div
class="oh-modal__dialog-body oh-modal__dialog-relative"
id="shiftRequestDetailTarget"
></div>
</div>
</div>
<script>
$(document).ready(function () {

View File

@@ -320,4 +320,20 @@ urlpatterns = [
views.delete_leaverequest_comment,
name="leave-request-delete-comment",
),
path(
"allocation-request-add-comment/<int:leave_id>/",
views.create_allocationrequest_comment,
name="allocation-request-add-comment",
),
path(
"allocation-request-view-comment/<int:leave_id>/",
views.view_allocationrequest_comment,
name="allocation-request-view-comment",
),
path(
"allocation-request-delete-comment/<int:comment_id>/",
views.delete_allocationrequest_comment,
name="allocation-request-delete-comment",
),
]

View File

@@ -3330,3 +3330,99 @@ def delete_leaverequest_comment(request, comment_id):
messages.success(request, _("Comment deleted successfully!"))
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
@login_required
def create_allocationrequest_comment(request, leave_id):
"""
This method renders form and template to create Allocation request comments
"""
leave = LeaveAllocationRequest.objects.filter(id=leave_id).first()
emp = request.user.employee_get
form = LeaveallocationrequestcommentForm(initial={'employee_id':emp.id, 'request_id':leave_id})
if request.method == "POST":
form = LeaveallocationrequestcommentForm(request.POST )
if form.is_valid():
form.instance.employee_id = emp
form.instance.request_id = leave
form.save()
form = LeaveallocationrequestcommentForm(initial={'employee_id':emp.id, 'request_id':leave_id})
messages.success(request, _("Comment added successfully!"))
if request.user.employee_get.id == leave.employee_id.id:
rec = leave.employee_id.employee_work_info.reporting_manager_id.employee_user_id
notify.send(
request.user.employee_get,
recipient=rec,
verb=f"{leave.employee_id}'s leave allocation request has received a comment.",
verb_ar=f"تلقت طلب تخصيص الإجازة لـ {leave.employee_id} تعليقًا.",
verb_de=f"{leave.employee_id}s Anfrage zur Urlaubszuweisung hat einen Kommentar erhalten.",
verb_es=f"La solicitud de asignación de permisos de {leave.employee_id} ha recibido un comentario.",
verb_fr=f"La demande d'allocation de congé de {leave.employee_id} a reçu un commentaire.",
redirect="/leave/leave-allocation-request-view",
icon="chatbox-ellipses",
)
elif request.user.employee_get.id == leave.employee_id.employee_work_info.reporting_manager_id.id:
rec = leave.employee_id.employee_user_id
notify.send(
request.user.employee_get,
recipient=rec,
verb="Your leave allocation request has received a comment.",
verb_ar="تلقى طلب تخصيص الإجازة الخاص بك تعليقًا.",
verb_de="Ihr Antrag auf Urlaubszuweisung hat einen Kommentar erhalten.",
verb_es="Tu solicitud de asignación de permisos ha recibido un comentario.",
verb_fr="Votre demande d'allocation de congé a reçu un commentaire.",
redirect="/leave/leave-allocation-request-view",
icon="chatbox-ellipses",
)
else:
rec = [leave.employee_id.employee_user_id, leave.employee_id.employee_work_info.reporting_manager_id.employee_user_id]
notify.send(
request.user.employee_get,
recipient=rec,
verb=f"{leave.employee_id}'s leave allocation request has received a comment.",
verb_ar=f"تلقت طلب تخصيص الإجازة لـ {leave.employee_id} تعليقًا.",
verb_de=f"{leave.employee_id}s Anfrage zur Urlaubszuweisung hat einen Kommentar erhalten.",
verb_es=f"La solicitud de asignación de permisos de {leave.employee_id} ha recibido un comentario.",
verb_fr=f"La demande d'allocation de congé de {leave.employee_id} a reçu un commentaire.",
redirect="/leave/leave-allocation-request-view",
icon="chatbox-ellipses",
)
return HttpResponse("<script>window.location.reload()</script>")
return render(
request,
"leave/leave_allocation_request/allocation_request_comment_form.html",
{
"form": form, "request_id":leave_id
},
)
@login_required
def view_allocationrequest_comment(request, leave_id):
"""
This method is used to show Allocation request comments
"""
comments = LeaveallocationrequestComment.objects.filter(request_id=leave_id).order_by('-created_at')
no_comments = False
if not comments.exists():
no_comments = True
return render(
request,
"leave/leave_allocation_request/comment_view.html",
{"comments": comments, 'no_comments': no_comments }
)
@login_required
def delete_allocationrequest_comment(request, comment_id):
"""
This method is used to delete Allocation request comments
"""
LeaveallocationrequestComment.objects.get(id=comment_id).delete()
messages.success(request, _("Comment deleted successfully!"))
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))