400 lines
24 KiB
HTML
400 lines
24 KiB
HTML
{% load i18n %}
|
|
{% load static %}
|
|
<!DOCTYPE html>
|
|
<html lang="{{ LANGUAGE_CODE|default:'en' }}" dir="{% if LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ complaint.reference_number }} - {% trans "Response" %}</title>
|
|
<link rel="stylesheet" href="{% static 'dist/css/tailwind.css' %}">
|
|
<link rel="stylesheet" href="{% static 'vendor/fonts/noto-kufi-arabic/arabic.css' %}">
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
|
body { font-family: 'Inter', 'Noto Kufi Arabic', sans-serif; }
|
|
[dir="rtl"] body, [dir="rtl"] input, [dir="rtl"] textarea, [dir="rtl"] select, [dir="rtl"] button {
|
|
font-family: 'Noto Kufi Arabic', sans-serif;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-slate-50 min-h-screen py-8 px-4">
|
|
<div class="w-full max-w-3xl mx-auto">
|
|
|
|
<!-- Back -->
|
|
{% if complaint.department %}
|
|
<a href="{% url 'organizations:department_detail' complaint.department.id %}" class="inline-flex items-center gap-2 text-sm text-slate hover:text-navy transition mb-4">← {% trans "Back to Department" %}</a>
|
|
{% endif %}
|
|
|
|
<!-- Logo -->
|
|
<div class="text-center mb-6">
|
|
<img src="{% static 'img/hh-logo.png' %}" alt="Al Hammadi Hospital" class="h-16 w-auto mx-auto inline-block">
|
|
</div>
|
|
|
|
<!-- Complaint Card -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6 mb-6">
|
|
<!-- Reference + Title -->
|
|
<div class="flex items-center justify-between mb-1">
|
|
<div class="flex items-center gap-2">
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider
|
|
{% if complaint.status == 'open' %}bg-yellow-100 text-yellow-700
|
|
{% elif complaint.status == 'in_progress' %}bg-blue-100 text-blue-700
|
|
{% elif complaint.status == 'resolved' %}bg-green-100 text-green-700
|
|
{% elif complaint.status == 'closed' %}bg-slate-100 text-slate-600
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ complaint.get_status_display }}
|
|
</span>
|
|
<span class="text-sm font-bold text-navy">{{ complaint.reference_number }}</span>
|
|
</div>
|
|
<a href="{% url 'complaints:complaint_explanation_pdf' complaint_id=complaint.id token=explanation.token %}" target="_blank"
|
|
class="inline-flex items-center gap-1.5 px-3 py-1.5 border border-slate-200 rounded-lg text-xs font-semibold text-slate hover:border-navy hover:text-navy transition">
|
|
<i data-lucide="download" class="w-3.5 h-3.5"></i>
|
|
{% trans "PDF" %}
|
|
</a>
|
|
</div>
|
|
<h1 class="text-xl font-bold text-navy mb-4">{{ complaint.title }}</h1>
|
|
|
|
<!-- Description -->
|
|
<div class="border-t border-slate-100 pt-4">
|
|
<p class="text-xs font-bold text-slate uppercase mb-2">{% trans "Description" %}</p>
|
|
<p class="text-base leading-relaxed text-slate">{{ complaint.description }}</p>
|
|
{% if complaint.short_description_en and complaint.short_description_en != complaint.description %}
|
|
<p class="text-sm leading-relaxed text-slate/70 mt-3">{{ complaint.short_description_en }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if error %}
|
|
<div class="mt-4 bg-red-50 border border-red-200 rounded-xl p-4">
|
|
<p class="text-sm text-red-700 font-medium">{{ error }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if explanation.request_message %}
|
|
<div class="bg-blue-50 border border-blue-200 rounded-xl p-4 mb-6 flex items-start gap-3">
|
|
<i data-lucide="info" class="w-4 h-4 text-blue-600 shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<p class="text-xs font-bold text-blue-700 uppercase mb-1">{% trans "Note from PX Team" %}</p>
|
|
<p class="text-sm text-slate-700">{{ explanation.request_message }}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if explanation.attachments.exists %}
|
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-4 mb-6">
|
|
<p class="text-xs font-bold text-slate/60 uppercase mb-2">{% trans "Attached Documents" %}</p>
|
|
<ul class="space-y-1.5">
|
|
{% for att in explanation.attachments.all %}
|
|
<li>
|
|
<a href="{{ att.file.url }}" target="_blank" class="inline-flex items-center gap-2 text-sm text-blue-600 hover:underline">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 10-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"/></svg>
|
|
{{ att.filename }}
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Action Choice -->
|
|
<div id="action-choice" class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6">
|
|
<p class="text-sm font-semibold text-slate mb-4">{% trans "How would you like to respond?" %}</p>
|
|
<div class="flex gap-3">
|
|
<button type="button" onclick="showReplyForm()" class="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-navy text-white rounded-xl font-bold text-sm hover:bg-blue transition">
|
|
<i data-lucide="message-square" class="w-4 h-4"></i>
|
|
{% trans "Submit Reply" %}
|
|
</button>
|
|
{% if investigate_url %}
|
|
<button type="button" onclick="goToInvestigation()" class="flex-1 flex items-center justify-center gap-2 px-4 py-3 border-2 border-slate-200 text-slate-700 rounded-xl font-bold text-sm hover:border-navy hover:text-navy transition">
|
|
<i data-lucide="message-square-plus" class="w-4 h-4"></i>
|
|
{% trans "Collect Feedback" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
<button type="button" onclick="showRejectForm()" class="w-full mt-3 flex items-center justify-center gap-2 px-4 py-2.5 border-2 border-red-200 text-red-600 rounded-xl font-bold text-sm hover:border-red-400 hover:bg-red-50 transition">
|
|
<i data-lucide="log-out" class="w-4 h-4"></i>
|
|
{% trans "Wrong Department" %}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Reject Routing Form -->
|
|
<div id="reject-section" style="display:none">
|
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6 mt-4">
|
|
{% if error %}
|
|
<div class="bg-red-50 border border-red-200 rounded-xl p-3 mb-4 text-sm text-red-700 font-semibold">
|
|
{% trans error %}
|
|
</div>
|
|
{% endif %}
|
|
<form method="post" class="space-y-4">
|
|
{% csrf_token %}
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 text-red-500"></i>
|
|
<h2 class="text-base font-bold text-navy">{% trans "Reject Routing" %}</h2>
|
|
</div>
|
|
<p class="text-sm text-slate">{% trans "Use this if the complaint was sent to the wrong department. The PX team will be notified to re-route it." %}</p>
|
|
|
|
<div>
|
|
<label for="rejection_reason" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Reason" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<textarea name="rejection_reason" id="rejection_reason" rows="4" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-sm text-slate focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none"
|
|
placeholder="{% trans 'Explain why this complaint does not belong to your department...' %}">{{ rejection_reason|default:'' }}</textarea>
|
|
</div>
|
|
|
|
{% if hospital_departments %}
|
|
<div>
|
|
<label for="suggested_department_id" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Suggest Correct Department (optional)" %}
|
|
</label>
|
|
<select name="suggested_department_id" id="suggested_department_id"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-sm text-slate focus:outline-none focus:border-navy bg-white">
|
|
<option value="">{% trans "Select Department" %}</option>
|
|
{% for dept in hospital_departments %}
|
|
<option value="{{ dept.id }}">{{ dept.get_localized_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="flex gap-3">
|
|
<button type="button" onclick="hideRejectForm()" class="flex-1 px-4 py-3 border-2 border-slate-200 text-slate-700 rounded-xl font-bold text-sm hover:border-slate-400 transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" name="action" value="reject_routing" class="flex-1 px-4 py-3 bg-red-500 text-white rounded-xl font-bold text-sm hover:bg-red-600 transition flex items-center justify-center gap-2"
|
|
onclick="return confirm('{% trans "Reject this routing as wrong department?" %}')">
|
|
<i data-lucide="log-out" class="w-4 h-4"></i> {% trans "Reject Routing" %}
|
|
</button>
|
|
</div>
|
|
<p class="text-xs text-slate/60 text-center">{% trans "This action cannot be undone." %}</p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reply Form -->
|
|
<div id="reply-section" style="display:none">
|
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6 mt-4">
|
|
{% if error %}
|
|
<div class="bg-red-50 border border-red-200 rounded-xl p-3 mb-4 text-sm text-red-700 font-semibold">
|
|
{% trans error %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<form method="post" enctype="multipart/form-data" class="space-y-4">
|
|
{% csrf_token %}
|
|
|
|
{% if otp_sent %}
|
|
<!-- ═══ OTP VERIFICATION BANNER (top, prominent) ═══ -->
|
|
<div class="bg-green-50 border border-green-200 rounded-xl p-4">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div class="flex items-center gap-2">
|
|
<i data-lucide="shield-check" class="w-5 h-5 text-green-600"></i>
|
|
<p class="text-sm font-bold text-green-800">{% trans "Verify your submission" %}</p>
|
|
</div>
|
|
<button type="submit" name="action" value="cancel_otp"
|
|
class="text-xs text-slate/60 hover:text-navy underline flex items-center gap-1">
|
|
<i data-lucide="pencil" class="w-3 h-3"></i>
|
|
{% trans "Edit response" %}
|
|
</button>
|
|
</div>
|
|
|
|
<p class="text-sm text-green-700 mb-3">
|
|
{% if otp_phone and otp_email %}{% trans "Verification code sent to your phone and email." %}
|
|
{% elif otp_phone %}{% trans "Verification code sent to your phone." %}
|
|
{% elif otp_email %}{% trans "Verification code sent to your email." %}
|
|
{% else %}{% trans "Verification code sent." %}{% endif %}
|
|
</p>
|
|
|
|
<div class="flex items-center gap-3">
|
|
<input type="text" name="otp_code" id="otpInput" maxlength="6" pattern="[0-9]{6}" autofocus
|
|
class="w-32 px-3 py-2 border-2 border-green-300 rounded-lg text-center text-lg font-bold tracking-widest focus:outline-none focus:border-green-600"
|
|
placeholder="000000" required
|
|
oninput="toggleVerifyBtn()">
|
|
<button type="submit" name="action" value="verify_submit" id="verifyBtn"
|
|
class="flex-1 px-4 py-2.5 bg-green-600 text-white rounded-xl font-bold text-sm hover:bg-green-700 transition disabled:opacity-40 disabled:cursor-not-allowed flex items-center justify-center gap-2"
|
|
disabled>
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
|
{% trans "Verify & Submit" %}
|
|
</button>
|
|
</div>
|
|
<div class="flex items-center justify-between mt-3">
|
|
<p class="text-xs text-slate/60">{% trans "Enter the 6-digit code to complete your submission." %}</p>
|
|
<button type="submit" name="action" value="send_code"
|
|
class="text-xs text-green-700 hover:text-green-800 underline">
|
|
{% trans "Resend code" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Locked form fields wrapper -->
|
|
<div class="relative rounded-xl" style="opacity: 0.5; pointer-events: none;">
|
|
<div class="absolute -top-2.5 left-4 px-2 py-0.5 bg-slate-200 text-slate-600 rounded text-[10px] font-bold uppercase tracking-wider flex items-center gap-1 z-10">
|
|
<i data-lucide="lock" class="w-3 h-3"></i>
|
|
{% trans "Locked" %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div>
|
|
<label for="explanation" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Your Response" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<textarea
|
|
id="explanation"
|
|
name="explanation"
|
|
rows="6"
|
|
required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-sm text-slate focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none"
|
|
placeholder="{% trans 'Write your response here...' %}"
|
|
>{{ final_reply|default:'' }}</textarea>
|
|
</div>
|
|
|
|
<!-- Assessment -->
|
|
<div class="bg-slate-50 rounded-xl p-4 space-y-4">
|
|
<p class="text-xs font-bold text-slate/60 uppercase tracking-wide">{% trans "Assessment" %}</p>
|
|
|
|
<!-- Q1: Negligence -->
|
|
<div>
|
|
<p class="text-sm font-semibold text-slate mb-2">{% trans "Is there any negligence resulting from carelessness?" %}</p>
|
|
<div class="flex items-center gap-4">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="negligence_finding" value="yes" class="w-4 h-4 accent-red-600" {% if negligence_finding == 'yes' %}checked{% endif %} required>
|
|
<span class="text-sm font-semibold text-red-700">{% trans "Yes" %}</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="negligence_finding" value="no" class="w-4 h-4 accent-green-600" {% if negligence_finding == 'no' %}checked{% endif %}>
|
|
<span class="text-sm font-semibold text-green-700">{% trans "No" %}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Q2: Policy issue -->
|
|
<div>
|
|
<p class="text-sm font-semibold text-slate mb-2">{% trans "Is it resulting from policies or regulations?" %}</p>
|
|
<div class="flex items-center gap-4">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="policy_issue_finding" value="yes" class="w-4 h-4 accent-red-600" {% if policy_issue_finding == 'yes' %}checked{% endif %} required>
|
|
<span class="text-sm font-semibold text-red-700">{% trans "Yes" %}</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="policy_issue_finding" value="no" class="w-4 h-4 accent-green-600" {% if policy_issue_finding == 'no' %}checked{% endif %}>
|
|
<span class="text-sm font-semibold text-green-700">{% trans "No" %}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Q3: Improvement project -->
|
|
<div>
|
|
<p class="text-sm font-semibold text-slate mb-2">{% trans "Does this complaint require an improvement project?" %}</p>
|
|
<div class="flex items-center gap-4 mb-2">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="requires_improvement_project" value="yes" class="w-4 h-4 accent-amber-600" {% if requires_improvement_project == 'yes' %}checked{% endif %} required onchange="toggleProjectField()">
|
|
<span class="text-sm font-semibold text-amber-700">{% trans "Yes" %}</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="radio" name="requires_improvement_project" value="no" class="w-4 h-4 accent-slate-400" {% if requires_improvement_project == 'no' %}checked{% endif %} onchange="toggleProjectField()">
|
|
<span class="text-sm font-semibold text-slate-500">{% trans "No" %}</span>
|
|
</label>
|
|
</div>
|
|
<div id="projectField" class="{% if requires_improvement_project != 'yes' %}hidden{% endif %}">
|
|
<textarea name="improvement_project_note" rows="3"
|
|
class="w-full px-3 py-2 border-2 border-slate-200 rounded-lg text-sm text-slate focus:outline-none focus:border-navy resize-none"
|
|
placeholder="{% trans 'Describe the required improvement project...' %}">{{ improvement_project_note|default:'' }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Attachments -->
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Attachments (Optional)" %}</label>
|
|
<input
|
|
type="file"
|
|
id="attachments"
|
|
name="attachments"
|
|
multiple
|
|
class="block w-full text-sm text-slate file:mr-4 file:py-2 file:px-4 file:rounded-xl file:border-0 file:text-sm file:font-semibold file:bg-slate-100 file:text-navy hover:file:bg-slate-200 file:transition cursor-pointer border-2 border-slate-200 rounded-xl"
|
|
>
|
|
</div>
|
|
|
|
<!-- Consent -->
|
|
<div class="bg-amber-50 border border-amber-200 rounded-xl p-4">
|
|
<label class="flex items-start gap-3 cursor-pointer">
|
|
<input type="checkbox" name="consent" id="consentCheckbox" class="mt-0.5 w-5 h-5 accent-navy shrink-0"
|
|
{% if consent_checked %}checked{% endif %}
|
|
onchange="toggleSubmitBtn()">
|
|
<span class="text-sm text-slate-700 leading-relaxed">
|
|
{% trans "I confirm that I have reviewed the complaint details. I acknowledge that my reply represents the official department response for this complaint, and I accept full responsibility for its accuracy and content." %}
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
{% if otp_sent %}
|
|
<!-- Close the locked-fields wrapper -->
|
|
</div>
|
|
{% else %}
|
|
<!-- Step 1: Send code (only shown when OTP not yet sent) -->
|
|
<button type="submit" name="action" value="send_code" id="sendCodeBtn"
|
|
class="w-full flex items-center justify-center gap-2 px-4 py-3 bg-navy text-white rounded-xl font-bold text-sm hover:bg-blue transition disabled:opacity-40 disabled:cursor-not-allowed"
|
|
disabled>
|
|
<i data-lucide="send" class="w-4 h-4"></i> {% trans "Send Verification Code" %}
|
|
</button>
|
|
{% endif %}
|
|
|
|
<p class="text-xs text-slate/60 text-center">{% trans "This link can only be used once." %}</p>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
lucide.createIcons();
|
|
|
|
function showReplyForm() {
|
|
document.getElementById('action-choice').style.display = 'none';
|
|
document.getElementById('reply-section').style.display = 'block';
|
|
}
|
|
|
|
function showRejectForm() {
|
|
document.getElementById('action-choice').style.display = 'none';
|
|
document.getElementById('reject-section').style.display = 'block';
|
|
}
|
|
|
|
function hideRejectForm() {
|
|
document.getElementById('reject-section').style.display = 'none';
|
|
document.getElementById('action-choice').style.display = 'block';
|
|
}
|
|
|
|
function goToInvestigation() {
|
|
window.location.href = "{{ investigate_url }}";
|
|
}
|
|
|
|
function toggleSubmitBtn() {
|
|
var cb = document.getElementById('consentCheckbox');
|
|
var btn = document.getElementById('sendCodeBtn');
|
|
if (btn) btn.disabled = !cb.checked;
|
|
}
|
|
function toggleVerifyBtn() {
|
|
var input = document.getElementById('otpInput');
|
|
var btn = document.getElementById('verifyBtn');
|
|
if (btn && input) btn.disabled = input.value.replace(/\s/g, '').length !== 6;
|
|
}
|
|
function toggleProjectField() {
|
|
var yesRadio = document.querySelector('input[name="requires_improvement_project"][value="yes"]');
|
|
var field = document.getElementById('projectField');
|
|
if (field && yesRadio) field.classList.toggle('hidden', !yesRadio.checked);
|
|
}
|
|
// Init directly — script is at the bottom of <body>, all elements exist.
|
|
// (DOMContentLoaded has already fired by the time this runs, so a listener
|
|
// would be dead code.)
|
|
toggleSubmitBtn();
|
|
toggleVerifyBtn();
|
|
toggleProjectField();
|
|
// Auto-show reply form if OTP was already sent (e.g. page reload after send_code)
|
|
{% if otp_sent %}
|
|
showReplyForm();
|
|
{% elif show_reject_form %}
|
|
showRejectForm();
|
|
{% endif %}
|
|
</script>
|
|
</body>
|
|
</html>
|