298 lines
17 KiB
HTML
298 lines
17 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Manager Review" %} - {{ complaint.reference_number }} - PX360{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="p-6 max-w-4xl mx-auto">
|
|
<div class="flex items-center gap-2 text-sm text-slate-500 mb-4">
|
|
<a href="{% url 'organizations:department_list' %}" class="hover:text-navy transition">{% trans "Departments" %}</a>
|
|
<i data-lucide="chevron-right" class="w-4 h-4"></i>
|
|
<a href="{% url 'organizations:department_detail' department.pk %}" class="hover:text-navy transition">{{ department.get_localized_name }}</a>
|
|
<i data-lucide="chevron-right" class="w-4 h-4"></i>
|
|
<span class="text-navy font-semibold">{% trans "Manager Review" %}</span>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 mb-6">
|
|
<div class="w-10 h-10 bg-amber-50 rounded-xl flex items-center justify-center">
|
|
<i data-lucide="shield-check" class="w-5 h-5 text-amber-600"></i>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-xl font-bold text-navy">{% trans "Review Champion Response" %}</h1>
|
|
<p class="text-sm text-slate-500">{{ complaint.reference_number }} · {{ department.get_localized_name }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-6">
|
|
<!-- Enhanced Complaint Card -->
|
|
<div class="bg-white rounded-2xl border border-slate-200 p-6">
|
|
<div class="flex items-start justify-between mb-4">
|
|
<h3 class="text-sm font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="file-text" class="w-4 h-4"></i> {% trans "Complaint Details" %}
|
|
</h3>
|
|
<div class="flex items-center gap-2">
|
|
<span class="px-2.5 py-0.5 rounded-full text-[10px] font-bold uppercase
|
|
{% if complaint.status == 'open' %}bg-blue-50 text-blue-700
|
|
{% elif complaint.status == 'in_progress' %}bg-amber-50 text-amber-700
|
|
{% elif complaint.status == 'resolved' %}bg-green-50 text-green-700
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ complaint.get_status_display }}
|
|
</span>
|
|
{% if complaint.severity %}
|
|
<span class="px-2.5 py-0.5 rounded-full text-[10px] font-bold uppercase
|
|
{% if complaint.severity == 'critical' %}bg-gray-800 text-white
|
|
{% elif complaint.severity == 'high' %}bg-red-50 text-red-700
|
|
{% elif complaint.severity == 'medium' %}bg-amber-50 text-amber-700
|
|
{% else %}bg-green-50 text-green-700{% endif %}">
|
|
{{ complaint.get_severity_display }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4 mb-4">
|
|
<div>
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Reference" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.reference_number }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Patient" %}</p>
|
|
<p class="text-sm font-semibold text-slate-700">{{ complaint.patient_name|default:complaint.contact_name|default:"—" }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Incident Date" %}</p>
|
|
<p class="text-sm font-semibold text-slate-700">{{ complaint.incident_date|date:"M d, Y"|default:"—" }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Department" %}</p>
|
|
<p class="text-sm font-semibold text-slate-700">{{ complaint.department|default:"—" }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{% if complaint.involved_staff.exists %}
|
|
<div class="mb-4">
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-1.5">{% trans "Accused Staff" %}</p>
|
|
<div class="flex flex-wrap gap-2">
|
|
{% for inv_staff in complaint.involved_staff.all %}
|
|
{% if inv_staff.role == 'accused' or inv_staff.role == 'Accused/Involved' %}
|
|
<span class="inline-flex items-center gap-1.5 px-3 py-1 bg-red-50 text-red-700 rounded-lg text-xs font-semibold">
|
|
<i data-lucide="user" class="w-3 h-3"></i>
|
|
{{ inv_staff.staff.get_full_name }}
|
|
</span>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if complaint.title %}
|
|
<div class="mb-2">
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Title" %}</p>
|
|
<p class="text-sm font-semibold text-navy">{{ complaint.title }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if complaint.description %}
|
|
<div>
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-0.5">{% trans "Description" %}</p>
|
|
<p class="text-sm text-slate-600 whitespace-pre-wrap">{{ complaint.description }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if investigation and investigation_staff_data %}
|
|
<!-- Investigation Questions & Answers (before champion response) -->
|
|
<div class="bg-white rounded-2xl border border-blue-200 p-6">
|
|
<h3 class="text-sm font-bold text-navy mb-4 flex items-center gap-2">
|
|
<i data-lucide="search" class="w-4 h-4 text-blue-600"></i>
|
|
{% trans "Investigation Questions & Answers" %}
|
|
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold uppercase
|
|
{% if investigation.status == 'questions_sent' %}bg-amber-100 text-amber-700
|
|
{% elif investigation.status == 'answers_received' %}bg-blue-100 text-blue-700
|
|
{% elif investigation.status == 'reply_submitted' %}bg-green-100 text-green-700{% endif %}">
|
|
{{ investigation.get_status_display }}
|
|
</span>
|
|
</h3>
|
|
<div class="space-y-4">
|
|
{% for sd in investigation_staff_data %}
|
|
<div class="border border-slate-200 rounded-xl p-4">
|
|
<div class="flex items-center justify-between mb-3 pb-2 border-b border-slate-100">
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-8 h-8 rounded-full bg-navy/10 flex items-center justify-center">
|
|
<span class="text-xs font-bold text-navy">{{ sd.staff.first_name|first|default:"?" }}{{ sd.staff.last_name|first|default:"" }}</span>
|
|
</div>
|
|
<span class="font-semibold text-navy text-sm">{{ sd.staff.get_full_name }}</span>
|
|
</div>
|
|
{% if sd.is_completed %}
|
|
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-green-100 text-green-700 uppercase">{% trans "Responded" %}</span>
|
|
{% else %}
|
|
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-amber-100 text-amber-700 uppercase">{% trans "Pending" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if sd.is_completed %}
|
|
<div class="space-y-3">
|
|
{% for pair in sd.qa_pairs %}
|
|
<div class="bg-slate-50 rounded-lg p-3">
|
|
<p class="text-xs font-bold text-slate-500 uppercase mb-1">{% trans "Q" %}{{ forloop.counter }}</p>
|
|
<p class="text-sm font-semibold text-slate-700 mb-2">{{ pair.question.question_text }}</p>
|
|
<p class="text-xs font-bold text-slate-500 uppercase mb-1">{% trans "Answer" %}</p>
|
|
<p class="text-sm text-slate-600 whitespace-pre-wrap">{{ pair.answer|default:"—" }}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="text-sm text-slate-400 italic">{% trans "This staff member has not responded yet." %}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Champion Response (after investigation) -->
|
|
<div class="bg-white rounded-2xl border border-slate-200 p-6">
|
|
<h3 class="text-sm font-bold text-navy mb-3 flex items-center gap-2">
|
|
<i data-lucide="message-square" class="w-4 h-4"></i> {% trans "Champion Response" %}
|
|
</h3>
|
|
{% if involved_dept.response_notes_en %}
|
|
<div class="bg-slate-50 rounded-xl p-4 border border-slate-200 mb-3">
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-1">{% trans "English" %}</p>
|
|
<p class="text-sm text-slate-700 whitespace-pre-wrap">{{ involved_dept.response_notes_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if involved_dept.response_notes_ar %}
|
|
<div class="bg-slate-50 rounded-xl p-4 border border-slate-200">
|
|
<p class="text-[10px] text-slate-400 uppercase font-bold mb-1">{% trans "Arabic" %}</p>
|
|
<p class="text-sm text-slate-700 whitespace-pre-wrap" dir="rtl">{{ involved_dept.response_notes_ar }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if involved_dept.response_submitted_at %}
|
|
<p class="text-xs text-slate-400 mt-2">
|
|
{% trans "Submitted" %}: {{ involved_dept.response_submitted_at|date:"Y-m-d H:i" }}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<form method="post" id="managerReviewForm">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="review_action" id="reviewAction" value="">
|
|
|
|
{% if questions %}
|
|
<div class="bg-white rounded-2xl border border-slate-200 p-6">
|
|
<h3 class="text-sm font-bold text-navy mb-4 flex items-center gap-2">
|
|
<i data-lucide="clipboard-list" class="w-4 h-4"></i> {% trans "Review Questions" %}
|
|
</h3>
|
|
<div class="space-y-5">
|
|
{% for q in questions %}
|
|
<div class="border-b border-slate-100 pb-4 last:border-0 last:pb-0">
|
|
<label class="block text-sm font-semibold text-slate-700 mb-2">
|
|
{{ q.get_localized_text }}
|
|
<span class="text-red-500">*</span>
|
|
</label>
|
|
|
|
{% if q.question_type == 'text' %}
|
|
<input type="text" name="question_{{ q.pk }}" required
|
|
class="w-full px-4 py-2.5 border-2 border-slate-200 rounded-xl text-sm focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20">
|
|
|
|
{% elif q.question_type == 'textarea' %}
|
|
<textarea name="question_{{ q.pk }}" rows="3" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-sm focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none"></textarea>
|
|
|
|
{% elif q.question_type == 'yes_no' %}
|
|
<div class="flex gap-4 mt-1">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="question_{{ q.pk }}" value="yes" required
|
|
class="w-4 h-4 text-navy border-slate-300 focus:ring-navy">
|
|
<span class="text-sm text-slate-700">{% trans "Yes" %}</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="question_{{ q.pk }}" value="no"
|
|
class="w-4 h-4 text-navy border-slate-300 focus:ring-navy">
|
|
<span class="text-sm text-slate-700">{% trans "No" %}</span>
|
|
</label>
|
|
</div>
|
|
|
|
{% elif q.question_type == 'rating' %}
|
|
<div class="flex gap-2 mt-1">
|
|
{% for i in "12345" %}
|
|
<label class="cursor-pointer">
|
|
<input type="radio" name="question_{{ q.pk }}" value="{{ i }}" required
|
|
class="hidden peer">
|
|
<div class="w-10 h-10 rounded-lg border-2 border-slate-200 flex items-center justify-center text-sm font-bold text-slate-400 peer-checked:bg-navy peer-checked:text-white peer-checked:border-navy transition hover:border-navy">
|
|
{{ i }}
|
|
</div>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
{% elif q.question_type == 'multiple_choice' %}
|
|
<div class="space-y-2 mt-1">
|
|
{% for choice in q.choices_json %}
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="question_{{ q.pk }}" value="{{ choice }}" required
|
|
class="w-4 h-4 text-navy border-slate-300 focus:ring-navy">
|
|
<span class="text-sm text-slate-700">{{ choice }}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div id="rejectionReasonBox" class="hidden bg-white rounded-2xl border border-red-200 p-6">
|
|
<h3 class="text-sm font-bold text-red-700 mb-2 flex items-center gap-2">
|
|
<i data-lucide="alert-circle" class="w-4 h-4"></i> {% trans "Rejection Reason" %}
|
|
</h3>
|
|
<textarea name="rejection_reason" id="rejectionReason" rows="3"
|
|
class="w-full px-4 py-3 border-2 border-red-200 rounded-xl text-sm focus:outline-none focus:border-red-400 focus:ring-2 focus:ring-red-200/40 resize-none"
|
|
placeholder="{% trans 'Please explain why you are rejecting this response...' %}"></textarea>
|
|
</div>
|
|
|
|
<div class="flex gap-3 pt-2">
|
|
<button type="button" onclick="submitReview('approve')"
|
|
class="flex-1 px-4 py-3 bg-green-600 text-white font-semibold rounded-xl hover:bg-green-700 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="check-circle" class="w-5 h-5"></i> {% trans "Approve & Forward to PX Team" %}
|
|
</button>
|
|
<button type="button" onclick="handleReject()"
|
|
class="flex-1 px-4 py-3 bg-red-600 text-white font-semibold rounded-xl hover:bg-red-700 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="x-circle" class="w-5 h-5"></i> {% trans "Reject & Return to Champion" %}
|
|
</button>
|
|
<a href="{% url 'organizations:department_detail' department.pk %}"
|
|
class="px-4 py-3 bg-slate-100 text-slate-700 font-semibold rounded-xl hover:bg-slate-200 transition text-center">
|
|
{% trans "Cancel" %}
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function submitReview(action) {
|
|
document.getElementById('reviewAction').value = action;
|
|
document.getElementById('managerReviewForm').submit();
|
|
}
|
|
|
|
function handleReject() {
|
|
var box = document.getElementById('rejectionReasonBox');
|
|
box.classList.remove('hidden');
|
|
document.getElementById('rejectionReason').focus();
|
|
box.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
|
|
var reasonEl = document.getElementById('rejectionReason');
|
|
reasonEl.setAttribute('required', 'required');
|
|
|
|
var submitBtn = box.closest('form').querySelectorAll('button')[1];
|
|
submitBtn.onclick = function() {
|
|
var reason = reasonEl.value.trim();
|
|
if (!reason) {
|
|
alert('{% trans "Please provide a rejection reason." %}');
|
|
return;
|
|
}
|
|
submitReview('reject');
|
|
};
|
|
}
|
|
</script>
|
|
{% endblock %}
|