From d277268126c26a8e46e759a17f399281797dc654 Mon Sep 17 00:00:00 2001 From: Horilla Date: Thu, 8 Aug 2024 11:43:15 +0530 Subject: [PATCH] [UPDT] RECRUITMENT: Added confirmation to candidate stage change --- .../components/candidate_stage_component.html | 34 ++- .../candidate_kanban_components.html | 2 + recruitment/templates/pipeline/nav.html | 194 +++++++++++------- .../templatetags/recruitmentfilters.py | 9 + recruitment/views/views.py | 3 + templates/index.html | 31 +++ 6 files changed, 178 insertions(+), 95 deletions(-) diff --git a/recruitment/templates/pipeline/components/candidate_stage_component.html b/recruitment/templates/pipeline/components/candidate_stage_component.html index 717769409..b877e70ec 100644 --- a/recruitment/templates/pipeline/components/candidate_stage_component.html +++ b/recruitment/templates/pipeline/components/candidate_stage_component.html @@ -207,24 +207,22 @@ {% endif %}
- - - + +
diff --git a/recruitment/templates/pipeline/kanban_components/candidate_kanban_components.html b/recruitment/templates/pipeline/kanban_components/candidate_kanban_components.html index 112892823..9b0ec1266 100644 --- a/recruitment/templates/pipeline/kanban_components/candidate_kanban_components.html +++ b/recruitment/templates/pipeline/kanban_components/candidate_kanban_components.html @@ -8,6 +8,8 @@ style="cursor: pointer;overflow: visible;" data-candidate = "{{cand.name}}" data-job-position ="{{cand.job_position_id}}" + data-pre_stage_id ="{{cand.stage_id.id}}" + data-stage_order = '{{cand.recruitment_id.ordered_stages|to_json|safe}}' onclick="window.location.href = `{% url 'candidate-view-individual' cand.id %}`" >
stage.id == currentStageId); + var targetStageIndex = parsedStageOrder.findIndex(stage => stage.id == targetStageId); + return targetStageIndex === currentStageIndex + 1 || currentStageIndex === targetStageIndex ; + } + htmx.onLoad(function (content) { var sortables = content.querySelectorAll(".hx-sortable"); for (var i = 0; i < sortables.length; i++) { var sortable = sortables[i]; window.candidateCurrentStage = undefined; - $(sortable).droppable({ - drop: function (event, ui) { - stageId = $(this) - .closest(".candidate-table") - .attr("data-stage-id"); - row = $(ui.draggable); - candidateId = row.attr("data-candidate-id"); - currentStage = row.find('[name="stage_id"]').val(); - if (stageId != window.candidateCurrentStage) { - array = $(this).find( - "input[type=text][name=order]:hidden" - ); - values = []; - setTimeout(function () { - for ( - let index = 0; - index < array.length; - index++ - ) { - values.push($(array[index]).val()); - } - $.ajax({ - type: "get", - url: "{% url 'update-candidate-stage-and-sequence' %}", - data: { - stage_id: stageId, - candidate_id: candidateId, - order: values, - }, - success: function (response) { - row.find('[name="stage_id"]').val( - stageId - ); - Toast.fire({ - icon: "success", - title: '{% trans "Sequence updated" %}', - position: "top-end", - }); - if (response.message) { - Swal.fire({ - title: response.message, - text: `Total vacancy is ${response.vacancy}.`, - icon: "info", - confirmButtonText: "Ok", - }); - } - }, - error: function (xhr, status, error) { - Toast.fire({ - icon: "error", - title: '{% trans "Something went wrong" %}', - position: "top-end", - }); - }, - }); - }, 100); - } - }, - }); + $(sortable).sortable({ connectWith: ".hx-sortable", animation: 150, ghostClass: "blue-background-class", items: "> :not(.htmx-indicator)", start: function (event, ui) { - row = $(ui.item); - candidateCurrentStage = row - .closest(".candidate-table") - .attr("data-stage-id"); + var row = $(ui.item); + window.candidateCurrentStage = row.closest(".candidate-table").attr("data-stage-id"); + }, + stop: function (event, ui) { + var row = $(ui.item); + var candidateId = row.attr("data-candidate-id"); + var currentStageId = parseInt($(this).closest(".candidate-table").attr("data-stage-id")); + var targetStageId = parseInt(row.find('[name="stage_id"]').val()); + + var stageOrderJson = row.attr("data-stage_order"); + + if (stageOrderJson) { + var parsedStageOrder = JSON.parse(stageOrderJson); + var preStageId = row.attr("data-pre_stage_id"); + var preStage = parsedStageOrder.find(stage => stage.id == preStageId); + var stageOrder = parsedStageOrder.map(stage => stage.id); + + if (!isNextStage(currentStageId, targetStageId, parsedStageOrder)) { + if (sessionStorage.getItem('showRecuitmentKanbanConfirmation') !== 'false') { + Swal.fire({ + title: "Confirm Stage Change", + text: "The candidate is not being moved to the next stage. Do you want to proceed?", + html: ` +

The candidate is not being moved to the next stage. Do you want to proceed?

+ + `, + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#008000", + confirmButtonText: "Confirm", + preConfirm: () => { + const doNotShowAgain = Swal.getPopup().querySelector('#doNotShowAgain').checked; + if (doNotShowAgain) { + sessionStorage.setItem('showRecuitmentKanbanConfirmation', 'false'); + } + } + }).then((result) => { + if (result.isConfirmed) { + handleValidDrop(targetStageId, candidateId, row, sortable); + } else { + $(sortable).sortable('cancel'); + } + }); + } else { + handleValidDrop(targetStageId, candidateId, row, sortable); + } + + } else { + handleValidDrop(targetStageId, candidateId, row, sortable); + } + } else { + handleValidDrop(targetStageId, candidateId, row, sortable); + } }, update: function (event, ui) { - array = $(this).find( - "input[type=text][name=order]:hidden" - ); - stageSelect = $(ui.item).find("[name=stage_id]"); - stageId = $(this).attr("data-stage-id"); - parent = $(this).parent(); + var array = $(this).find("input[type=text][name=order]:hidden"); + var stageSelect = $(ui.item).find("[name=stage_id]"); + var stageId = $(this).attr("data-stage-id"); + var parent = $(this).parent(); + if (stageId != stageSelect.val()) { stageSelect.val(stageId); - stageTitle = $( - `[name=stage_id] option[value=${stageId}]:first` - ).html(); - stageSelect - .next() - .find(".select2-selection__rendered") - .html(stageTitle); + var stageTitle = $(`[name=stage_id] option[value=${stageId}]:first`).html(); + stageSelect.next().find(".select2-selection__rendered").html(stageTitle); } - values = []; + + var values = []; for (let index = 0; index < array.length; index++) { values.push($(array[index]).val()); } + $.ajax({ type: "get", url: "{% url 'update-candidate-sequence' %}", @@ -523,7 +518,52 @@ }); } }); + + function handleValidDrop(stageId, candidateId, row, sortable) { + if (stageId != window.candidateCurrentStage) { + var array = $(sortable).find("input[type=text][name=order]:hidden"); + var values = []; + setTimeout(function () { + for (let index = 0; index < array.length; index++) { + values.push($(array[index]).val()); + } + $.ajax({ + type: "get", + url: "{% url 'update-candidate-stage-and-sequence' %}", + data: { + stage_id: stageId, + candidate_id: candidateId, + order: values, + }, + success: function (response) { + row.find('[name="stage_id"]').val(stageId); + Toast.fire({ + icon: "success", + title: '{% trans "Sequence updated" %}', + position: "top-end", + }); + if (response.message) { + Swal.fire({ + title: response.message, + text: `Total vacancy is ${response.vacancy}.`, + icon: "info", + confirmButtonText: "Ok", + }); + } + }, + error: function (xhr, status, error) { + Toast.fire({ + icon: "error", + title: '{% trans "Something went wrong" %}', + position: "top-end", + }); + }, + }); + }, 100); + } + } }); + $(document).on("htmx:beforeRequest", function (event) { var target = event.target; diff --git a/recruitment/templatetags/recruitmentfilters.py b/recruitment/templatetags/recruitmentfilters.py index 532e8d150..1c4016d43 100644 --- a/recruitment/templatetags/recruitmentfilters.py +++ b/recruitment/templatetags/recruitmentfilters.py @@ -5,6 +5,7 @@ This module is used to write custom template filters. """ +import json import uuid from django import template @@ -155,3 +156,11 @@ def pipeline_grouper(grouper: dict = {}): This method is used itemize the dictionary """ return grouper["title"], grouper["stages"] + + +@register.filter(name="to_json") +def to_json(value): + ordered_list = [ + {"id": val.id, "stage": val.stage, "type": val.stage_type} for val in value + ] + return json.dumps(ordered_list) diff --git a/recruitment/views/views.py b/recruitment/views/views.py index d38c4119e..b7eb2960e 100644 --- a/recruitment/views/views.py +++ b/recruitment/views/views.py @@ -825,6 +825,9 @@ def candidate_stage_update(request, cand_id): Args: id : candidate_id """ + print( + "heloooooooooooooooooooooooooooooooooooooooooooooooooo000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) stage_id = request.POST["stageId"] candidate_obj = Candidate.objects.get(id=cand_id) history_queryset = candidate_obj.history_set.all().first() diff --git a/templates/index.html b/templates/index.html index f8f708b52..59a1f0113 100755 --- a/templates/index.html +++ b/templates/index.html @@ -679,6 +679,37 @@ }, }); } + function checkSequence(element) { + var preStageId = $(element).data("stage_id") + var canIds = $(element).data("cand_id") + var stageOrderJson = $(element).attr("data-stage_order") + var stageId = $(element).val() + + var parsedStageOrder = JSON.parse(stageOrderJson); + + var stage = parsedStageOrder.find(stage => stage.id == stageId); + var preStage = parsedStageOrder.find(stage => stage.id == preStageId); + var stageOrder = parsedStageOrder.map(stage => stage.id); + + if (stageOrder.indexOf(parseInt(stageId)) != stageOrder.indexOf(parseInt(preStageId)) + 1 && stage.type != "cancelled" ) { + Swal.fire({ + title: "Confirm", + text: `Are you sure to change the candidate from ${preStage.stage} stage to ${stage.stage} stage`, + icon: 'info', + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + updateCandStage(canIds,stageId,preStageId) + } + }); + } + else { + updateCandStage(canIds,stageId,preStageId) + } + }