[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 django import template
from django.contrib import messages
from django.http import HttpResponse
from django.shortcuts import render
from django.urls import reverse_lazy
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 employee.models import Employee
@@ -59,7 +60,7 @@ class BonusPointSettingNavView(views.HorillaNavView):
data-target="#genericModal"
"""
nav_title = _trans("Bonus Point Setting")
nav_title = _("Bonus Point Setting")
search_url = reverse_lazy("bonus-point-setting-list-view")
search_swap_target = "#listContainer"
@@ -73,7 +74,7 @@ class BonusPointSettingFormView(views.HorillaFormView):
form_class = BonusPointSettingForm
model = models.BonusPointSetting
new_display_title = _trans("Create Bonus Point Setting")
new_display_title = _("Create Bonus Point Setting")
template_name = "bonus/bonus_form.html"
def get_form_kwargs(self):
@@ -102,7 +103,7 @@ class BonusPointSettingFormView(views.HorillaFormView):
message = "Bonus Point Setting updated"
form.save()
messages.success(self.request, _trans(message))
messages.success(self.request, _(message))
return self.HttpResponse()
return super().form_valid(form)
@@ -170,22 +171,22 @@ class EmployeeBonusPointNavView(views.HorillaNavView):
data-target="#genericModal"
"""
nav_title = _trans("Employee Bonus Point ")
nav_title = _("Employee Bonus Point ")
search_url = reverse_lazy("employee-bonus-point-list-view")
search_swap_target = "#listContainer"
group_by_fields = [
("employee_id", _trans("Employee")),
("employee_id", _("Employee")),
(
"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__job_position_id", _trans("Job Position")),
("employee_id__employee_work_info__department_id", _("Department")),
("employee_id__employee_work_info__job_position_id", _("Job Position")),
(
"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
model = models.EmployeeBonusPoint
new_display_title = _trans("Create Employee Bonus Point ")
new_display_title = _("Create Employee Bonus Point ")
# template_name = "bonus/bonus_form.html"
def get_context_data(self, **kwargs):
@@ -220,7 +221,7 @@ class EmployeeBonusPointFormView(views.HorillaFormView):
if form.instance.pk:
message = "Bonus Point updated"
form.save()
messages.success(self.request, _trans(message))
messages.success(self.request, _(message))
return self.HttpResponse(
"""
<script>
@@ -285,7 +286,7 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
form_class = EmployeeFeedbackForm
model = models.Feedback
new_display_title = _trans("Share Feedback request ")
new_display_title = _("Share Feedback request ")
# template_name = "bonus/bonus_form.html"
def get_context_data(self, **kwargs):
@@ -308,7 +309,7 @@ class FeedbackEmployeeFormView(views.HorillaFormView):
)
form.cleaned_data["others_id"] = other_employees
form.save()
messages.success(self.request, _trans(message))
messages.success(self.request, _(message))
return self.HttpResponse("<script>window.location.reload()</script>")
return super().form_valid(form)
@@ -323,11 +324,20 @@ class BulkFeedbackFormView(views.HorillaFormView):
form_class = BulkFeedbackForm
model = models.Feedback
view_id = "BulkFeedbackForm"
new_display_title = _trans("Bulk Feedback request ")
new_display_title = _("Bulk Feedback request ")
template_name = "feedback/bulk_feedback_form.html"
def get_context_data(self, **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
def form_invalid(self, form: Any) -> HttpResponse:
@@ -348,8 +358,12 @@ class BulkFeedbackFormView(views.HorillaFormView):
manager_id = (
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 = {
"review_cycle": f"{cleaned_data['title']}-{employee} feedback",
"review_cycle": render_title,
"employee_id": employee,
"manager_id": manager_id,
"question_template_id": cleaned_data["question_template_id"],
@@ -392,6 +406,6 @@ class BulkFeedbackFormView(views.HorillaFormView):
feedback, cleaned_data["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 super().form_valid(form)

View File

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

View File

@@ -1,11 +1,134 @@
{% 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" %}
<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>
$(document).ready(function(){
$('#id_cyclic_feedback_period_parent_div').addClass('d-none')
$('#id_cyclic_feedback_days_count_parent_div').addClass('d-none')
})
$(document).ready(function () {
$('#id_cyclic_feedback_period_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
function getCookie(name) {
let cookieValue = null;