[ADD] RECRUITMENT: Bulk mail send feature

This commit is contained in:
Horilla
2024-03-29 10:05:42 +05:30
parent fe94b01b51
commit 1f17e59860
5 changed files with 236 additions and 177 deletions

View File

@@ -75,7 +75,7 @@
data-job-position="{{cand.job_position_id}}">
<div class="oh-sticky-table__sd" style="z-index: 11 !important;" onclick="event.stopPropagation()">
<div class="centered-div">
<input type="checkbox" id="65"
<input type="checkbox" id="{{cand.id}}"
class="oh-input candidate-checkbox oh-input__checkbox stage-candidate-row"
onchange="highlightRow($(this));
if (!$(this).is(':checked')) {

View File

@@ -53,6 +53,12 @@
class="oh-dropdown__link">{% trans "Edit" %}</a>
</li>
<li class="oh-dropdown__item" style="cursor: pointer;">
<a hx-get='{% url "send-mail" %}?stage_id={{stage.id}}' hx-target="#updateStageModalBody"
data-toggle="oh-modal-toggle" data-target="#updateStageModal"
class="oh-dropdown__link">{% trans "Bulk mail" %}</a>
</li>
{% endif %}
{% if perms.recruitment.delete_stage %}
<li class="oh-dropdown__item">

View File

@@ -1,136 +1,163 @@
{% load i18n %}
<div id="ack-message-{{cand.id}}">
</div>
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="sendMailModalLabel"><h5>{% trans 'Send Mail' %}</h5></span>
<button class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
</div>
<div class="oh-modal__dialog-body">
<form onsubmit="$(this).closest('.oh-modal--show').removeClass('oh-modal--show')"
hx-post='{% url "send-acknowledgement" %} '
hx-swap="none"
class="oh-general__tab-target oh-profile-section"
id='ack-form-{{cand.id}}'
hx-target="#ack-message-{{cand.id}}"
hx-encoding="multipart/form-data">
<input type="hidden" value="{{cand.id}}" name="id">
<div class="modal-body">
<div class="oh-timeoff-modal__profile-content">
<div class="oh-profile mb-2">
<div class="oh-profile__avatar">
<img src="https://ui-avatars.com/api/?name={{cand.name}}&amp;background=random"
class="oh-profile__image me-2">
</div>
<div class="oh-timeoff-modal__profile-info">
<span class="oh-timeoff-modal__user fw-bold">{{cand.name}}</span>
<span class="oh-timeoff-modal__user m-0" style="font-size: 18px; color: #4d4a4a">
{{cand.job_position_id.job_position}} /
{{cand.recruitment_id}}</span>
</div>
</div>
</div>
<div class="form-group mt-2">
<label for="to">
<h6>{% trans "To" %}</h6>
</label>
<input required type="text" value="{{cand.email}}" name='to' class="oh-input w-100" id="to"
placeholder="Subject">
</div>
<div class="form-group mt-2">
<label for="subject">
<h6>{% trans "Subject" %}</h6>
</label>
<input required type="text" placeholder="Congrats..." 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">
<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>
<textarea hidden data-summernote name="body" required class="oh-input w-100" id="body" cols="30"
rows="2"></textarea>
</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-{{cand.id}}"
name="submit" id="submit" onclick="sendMail()" value="{% trans 'Send Mail' %}">
</div>
</form>
</div>
<script>
button = document.querySelector('.submit-send')
button.onclick = function (event) {
var element = event.target;
var valid = true
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 () {
$("#template").change(function (e) {
var id = $(this).val();
if (id.length) {
$.ajax({
type: "get",
url: `/recruitment/get-template/${id}/`,
data: { "candidate_id": "{{cand.id}}" },
dataType: "Json",
success: function (response) {
console.log(response.body);
$('#ack-form-{{cand.id}} [name="body"]').html(response.body).change()
$('#ack-form-{{cand.id}} [class="note-editable"]').html(response.body)
}
});
}
});
});
{% load i18n %}
<div id="ack-message-{{cand.id}}">
</div>
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="sendMailModalLabel"><h5>{% trans 'Send Mail' %}</h5></span>
<button class="oh-modal__close" aria-label="Close"><ion-icon name="close-outline"></ion-icon></button>
</div>
<div class="oh-modal__dialog-body">
<form onsubmit="$(this).closest('.oh-modal--show').removeClass('oh-modal--show')"
hx-post='{% url "send-acknowledgement" %} '
hx-swap="none"
class="oh-general__tab-target oh-profile-section"
id='ack-form-{{cand.id}}'
hx-target="#ack-message-{{cand.id}}"
hx-encoding="multipart/form-data">
<input type="hidden" value="{{cand.id}}" name="id">
<div class="modal-body">
{% if cand %}
<div class="oh-timeoff-modal__profile-content">
<div class="oh-profile mb-2">
<div class="oh-profile__avatar">
<img src="https://ui-avatars.com/api/?name={{cand.name}}&amp;background=random"
class="oh-profile__image me-2">
</div>
<div class="oh-timeoff-modal__profile-info">
<span class="oh-timeoff-modal__user fw-bold">{{cand.name}}</span>
<span class="oh-timeoff-modal__user m-0" style="font-size: 18px; color: #4d4a4a">
{{cand.job_position_id.job_position}} /
{{cand.recruitment_id}}</span>
</div>
</div>
</div>
<div class="form-group mt-2">
<label for="to">
<h6>{% trans "To" %}</h6>
</label>
<input required type="text" value="{{cand.email}}" name='to' class="oh-input w-100" id="to"
placeholder="Subject">
</div>
{% endif %}
<div class="form-group mt-2">
<label for="candidates">
<h6>{% trans "Also send to" %}</h6>
</label>
<select class="oh-select oh-select-2" {% if not cand %} required {% endif %} name="candidates" id="candidates" multiple>
{% for cand in candidates %}
<option value="{{cand.id}}">{{cand}}</option>
{% endfor %}
</select>
</div>
<div class="form-group mt-2">
<label for="subject">
<h6>{% trans "Subject" %}</h6>
</label>
<input required type="text" placeholder="Congrats..." 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">
<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>
<textarea hidden data-summernote name="body" required class="oh-input w-100" id="body" cols="30"
rows="2"></textarea>
</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-{{cand.id}}"
name="submit" id="submit" onclick="sendMail()" value="{% trans 'Send Mail' %}">
</div>
</form>
</div>
<script>
{% if stage_id %}
$(document).ready(function () {
var idsArray = $("#candidateContainer{{stage_id}}")
.find(".candidate-checkbox[type=checkbox]:checked")
.map(function() {
console.log($(this))
return this.id;
}).get();
$("#candidates").val(idsArray).change()
});
{% else %}
var selectedIds = JSON.parse($("#selectedInstances").attr("data-ids"));
$("#candidates[name=candidates]select[multiple]").val(selectedIds).change()
{% endif %}
</script>
<script>
button = document.querySelector('.submit-send')
button.onclick = function (event) {
var element = event.target;
var valid = true
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 () {
$("#template").change(function (e) {
var id = $(this).val();
if (id.length) {
$.ajax({
type: "get",
url: `/recruitment/get-template/${id}/`,
data: { "candidate_id": "{{cand.id}}" },
dataType: "Json",
success: function (response) {
console.log(response.body);
$('#ack-form-{{cand.id}} [name="body"]').html(response.body).change()
$('#ack-form-{{cand.id}} [class="note-editable"]').html(response.body)
}
});
}
});
});
</script>

View File

@@ -198,6 +198,7 @@ urlpatterns = [
name="note-delete-individual",
),
path("send-mail/<int:cand_id>/", views.form_send_mail, name="send-mail"),
path("send-mail/", views.form_send_mail, name="send-mail"),
path("candidate-view/", views.candidate_view, name="candidate-view"),
path(
"candidate-filter-view",

View File

@@ -1426,16 +1426,32 @@ def candidate_history(request, cand_id):
@login_required
@hx_request_required
@manager_can_enter(perm="recruitment.change_candidate")
def form_send_mail(request, cand_id):
def form_send_mail(request, cand_id=None):
"""
This method is used to render the bootstrap modal content body form
"""
candidate_obj = Candidate.objects.get(id=cand_id)
candidate_obj = None
stage_id = None
if request.GET.get("stage_id"):
stage_id = eval(request.GET.get("stage_id"))
if cand_id:
candidate_obj = Candidate.objects.get(id=cand_id)
candidates = Candidate.objects.all()
if stage_id and isinstance(stage_id, int):
candidates = candidates.filter(stage_id__id=stage_id)
else:
stage_id = None
templates = RecruitmentMailTemplate.objects.all()
return render(
request,
"pipeline/pipeline_components/send_mail.html",
{"cand": candidate_obj, "templates": templates},
{
"cand": candidate_obj,
"templates": templates,
"candidates": candidates,
"stage_id": stage_id,
},
)
@@ -1445,58 +1461,67 @@ def send_acknowledgement(request):
"""
This method is used to send acknowledgement mail to the candidate
"""
candidate_id = request.POST["id"]
candidate_id = request.POST.get("id")
subject = request.POST.get("subject")
bdy = request.POST.get("body")
candidate_ids = request.POST.getlist("candidates")
candidates = Candidate.objects.filter(id__in=candidate_ids)
other_attachments = request.FILES.getlist("other_attachments")
attachments = [
(file.name, file.read(), file.content_type) for file in other_attachments
]
email_backend = ConfiguredEmailBackend()
host = email_backend.dynamic_username
candidate_obj = Candidate.objects.get(id=candidate_id)
if candidate_id:
candidate_obj = Candidate.objects.filter(id=candidate_id)
else:
candidate_obj = Candidate.objects.none()
candidates = (candidates | candidate_obj).distinct()
template_attachment_ids = request.POST.getlist("template_attachments")
bodys = list(
RecruitmentMailTemplate.objects.filter(
id__in=template_attachment_ids
).values_list("body", flat=True)
)
for html in bodys:
# due to not having solid template we first need to pass the context
template_bdy = template.Template(html)
for candidate in candidates:
bodys = list(
RecruitmentMailTemplate.objects.filter(
id__in=template_attachment_ids
).values_list("body", flat=True)
)
for html in bodys:
# due to not having solid template we first need to pass the context
template_bdy = template.Template(html)
context = template.Context(
{"instance": candidate, "self": request.user.employee_get}
)
render_bdy = template_bdy.render(context)
attachments.append(
(
"Document",
generate_pdf(render_bdy, {}, path=False, title="Document").content,
"application/pdf",
)
)
template_bdy = template.Template(bdy)
context = template.Context(
{"instance": candidate_obj, "self": request.user.employee_get}
{"instance": candidate, "self": request.user.employee_get}
)
render_bdy = template_bdy.render(context)
attachments.append(
(
"Document",
generate_pdf(render_bdy, {}, path=False, title="Document").content,
"application/pdf",
)
to = candidate.email
email = EmailMessage(
subject,
render_bdy,
host,
[to],
)
email.content_subtype = "html"
template_bdy = template.Template(bdy)
context = template.Context(
{"instance": candidate_obj, "self": request.user.employee_get}
)
render_bdy = template_bdy.render(context)
to = request.POST["to"]
email = EmailMessage(
subject,
render_bdy,
host,
[to],
)
email.content_subtype = "html"
email.attachments = attachments
try:
email.send()
messages.success(request, "Mail sent to candidate")
except Exception as e:
logger.exception(e)
messages.error(request, "Something went wrong")
email.attachments = attachments
try:
email.send()
messages.success(request, "Mail sent to candidate")
except Exception as e:
logger.exception(e)
messages.error(request, "Something went wrong")
return HttpResponse("<script>window.location.reload()</script>")