All checks were successful
Build and Push Docker Image / build (push) Successful in 4m14s
Reference numbers (unified scheme PREFIX-YYYYMM-HOSP-NNNN, e.g. CMP-202606-HHN-0001): - new ReferenceSequence model + generate_reference() helper (apps/core) - Complaint/Inquiry/Observation/Appreciation/Suggestion emit unified refs via save() - prefix-based auto-routing in public track API (CMP/INQ/OBS trackable; APR/SGT internal-only) - removed legacy CMP-/INQ- generators in ui_views, integrations, px_sources - migrations: core.0003_referencesequence, appreciation.0006, feedback.0008, observations.0012 - unit tests (format, sanitization, monthly reset, 40-thread concurrency) QA audit: - isolated E2E hospital sandbox mirroring HH-N + 10 role users (create_e2e_isolated_env) - feedback-modules-audit.spec.ts + audit helper (headed, run-to-completion) - reports/feedback-modules-qa-report.md Also bundles accumulated in-progress work across complaints, observations, organizations, templates, and other modules.
203 lines
13 KiB
HTML
203 lines
13 KiB
HTML
{% load i18n %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h3 class="text-xl font-bold text-navy">{% trans "Involved Departments" %}</h3>
|
|
{% if can_edit and complaint.is_active_status %}
|
|
<a href="{% url 'complaints:involved_department_add' complaint_pk=complaint.pk %}"
|
|
class="px-4 py-2 bg-gradient-to-r from-navy to-blue text-white rounded-xl font-semibold hover:opacity-90 transition flex items-center gap-2">
|
|
<i data-lucide="plus" class="w-4 h-4"></i> {% trans "Add Department" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if can_edit and complaint.is_active_status and ai_department_suggested %}
|
|
<div class="bg-blue-50 border border-blue-200 rounded-xl p-5 mb-4">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center">
|
|
<i data-lucide="sparkles" class="w-5 h-5 text-blue-600"></i>
|
|
</div>
|
|
<div>
|
|
<div class="flex items-center gap-2">
|
|
<h4 class="font-bold text-navy">{{ complaint.department.name }}</h4>
|
|
<span class="px-2 py-0.5 bg-blue-100 text-blue-700 rounded-lg text-xs font-semibold">{% trans "AI Suggestion" %}</span>
|
|
</div>
|
|
<p class="text-sm text-slate">{% trans "AI suggested this department based on the complaint analysis." %}</p>
|
|
</div>
|
|
</div>
|
|
<form method="post" action="{% url 'complaints:confirm_ai_department_suggestion' complaint_pk=complaint.pk %}" class="inline">
|
|
{% csrf_token %}
|
|
<button type="submit" class="px-4 py-2 bg-gradient-to-r from-navy to-blue text-white rounded-xl font-semibold hover:opacity-90 transition flex items-center gap-2">
|
|
<i data-lucide="check" class="w-4 h-4"></i> {% trans "Confirm" %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if complaint.involved_departments.exists %}
|
|
<div class="space-y-4">
|
|
{% for dept in complaint.involved_departments.all %}
|
|
<div class="bg-white border border-slate-200 rounded-xl p-5 hover:shadow-md transition {% if dept.is_primary %}border-l-4 border-l-navy{% endif %}">
|
|
<div class="flex justify-between items-start">
|
|
<div class="flex-1">
|
|
<div class="flex items-center gap-3 mb-2">
|
|
<h4 class="font-bold text-navy">{{ dept.department.name }}</h4>
|
|
{% if dept.is_primary %}
|
|
<span class="px-2 py-1 bg-navy text-white rounded-lg text-xs font-bold">{% trans "PRIMARY" %}</span>
|
|
{% endif %}
|
|
<span class="px-2 py-1 bg-light text-navy rounded-lg text-xs font-semibold">{{ dept.get_role_display }}</span>
|
|
</div>
|
|
|
|
{% if dept.assigned_to %}
|
|
<div class="flex items-center gap-2 text-sm text-slate mb-2">
|
|
<i data-lucide="user-check" class="w-4 h-4 text-blue"></i>
|
|
<span>{% trans "Assigned to:" %} <strong>{{ dept.assigned_to.get_full_name }}</strong></span>
|
|
{% if dept.assigned_at %}
|
|
<span class="text-slate">({{ dept.assigned_at|date:"M d, Y" }})</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if dept.notes %}
|
|
<div class="bg-slate-50 rounded-lg p-3 mt-2">
|
|
<p class="text-sm text-slate">{{ dept.notes }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if dept.response_submitted %}
|
|
{% if dept.manager_review_status == 'rejected' %}
|
|
<div class="rounded-lg p-3 mt-3 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>
|
|
{% elif dept.manager_review_status == 'pending' %}
|
|
<div class="rounded-lg p-3 mt-3 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 dept.acceptance_status == 'acceptable' %}
|
|
<div class="rounded-lg p-3 mt-3 border bg-green-50 border-green-200">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>
|
|
<span class="text-sm font-semibold text-green-700">{% trans "Accepted" %}</span>
|
|
<span class="text-xs text-slate">{{ dept.response_submitted_at|date:"M d, Y H:i" }}</span>
|
|
{% if dept.accepted_by %}
|
|
<span class="text-xs text-slate">— {% trans "Reviewed by" %} {{ dept.accepted_by.get_full_name }}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if dept.response_notes_en %}
|
|
<p class="text-sm text-slate-700 mt-1">{{ dept.response_notes_en }}</p>
|
|
{% endif %}
|
|
{% if dept.response_notes_ar %}
|
|
<p class="text-sm text-slate-700 mt-1" dir="rtl">{{ dept.response_notes_ar }}</p>
|
|
{% endif %}
|
|
{% if dept.acceptance_notes %}
|
|
<p class="text-xs text-slate mt-1 italic">{% trans "Review notes:" %} {{ dept.acceptance_notes }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% elif dept.acceptance_status == 'not_acceptable' %}
|
|
<div class="rounded-lg p-3 mt-3 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 PX Admin" %}</span>
|
|
</div>
|
|
{% if dept.acceptance_notes %}
|
|
<p class="text-xs text-slate mt-1 italic">{% trans "Reason:" %} {{ dept.acceptance_notes }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% elif dept.acceptance_status == 'pending' and dept.manager_review_status == 'approved' %}
|
|
<div class="rounded-lg p-3 mt-3 border bg-green-50 border-green-200">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>
|
|
<span class="text-sm font-semibold text-green-700">{% trans "Manager Approved" %}</span>
|
|
<span class="text-xs text-slate">{{ dept.response_submitted_at|date:"M d, Y H:i" }}</span>
|
|
</div>
|
|
{% if dept.response_notes_en %}
|
|
<p class="text-sm text-slate-700 mt-1">{{ dept.response_notes_en }}</p>
|
|
{% endif %}
|
|
{% if dept.response_notes_ar %}
|
|
<p class="text-sm text-slate-700 mt-1" dir="rtl">{{ dept.response_notes_ar }}</p>
|
|
{% endif %}
|
|
{% if can_review_dept_response %}
|
|
<div class="flex gap-2 mt-3">
|
|
<form method="post" action="{% url 'complaints:involved_department_review_response' pk=dept.pk %}" class="inline">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="acceptance_status" value="acceptable">
|
|
<button type="submit" class="px-3 py-1.5 bg-green-600 text-white text-xs font-bold rounded-lg hover:bg-green-700 transition flex items-center gap-1">
|
|
<i data-lucide="check" class="w-3 h-3"></i> {% trans "Acceptable" %}
|
|
</button>
|
|
</form>
|
|
<form method="post" action="{% url 'complaints:involved_department_review_response' pk=dept.pk %}" class="inline">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="acceptance_status" value="not_acceptable">
|
|
<input type="text" name="acceptance_notes" placeholder="{% trans 'Rejection reason...' %}" required
|
|
class="px-2 py-1 border rounded-lg text-xs w-48">
|
|
<button type="submit" class="px-3 py-1.5 bg-red-600 text-white text-xs font-bold rounded-lg hover:bg-red-700 transition flex items-center gap-1">
|
|
<i data-lucide="x" class="w-3 h-3"></i> {% trans "Not Acceptable" %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="rounded-lg p-3 mt-3 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 Review" %}</span>
|
|
<span class="text-xs text-slate">{{ dept.response_submitted_at|date:"M d, Y H:i" }}</span>
|
|
</div>
|
|
{% if dept.response_notes_en %}
|
|
<p class="text-sm text-slate-700 mt-1">{{ dept.response_notes_en }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if can_edit and complaint.is_active_status %}
|
|
<div class="flex items-center gap-2 ml-4">
|
|
{% if not dept.response_submitted %}
|
|
<button type="button"
|
|
onclick="openDeptResponseModal('complaint', '{{ dept.pk }}', '{% url 'complaints:involved_department_response' pk=dept.pk %}', '{{ complaint.reference_number }}', '{{ dept.department.name|escapejs }}')"
|
|
class="p-2 text-slate hover:text-blue transition" title="{% trans 'Submit Response' %}">
|
|
<i data-lucide="message-square" class="w-5 h-5"></i>
|
|
</button>
|
|
{% endif %}
|
|
<a href="{% url 'complaints:involved_department_edit' pk=dept.pk %}"
|
|
class="p-2 text-slate hover:text-navy transition" title="{% trans 'Edit' %}">
|
|
<i data-lucide="edit-2" class="w-5 h-5"></i>
|
|
</a>
|
|
<form method="post" action="{% url 'complaints:involved_department_remove' pk=dept.pk %}"
|
|
class="inline" onsubmit="return confirm('{% trans "Are you sure you want to remove this department?" %}')">
|
|
{% csrf_token %}
|
|
<button type="submit" class="p-2 text-slate hover:text-red-500 transition" title="{% trans 'Remove' %}">
|
|
<i data-lucide="trash-2" class="w-5 h-5"></i>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-12">
|
|
<i data-lucide="building-2" class="w-16 h-16 mx-auto text-slate-300 mb-4"></i>
|
|
<p class="text-slate mb-4">{% trans "No departments involved yet" %}</p>
|
|
{% if can_edit and complaint.is_active_status %}
|
|
<a href="{% url 'complaints:involved_department_add' complaint_pk=complaint.pk %}"
|
|
class="px-4 py-2 bg-gradient-to-r from-navy to-blue text-white rounded-xl font-semibold hover:opacity-90 transition inline-flex items-center gap-2">
|
|
<i data-lucide="plus" class="w-4 h-4"></i> {% trans "Add First Department" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</section>
|