[UPDT] EMPLOYEE: Updated employee dashboard

This commit is contained in:
Horilla
2025-09-26 15:54:05 +05:30
parent fd5f624d25
commit a05378a49c
3 changed files with 238 additions and 202 deletions

View File

@@ -179,29 +179,57 @@ def get_template(request, emp_id):
@login_required
def get_mail_preview(request):
"""
This method is used to return the mail template
Returns the mail template preview as HTML.
"""
body = request.GET.get("body")
template_bdy = template.Template(body)
body = request.POST.get("body")
if not body:
return HttpResponse("No body provided", status=400)
emp_id = request.GET.get("emp_id")
if emp_id:
employee = Employee.objects.get(id=emp_id)
context = template.Context(
{
"instance": employee,
"self": request.user.employee_get,
"request": request,
}
employee_ids = request.POST.getlist("employees")
# Fetch one employee for preview if provided
employee_obj = None
if emp_id or employee_ids:
ids = [emp_id] if emp_id else employee_ids
employee_obj = Employee.objects.filter(id__in=ids).first()
if not employee_obj:
return HttpResponse("Employee not found", status=404)
# Build context
context = {
"instance": employee_obj,
"model_instance": employee_obj,
"self": getattr(request.user, "employee_get", None),
"request": request,
}
# Render template
rendered_body = template.Template(body).render(template.Context(context)) or " "
# Add preview note if multiple employees
if employee_ids and len(employee_ids) > 1 and employee_obj:
rendered_body = (
f"<p style='color:gray; font-size:13px;'>"
f"Preview shown for {employee_obj.get_full_name()}. "
f"Mail will be personalized for {len(employee_ids)} employees."
f"</p>{rendered_body}"
)
body = template_bdy.render(context) or " "
return JsonResponse({"body": body})
# Wrap in styled div
textarea_field = (
f'<div class="oh-input oh-input--textarea" '
f'style="border: solid .1px #dbd7d7; padding:5px;">{rendered_body}</div>'
)
return HttpResponse(textarea_field, content_type="text/html")
@login_required
@manager_can_enter(perm="recruitment.change_employee")
@manager_can_enter(perm="employee.change_employee")
def send_mail_to_employee(request):
"""
This method is used to send acknowledgement mail to the candidate
This method is used to send acknowledgement mail to the employee
"""
employee_id = request.POST["id"]
subject = request.POST.get("subject")

View File

@@ -1,186 +1,190 @@
{% load i18n %}
<div id="ack-message-{{employee.id}}">
</div>
<form onsubmit="$(this).closest('.oh-modal--show').removeClass('oh-modal--show')"
hx-post='{% url "send-mail-to-employee" %} ' class="oh-general__tab-target oh-profile-section"
id='ack-form-{{employee.id}}' hx-target="#ack-message-{{employee.id}}" hx-encoding="multipart/form-data">
<input type="hidden" value="{{employee.id}}" name="id">
<div class="modal-body">
{% if employee %}
<div class="oh-timeoff-modal__profile-content">
<div class="oh-profile mb-2">
<div class="oh-profile__avatar">
<img src="{{employee.get_avatar}}"
class="oh-profile__image me-2">
{% comment %} # 875 {% endcomment %}
<button hidden id="previewHxButton"
hx-post="{% url 'get-employee-mail-preview' %}?{% if employee %}emp_id={{ employee.id }}&{% endif %}"
hx-target="#preview"
hx-include="#ack-form-{{ employee.id }}"
hx-on-htmx-before-request="if (!$('#template').val()) { event.preventDefault(); }">
</button>
<div id="ack-message-{{ employee.id }}"></div>
<form id="ack-form-{{ employee.id }}" class="oh-general__tab-target oh-profile-section"
hx-post="{% url 'send-mail-to-employee' %}" hx-target="#ack-message-{{ employee.id }}"
hx-encoding="multipart/form-data" onsubmit="return handleFormSubmit(event, {{ employee.id }})">
<input type="hidden" name="id" value="{{ employee.id }}">
<div class="modal-body">
{% if employee %}
<div class="oh-timeoff-modal__profile-content">
<div class="oh-profile mb-2">
<div class="oh-profile__avatar">
<img src="{{ employee.get_avatar }}" class="oh-profile__image me-2" alt="Employee Avatar">
</div>
<div class="oh-timeoff-modal__profile-info">
<span class="oh-timeoff-modal__user fw-bold">{{ employee.get_full_name }}</span>
<span class="oh-timeoff-modal__user m-0" style="font-size: 18px; color: #4d4a4a">
{{ employee.get_job_position }} / {{ employee.get_department }}
</span>
</div>
</div>
</div>
{% endif %}
<!-- Also Send To -->
<div class="form-group mt-2">
<label for="employees"><h6>{% trans "Also send to" %}</h6></label>
<select class="oh-select oh-select-2" name="employees" id="employees" multiple {% if not employee %} required {% endif %}>
{% for emp in employees %}
<option value="{{ emp.id }}">{{ emp }}</option>
{% endfor %}
</select>
</div>
<div class="oh-timeoff-modal__profile-info">
<span class="oh-timeoff-modal__user fw-bold">{{employee.get_full_name}}</span>
<span class="oh-timeoff-modal__user m-0" style="font-size: 18px; color: #4d4a4a">
{{employee.get_job_position}} /
{{employee.get_department}}</span>
<!-- Subject -->
<div class="form-group mt-2">
<label for="subject"><h6>{% trans "Subject" %}</h6></label>
<input required type="text" name="subject" class="oh-input w-100" id="subject" placeholder="Important Reminder">
</div>
</div>
</div>
{% endif %}
<div class="form-group mt-2">
<label for="employees">
<h6>{% trans "Also send to" %}</h6>
</label>
<select class="oh-select oh-select-2" {% if not employee %} required {% endif %} name="employees" id="employees" multiple>
{% for employee in employees %}
<option value="{{employee.id}}">{{employee}}</option>
{% endfor %}
</select>
</div>
<div class="form-group mt-2">
<label for="subject">
<h6>{% trans "Subject" %}</h6>
</label>
<input required type="text" placeholder="Important Reminder" name='subject' class="oh-input w-100" id="subject"
placeholder="Subject">
</div>
<div class="form-group mt-2">
<label for="template">
<h6>{% trans "Template" %}</h6>
</label>
<select name="template" class="w-100 oh-select" id="template" onchange="templateChange($(this))">
<option value="">----</option>
{% for template in templates %}
<option value="{{template.id}}">{{template.title}}</option>
{% endfor %}
</select>
</div>
<div class="form-group mt-2">
<label for="body">
<h6>{% trans "Message Body" %}</h6>
</label>
<div class="oh-input__group">
<div class="oh-tabs__view-buttons mt-2">
<span class="tab-btn active" data-tab="write">
<span>{% trans "Write" %}</span>
</span>
<span class="tab-btn" onclick="previewMail()" data-tab="preview">
<span>{% trans "Preview" %}</span>
</span>
<!-- Template -->
<div class="form-group mt-2">
<label for="template"><h6>{% trans "Template" %}</h6></label>
<select name="template" class="w-100 oh-select" id="template" hx-on:change="loadTemplate(this)">
<option value="">----</option>
{% for template in templates %}
<option value="{{ template.id }}">{{ template.title }}</option>
{% endfor %}
</select>
</div>
<div id="write" class="oh-tabs__view active">
<textarea id="writeField" data-summernote name="body" required class="oh-input oh-input--textarea" placeholder="Type something here..."></textarea>
<!-- Message Body -->
<div class="form-group mt-2">
<label for="body"><h6>{% trans "Message Body" %}</h6></label>
<div class="oh-input__group">
<div class="oh-tabs__view-buttons mt-2">
<span class="tab-btn active" data-tab="write"><span>{% trans "Write" %}</span></span>
<span class="tab-btn" data-tab="preview" hx-on:click="$('#previewHxButton').click();"><span>{% trans "Preview" %}</span></span>
</div>
<div id="write" class="oh-tabs__view active">
<textarea id="writeField" data-summernote name="body" required
class="oh-input oh-input--textarea" placeholder="Type something here..."></textarea>
</div>
<div id="preview" class="oh-tabs__view">
<div id="previewField" class="oh-input oh-input--textarea" placeholder="preview..."></div>
</div>
</div>
</div>
<div id="preview" class="oh-tabs__view">
<div id="previewField" disabled class="oh-input oh-input--textarea" placeholder="preview..."></div>
<div class="form-hint" style="margin-top: 10px; font-size: 14px; color: #888;">
{% trans "Hint: Type '{' to get sender or receiver data" %}
</div>
<!-- Template as Attachment -->
<div class="form-group mt-2">
<label for="template_attachments"><h6>{% trans "Template as Attachment" %}</h6></label>
<select name="template_attachments" class="w-100 oh-select" id="template_attachments" multiple>
{% for template in templates %}
<option value="{{ template.id }}">{{ template.title }}</option>
{% endfor %}
</select>
</div>
<!-- Other Attachments -->
<div class="form-group mt-2">
<label for="other_attachments"><h6>{% trans "Other Attachments" %}</h6></label>
<input type="file" name="other_attachments" id="other_attachments" multiple style="display: block;">
</div>
<div id="messages"></div>
<div class="modal-footer d-flex flex-row-reverse mt-3">
<button type="submit" class="oh-btn oh-btn--secondary submit-send">{% trans "Send Mail" %}</button>
</div>
</div>
</div>
<div class="form-hint" style="margin-top: 10px; font-size: 14px; color: #888;">
{% trans "Hint: Type '{' to get sender or receiver data" %}
</div>
<div class="form-group mt-2">
<label for="template_attachments">
<h6>{% trans "Template as Attachment" %}</h6>
</label>
<select name="template_attachments" class="w-100 oh-select" id="template_attachments" multiple>
{% for template in templates %}
<option value="{{template.id}}">{{template.title}}</option>
{% endfor %}
</select>
</div>
<div class="form-group mt-2">
<label for="other_attachments">
<h6>{% trans "Other Attachments" %}</h6>
</label>
<input type="file" name="other_attachments" id="other_attachments" multiple style="display: block;">
</div>
<div class="modal-footer d-flex flex-row-reverse mt-3">
<input type="submit" class="oh-btn oh-btn--secondary submit-send" data-message-id="ack-message-{{employee.id}}"
name="submit" id="submit" onclick="sendMail()" value="{% trans 'Send Mail' %}">
</div>
</form>
<script>
button = document.querySelector('.submit-send')
button.onclick = function (event) {
var element = event.target;
var valid = true
// ---------- UTILITIES ----------
function showError(message) {
$("#messages").html(`<div class="oh-alert oh-alert--animated oh-alert--danger" role="alert">${message}</div>`);
}
function clearErrors() { $("#messages").empty(); }
if (!$('#employees').val().length && "{{employee|default:'false'|safe}}"=="false") {
valid=false
$(`#messages`).html($(`
<div class="oh-alert oh-alert--animated oh-alert--danger" role="alert">
This field is required
</div>
`));
}
else if (!$("#subject").val().length) {
valid=false
$(`#messages`).html($(`
<div class="oh-alert oh-alert--animated oh-alert--danger" role="alert">
The message subject is required
</div>
`));
}
else if (!$("#body").val().length) {
valid=false
$(`#messages`).html($(`
<div class="oh-alert oh-alert--animated oh-alert--danger" role="alert">
The message body is required
</div>
`));
}
if (valid) {
Swal.fire({
title: "Processing",
text: "Mail will sent on the background",
icon: "info"
});
}
};
$(document).ready(function () {
var selectedIds = JSON.parse($("#selectedInstances").attr("data-ids"));
$("#employees[name=employees]select[multiple]").val(selectedIds).change()
});
function templateChange(e) {
var id = e.val();
if (id.length) {
$.ajax({
type: "get",
url: `/employee/get-template/${id}/`,
data: { "instance_id": "{{employee.id}}" },
dataType: "Json",
success: function (response) {
$('#ack-form-{{employee.id}} [name="body"]').html(response.body).change()
$('#ack-form-{{employee.id}} [class="note-editable"]').html(response.body)
// ---------- VALIDATION ----------
function validateForm(formId, hasEmployee) {
clearErrors();
let valid = true;
if (!$('#employees').val().length && !hasEmployee) {
showError("Please select at least one employee.");
valid = false;
} else if (!$("#subject").val().trim()) {
showError("The message subject is required.");
valid = false;
} else {
let body = $(`#writeField`).summernote('code');
if (!body || body === '<p><br></p>') {
showError("The message body is required.");
valid = false;
}
}
});
return valid;
}
};
function previewMail(){
var id = $('#template').val();
var body = $('#ack-form-{{employee.id}} [name="body"]').val()
if (body && body.trim() !== '') {
$.ajax({
type: "get",
url: "{% url 'get-employee-mail-preview' %}" ,
data: {"body":body,"emp_id":{{employee.id}} },
dataType: "Json",
success: function (response) {
$('#previewField').html(response.body)
}
});
}
}
initializeSummernote({{employee.id}},{{ searchWords|safe }});
const buttons = document.querySelectorAll(".tab-btn");
const contents = document.querySelectorAll(".oh-tabs__view ");
buttons.forEach((button) => {
button.addEventListener("click", () => {
buttons.forEach((btn) => btn.classList.remove("active"));
button.classList.add("active");
contents.forEach((content) => content.classList.remove("active"));
document.getElementById(button.dataset.tab).classList.add("active");
// ---------- FORM SUBMIT ----------
function handleFormSubmit(event, empId) {
event.preventDefault();
const hasEmployee = "{{employee|default:'false'|safe}}" !== "false";
if (!validateForm(empId, hasEmployee)) return false;
Swal.fire({
title: "Processing",
text: "Mail will be sent in the background",
icon: "info",
timer: 1500,
showConfirmButton: false
});
return true;
}
// ---------- TEMPLATE LOAD ----------
function loadTemplate(select) {
const id = $(select).val();
if (!id) return;
$.ajax({
type: "get",
url: `/employee/get-template/${id}/`,
dataType: "json",
beforeSend: () => { $('#writeField').summernote('disable'); },
success: function(response) { $('#writeField').summernote('code', response.body); },
complete: () => { $('#writeField').summernote('enable'); }
});
}
// ---------- INIT ----------
$(document).ready(function() {
// Load preselected employees if any
let selectedIds = JSON.parse($("#selectedInstances").attr("data-ids") || "[]");
$("#employees").val(selectedIds).change();
// Initialize Summernote conditionally
{% if employee %}
initializeSummernote({{ employee.id }}, {{ searchWords|safe }});
{% else %}
if (selectedIds.length > 0) {
initializeSummernote(selectedIds[0], {{ searchWords|safe }});
}
{% endif %}
// Tab switching
document.querySelectorAll(".tab-btn").forEach((btn) => {
btn.addEventListener("click", () => {
document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active"));
btn.classList.add("active");
document.querySelectorAll(".oh-tabs__view").forEach(c => c.classList.remove("active"));
document.getElementById(btn.dataset.tab).classList.add("active");
});
});
});
</script>

View File

@@ -1,25 +1,29 @@
<style>
#enlargeImageContainer {
position: absolute;
left: -300px;
top: 100px;
height: 200px;
width: 200px;
}
#enlargeImageContainer {
position: absolute;
left: -300px;
top: 100px;
height: 200px;
width: 200px;
}
</style>
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title">
{{ policy.title }}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<h2 class="oh-modal__dialog-title">{{ policy.title }}</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body">
<p class="oh-card__footer--border-top"></p>
{{ policy.body|safe }}
<div id="attachmentContainer" class="d-flex" hx-get="{% url 'get-attachments-policy' %}?policy_id={{ policy.id }}" hx-trigger="load"></div>
<div class="oh-modal__dialog-body border rounded-md shadow-card m-[20px]">
<p class="oh-card__footer--border-top"></p>
{{ policy.body|safe }}
<div
id="attachmentContainer"
class="d-flex"
hx-get="{% url 'get-attachments-policy' %}?policy_id={{ policy.id }}"
hx-trigger="load"
></div>
<div id="enlargeImageContainer"></div>
<div id="enlargeImageContainer"></div>
</div>
<div class="h-1"></div>