[ADD] ONBOARDING: Task status chart on onboarding dashboard
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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>
|
||||
|
||||
61
onboarding/templates/onboarding/dashboard/status_list.html
Normal file
61
onboarding/templates/onboarding/dashboard/status_list.html
Normal 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>
|
||||
68
onboarding/templates/onboarding/dashboard/task_report.html
Normal file
68
onboarding/templates/onboarding/dashboard/task_report.html
Normal 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>
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user