657 lines
36 KiB
HTML
657 lines
36 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ complaint.reference_number }} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.tab-active {
|
|
border-bottom: 3px solid #005696;
|
|
color: #005696;
|
|
font-weight: 700;
|
|
}
|
|
.tab-inactive {
|
|
color: #64748b;
|
|
font-weight: 500;
|
|
}
|
|
.tab-inactive:hover {
|
|
color: #005696;
|
|
}
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
}
|
|
.timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 8px;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: #e5e7eb;
|
|
}
|
|
.timeline-item {
|
|
position: relative;
|
|
padding-bottom: 24px;
|
|
}
|
|
.timeline-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -26px;
|
|
top: 2px;
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 50%;
|
|
background: white;
|
|
border: 3px solid #005696;
|
|
z-index: 1;
|
|
}
|
|
.timeline-item.status_change::before { border-color: #f97316; }
|
|
.timeline-item.assignment::before { border-color: #3b82f6; }
|
|
.timeline-item.escalation::before { border-color: #ef4444; }
|
|
.timeline-item.note::before { border-color: #22c55e; }
|
|
|
|
.inner-tab-active {
|
|
border-bottom: 2px solid #005696;
|
|
color: #005696;
|
|
font-weight: 700;
|
|
background: white;
|
|
}
|
|
.inner-tab-inactive {
|
|
color: #64748b;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
|
|
|
|
<!-- Breadcrumb & Header -->
|
|
<header class="mb-6">
|
|
<div class="flex items-center gap-2 text-sm text-slate mb-2">
|
|
<a href="{% url 'complaints:complaint_list' %}" class="hover:text-navy">{% trans "Cases" %}</a>
|
|
<i data-lucide="chevron-right" class="w-4 h-4"></i>
|
|
<span class="font-bold text-navy">{{ complaint.reference_number }}</span>
|
|
<span class="ml-2 px-2 py-0.5 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-gray-100 text-gray-700
|
|
{% else %}bg-gray-100 text-gray-700{% endif %}">
|
|
{{ complaint.get_status_display }}
|
|
</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<h1 class="text-2xl font-bold text-navy">{{ complaint.title }}</h1>
|
|
<div class="flex items-center gap-3">
|
|
{% comment %} <a href="{% url 'complaints:complaint_pdf' complaint.id %}" target="_blank"
|
|
class="text-slate hover:text-navy px-3 py-2 text-sm font-semibold flex items-center gap-2 border rounded-lg hover:bg-light transition">
|
|
<i data-lucide="printer" class="w-4 h-4"></i> {% trans "PDF View" %}
|
|
</a> {% endcomment %}
|
|
{% if can_edit and complaint.is_active_status and complaint.assigned_to == current_user %}
|
|
<button onclick="showResolveModal()" class="bg-navy text-white px-4 py-2 rounded-lg text-sm font-bold shadow-md hover:bg-blue transition">
|
|
{% trans "Resolve Case" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Tab Navigation -->
|
|
<nav class="bg-white px-6 flex gap-6 border-b shadow-sm mb-6 rounded-t-2xl">
|
|
<button class="py-4 text-sm tab-active" onclick="switchTab('details')" id="tab-details">
|
|
{% trans "Details" %}
|
|
</button>
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('departments')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-departments">
|
|
{% trans "Departments" %} ({{ complaint.involved_departments_count }})
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button>
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('staff')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-staff">
|
|
{% trans "Staff" %} ({{ complaint.involved_staff_count }})
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button>
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('timeline')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-timeline">
|
|
{% trans "Timeline" %} ({{ complaint.updates_count }})
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button>
|
|
{% comment %} <button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('attachments')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-attachments">
|
|
{% trans "Attachments" %} ({{ complaint.attachments_count }})
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button> {% endcomment %}
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('actions')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-actions">
|
|
{% trans "PX Actions" %} ({{ px_actions.count }})
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button>
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %} flex items-center gap-2"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('ai')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-ai">
|
|
<i data-lucide="sparkles" class="w-4 h-4 {% if not complaint.assigned_to == current_user and complaint.is_active_status %}text-slate-300{% endif %}"></i> {% trans "AI Analysis" %}
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3"></i>{% endif %}
|
|
</button>
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('explanation')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-explanation">
|
|
{% trans "Explanation" %}
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button>
|
|
<button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %}"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('resolution')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-resolution">
|
|
{% trans "Resolution" %}
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3 inline ml-1"></i>{% endif %}
|
|
</button>
|
|
{% comment %} <button class="py-4 text-sm {% if complaint.assigned_to == current_user or not complaint.is_active_status %}tab-inactive{% else %}text-slate-300 cursor-not-allowed{% endif %} flex items-center gap-1"
|
|
{% if complaint.assigned_to == current_user or not complaint.is_active_status %}onclick="switchTab('adverse')"{% else %}onclick="showActivationRequired()"{% endif %} id="tab-adverse">
|
|
<i data-lucide="shield-alert" class="w-4 h-4 {% if not complaint.assigned_to == current_user and complaint.is_active_status %}text-slate-300{% endif %}"></i>
|
|
{% trans "Adverse Actions" %}
|
|
{% if adverse_actions %}
|
|
<span class="ml-1 px-1.5 py-0.5 bg-red-100 text-red-700 text-xs rounded-full">{{ adverse_actions.count }}</span>
|
|
{% endif %}
|
|
{% if not complaint.assigned_to == current_user and complaint.is_active_status %}<i data-lucide="lock" class="w-3 h-3"></i>{% endif %}
|
|
</button> {% endcomment %}
|
|
</nav>
|
|
|
|
<!-- Main Content Grid -->
|
|
<main class="grid grid-cols-12 gap-6">
|
|
|
|
<!-- Left Column (Content) -->
|
|
<div class="col-span-8 space-y-6">
|
|
|
|
<!-- Details Tab -->
|
|
<div id="panel-details" class="tab-panel">
|
|
<!-- Complaint Info Card -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 mb-6">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="file-warning" class="w-5 h-5 text-blue"></i>
|
|
{% trans "Complaint Details" %}
|
|
</h3>
|
|
{% if complaint.source %}
|
|
<span class="text-[10px] text-slate font-bold uppercase tracking-widest">
|
|
{% trans "Source:" %} {{ complaint.source.name_en }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if complaint.description %}
|
|
<div class="bg-light/40 p-4 rounded-xl mb-6 border-l-4 border-blue">
|
|
<p class="text-sm leading-relaxed text-slate italic">
|
|
"{{ complaint.description }}"
|
|
</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="grid grid-cols-3 gap-6">
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-bold text-slate uppercase">{% trans "Location" %}</p>
|
|
<p class="text-sm font-bold text-navy">
|
|
{% if complaint.location %}{{ complaint.location.name }}{% else %}-{% endif %}
|
|
</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-bold text-slate uppercase">{% trans "Severity" %}</p>
|
|
<p class="text-sm font-bold
|
|
{% if complaint.severity == 'critical' %}text-red-600
|
|
{% elif complaint.severity == 'high' %}text-orange-600
|
|
{% elif complaint.severity == 'medium' %}text-yellow-600
|
|
{% else %}text-green-600{% endif %}">
|
|
{{ complaint.get_severity_display }}
|
|
</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="text-[10px] font-bold text-slate uppercase">{% trans "Response Deadline" %}</p>
|
|
<p class="text-sm font-bold {% if complaint.is_overdue %}text-red-600{% else %}text-navy{% endif %}">
|
|
{{ complaint.due_at|date:"d M Y, h:i A" }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{% if complaint.expected_result %}
|
|
<div class="mt-6 pt-4 border-t border-slate-100">
|
|
<p class="text-[10px] font-bold text-slate uppercase mb-2">{% trans "Expected Result" %}</p>
|
|
<p class="text-sm text-slate">{{ complaint.expected_result }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<!-- Classification -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 mb-6">
|
|
<h3 class="font-bold text-navy mb-4 flex items-center gap-2">
|
|
<i data-lucide="layers" class="w-5 h-5 text-blue"></i>
|
|
{% trans "Classification" %}
|
|
</h3>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
{% if complaint.domain %}
|
|
<div class="bg-blue-50 p-3 rounded-xl">
|
|
<p class="text-[10px] font-bold text-blue uppercase">{% trans "Domain" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.domain.name_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if complaint.category %}
|
|
<div class="bg-sky-50 p-3 rounded-xl">
|
|
<p class="text-[10px] font-bold text-sky uppercase">{% trans "Category" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.category.name_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if complaint.subcategory_obj %}
|
|
<div class="bg-green-50 p-3 rounded-xl">
|
|
<p class="text-[10px] font-bold text-green uppercase">{% trans "Subcategory" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.subcategory_obj.name_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if complaint.classification_obj %}
|
|
<div class="bg-yellow-50 p-3 rounded-xl">
|
|
<p class="text-[10px] font-bold text-yellow uppercase">{% trans "Classification" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.classification_obj.name_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Patient Info -->
|
|
{% if complaint.patient %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 flex items-center gap-2">
|
|
<i data-lucide="user" class="w-5 h-5 text-blue"></i>
|
|
{% trans "Patient Information" %}
|
|
</h3>
|
|
<div class="grid grid-cols-3 gap-4">
|
|
<div>
|
|
<p class="text-[10px] font-bold text-slate uppercase">{% trans "Name" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.patient.get_full_name }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-[10px] font-bold text-slate uppercase">{% trans "MRN" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.patient.mrn|default:"-" }}</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-[10px] font-bold text-slate uppercase">{% trans "Phone" %}</p>
|
|
<p class="text-sm font-bold text-navy">{{ complaint.patient.phone|default:"-" }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Departments Tab -->
|
|
<div id="panel-departments" class="tab-panel hidden">
|
|
{% include "complaints/partials/departments_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Staff Tab -->
|
|
<div id="panel-staff" class="tab-panel hidden">
|
|
{% include "complaints/partials/staff_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Timeline Tab -->
|
|
<div id="panel-timeline" class="tab-panel hidden">
|
|
{% include "complaints/partials/timeline_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Attachments Tab -->
|
|
<div id="panel-attachments" class="tab-panel hidden">
|
|
{% include "complaints/partials/attachments_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Actions Tab -->
|
|
<div id="panel-actions" class="tab-panel hidden">
|
|
{% include "complaints/partials/actions_panel.html" %}
|
|
</div>
|
|
|
|
<!-- AI Analysis Tab -->
|
|
<div id="panel-ai" class="tab-panel hidden">
|
|
{% include "complaints/partials/ai_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Explanation Tab -->
|
|
<div id="panel-explanation" class="tab-panel hidden">
|
|
{% include "complaints/partials/explanation_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Resolution Tab -->
|
|
<div id="panel-resolution" class="tab-panel hidden">
|
|
{% include "complaints/partials/resolution_panel.html" %}
|
|
</div>
|
|
|
|
<!-- Adverse Actions Tab -->
|
|
<div id="panel-adverse" class="tab-panel hidden">
|
|
{% include "complaints/partials/adverse_actions_panel.html" %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column (Sidebar) -->
|
|
<div class="col-span-4 space-y-6">
|
|
|
|
<!-- Quick Actions -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm">{% trans "Quick Actions" %}</h3>
|
|
<div class="grid grid-cols-2 gap-3">
|
|
{% if can_edit and complaint.is_active_status %}
|
|
<!-- Activate Button - Always show when can edit and status is active -->
|
|
<form method="post" action="{% url 'complaints:complaint_activate' pk=complaint.pk %}" class="contents">
|
|
{% csrf_token %}
|
|
<button type="submit"
|
|
class="p-3 border-blue-200 bg-blue-50 rounded-xl hover:bg-blue-100 flex flex-col items-center gap-2 group transition col-span-2 mb-2"
|
|
{% if complaint.assigned_to == current_user %}disabled style="opacity: 0.5; cursor: not-allowed;"{% endif %}>
|
|
<i data-lucide="{% if complaint.assigned_to == current_user %}check-circle{% else %}play-circle{% endif %}" class="w-5 h-5 text-blue"></i>
|
|
<span class="text-[10px] font-bold text-blue uppercase">
|
|
{% if complaint.assigned_to == current_user %}{% trans "Activated" %}{% else %}{% trans "Activate" %}{% endif %}
|
|
</span>
|
|
</button>
|
|
</form>
|
|
|
|
{% if complaint.assigned_to == current_user %}
|
|
<!-- Action buttons only shown when activated -->
|
|
<button onclick="showResolveModal()" class="p-3 border rounded-xl hover:bg-light flex flex-col items-center gap-2 group transition">
|
|
<i data-lucide="check-circle-2" class="w-5 h-5 text-slate group-hover:text-green-600"></i>
|
|
<span class="text-[10px] font-bold uppercase">{% trans "Resolve" %}</span>
|
|
</button>
|
|
<button onclick="showFollowUpModal()" class="p-3 border rounded-xl hover:bg-light flex flex-col items-center gap-2 group transition">
|
|
<i data-lucide="clock" class="w-5 h-5 text-slate group-hover:text-orange-500"></i>
|
|
<span class="text-[10px] font-bold uppercase">{% trans "Follow Up" %}</span>
|
|
</button>
|
|
<button onclick="showEscalateModal()" class="p-3 border-red-100 bg-red-50 rounded-xl hover:bg-red-100 flex flex-col items-center gap-2 group transition">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 text-red-500"></i>
|
|
<span class="text-[10px] font-bold text-red-600 uppercase">{% trans "Escalate" %}</span>
|
|
</button>
|
|
{% else %}
|
|
<!-- Show message that activation is required -->
|
|
<div class="col-span-2 bg-yellow-50 border border-yellow-200 rounded-xl p-3 text-center">
|
|
<i data-lucide="lock" class="w-4 h-4 text-yellow-600 mx-auto mb-1"></i>
|
|
<p class="text-xs text-yellow-700">{% trans "Activate this complaint to perform actions" %}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="col-span-2 text-center py-4 text-slate text-sm">
|
|
{% trans "No actions available for this status" %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Staff Assignment -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm">
|
|
{% trans "Staff Assignment" %} ({{ complaint.involved_staff_count }})
|
|
</h3>
|
|
{% if complaint.involved_staff.exists %}
|
|
<div class="space-y-3">
|
|
{% for staff_inv in complaint.involved_staff.all|slice:":3" %}
|
|
<div class="flex items-center gap-3 p-3 bg-light/30 rounded-xl">
|
|
<div class="w-10 h-10 bg-navy rounded-full flex items-center justify-center text-white font-bold text-sm">
|
|
{{ staff_inv.staff.first_name|first }}{{ staff_inv.staff.last_name|first }}
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<p class="text-sm font-bold text-navy truncate">{{ staff_inv.staff }}</p>
|
|
<p class="text-[10px] text-slate">{{ staff_inv.get_role_display }}</p>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% if complaint.involved_staff_count > 3 %}
|
|
<button onclick="switchTab('staff')" class="text-blue text-xs font-bold hover:underline w-full text-center">
|
|
{% trans "View all" %} {{ complaint.involved_staff_count }} {% trans "staff" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="bg-light/30 border-2 border-dashed border-slate-200 rounded-2xl p-6 text-center">
|
|
<div class="w-10 h-10 bg-slate-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
|
<i data-lucide="user" class="text-slate-400 w-6 h-6"></i>
|
|
</div>
|
|
<p class="text-[11px] text-slate font-medium mb-3 tracking-tight">
|
|
{% trans "No staff assigned to this case yet." %}
|
|
</p>
|
|
{% if can_edit %}
|
|
<a href="{% url 'complaints:involved_staff_add' complaint_pk=complaint.pk %}"
|
|
class="bg-white border text-blue text-[11px] font-bold px-4 py-2 rounded-lg hover:shadow-sm inline-block">
|
|
{% trans "Select Staff" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<!-- Assignment Info -->
|
|
<section class="bg-navy rounded-2xl p-6 shadow-lg text-white">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<i data-lucide="info" class="w-5 h-5 text-blue"></i>
|
|
<h3 class="font-bold text-sm">{% trans "Assignment Info" %}</h3>
|
|
</div>
|
|
<ul class="space-y-3 text-[11px] opacity-90">
|
|
<li class="flex justify-between border-b border-white/10 pb-2">
|
|
<span>{% trans "Main Dept:" %}</span>
|
|
<span class="font-bold">{{ complaint.department.name|default:"-" }}</span>
|
|
</li>
|
|
<li class="flex justify-between border-b border-white/10 pb-2">
|
|
<span>{% trans "Assigned To:" %}</span>
|
|
<span class="font-bold">{{ complaint.assigned_to.get_full_name|default:"Unassigned" }}</span>
|
|
</li>
|
|
<li class="flex justify-between border-b border-white/10 pb-2">
|
|
<span>{% trans "TAT Goal:" %}</span>
|
|
<span class="font-bold">{{ complaint.due_at|timeuntil }}</span>
|
|
</li>
|
|
<li class="flex justify-between">
|
|
<span>{% trans "Status:" %}</span>
|
|
<span class="font-bold uppercase text-blue">{{ complaint.get_status_display }}</span>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<!-- Departments Summary -->
|
|
{% if complaint.involved_departments_count > 0 %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm">
|
|
{% trans "Involved Departments" %} ({{ complaint.involved_departments_count }})
|
|
</h3>
|
|
<div class="space-y-2">
|
|
{% for dept in complaint.involved_departments.all %}
|
|
<div class="flex items-center gap-2 p-2 rounded-lg {% if dept.is_primary %}bg-light{% else %}bg-slate-50{% endif %}">
|
|
<i data-lucide="building-2" class="w-4 h-4 {% if dept.is_primary %}text-navy{% else %}text-slate{% endif %}"></i>
|
|
<span class="text-xs font-medium {% if dept.is_primary %}text-navy font-bold{% else %}text-slate{% endif %}">
|
|
{{ dept.department.name }}
|
|
</span>
|
|
{% if dept.is_primary %}
|
|
<span class="ml-auto text-[9px] bg-navy text-white px-1.5 py-0.5 rounded">{% trans "PRIMARY" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Resolve Modal -->
|
|
<div id="resolveModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-lg mx-4 shadow-2xl">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center">
|
|
<i data-lucide="check-circle" class="w-5 h-5 text-green-600"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy">{% trans "Resolve Complaint" %}</h3>
|
|
</div>
|
|
<p class="text-slate mb-4 text-sm">{% trans "Please provide resolution details before marking this complaint as resolved." %}</p>
|
|
<form method="post" action="{% url 'complaints:complaint_change_status' pk=complaint.pk %}">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="status" value="resolved">
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Resolution Notes" %} <span class="text-red-500">*</span></label>
|
|
<textarea name="resolution" rows="4" class="w-full border border-slate-200 rounded-xl p-4 text-sm focus:ring-2 focus:ring-navy/20 outline-none" placeholder="{% trans 'Enter resolution details...' %}" required></textarea>
|
|
</div>
|
|
<div class="flex gap-3">
|
|
<button type="button" onclick="closeModal('resolveModal')" class="flex-1 px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-slate-50 transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" class="flex-1 px-4 py-2 bg-green-500 text-white rounded-xl font-semibold hover:bg-green-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i> {% trans "Mark Resolved" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Assign Modal -->
|
|
<div id="assignModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-lg mx-4 shadow-2xl">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
|
|
<i data-lucide="user-plus" class="w-5 h-5 text-blue"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy">{% trans "Assign Complaint" %}</h3>
|
|
</div>
|
|
<p class="text-slate mb-4 text-sm">{% trans "Select a user to assign this complaint to." %}</p>
|
|
<form method="post" action="{% url 'complaints:complaint_assign' pk=complaint.pk %}">
|
|
{% csrf_token %}
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Assign To" %} <span class="text-red-500">*</span></label>
|
|
<select name="user_id" class="w-full border border-slate-200 rounded-xl p-3 text-sm focus:ring-2 focus:ring-navy/20 outline-none" required>
|
|
<option value="">{% trans "Select User" %}</option>
|
|
{% for user in assignable_users %}
|
|
<option value="{{ user.id }}">{{ user.get_full_name }} {% if user.department %}({{ user.department.name }}){% endif %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="flex gap-3">
|
|
<button type="button" onclick="closeModal('assignModal')" class="flex-1 px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-slate-50 transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" class="flex-1 px-4 py-2 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition flex items-center justify-center gap-2">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i> {% trans "Assign" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Follow Up Modal -->
|
|
<div id="followUpModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-lg mx-4 shadow-2xl">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<div class="w-10 h-10 bg-orange-100 rounded-full flex items-center justify-center">
|
|
<i data-lucide="clock" class="w-5 h-5 text-orange-500"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy">{% trans "Add Follow Up Note" %}</h3>
|
|
</div>
|
|
<p class="text-slate mb-4 text-sm">{% trans "Add a note or update about this complaint." %}</p>
|
|
<form method="post" action="{% url 'complaints:complaint_add_note' pk=complaint.pk %}">
|
|
{% csrf_token %}
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Note" %} <span class="text-red-500">*</span></label>
|
|
<textarea name="note" rows="4" class="w-full border border-slate-200 rounded-xl p-4 text-sm focus:ring-2 focus:ring-navy/20 outline-none" placeholder="{% trans 'Enter your note...' %}" required></textarea>
|
|
</div>
|
|
<div class="flex gap-3">
|
|
<button type="button" onclick="closeModal('followUpModal')" class="flex-1 px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-slate-50 transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" class="flex-1 px-4 py-2 bg-orange-500 text-white rounded-xl font-semibold hover:bg-orange-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="plus" class="w-4 h-4"></i> {% trans "Add Note" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Escalate Modal -->
|
|
<div id="escalateModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center">
|
|
<div class="bg-white rounded-2xl p-6 w-full max-w-lg mx-4 shadow-2xl">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<div class="w-10 h-10 bg-red-100 rounded-full flex items-center justify-center">
|
|
<i data-lucide="alert-triangle" class="w-5 h-5 text-red-500"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy">{% trans "Escalate Complaint" %}</h3>
|
|
</div>
|
|
<p class="text-slate mb-4 text-sm">{% trans "Escalate this complaint to a manager or higher authority." %}</p>
|
|
<form method="post" action="{% url 'complaints:complaint_escalate' pk=complaint.pk %}">
|
|
{% csrf_token %}
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Escalate To" %}</label>
|
|
<select name="escalate_to" class="w-full border border-slate-200 rounded-xl p-3 text-sm focus:ring-2 focus:ring-navy/20 outline-none">
|
|
<option value="">{% trans "Select Manager (Optional)" %}</option>
|
|
{% for target in escalation_targets %}
|
|
<option value="{{ target.staff.id }}" {% if target.is_line_manager %}selected{% endif %}>
|
|
{{ target.staff.get_full_name }} {% if target.is_line_manager %}{% trans "(Line Manager)" %}{% elif target.is_manager %}{% trans "(Manager)" %}{% endif %}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<p class="text-xs text-slate mt-1">{% trans "If not selected, will escalate to the staff's direct manager." %}</p>
|
|
</div>
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Reason for Escalation" %} <span class="text-red-500">*</span></label>
|
|
<textarea name="reason" rows="3" class="w-full border border-slate-200 rounded-xl p-4 text-sm focus:ring-2 focus:ring-navy/20 outline-none" placeholder="{% trans 'Enter escalation reason...' %}" required></textarea>
|
|
</div>
|
|
<div class="flex gap-3">
|
|
<button type="button" onclick="closeModal('escalateModal')" class="flex-1 px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-slate-50 transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" class="flex-1 px-4 py-2 bg-red-500 text-white rounded-xl font-semibold hover:bg-red-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="alert-triangle" class="w-4 h-4"></i> {% trans "Escalate" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab Switching JavaScript -->
|
|
<script>
|
|
function showActivationRequired() {
|
|
alert('{% trans "Please activate this complaint first to access this tab." %}');
|
|
}
|
|
|
|
function switchTab(tabName) {
|
|
// Hide all panels
|
|
document.querySelectorAll('.tab-panel').forEach(panel => {
|
|
panel.classList.add('hidden');
|
|
});
|
|
|
|
// Show selected panel
|
|
document.getElementById('panel-' + tabName).classList.remove('hidden');
|
|
|
|
// Update tab styles
|
|
document.querySelectorAll('nav button').forEach(tab => {
|
|
tab.classList.remove('tab-active');
|
|
tab.classList.add('tab-inactive');
|
|
});
|
|
|
|
document.getElementById('tab-' + tabName).classList.remove('tab-inactive');
|
|
document.getElementById('tab-' + tabName).classList.add('tab-active');
|
|
|
|
// Reinitialize icons for newly visible content
|
|
if (window.lucide) {
|
|
lucide.createIcons();
|
|
}
|
|
}
|
|
|
|
// Modal functions
|
|
function showResolveModal() {
|
|
document.getElementById('resolveModal').classList.remove('hidden');
|
|
}
|
|
|
|
function showAssignModal() {
|
|
document.getElementById('assignModal').classList.remove('hidden');
|
|
}
|
|
|
|
function showFollowUpModal() {
|
|
document.getElementById('followUpModal').classList.remove('hidden');
|
|
}
|
|
|
|
function showEscalateModal() {
|
|
document.getElementById('escalateModal').classList.remove('hidden');
|
|
}
|
|
|
|
function closeModal(modalId) {
|
|
document.getElementById(modalId).classList.add('hidden');
|
|
}
|
|
|
|
// Close modal when clicking outside
|
|
window.onclick = function(event) {
|
|
if (event.target.classList.contains('fixed')) {
|
|
event.target.classList.add('hidden');
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{% endblock %}
|