[UPDT] PMS: Bulk feedback create form is updated with title suggestion
This commit is contained in:
50
pms/cbvs.py
50
pms/cbvs.py
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user