[UPDT] PMS: Bulk feedback create form is updated with title suggestion

This commit is contained in:
Horilla
2025-05-09 15:02:03 +05:30
parent 046f0bf211
commit 0fafc7c27f
3 changed files with 160 additions and 22 deletions

View File

@@ -1,11 +1,12 @@
from typing import Any from typing import Any
from django import template
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _trans from django.utils.translation import gettext_lazy as _
from base.methods import filter_own_and_subordinate_recordes, is_reportingmanager from base.methods import filter_own_and_subordinate_recordes, is_reportingmanager
from employee.models import Employee from employee.models import Employee
@@ -59,7 +60,7 @@ class BonusPointSettingNavView(views.HorillaNavView):
data-target="#genericModal" data-target="#genericModal"
""" """
nav_title = _trans("Bonus Point Setting") nav_title = _("Bonus Point Setting")
search_url = reverse_lazy("bonus-point-setting-list-view") search_url = reverse_lazy("bonus-point-setting-list-view")
search_swap_target = "#listContainer" search_swap_target = "#listContainer"
@@ -73,7 +74,7 @@ class BonusPointSettingFormView(views.HorillaFormView):
form_class = BonusPointSettingForm form_class = BonusPointSettingForm
model = models.BonusPointSetting model = models.BonusPointSetting
new_display_title = _trans("Create Bonus Point Setting") new_display_title = _("Create Bonus Point Setting")
template_name = "bonus/bonus_form.html" template_name = "bonus/bonus_form.html"
def get_form_kwargs(self): def get_form_kwargs(self):
@@ -102,7 +103,7 @@ class BonusPointSettingFormView(views.HorillaFormView):
message = "Bonus Point Setting updated" message = "Bonus Point Setting updated"
form.save() form.save()
messages.success(self.request, _trans(message)) messages.success(self.request, _(message))
return self.HttpResponse() return self.HttpResponse()
return super().form_valid(form) return super().form_valid(form)
@@ -170,22 +171,22 @@ class EmployeeBonusPointNavView(views.HorillaNavView):
data-target="#genericModal" data-target="#genericModal"
""" """
nav_title = _trans("Employee Bonus Point ") nav_title = _("Employee Bonus Point ")
search_url = reverse_lazy("employee-bonus-point-list-view") search_url = reverse_lazy("employee-bonus-point-list-view")
search_swap_target = "#listContainer" search_swap_target = "#listContainer"
group_by_fields = [ group_by_fields = [
("employee_id", _trans("Employee")), ("employee_id", _("Employee")),
( (
"employee_id__employee_work_info__reporting_manager_id", "employee_id__employee_work_info__reporting_manager_id",
_trans("Reporting Manager"), _("Reporting Manager"),
), ),
("employee_id__employee_work_info__department_id", _trans("Department")), ("employee_id__employee_work_info__department_id", _("Department")),
("employee_id__employee_work_info__job_position_id", _trans("Job Position")), ("employee_id__employee_work_info__job_position_id", _("Job Position")),
( (
"employee_id__employee_work_info__employee_type_id", "employee_id__employee_work_info__employee_type_id",
_trans("Employement Type"), _("Employement Type"),
), ),
("employee_id__employee_work_info__company_id", _trans("Company")), ("employee_id__employee_work_info__company_id", _("Company")),
] ]
@@ -198,7 +199,7 @@ class EmployeeBonusPointFormView(views.HorillaFormView):
form_class = EmployeeBonusPointForm form_class = EmployeeBonusPointForm
model = models.EmployeeBonusPoint model = models.EmployeeBonusPoint
new_display_title = _trans("Create Employee Bonus Point ") new_display_title = _("Create Employee Bonus Point ")
# template_name = "bonus/bonus_form.html" # template_name = "bonus/bonus_form.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@@ -220,7 +221,7 @@ class EmployeeBonusPointFormView(views.HorillaFormView):
if form.instance.pk: if form.instance.pk:
message = "Bonus Point updated" message = "Bonus Point updated"
form.save() form.save()
messages.success(self.request, _trans(message)) messages.success(self.request, _(message))
return self.HttpResponse( return self.HttpResponse(
""" """
<script> <script>
@@ -285,7 +286,7 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
form_class = EmployeeFeedbackForm form_class = EmployeeFeedbackForm
model = models.Feedback model = models.Feedback
new_display_title = _trans("Share Feedback request ") new_display_title = _("Share Feedback request ")
# template_name = "bonus/bonus_form.html" # template_name = "bonus/bonus_form.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@@ -308,7 +309,7 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
) )
form.cleaned_data["others_id"] = other_employees form.cleaned_data["others_id"] = other_employees
form.save() form.save()
messages.success(self.request, _trans(message)) messages.success(self.request, _(message))
return self.HttpResponse("<script>window.location.reload()</script>") return self.HttpResponse("<script>window.location.reload()</script>")
return super().form_valid(form) return super().form_valid(form)
@@ -323,11 +324,20 @@ class BulkFeedbackFormView(views.HorillaFormView):
form_class = BulkFeedbackForm form_class = BulkFeedbackForm
model = models.Feedback model = models.Feedback
view_id = "BulkFeedbackForm" view_id = "BulkFeedbackForm"
new_display_title = _trans("Bulk Feedback request ") new_display_title = _("Bulk Feedback request ")
template_name = "feedback/bulk_feedback_form.html" template_name = "feedback/bulk_feedback_form.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
hints = {
"Employee|Full name": "employee.get_full_name",
"Employee|Email": "employee.get_mail",
"Employee|Employee Type": "employee.get_employee_type",
"Employee|Work Type": "employee.get_work_type",
"Employee|Company": "employee.get_company",
"Employee|Job position": "employee.get_job_position",
}
context["hints"] = hints
return context return context
def form_invalid(self, form: Any) -> HttpResponse: def form_invalid(self, form: Any) -> HttpResponse:
@@ -348,8 +358,12 @@ class BulkFeedbackFormView(views.HorillaFormView):
manager_id = ( manager_id = (
reporting_manager if cleaned_data["include_manager"] else None reporting_manager if cleaned_data["include_manager"] else None
) )
title_template = cleaned_data["title"]
temp = template.Template(title_template)
title_context = template.Context({"employee": employee})
render_title = temp.render(title_context)
data = { data = {
"review_cycle": f"{cleaned_data['title']}-{employee} feedback", "review_cycle": render_title,
"employee_id": employee, "employee_id": employee,
"manager_id": manager_id, "manager_id": manager_id,
"question_template_id": cleaned_data["question_template_id"], "question_template_id": cleaned_data["question_template_id"],
@@ -392,6 +406,6 @@ class BulkFeedbackFormView(views.HorillaFormView):
feedback, cleaned_data["other_employees"] feedback, cleaned_data["other_employees"]
) )
feedback.others_id.add(*other_employees) feedback.others_id.add(*other_employees)
messages.success(self.request, _trans(message)) messages.success(self.request, _(message))
return self.HttpResponse("<script>window.location.reload()</script>") return self.HttpResponse("<script>window.location.reload()</script>")
return super().form_valid(form) return super().form_valid(form)

View File

@@ -1288,6 +1288,7 @@ class BulkFeedbackForm(HorillaModelForm):
) )
self.fields["status"].initial = "Not Started" self.fields["status"].initial = "Not Started"
self.fields["cyclic_feedback"].widget.attrs["onchange"] = "cyclicFeedback()" self.fields["cyclic_feedback"].widget.attrs["onchange"] = "cyclicFeedback()"
self.fields["title"].widget.attrs["autocomplete"] = "off"
def clean(self): def clean(self):
cleaned_data = super().clean() cleaned_data = super().clean()

View File

@@ -1,11 +1,134 @@
{% load static %} {% load static %}
<style>
.note-hint-popover {
position: absolute;
z-index: 1000;
display: none;
width: 220px;
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
font-size: 14px;
border-radius: 4px;
}
.note-popover-arrow {
display: none;
}
.note-children-container {
max-height: 200px;
overflow-y: auto;
}
.note-hint-item {
padding: 6px 10px;
cursor: pointer;
}
.note-hint-item.active,
.note-hint-item:hover {
background-color: #f0f0f0;
}
.note-hint-item.active {
background-color: #d0e7ff; /* light blue */
}
</style>
{% include "generic/horilla_form.html" %} {% include "generic/horilla_form.html" %}
<div class="note-popover bottom note-hint-popover" id="hint-popover">
<div class="note-popover-arrow"></div>
<div class="popover-content note-children-container" id="hint-content"></div>
</div>
<script> <script>
$(document).ready(function(){ $(document).ready(function () {
$('#id_cyclic_feedback_period_parent_div').addClass('d-none') $('#id_cyclic_feedback_period_parent_div').addClass('d-none');
$('#id_cyclic_feedback_days_count_parent_div').addClass('d-none') $('#id_cyclic_feedback_days_count_parent_div').addClass('d-none');
})
let selectedIndex = -1;
const input = document.querySelector('[name=title]');
const popover = document.getElementById('hint-popover');
const content = document.getElementById('hint-content');
const suggestion = {{ hints|safe }};
const hints = Object.keys(suggestion);
input.addEventListener('input', function (e) {
const cursorPos = input.selectionStart;
const value = input.value.slice(0, cursorPos);
const match = value.match(/\{[^}]*$/);
if (match) {
const query = match[0].slice(1); // after '{'
const filtered = hints.filter(h => h.toLowerCase().includes(query.toLowerCase()));
if (filtered.length > 0) {
selectedIndex = 0;
renderHints(filtered, cursorPos);
} else {
popover.style.display = 'none';
}
} else {
popover.style.display = 'none';
}
});
input.addEventListener('keydown', function (e) {
const items = content.querySelectorAll('.note-hint-item');
if (!items.length || popover.style.display === 'none') return;
if (e.key === 'ArrowDown') {
e.preventDefault();
selectedIndex = (selectedIndex + 1) % items.length;
updateSelection(items);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
selectedIndex = (selectedIndex - 1 + items.length) % items.length;
updateSelection(items);
} else if (e.key === 'Enter') {
if (selectedIndex >= 0 && selectedIndex < items.length) {
e.preventDefault();
const selectedItem = items[selectedIndex];
const word = suggestion[selectedItem.textContent];
const cursorPos = input.selectionStart;
const before = input.value.slice(0, cursorPos).replace(/\{[^}]*$/, '{'+'{' + word +'}'+'}');
const after = input.value.slice(cursorPos);
input.value = before + after;
popover.style.display = 'none';
}
}
});
function renderHints(filtered, cursorPos) {
content.innerHTML = filtered.map((h, i) =>
`<div class="note-hint-item ${i === selectedIndex ? 'active' : ''}">${h}</div>`
).join('');
const rect = input.getBoundingClientRect();
popover.style.left = `${rect.left + window.scrollX}px`;
popover.style.top = `${rect.bottom + window.scrollY}px`;
popover.style.display = 'block';
content.querySelectorAll('.note-hint-item').forEach((item, i) => {
item.addEventListener('mousedown', () => {
const word = suggestion[item.textContent];
const cursorPos = input.selectionStart;
const before = input.value.slice(0, cursorPos).replace(/\{[^}]*$/, '{'+'{' + word +'}'+'}');
const after = input.value.slice(cursorPos);
input.value = before + after;
popover.style.display = 'none';
});
});
}
function updateSelection(items) {
items.forEach((item, i) => {
item.classList.toggle('active', i === selectedIndex);
if (i === selectedIndex) {
item.scrollIntoView({ block: 'nearest' });
}
});
}
});
// this function is used to generate csrf token // this function is used to generate csrf token
function getCookie(name) { function getCookie(name) {
let cookieValue = null; let cookieValue = null;