[UPDT] OFFBOARDING: Note section to new design and workflow
This commit is contained in:
@@ -295,7 +295,6 @@ class OffboardingNote(models.Model):
|
||||
attachments = models.ManyToManyField(
|
||||
OffboardingStageMultipleFile, blank=True, editable=False
|
||||
)
|
||||
title = models.CharField(max_length=20, null=True)
|
||||
description = models.TextField(null=True, blank=True,max_length=255)
|
||||
note_by = models.ForeignKey(
|
||||
Employee, on_delete=models.SET_NULL, null=True, editable=False
|
||||
|
||||
@@ -1,43 +1,174 @@
|
||||
{% load i18n static %}
|
||||
<style>
|
||||
#enlargeImageContainer {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 100px;
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
#enlargeImageContainer {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 100px;
|
||||
height: 200px;
|
||||
width: 400px;
|
||||
}
|
||||
</style>
|
||||
<a hx-get="{% url 'add-offboarding-note' %}?employee_id={{ employee.id }}" style="width: 125px;position: sticky;top: 0;" hx-target="#offboardingModalBody" data-toggle="oh-modal-toggle" data-target="#offboardingModal" class="mb-3 oh-btn oh-btn--secondary">
|
||||
<ion-icon name="add-outline" role="img" class="md hydrated" aria-label="add outline"></ion-icon>
|
||||
{% trans 'Add note' %}
|
||||
</a>
|
||||
<ol class="oh-activity-sidebar__qa-list" role="list">
|
||||
{% for note in employee.offboardingnote_set.all %}
|
||||
<li class="oh-activity-sidebar__qa-item">
|
||||
<span class="oh-activity-sidebar__q">{{ note.title }}</span>
|
||||
<span class="oh-activity-sidebar__a">{{ note.description }}</span>
|
||||
|
||||
<div class="d-flex mt-2 mb-2">
|
||||
{% for attachment in note.attachments.all %}
|
||||
<a href="{{ attachment.attachment.url }}" rel="noopener noreferrer" target="_blank"><span class="oh-file-icon oh-file-icon--pdf" onmouseover="enlargeImage('{{ attachment.attachment.url }}',$(this))" style="width:40px;height:40px"><img src="{% static 'images/ui/minus-icon.png' %}" style="display:block;width:50%;height:50%" hx-get="{% url 'delete-note-attachment' %}?ids={{ attachment.id }}&employee_id={{ employee.id }}" hx-target="#activitySidebar" onclick="event.stopPropagation();event.preventDefault()" /></span></a>
|
||||
{% endfor %}
|
||||
{% if messages %}
|
||||
<div class="oh-alert-container">
|
||||
{% for message in messages %}
|
||||
<div class="oh-alert oh-alert--animated {{message.tags}}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
$(".oh-modal__close").click();
|
||||
}, 1000);
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<form hx-post="{% url 'view-offboarding-note' %}?note_id={{ note.id }}&employee_id={{ employee.id }}" hx-target="#noteContainer" class="add-files-form" hx-encoding="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<label for="addFile_20" title="Add Files"><ion-icon name="add-outline" style="font-size: 24px" role="img" class="md hydrated" aria-label="add outline"></ion-icon></label>
|
||||
<input type="file" name="files" class="d-none" multiple="true" id="addFile_20" onchange="submitForm(this)" />
|
||||
<input type="submit" class="d-none add_more_submit" value="save" />
|
||||
</form>
|
||||
</div>
|
||||
<span class="oh-activity-sidebar__a">
|
||||
{% trans 'by' %}
|
||||
<img src="{{ note.note_by.get_avatar }}" style="width: 1.5em; border-radius: 100%" />
|
||||
{{ note.note_by.get_full_name }} @{{ note.stage_id }}
|
||||
</span>
|
||||
<div style="width: 50%;">
|
||||
<div id="enlargeImageContainer" class="enlargeImageContainer"></div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<div class="oh-activity-sidebar__header">
|
||||
<a
|
||||
style="cursor: pointer;"
|
||||
onclick="$('.oh-activity-sidebar--show').removeClass('oh-activity-sidebar--show');">
|
||||
<ion-icon
|
||||
name="chevron-forward-outline"
|
||||
class="oh-activity-sidebar__header-icon me-2 oh-activity-sidebar__close"
|
||||
data-target="#activitySidebar"
|
||||
></ion-icon>
|
||||
</a>
|
||||
<span class="oh-activity-sidebar__title">{{employee}}'s {% trans "Notes" %}</span>
|
||||
</div>
|
||||
|
||||
<form method="post"
|
||||
hx-target="#noteContainer"
|
||||
hx-post="{% url 'add-offboarding-note' %}?employee_id={{ employee.id }}" id="commentForm">
|
||||
{% csrf_token %}
|
||||
|
||||
<div>
|
||||
<input type="text" name="description" id="commentInput" class="oh-input w-100" placeholder="Add notes">
|
||||
<button type="submit" id="commentButton" class="oh-btn oh-btn--secondary mt-2 mr-0 oh-btn--w-100-resp" style="display: none;">
|
||||
{% trans "Add" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="oh-inner-sidebar-content__footer"></div>
|
||||
</form>
|
||||
|
||||
{% if employee.offboardingnote_set.all %}
|
||||
<ol class="oh-activity-sidebar__qa-list" role="list">
|
||||
{% for note in employee.offboardingnote_set.all %}
|
||||
<li class="oh-activity-sidebar__qa-item">
|
||||
<span class="oh-activity-sidebar__q">{{ note.description }}
|
||||
<span class="float-end" title="Delete" hx-get="{% url 'offboarding-note-delete' note.id %}" hx-swap="innerHTML" hx-target="#noteContainer">
|
||||
<ion-icon name="close-outline" style="font-size: 24px" role="img" class="md hydrated" aria-label="close outline"></ion-icon></span>
|
||||
</span>
|
||||
|
||||
<div class="d-flex mt-2 mb-2">
|
||||
{% for file in note.attachments.all %}
|
||||
<a
|
||||
href="{{ file.files.url }}"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
><span
|
||||
class="oh-file-icon oh-file-icon--pdf"
|
||||
onmouseover="enlargeImage('{{ file.attachment.url }}',$(this))"
|
||||
style="width: 40px; height: 40px"
|
||||
><img
|
||||
src="{% static 'images/ui/minus-icon.png' %}"
|
||||
style="display: block; width: 50%; height: 50%"
|
||||
hx-get="{% url 'delete-note-attachment' %}?ids={{ file.id }}&employee_id={{ employee.id }}"
|
||||
hx-target="#noteContainer"
|
||||
onclick="event.stopPropagation();event.preventDefault()" /></span
|
||||
></a>
|
||||
{% endfor %}
|
||||
|
||||
<form
|
||||
hx-post="{% url 'view-offboarding-note' employee.id %}?note_id={{ note.id }}"
|
||||
class="add-files-form"
|
||||
hx-encoding="multipart/form-data"
|
||||
hx-swap="innerHTML" hx-target="#noteContainer"
|
||||
|
||||
>
|
||||
{% csrf_token %}
|
||||
<label for="addFile_{{note.id}}" title="Add Files"
|
||||
><ion-icon
|
||||
name="add-outline"
|
||||
style="font-size: 24px"
|
||||
role="img"
|
||||
class="md hydrated"
|
||||
aria-label="add outline"
|
||||
></ion-icon
|
||||
></label>
|
||||
<input
|
||||
type="file"
|
||||
name="files"
|
||||
class="d-none"
|
||||
multiple="true"
|
||||
id="addFile_{{note.id}}"
|
||||
onchange="submitForm(this)"
|
||||
/>
|
||||
<input
|
||||
type="submit"
|
||||
class="d-none add_more_submit"
|
||||
value="save"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
<span class="oh-activity-sidebar__a">
|
||||
{% trans 'by' %}
|
||||
<img
|
||||
src="{{ note.note_by.get_avatar }}"
|
||||
style="width: 1.5em; border-radius: 100%"
|
||||
/>
|
||||
{{ note.updated_by }} @ {{note.stage_id }}
|
||||
{% trans "stage" %}
|
||||
</span>
|
||||
<div style="width: 50%">
|
||||
<div id="enlargeImageContainer" class="enlargeImageContainer"></div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% else %}
|
||||
<div class="oh-timeoff-modal__profile-content">
|
||||
<div class="oh-404">
|
||||
<div class="">
|
||||
<span class="oh-timeoff-title fw-bold" style="display: block"
|
||||
>{% trans "No notes have been added for this employee" %}</span
|
||||
>
|
||||
<img
|
||||
style="display: block; width: 100px; margin: 20px auto"
|
||||
src="/static/images/ui/no_notes.png"
|
||||
class=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- start of comment modal -->
|
||||
<div
|
||||
class="oh-modal"
|
||||
id="shiftcommentModal"
|
||||
role="dialog"
|
||||
aria-labelledby="emptagModal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="oh-modal__dialog" id="shiftRequestCommentForm">
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of comment modal -->
|
||||
|
||||
|
||||
<script>
|
||||
// Get references to the input field and comment button
|
||||
const commentInput = document.getElementById('commentInput');
|
||||
const commentButton = document.getElementById('commentButton');
|
||||
|
||||
// Add event listener to the input field
|
||||
commentInput.addEventListener('input', function() {
|
||||
// Show the comment button if the input field is not empty, hide it otherwise
|
||||
if (commentInput.value.trim() !== '') {
|
||||
commentButton.style.display = 'inline';
|
||||
} else {
|
||||
commentButton.style.display = 'none';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -33,3 +33,8 @@
|
||||
<div class="oh-modal__dialog-body" id="offboardingModalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oh-activity-sidebar" id="activitySidebar" style="z-index:1000">
|
||||
|
||||
<div class="oh-activity-sidebar__body" id="noteContainer">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,22 +8,6 @@
|
||||
<div class="oh-card" id="offboardingBody{{offboarding.offboarding.id}}">
|
||||
{% include "offboarding/stage/offboarding_body.html" %}
|
||||
</div>
|
||||
<div class="oh-activity-sidebar" id="activitySidebar">
|
||||
<div class="oh-activity-sidebar__header">
|
||||
<a
|
||||
style="cursor: pointer;"
|
||||
onclick="$('.oh-activity-sidebar--show').removeClass('oh-activity-sidebar--show');">
|
||||
<ion-icon
|
||||
name="chevron-back-outline"
|
||||
class="oh-activity-sidebar__header-icon me-2 oh-activity-sidebar__close"
|
||||
data-target="#activitySidebar"
|
||||
></ion-icon>
|
||||
</a>
|
||||
<span class="oh-activity-sidebar__title"> {% trans "Notes" %} </span>
|
||||
</div>
|
||||
<div class="oh-activity-sidebar__body" id="noteContainer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// This lines is used to set default selected stage for exits lines
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
<div class="oh-sticky-table__td">
|
||||
{% trans 'In' %} {{ employee.notice_period }} {{ employee.get_unit_display }}
|
||||
</div>
|
||||
<div class="oh-sticky-table__td">{{ employee.notice_period_starts }}</div>
|
||||
<div class="oh-sticky-table__td">{{ employee.notice_period_ends }}</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">{{ employee.notice_period_starts }}</div>
|
||||
<div class="oh-sticky-table__td dateformat_changer">{{ employee.notice_period_ends }}</div>
|
||||
{% if request.user.employee_get|is_any_stage_manager or perms.offboarding.change_offboarding or perms.offboarding.change_offboardingemployee %}
|
||||
<div class="oh-sticky-table__td" onclick="event.stopPropagation()">
|
||||
<form hx-get="{% url "offboarding-change-stage" %}?employee_ids={{employee.id}}"
|
||||
@@ -62,7 +62,7 @@
|
||||
title="{% trans 'Notes' %}"
|
||||
class="oh-btn oh-btn--light oh-activity-sidebar__open"
|
||||
data-target="#activitySidebar"
|
||||
hx-get="{% url 'view-offboarding-note' %}?employee_id={{ employee.id }}"
|
||||
hx-get="{% url 'view-offboarding-note' employee.id %}"
|
||||
hx-target="#noteContainer"
|
||||
style="flex: 1 0 auto; width: 20px; height: 40.68px; padding: 0"
|
||||
onclick="event.stopPropagation()"
|
||||
@@ -129,7 +129,7 @@
|
||||
{% endfor %} {% else %}
|
||||
<button
|
||||
hx-get="{% url "offboarding-assign-task" %}?employee_ids={{employee.id}}&task_id={{task.id}}"
|
||||
hx-target="#offboardingBody{{offboarding.id}}"
|
||||
hx-target="#offboardingBody{{offboarding.offboarding.id}}"
|
||||
class="oh-checkpoint-badge text-info"
|
||||
data-toggle="oh-modal-toggle"
|
||||
onclick="event.stopPropagation()"
|
||||
@@ -143,9 +143,6 @@
|
||||
</div>
|
||||
{% endif %} {% endfor %}
|
||||
<script>
|
||||
$("[data-archive-stage=true]")
|
||||
.find(".oh-sticky-table__tr[data-employee-id]")
|
||||
.hide();
|
||||
function showArchived($element) {
|
||||
let checked = $element.is(":checked");
|
||||
if (checked) {
|
||||
|
||||
@@ -21,7 +21,7 @@ urlpatterns = [
|
||||
path(
|
||||
"offboarding-change-stage", views.change_stage, name="offboarding-change-stage"
|
||||
),
|
||||
path("view-offboarding-note", views.view_notes, name="view-offboarding-note"),
|
||||
path("view-offboarding-note/<int:employee_id>/", views.view_notes, name="view-offboarding-note"),
|
||||
path("add-offboarding-note", views.add_note, name="add-offboarding-note"),
|
||||
path(
|
||||
"delete-note-attachment", views.delete_attachment, name="delete-note-attachment"
|
||||
@@ -40,6 +40,11 @@ urlpatterns = [
|
||||
views.offboarding_individual_view,
|
||||
name="offboarding-individual-view",
|
||||
),
|
||||
path(
|
||||
"offboarding-note-delete/<int:note_id>/",
|
||||
views.offboarding_note_delete,
|
||||
name="offboarding-note-delete",
|
||||
),
|
||||
path(
|
||||
"resignation-requests-view/",
|
||||
views.request_view,
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.contrib import messages
|
||||
from django.contrib.auth.models import User
|
||||
from base.context_processors import intial_notice_period
|
||||
from employee.models import Employee
|
||||
from horilla.decorators import login_required, permission_required
|
||||
from horilla.decorators import login_required, manager_can_enter, permission_required
|
||||
from base.views import paginator_qry
|
||||
from offboarding.decorators import (
|
||||
any_manager_can_enter,
|
||||
@@ -53,6 +53,7 @@ from recruitment.pipeline_grouper import group_by_queryset
|
||||
|
||||
def pipeline_grouper(filters={}, offboardings=[]):
|
||||
groups = []
|
||||
|
||||
for offboarding in offboardings:
|
||||
stages = PipelineStageFilter(
|
||||
filters, queryset=offboarding.offboardingstage_set.all()
|
||||
@@ -63,28 +64,35 @@ def pipeline_grouper(filters={}, offboardings=[]):
|
||||
all_stages_grouper.append({"grouper": stage, "list": []})
|
||||
stage_employees = PipelineEmployeeFilter(
|
||||
filters,
|
||||
OffboardingEmployee.objects.filter(stage_id=stage).order_by("-id"),
|
||||
).qs
|
||||
OffboardingEmployee.objects.filter(stage_id=stage),
|
||||
).qs.order_by("stage_id__id")
|
||||
page_name = "page" + stage.title + str(offboarding.id)
|
||||
grouper = group_by_queryset(
|
||||
employee_grouper = group_by_queryset(
|
||||
stage_employees,
|
||||
"stage_id",
|
||||
filters.get(page_name),
|
||||
page_name,
|
||||
).object_list
|
||||
data["stages"] = data["stages"] + grouper
|
||||
|
||||
data["stages"] = data["stages"] + employee_grouper
|
||||
|
||||
ordered_data = []
|
||||
existing_grouper_ids = list({item["grouper"].id for item in data["stages"]})
|
||||
for item in all_stages_grouper:
|
||||
if item["grouper"].id not in existing_grouper_ids:
|
||||
data["stages"].append(
|
||||
{
|
||||
"grouper": item["grouper"],
|
||||
"list": [],
|
||||
"dynamic_name": f'dynamic_page_page{item["grouper"].id}',
|
||||
}
|
||||
)
|
||||
|
||||
if set(existing_grouper_ids) != set(stages.values_list("id", flat=True)):
|
||||
for sg in stages:
|
||||
for grouper in data["stages"]:
|
||||
if sg != grouper["grouper"]:
|
||||
ordered_data.append(
|
||||
{"grouper": sg, "list": [], "dynamic_name": ""}
|
||||
)
|
||||
else:
|
||||
ordered_data.append(grouper)
|
||||
else:
|
||||
ordered_data = data["stages"]
|
||||
data = {
|
||||
"offboarding": offboarding,
|
||||
"stages": ordered_data,
|
||||
}
|
||||
groups.append(data)
|
||||
return groups
|
||||
|
||||
@@ -337,7 +345,7 @@ def change_stage(request):
|
||||
@any_manager_can_enter(
|
||||
"offboarding.view_offboardingnote", offboarding_employee_can_enter=True
|
||||
)
|
||||
def view_notes(request):
|
||||
def view_notes(request, employee_id=None):
|
||||
"""
|
||||
This method is used to render all the notes of the employee
|
||||
"""
|
||||
@@ -352,8 +360,9 @@ def view_notes(request):
|
||||
attachment.save()
|
||||
attachments.append(attachment)
|
||||
note.attachments.add(*attachments)
|
||||
offboarding_employee_id = request.GET["employee_id"]
|
||||
offboarding_employee_id = employee_id
|
||||
employee = OffboardingEmployee.objects.get(id=offboarding_employee_id)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"offboarding/note/view_notes.html",
|
||||
@@ -377,23 +386,35 @@ def add_note(request):
|
||||
form.instance.employee_id = employee
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return HttpResponse(
|
||||
f"""
|
||||
<div id="asfdoiioe09092u09un320" hx-get="/offboarding/view-offboarding-note?employee_id={employee.pk}"
|
||||
hx-target="#noteContainer"
|
||||
>
|
||||
</div>
|
||||
<script>
|
||||
$("#asfdoiioe09092u09un320").click()
|
||||
$(".oh-modal--show").removeClass("oh-modal--show")
|
||||
</script>
|
||||
"""
|
||||
)
|
||||
messages.success(request, _("Note added successfully"))
|
||||
return redirect("view-offboarding-note", employee_id=employee.id)
|
||||
return render(
|
||||
request, "offboarding/note/form.html", {"form": form, "employee": employee}
|
||||
request,
|
||||
"offboarding/note/view_notes.html",
|
||||
{
|
||||
"form": form,
|
||||
"employee": employee,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
@manager_can_enter(perm="offboarding.delete_offboardingNote")
|
||||
def offboarding_note_delete(request, note_id):
|
||||
"""
|
||||
This method is used to delete the offboarding note
|
||||
"""
|
||||
try:
|
||||
note = OffboardingNote.objects.get(id=note_id)
|
||||
employee = note.employee_id.id
|
||||
note.delete()
|
||||
messages.success(request, _("Note deleted"))
|
||||
except OffboardingNote.DoesNotExist:
|
||||
messages.error(request, _("Note not found."))
|
||||
|
||||
return redirect("view-offboarding-note", employee_id=employee)
|
||||
|
||||
|
||||
@login_required
|
||||
@permission_required("offboarding.delete_offboardingnote")
|
||||
def delete_attachment(request):
|
||||
@@ -521,10 +542,17 @@ def task_assign(request):
|
||||
offboarding = employees.first().stage_id.offboarding_id
|
||||
stage_forms = {}
|
||||
stage_forms[str(offboarding.id)] = StageSelectForm(offboarding=offboarding)
|
||||
groups = pipeline_grouper({}, [task.stage_id.offboarding_id])
|
||||
for item in groups:
|
||||
setattr(item["offboarding"], "stages", item["stages"])
|
||||
return render(
|
||||
request,
|
||||
"offboarding/stage/offboarding_body.html",
|
||||
{"offboarding": offboarding, "stage_forms": stage_forms},
|
||||
{
|
||||
"offboarding": groups[0],
|
||||
"stage_forms": stage_forms,
|
||||
"response_message": _("Task Assigned"),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user