[ADD] RECRUITMENT : New feature for survey section to view the survey template and rearrange the questions

This commit is contained in:
Horilla
2024-09-11 11:40:50 +05:30
parent 3952a5ac27
commit 46ac82ac8c
8 changed files with 439 additions and 29 deletions

View File

@@ -851,7 +851,11 @@ class SurveyForm(forms.Form):
def __init__(self, recruitment, *args, **kwargs) -> None:
super().__init__(recruitment, *args, **kwargs)
questions = recruitment.recruitmentsurvey_set.all()
context = {"form": self, "questions": questions}
all_questions = RecruitmentSurvey.objects.none() | questions
for template in recruitment.survey_templates.all():
questions = template.recruitmentsurvey_set.all()
all_questions = all_questions | questions
context = {"form": self, "questions": all_questions.distinct()}
form = render_to_string("survey_form.html", context)
self.form = form
return
@@ -859,6 +863,22 @@ class SurveyForm(forms.Form):
# self
class SurveyPreviewForm(forms.Form):
"""
SurveyTemplateForm
"""
def __init__(self, template, *args, **kwargs) -> None:
super().__init__(template, *args, **kwargs)
all_questions = RecruitmentSurvey.objects.filter(template_id__in=[template])
context = {"form": self, "questions": all_questions.distinct()}
form = render_to_string("survey_preview_form.html", context)
self.form = form
return
# for question in questions:
# self
class TemplateForm(ModelForm):
"""
TemplateForm

View File

@@ -0,0 +1,24 @@
{% extends 'index.html' %}
{% block content %}
{% load i18n %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<div class="oh-survey_page">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<h1 class="oh-main__titlebar-title fw-bold">
{{template}} {% trans "Survey" %}
</h1>
</section>
{{form}}
</form>
</div>
{% endblock content %}

View File

@@ -23,12 +23,15 @@
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% if perms.recruitment.chane_surveytemplate %}
<li class="oh-dropdown__item">
<a href="{% url 'survey-template-preview' grouper.grouper %}" class="oh-dropdown__link">{% trans "Preview" %}</a>
</li>
{% if perms.recruitment.change_surveytemplate %}
<li class="oh-dropdown__item">
<a hx-get="{% url 'survey-template-question-add' %}?title={{grouper.grouper}}" hx-target="#templateModalBody" data-toggle="oh-modal-toggle" data-target="#templateModal" class="oh-dropdown__link">{% trans "Add Questions" %}</a>
</li>
{% endif %}
{% if perms.recruitment.chane_surveytemplate %}
{% if perms.recruitment.change_surveytemplate %}
<li class="oh-dropdown__item">
<a hx-get="{% url 'survey-template-create' %}?title={{grouper.grouper}}" hx-target="#templateModalBody" data-toggle="oh-modal-toggle" data-target="#templateModal" class="oh-dropdown__link">{% trans "Edit" %}</a>
</li>

View File

@@ -28,25 +28,26 @@
></textarea>
</div>
{% elif question.type == "options" %}
<div class="d-block oh-card">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<select
name="{{question.question}}"
class="oh-select oh-select-2--large oh-select-2 w-100 select2-hidden-accessible"
id="id_{{question.id}}"
data-select2-id="select2-data-id_employee_id1"
aria-hidden="true"
{% if question.is_mandatory %}required{% endif %}
>
<option value="">-------------------------</option>
{% for choice in question.choices %}
<option value="{{choice}}">{{choice}}</option>
{% endfor %}
</select>
<div class="d-block oh-card">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<select
name="{{question.question}}"
class="oh-select oh-select-2--large oh-select-2 w-100 select2-hidden-accessible"
id="id_{{question.id}}"
data-select2-id="select2-data-id_employee_id1"
aria-hidden="true"
{% if question.is_mandatory %}required{% endif %}
>
<option value="">-------------------------</option>
{% for choice in question.choices %}
<option value="{{choice}}">{{choice}}</option>
{% endfor %}
</select>
</div>
{% elif question.type == "multiple" %}
<div class="d-block oh-card">
{% if question.is_mandatory %}
@@ -125,12 +126,14 @@
/>
</div>
{% elif question.type == "rating" %}
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
{% include "rating_input.html" %}
<div class="d-block oh-card">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
{% include "rating_input.html" %}
</div>
{% elif question.type == "checkbox" %}
<div class="d-block oh-card">
{% if question.is_mandatory %}

View File

@@ -0,0 +1,298 @@
{% load i18n %}{% load widget_tweaks %} {% load attendancefilters %}
{% load static %}
<style>
.question-container {
padding: 0px;
}
</style>
<div class="oh-general__tab-target" id="personal">
<div class="oh-alert-container">
</div>
<div class="oh-wrapper oh-survey-ques">
<div class="row pb-5">
<div class="col-12">{{form.non_field_errors}}</div>
{% for question in questions %}
{% if question.type == "textarea" %}
<div class="d-flex oh-card mt-2" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<textarea
type="text"
name="{{question.question}}"
class="oh-input w-100"
id="id_{{qgvuestion.id}}"
{% if question.is_mandatory %}required{% endif %}
></textarea>
</div>
</div>
{% elif question.type == "options" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<select
name="{{question.question}}"
class="oh-select oh-select-2--large oh-select-2 w-100 select2-hidden-accessible"
id="id_{{question.id}}"
data-select2-id="select2-data-id_employee_id1"
aria-hidden="true"
{% if question.is_mandatory %}required{% endif %}
>
<option value="">-------------------------</option>
{% for choice in question.choices %}
<option value="{{choice}}">{{choice}}</option>
{% endfor %}
</select>
</div>
</div>
{% elif question.type == "multiple" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<select
name="multiple_choices_{{ question.question }}"
class="oh-select oh-select-2--large oh-select-2 w-100 select2-hidden-accessible"
style="height: 55px"
id="id_{{ question.id }}"
{% if question.is_mandatory %}required{% endif %}
multiple>
{% for choice in question.choices %}
<option value="{{ choice }}">{{ choice }}</option>
{% endfor %}
</select>
</div>
</div>
{% elif question.type == "percentage" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">
{{ question.question }}
<span class='text-danger'>* {% trans "Mandatory Question" %}</span>
</span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<div class="oh-container oh-container--outline my-2 w-25">
<input
type="number"
class="oh-input oh-input--small oh-input--block px-0"
style="width: 70px; text-align: center;"
min="0"
value="0"
max="100"
name="percentage_{{ question.question }}"
id="id_{{ question.id }}"
{% if question.is_mandatory %}required{% endif %}
/>
<input
type="text"
class="oh-input w-25 oh-input--small oh-input--block px-0"
value="%"
readonly
/>
</div>
</div>
</div>
{% elif question.type == "file" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<input
type="file"
class="oh-input w-25"
name="file_{{question.question}}"
id="id_{{question.id}}"
{% if question.is_mandatory %}required{% endif %}
/>
</div>
</div>
{% elif question.type == "date" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<input
type="date"
class="oh-input w-25"
name="date_{{question.question}}"
id="id_{{question.id}}"
{% if question.is_mandatory %}required{% endif %}
/>
</div>
</div>
{% elif question.type == "rating" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<div class="oh-rate">
<input type="radio" id="star5" name="rate" value="5" />
<label for="star5" title="5 Stars">5 stars</label>
<input type="radio" id="star4" name="rate" value="4" />
<label for="star4" title="4 Stars">4 stars</label>
<input type="radio" id="star3" name="rate" value="3" />
<label for="star3" title="3 Stars">3 stars</label>
<input type="radio" id="star2" name="rate" value="2" />
<label for="star2" title="2 Stars">2 stars</label>
<input type="radio" id="star1" name="rate" value="1" />
<label for="star1" title="1 Star">1 star</label>
</div>
</div>
</div>
{% elif question.type == "checkbox" %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="oh-input__group">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<div class="oh-input-picker-group">
<div class="oh-input-picker oh-input-picker--sm oh-input-picker--selected" style="width: 80px;">
<span>
<ion-icon name="checkmark-circle-outline"></ion-icon>
Yes
</span>
<input type="radio" name="{{ question.question }}" value="Yes" selected>
</div>
<div class="oh-input-picker oh-input-picker--sm" style="width: 80px;">
<span>
<ion-icon name="close-circle-outline"></ion-icon>
No
</span>
<input type="radio" name="{{ question.question }}" value="No">
</div>
</div>
</div>
</div>
{% else %}
<div class="d-flex oh-card" data-question-id="{{question.id}}">
<div class="drag-handle">
<img src="/static/images/ui/drag.svg" alt="">
</div>
<div class="d-block w-100">
{% if question.is_mandatory %}
<span class="oh-label oh-label--question">{{ question.question }}<span class='text-danger'> * {% trans "Mandatory Question" %}</span></span>
{% else %}
<span class="oh-label oh-label--question">{{ question.question }}</span>
{% endif %}
<input
type="{{question.type}}"
class="oh-input w-100"
name="{{question.question}}"
id="id_{{question.id}}"
{% if question.is_mandatory %}required{% endif %}
/>
</div>
</div>
{% endif %} {% endfor %}
</div>
{% comment %} <div class="d-flex justify-content-end align-items-center w-100 mt-4">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--w-100-resp">
{% trans "Save" %}
</button>
</div> {% endcomment %}
<div class="col-sm-12 col-md-12 col-lg-12">
<p class="small text-muted text-center">
&copy; {% now 'Y' %} {{white_label_company_name}}. All rights resevered.
</p>
</div>
</div>
</div>
</div>
</div>
<script>
$(function () {
$('.pb-5').sortable({
start: function (event, ui) {
ui.item.css({
"border": "1px solid hsl(8, 77%, 56%)",
"transform": "scale(1.01)",
"cursor": "grabbing"
});
},
stop: function (event, ui) {
ui.item.css({
"background-color": "",
"cursor": "grab",
"border": "",
"transform": ""
});
// Gather the necessary question data (e.g., question ID and new order)
var questionId = ui.item.data('question-id');
var newPosition = ui.item.index(); // Get the new position of the item in the list
// Make an AJAX request to send the updated order of the questions
$.ajax({
url: '{% url "update-question-order" %}', // Replace with your actual endpoint URL
method: 'POST',
data: {
'question_id': questionId, // Send the question ID
'new_position': newPosition, // Send the new position
'csrfmiddlewaretoken': '{{ csrf_token }}' // CSRF token for security if using Django
},
success: function(response) {
// Check if the server response indicates success
if (response.success) {
// Display the success message
$('.oh-alert-container').html('<div class="oh-alert oh-alert--animated oh-alert--success">' + response.message + '</div>');
}
},
error: function(xhr, status, error) {
// Handle error response
$('.oh-alert-container').html('<div class="oh-alert oh-alert--animated oh-alert--danger">' + 'Failed to update question order.' + '</div>');
}
});
}
});
});
</script>

View File

@@ -323,6 +323,16 @@ urlpatterns = [
views.stage_sequence_update,
name="stage-sequence-update",
),
path(
"survey-template-preview/<str:title>/",
recruitment.views.surveys.survey_preview,
name="survey-template-preview",
),
path(
"update-question-order",
recruitment.views.surveys.question_order_update,
name="update-question-order",
),
path(
"recruitment-application-survey",
recruitment.views.surveys.survey_form,

View File

@@ -13,7 +13,7 @@ from django.core import serializers
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models import ProtectedError
from django.http import HttpResponse, HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import redirect, render
from django.utils.translation import gettext_lazy as _
@@ -30,6 +30,7 @@ from recruitment.forms import (
ApplicationForm,
QuestionForm,
SurveyForm,
SurveyPreviewForm,
TemplateForm,
)
from recruitment.models import (
@@ -56,6 +57,56 @@ def survey_form(request):
return render(request, "survey/form.html", {"form": form})
def survey_preview(request, title):
"""
Used to render survey form to the candidate
"""
# title = request.GET.get("title")
template = SurveyTemplate.objects.get(title=str(title))
form = SurveyPreviewForm(template=template).form
return render(
request,
"survey/survey_preview.html",
{"form": form, "template": template},
)
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def question_order_update(request):
if request.method == "POST":
# Extract data from the request
question_id = request.POST.get("question_id")
new_position = int(request.POST.get("new_position"))
qs = RecruitmentSurvey.objects.get(id=question_id)
print("____OLD POSITION______")
print(qs.sequence)
print("____NEW POSITION______")
print(new_position)
if qs.sequence > new_position:
new_position = new_position
if qs.sequence <= new_position:
new_position = new_position - 1
old_qs = RecruitmentSurvey.objects.filter(sequence=new_position)
for i in old_qs:
i.sequence = new_position + 1
i.save()
qs.sequence = int(new_position)
qs.save()
return JsonResponse(
{"success": True, "message": "Question order updated successfully"}
)
return JsonResponse({"error": "Invalid request method"}, status=405)
def candidate_survey(request):
"""
Used to render survey form to the candidate

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve" class=""><g><clipPath id="a"><path d="M0 0h24v24H0z" fill="#000000" opacity="1" data-original="#000000" class=""></path></clipPath><g clip-path="url(#a)"><path fill="#939393" d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" opacity="1" data-original="#323232" class=""></path></g></g></svg>

After

Width:  |  Height:  |  Size: 754 B