HH/templates/complaints/partials/explanation_panel.html
2026-07-11 19:24:28 +03:00

383 lines
21 KiB
HTML

{% load i18n %}
<section class="bg-white rounded-2xl p-4 shadow-sm border border-slate-100">
<h3 class="text-lg font-bold text-navy mb-4">{% trans "Send To Department" %}</h3>
<!-- Department Response Status (always shown when departments were sent) -->
{% if complaint.sent_to_any_department %}
<div class="space-y-3 mb-4">
{% for idept in complaint.involved_departments.all %}
{% if idept.sent %}
<div class="border border-slate-200 rounded-xl p-3">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<i data-lucide="building-2" class="w-4 h-4 text-navy"></i>
<span class="font-semibold text-navy text-sm">{{ idept.department.get_localized_name }}</span>
{% if idept.is_primary %}
<span class="px-1.5 py-0.5 bg-navy/10 text-navy rounded text-[9px] font-bold">{% trans "Primary" %}</span>
{% endif %}
</div>
<span class="text-[10px] text-slate">{{ idept.forwarded_at|date:"M d, Y" }}</span>
</div>
<div class="flex items-center gap-2 flex-wrap">
{% if not idept.response_submitted %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-amber-100 text-amber-700 uppercase">
<i data-lucide="clock" class="w-3 h-3"></i> {% trans "Awaiting Response" %}
</span>
{% elif idept.manager_review_status == 'pending' %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-amber-100 text-amber-700 uppercase">
<i data-lucide="shield" class="w-3 h-3"></i> {% trans "Pending Manager Review" %}
</span>
{% elif idept.manager_review_status == 'rejected' %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-red-100 text-red-700 uppercase">
<i data-lucide="x-circle" class="w-3 h-3"></i> {% trans "Rejected by Manager" %}
</span>
{% elif idept.manager_review_status == 'approved' and idept.acceptance_status == 'pending' %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-green-100 text-green-700 uppercase">
<i data-lucide="check-circle" class="w-3 h-3"></i> {% trans "Approved — Awaiting PX Review" %}
</span>
{% elif idept.acceptance_status == 'acceptable' %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-green-100 text-green-700 uppercase">
<i data-lucide="check-circle" class="w-3 h-3"></i> {% trans "Accepted" %}
</span>
{% elif idept.acceptance_status == 'not_acceptable' %}
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-bold bg-red-100 text-red-700 uppercase">
<i data-lucide="x-circle" class="w-3 h-3"></i> {% trans "Rejected by PX" %}
</span>
{% endif %}
</div>
{% if idept.response_notes %}
<div class="mt-2 bg-slate-50 rounded-lg p-2">
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Response" %}</p>
<p class="text-xs text-slate-600">{{ idept.response_notes|truncatechars:150 }}</p>
</div>
{% endif %}
{% if idept.manager_reviews.exists %}
<div class="mt-2 bg-blue-50 rounded-lg p-2 border border-blue-100">
<p class="text-[10px] text-blue-600 uppercase font-bold mb-1.5 flex items-center gap-1">
<i data-lucide="shield-check" class="w-3 h-3"></i> {% trans "Manager Review Answers" %}
</p>
{% for review in idept.manager_reviews.all %}
<div class="space-y-1.5">
{% for answer in review.answers.all %}
<div class="flex items-start gap-2 text-xs">
<span class="text-slate-500 font-semibold min-w-[140px]">{{ answer.question.text_ar|default:answer.question.text_en }}:</span>
<span class="font-bold
{% if answer.text_value == 'yes' %}text-red-600
{% elif answer.text_value == 'no' %}text-green-600
{% else %}text-navy{% endif %}">
{% if answer.text_value == 'yes' %}{% trans "Yes" %}{% elif answer.text_value == 'no' %}{% trans "No" %}{% else %}{{ answer.text_value }}{% endif %}
</span>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% if can_manage_actions and complaint.is_active_status %}
<button type="button" onclick="showSendModal('{{ complaint.id }}', 'complaint')" class="px-3 py-1.5 text-xs bg-gradient-to-r from-navy to-blue text-white rounded-lg font-semibold hover:opacity-90 transition inline-flex items-center gap-2 mb-4">
<i data-lucide="plus" class="w-4 h-4"></i> {% trans "Send to Another Department" %}
</button>
{% endif %}
{% endif %}
{% if explanations %}
<div class="flex items-center justify-between mb-4">
<div></div>
{% if can_manage_actions and complaint.is_active_status %}
<button type="button" onclick="showSendModal('{{ complaint.id }}', 'complaint')" class="px-3 py-1.5 text-xs bg-gradient-to-r from-navy to-blue text-white rounded-lg font-semibold hover:opacity-90 transition inline-flex items-center gap-2">
<i data-lucide="plus" class="w-4 h-4"></i> {% trans "Send to Department" %}
</button>
{% endif %}
</div>
<div class="space-y-6">
{% for exp in explanations %}
<div class="bg-white border border-slate-200 rounded-xl p-5">
<!-- Header: Staff Info + Status -->
<div class="flex justify-between items-start mb-4">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-navy rounded-full flex items-center justify-center text-white font-bold text-sm">
{{ exp.staff.first_name|first }}{{ exp.staff.last_name|first }}
</div>
<div>
<h4 class="font-bold text-navy">{{ exp.staff }}</h4>
<div class="flex items-center gap-2 text-xs text-slate">
<span>{{ exp.staff.employee_id }}</span>
{% if exp.staff.department %}
<span></span>
<span>{{ exp.staff.department.get_localized_name }}</span>
{% endif %}
</div>
</div>
</div>
<div class="flex flex-col items-end gap-1">
<!-- Submission Status -->
<span class="px-3 py-1 {% if exp.is_used %}bg-green-100 text-green-600{% else %}bg-yellow-100 text-yellow-600{% endif %} rounded-lg text-xs font-bold">
{% if exp.is_used %}{% trans "Submitted" %}{% else %}{% trans "Pending" %}{% endif %}
</span>
</div>
</div>
<!-- Explanation Content -->
{% if exp.is_used and exp.explanation %}
{% with linked_dept=exp.linked_involved_department %}
{% if linked_dept and linked_dept.manager_review_status == 'pending' %}
<div class="rounded-lg p-3 mb-4 border bg-amber-50 border-amber-200">
<div class="flex items-center gap-2 mb-1">
<i data-lucide="clock" class="w-4 h-4 text-amber-500"></i>
<span class="text-sm font-semibold text-amber-700">{% trans "Pending Manager Review" %}</span>
</div>
<p class="text-xs text-slate-500">{% trans "Awaiting department manager approval." %}</p>
</div>
{% elif linked_dept and linked_dept.manager_review_status == 'rejected' %}
<div class="rounded-lg p-3 mb-4 border bg-red-50 border-red-200">
<div class="flex items-center gap-2 mb-1">
<i data-lucide="x-circle" class="w-4 h-4 text-red-500"></i>
<span class="text-sm font-semibold text-red-700">{% trans "Rejected by Manager" %}</span>
</div>
<p class="text-xs text-slate-500">{% trans "Champion needs to re-submit." %}</p>
</div>
{% else %}
<div class="bg-slate-50 rounded-xl p-4 mb-4">
<p class="text-slate text-sm">{{ exp.explanation }}</p>
{% if exp.attachment_count > 0 %}
<div class="mt-3 pt-3 border-t border-slate-200 flex items-center gap-2 text-xs text-slate">
<i data-lucide="paperclip" class="w-4 h-4"></i>
<span>{{ exp.attachment_count }} {% trans "attachment(s)" %}</span>
</div>
{% endif %}
</div>
{% endif %}
{% endwith %}
{% endif %}
{% for inv in exp.investigation.all %}
<div class="mt-4 pt-4 border-t border-slate-100">
<div class="flex items-center gap-2 mb-3">
<i data-lucide="search" class="w-4 h-4 text-navy"></i>
<span class="text-sm font-bold text-navy">{% trans "Feedback" %}</span>
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold uppercase
{% if inv.status == 'questions_sent' %}bg-amber-100 text-amber-700
{% elif inv.status == 'answers_received' %}bg-blue-100 text-blue-700
{% elif inv.status == 'reply_submitted' %}bg-green-100 text-green-700{% endif %}">
{{ inv.get_status_display }}
</span>
<span class="text-xs text-slate ml-auto">{{ inv.questions.count }} {% trans "questions" %}</span>
</div>
<div class="space-y-1.5 mb-3">
{% for resp in inv.responses.all %}
<div class="flex items-center gap-2 text-xs">
{% if resp.is_completed %}
<i data-lucide="check-circle" class="w-3.5 h-3.5 text-green-500 shrink-0"></i>
<span class="text-slate">{{ resp.staff.get_full_name }}</span>
<span class="text-slate ml-auto">{{ resp.completed_at|date:"M d, H:i" }}</span>
{% else %}
<i data-lucide="clock" class="w-3.5 h-3.5 text-amber-500 shrink-0"></i>
<span class="text-slate">{{ resp.staff.get_full_name }}</span>
<span class="text-amber-600 font-semibold ml-auto">{% trans "Pending" %}</span>
{% endif %}
</div>
{% endfor %}
</div>
<!-- Investigation Questions & Answers -->
{% if inv.questions.exists %}
<div class="mt-3 space-y-3">
{% for question in inv.questions.all %}
<div class="bg-slate-50 rounded-lg p-3 border border-slate-200">
<div class="flex items-start gap-2 mb-2">
<span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-navy text-white text-[10px] font-bold flex-shrink-0 mt-0.5">{{ forloop.counter }}</span>
<p class="text-sm font-semibold text-slate-700">{{ question.question_text }}</p>
</div>
<div class="ml-7 space-y-1.5">
{% for resp in inv.responses.all %}
{% if resp.is_completed %}
{% with answer=resp.answers.all %}
{% for a in answer %}
{% if a.question_id == question.id and a.answer_text %}
<div class="flex items-start gap-1.5 text-xs">
<i data-lucide="corner-up-right" class="w-3 h-3 text-slate-400 flex-shrink-0 mt-0.5"></i>
<span class="text-slate-500 font-medium">{{ resp.staff.get_full_name }}:</span>
<span class="text-slate-600">{{ a.answer_text|truncatewords:40 }}</span>
</div>
{% endif %}
{% endfor %}
{% endwith %}
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% if inv.status == 'answers_received' and inv.explanation.token %}
<a href="/complaints/{{ complaint.id }}/investigate/review/{{ inv.explanation.token }}/"
class="inline-flex items-center gap-1.5 px-3 py-1.5 bg-navy text-white rounded-lg text-xs font-semibold hover:bg-blue transition">
<i data-lucide="eye" class="w-3.5 h-3.5"></i> {% trans "Review & Submit Reply" %}
</a>
{% endif %}
{% if inv.status == 'reply_submitted' and inv.final_reply %}
<div class="bg-slate-50 rounded-lg p-3 mt-2">
<p class="text-xs font-bold text-slate uppercase mb-1">{% trans "Final Reply" %}</p>
<p class="text-sm text-slate-700">{{ inv.final_reply|truncatewords:30 }}</p>
</div>
{% endif %}
{% if inv.status == 'reply_submitted' and inv.negligence_finding %}
<div class="mt-2 flex flex-wrap gap-2">
{% if inv.negligence_finding == 'yes' %}
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-red-100 text-red-700">{% trans "Negligence" %}</span>
{% endif %}
{% if inv.policy_issue_finding == 'yes' %}
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-orange-100 text-orange-700">{% trans "Policy Issue" %}</span>
{% endif %}
{% if inv.requires_improvement_project == 'yes' %}
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-amber-100 text-amber-700">{% trans "Improvement Project" %}</span>
{% endif %}
</div>
{% endif %}
{% if inv.status == 'reply_submitted' and inv.requires_improvement_project == 'yes' and inv.improvement_project_note %}
<div class="bg-amber-50 border border-amber-200 rounded-lg p-3 mt-2">
<p class="text-xs font-bold text-amber-700 uppercase mb-1">{% trans "Improvement Project Recommended" %}</p>
<p class="text-sm text-amber-800">{{ inv.improvement_project_note }}</p>
</div>
{% endif %}
</div>
{% endfor %}
<!-- Pending Actions -->
{% if can_manage_actions and complaint.is_active_status and not exp.is_used %}
<div class="mt-4 pt-4 border-t border-slate-100">
<div class="flex flex-wrap gap-2">
{% if not exp.reminder_sent_at %}
<button onclick="sendExplanationReminder('{{ exp.id }}', 'first')" class="px-3 py-1.5 bg-amber-500 text-white rounded-lg text-xs font-semibold hover:bg-amber-600 transition inline-flex items-center gap-2">
<i data-lucide="bell" class="w-4 h-4"></i> {% trans "Send Reminder" %}
</button>
{% elif not exp.second_reminder_sent_at %}
<button onclick="sendExplanationReminder('{{ exp.id }}', 'second')" class="px-3 py-1.5 bg-orange-500 text-white rounded-lg text-xs font-semibold hover:bg-orange-600 transition inline-flex items-center gap-2">
<i data-lucide="alert-triangle" class="w-4 h-4"></i> {% trans "Send Second Reminder" %}
</button>
{% else %}
<span class="px-3 py-1.5 bg-slate-100 text-slate-400 rounded-lg text-xs font-semibold inline-flex items-center gap-2">
<i data-lucide="bell-check" class="w-4 h-4"></i> {% trans "Reminders Sent" %}
</span>
{% endif %}
<button onclick="resendExplanation('{{ exp.token }}')" class="px-3 py-1.5 bg-navy text-white rounded-lg text-xs font-semibold hover:bg-blue transition inline-flex items-center gap-2">
<i data-lucide="refresh-cw" class="w-4 h-4"></i> {% trans "Resend Link" %}
</button>
</div>
{% if exp.reminder_sent_at %}
<div class="mt-2 text-xs text-slate flex flex-wrap gap-x-4 gap-y-1">
<span>{% trans "1st reminder:" %} {{ exp.reminder_sent_at|date:"Y-m-d H:i" }}</span>
{% if exp.second_reminder_sent_at %}
<span>{% trans "2nd reminder:" %} {{ exp.second_reminder_sent_at|date:"Y-m-d H:i" }}</span>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
{% if not complaint.sent_to_any_department %}
<div class="text-center py-12">
<i data-lucide="message-square" class="w-16 h-16 mx-auto text-slate-300 mb-4"></i>
<p class="text-slate mb-4">{% trans "No requests sent yet" %}</p>
{% if can_manage_actions and complaint.is_active_status %}
<button type="button" onclick="showSendModal('{{ complaint.id }}', 'complaint')" class="px-3 py-1.5 text-xs bg-gradient-to-r from-navy to-blue text-white rounded-lg font-semibold hover:opacity-90 transition inline-flex items-center gap-2">
<i data-lucide="plus" class="w-4 h-4"></i> {% trans "Send to Department" %}
</button>
{% endif %}
</div>
{% endif %}
{% endif %}
</section>
function sendExplanationReminder(explanationId, reminderType) {
const label = reminderType === 'first' ? '{% trans "first" %}' : '{% trans "second" %}';
if (!confirm('{% trans "Are you sure you want to send the" %} ' + label + ' {% trans "reminder?" %}')) {
return;
}
const csrfToken = getCookie('csrftoken');
fetch(`/complaints/api/complaints/{{ complaint.id }}/send_explanation_reminder/`, {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
explanation_id: explanationId,
reminder_type: reminderType
})
})
.then(response => {
if (response.ok) {
location.reload();
} else {
response.json().then(data => {
alert(data.error || '{% trans "Failed to send reminder. Please try again." %}');
});
}
})
.catch(error => {
console.error('Error:', error);
alert('{% trans "An error occurred. Please try again." %}');
});
}
function resendExplanation(token) {
if (!confirm('{% trans "Are you sure you want to resend the explanation request?" %}')) {
return;
}
fetch('/complaints/{{ complaint.id }}/resend-explanation/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
}
})
.then(response => {
if (response.ok) {
alert('{% trans "Explanation request resent successfully!" %}');
} else {
alert('{% trans "Failed to resend explanation request. Please try again." %}');
}
})
.catch(error => {
console.error('Error:', error);
alert('{% trans "An error occurred. Please try again." %}');
});
}
// Helper function to get CSRF token
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
</script>