[ADD] ONBOARDING: Task status chart on onboarding dashboard

This commit is contained in:
Horilla
2024-02-05 13:53:16 +05:30
parent 732a896be4
commit 71c5ee14c4
7 changed files with 258 additions and 9 deletions

View File

@@ -4,7 +4,8 @@ decorators.py
Custom decorators for permission and manager checks in the application.
"""
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from employee.models import Employee
from recruitment.models import Recruitment
from onboarding.models import OnboardingTask,OnboardingStage
@@ -70,7 +71,12 @@ def all_manager_can_enter(function, perm):
if user.has_perm(perm) or is_manager:
return function(request, *args, **kwargs)
messages.info(request, "You dont have permission.")
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
previous_url = request.META.get("HTTP_REFERER", "/")
script = f'<script>window.location.href = "{previous_url}"</script>'
key = "HTTP_HX_REQUEST"
if key in request.META.keys():
return render(request,"decorator_404.html")
return HttpResponse(script)
return _function
@@ -91,7 +97,12 @@ def stage_manager_can_enter(function, perm):
if user.has_perm(perm) or is_manager:
return function(request, *args, **kwargs)
messages.info(request, "You dont have permission.")
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
previous_url = request.META.get("HTTP_REFERER", "/")
script = f'<script>window.location.href = "{previous_url}"</script>'
key = "HTTP_HX_REQUEST"
if key in request.META.keys():
return render(request,"decorator_404.html")
return HttpResponse(script)
return _function
@@ -109,6 +120,11 @@ def recruitment_manager_can_enter(function, perm):
if user.has_perm(perm) or is_manager:
return function(request, *args, **kwargs)
messages.info(request, "You dont have permission.")
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
previous_url = request.META.get("HTTP_REFERER", "/")
script = f'<script>window.location.href = "{previous_url}"</script>'
key = "HTTP_HX_REQUEST"
if key in request.META.keys():
return render(request,"decorator_404.html")
return HttpResponse(script)
return _function

View File

@@ -12,6 +12,7 @@ from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from horilla_audit.models import HorillaAuditInfo, HorillaAuditLog
from recruitment.models import Recruitment, Candidate
from employee.models import Employee
from base.horilla_company_manager import HorillaCompanyManager
@@ -133,7 +134,6 @@ class CandidateTask(models.Model):
"""
choice = (
("", ""),
("todo", _("Todo")),
("scheduled", _("Scheduled")),
("ongoing", _("Ongoing")),
@@ -155,6 +155,12 @@ class CandidateTask(models.Model):
)
onboarding_task_id = models.ForeignKey(OnboardingTask, on_delete=models.PROTECT)
objects = HorillaCompanyManager("candidate_id__recruitment_id__company_id")
history = HorillaAuditLog(
related_name="history_set",
bases=[
HorillaAuditInfo,
],
)
def __str__(self):
return f"{self.candidate_id}|{self.onboarding_task_id}"

View File

@@ -2,7 +2,23 @@
{% block content %}
{% load static i18n %}
{% load i18n %}
<style>
.done-task{
background-color: #9acd322e !important;
}
.scheduled-task{
background-color: #00afff2e !important;
}
.ongoing-task{
background-color: #e6ff002e !important;
}
.stuck-task{
background-color: #ff45003b !important;
}
.todo-task{
background-color: #8d8d8d2e !important;
}
</style>
<!-- End of Navigation -->
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper">
@@ -120,6 +136,13 @@
{% endif %}
</div>
</div>
<div
class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable"
id="taskStatus"
hx-get="{% url "task-report-onboarding" %}"
hx-trigger="load"
>
</div>
</div>
</div>

View File

@@ -0,0 +1,61 @@
{% load i18n %}
<div class="oh-main__titlebar oh-main__titlebar--left">
<span class="oh-main__titlebar-title fw-bold mb-0 text-dark">{{ candidate_tasks.first.onboarding_task_id.task_title }}</span>
</div>
<div class="oh-sticky-table mt-3 m-2">
<div class="oh-sticky-table__tbody">
{% for task in candidate_tasks %}
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__sd d-flex justify-content-between" style="width: 477px;overflow: hidden;">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{ task.candidate_id.get_avatar }}" class="oh-profile__image" />
</div>
<span class="oh-profile__name oh-text--dark">{{ task.candidate_id.name }}</span>
</div>
<span class="oh-recuritment_tag {% if task.status == 'done' %} done-task {% elif task.status == 'scheduled' %} scheduled-task{% elif task.status == 'ongoing' %} ongoing-task{% elif task.status == 'stuck' %} dd stuck-task {% elif task.status == 'todo' %} todo-task{% endif %}">
<select hx-swap="none" onchange="
if ($(this).val() == 'done') {
$(this).parent().addClass('done-task')
}else{
$(this).parent().removeClass('done-task')
}
if ($(this).val() == 'scheduled') {
$(this).parent().addClass('scheduled-task')
}else{
$(this).parent().removeClass('scheduled-task')
}
if ($(this).val() == 'ongoing') {
$(this).parent().addClass('ongoing-task')
}else{
$(this).parent().removeClass('ongoing-task')
}
if ($(this).val() == 'stuck') {
$(this).parent().addClass('stuck-task')
}else{
$(this).parent().removeClass('stuck-task')
}
if ($(this).val() == 'todo') {
$(this).parent().addClass('todo-task')
}else{
$(this).parent().removeClass('todo-task')
}
" hx-get="{% url 'change-task-status' %}?task_id={{task.id}}"
name="status"
class=""
style="width: 150px;"
id="task_status{{task.id}}">
{% for status in task.choice %}
<option value="{{status.0}}"
{% if status.0 == task.status %}
selected
{% endif %}
>{{status.1}}</option>
{% endfor %}
</select>
</span>
</div>
</div>
{% endfor %}
</div>
</div>

View File

@@ -0,0 +1,68 @@
{% load i18n %}
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider mb-0">
<span class="oh-card-dashboard__title">{% trans 'My Onboarding Tasks' %}</span>
</div>
{% if tasks %}
<div class="oh-card-dashboard__body">
<div class="oh-sticky-table" style="max-height:320px;">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th" align="center">
{% trans 'Task' %}
</div>
<div class="oh-sticky-table__th" style="width: 100px;" align="center">
{% trans 'Todo' %}
</div>
<div class="oh-sticky-table__th" style="width: 100px;" align="center">
{% trans 'Scheduled' %}
</div>
<div class="oh-sticky-table__th" style="width: 100px;" align="center">
{% trans 'Ongoing' %}
</div>
<div class="oh-sticky-table__th" style="width: 100px;" align="center">
{% trans 'Stuck' %}
</div>
<div class="oh-sticky-table__th oh-sticky-table__right" style="width: 100px;" align="center">
{% trans 'Done' %}
</div>
</div>
</div>
<div class="oh-sticky-table__tbody">
{% for task in tasks %}
<div class="oh-sticky-table__tr" draggable="true" data-toggle="oh-modal-toggle" data-target="#taskModal" hx-get="{% url 'candidate-tasks-status' %}?task_id={{ task.task.id }}" hx-target="#taskModalBody">
<div class="oh-sticky-table__sd">
{{ task.task }}
<span title="Total ({{ task.total_candidates }}) candidates in this task">({{ task.total_candidates }})</span>
</div>
<div class="oh-sticky-table__td">{{ task.todo }}</div>
<div class="oh-sticky-table__td">{{ task.scheduled }}</div>
<div class="oh-sticky-table__td">{{ task.ongoing }}</div>
<div class="oh-sticky-table__td">{{ task.stuck }}</div>
<div class="oh-sticky-table__td oh-sticky-table__right" title="{% trans 'Send Mail' %}">{{ task.done }}</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 70px;margin: 20px auto ;" src="/static/images/ui/joiningchart.png" class="" alt="" />
<h3 style="font-size:16px" class="oh-404__subtitle">{% trans 'No data Found...' %}</h3>
</div>
</div>
{% endif %}
</div>
<div class="oh-modal" id="taskModal" role="dialog" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width: 550px">
<div class="oh-modal__dialog-header">
<button type="button" class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
</div>
<div class="oh-modal__dialog-body" id="taskModalBody"></div>
</div>
</div>

View File

@@ -125,5 +125,14 @@ urlpatterns = [
views.onboarding_send_mail,
name="onboarding-send-mail",
),
path("update-probotion-end",views.update_probation_end,name="update-probotion-end")
path(
"update-probotion-end", views.update_probation_end, name="update-probotion-end"
),
path("task-report-onboarding", views.task_report, name="task-report-onboarding"),
path(
"candidate-tasks-status",
views.candidate_tasks_status,
name="candidate-tasks-status",
),
path("change-task-status", views.change_task_status, name="change-task-status"),
]

View File

@@ -29,7 +29,7 @@ from django.views.decorators.http import require_http_methods
from base.models import JobPosition
from notifications.signals import notify
from horilla import settings
from horilla.decorators import login_required, hx_request_required, manager_can_enter
from horilla.decorators import login_required, hx_request_required
from horilla.decorators import permission_required
from base.methods import generate_pdf, get_key_instances, get_pagination, sortby
from recruitment.models import Candidate, Recruitment, RecruitmentMailTemplate
@@ -439,7 +439,7 @@ def candidate_delete(request, obj_id):
@login_required
@manager_can_enter("onboarding.view_candidatestage")
@all_manager_can_enter("onboarding.view_candidatestage")
def candidates_single_view(request, id, **kwargs):
"""
Candidate individual view for the onboarding candidates
@@ -1535,3 +1535,69 @@ def update_probation_end(request):
probation_end = request.GET["probation_end"]
Candidate.objects.filter(id__in=candidate_id).update(probation_end=probation_end)
return JsonResponse({"message": "Probation end date updated", "type": "success"})
@login_required
@all_manager_can_enter("onboarding.change_onboardingtask")
def task_report(request):
"""
This method is used to show the task report.
"""
stage_id = request.GET.get("stage_id")
employee_id = request.GET.get("employee_id")
if not employee_id:
employee_id = request.user.employee_get.id
my_tasks = OnboardingTask.objects.filter(
employee_id__id=employee_id,
candidates__recruitment_id__closed=False,
).distinct()
tasks = []
for task in my_tasks:
tasks.append(
{
"task": task,
"total_candidates": task.candidatetask_set.count(),
"todo": task.candidatetask_set.filter(status="todo").count(),
"scheduled": task.candidatetask_set.filter(status="scheduled").count(),
"ongoing": task.candidatetask_set.filter(status="ongoing").count(),
"stuck": task.candidatetask_set.filter(status="stuck").count(),
"done": task.candidatetask_set.filter(status="done").count(),
}
)
return render(request, "onboarding/dashboard/task_report.html", {"tasks": tasks})
@login_required
@all_manager_can_enter("onboarding.view_candidatetask")
def candidate_tasks_status(request):
"""
This method is used to render template to show the onboarding tasks
"""
task_id = request.GET["task_id"]
candidate_tasks = CandidateTask.objects.filter(onboarding_task_id__id=task_id)
return render(
request,
"onboarding/dashboard/status_list.html",
{"candidate_tasks": candidate_tasks},
)
@login_required
@all_manager_can_enter("onboarding.change_candidatetask")
def change_task_status(request):
"""
This method is to update the candidate task
"""
task_id = request.GET["task_id"]
candidate_task = CandidateTask.objects.get(id=task_id)
status = request.GET["status"]
if status in [
"todo",
"scheduled",
"ongoing",
"stuck",
"done",
]:
candidate_task.status = status
candidate_task.save()
return HttpResponse("Success")