1324 lines
73 KiB
HTML
1324 lines
73 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Inquiry" %} #{{ inquiry.reference_number|truncatechars:15 }} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.page-header-gradient {
|
|
background: linear-gradient(135deg, #005696 0%, #0069a8 50%, #007bbd 100%);
|
|
color: white;
|
|
padding: 1.5rem 2rem;
|
|
border-radius: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 86, 150, 0.2);
|
|
}
|
|
.section-card {
|
|
background: white;
|
|
border-radius: 1rem;
|
|
border: 2px solid #e2e8f0;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.section-card:hover {
|
|
border-color: #005696;
|
|
box-shadow: 0 10px 25px -5px rgba(0, 86, 150, 0.15);
|
|
}
|
|
.section-header {
|
|
padding: 1rem 1.5rem;
|
|
border-bottom: 2px solid #e2e8f0;
|
|
background: linear-gradient(to right, #f8fafc, #f1f5f9);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
.section-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 0.75rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 2rem;
|
|
}
|
|
.timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 8px;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: #e2e8f0;
|
|
}
|
|
.timeline-item {
|
|
position: relative;
|
|
padding-bottom: 1.5rem;
|
|
}
|
|
.timeline-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -1.625rem;
|
|
top: 4px;
|
|
width: 14px;
|
|
height: 14px;
|
|
border-radius: 50%;
|
|
background: white;
|
|
border: 3px solid #007bbd;
|
|
z-index: 1;
|
|
}
|
|
.timeline-item.status_change::before { border-color: #f59e0b; }
|
|
.timeline-item.response::before { border-color: #10b981; }
|
|
.timeline-item.note::before { border-color: #005696; }
|
|
.timeline-item.sent_to_department::before { border-color: #007bbd; }
|
|
.timeline-item.department_response::before { border-color: #007bbd; }
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
.spinner {
|
|
display: inline-block;
|
|
width: 1rem;
|
|
height: 1rem;
|
|
border: 2px solid rgba(255,255,255,0.3);
|
|
border-top-color: white;
|
|
border-radius: 50%;
|
|
animation: spin 0.6s linear infinite;
|
|
}
|
|
.spinner-dark {
|
|
border-color: rgba(0,86,150,0.2);
|
|
border-top-color: #005696;
|
|
}
|
|
.ai-suggestion-card {
|
|
border: 2px dashed #bae6fd;
|
|
border-radius: 0.75rem;
|
|
padding: 1rem;
|
|
background: #eef6fb;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
}
|
|
.ai-suggestion-card:hover {
|
|
border-color: #007bbd;
|
|
background: #e0f2fe;
|
|
}
|
|
.ai-suggestion-card.selected {
|
|
border-style: solid;
|
|
border-color: #005696;
|
|
background: #e0f2fe;
|
|
box-shadow: 0 0 0 2px #005696;
|
|
}
|
|
.lang-tab {
|
|
padding: 0.5rem 1rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
border-radius: 0.5rem 0.5rem 0 0;
|
|
cursor: pointer;
|
|
border: 1px solid #e2e8f0;
|
|
border-bottom: none;
|
|
background: #f1f5f9;
|
|
color: #64748b;
|
|
transition: all 0.2s ease;
|
|
}
|
|
.lang-tab.active {
|
|
background: white;
|
|
color: #005696;
|
|
border-color: #bae6fd;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header-gradient">
|
|
<div class="flex justify-between items-center">
|
|
<div>
|
|
<div class="flex items-center gap-2 text-blue-100 text-sm mb-2">
|
|
{% if source_user %}
|
|
<a href="{% url 'px_sources:source_user_inquiry_list' %}" class="hover:text-white transition">{% trans "My Inquiries" %}</a>
|
|
{% else %}
|
|
<a href="{% url 'inquiries:inquiry_list' %}" class="hover:text-white transition">{% trans "Inquiries" %}</a>
|
|
{% endif %}
|
|
<i data-lucide="chevron-right" class="w-3 h-3"></i>
|
|
<span class="text-white">{% trans "Detail" %}</span>
|
|
</div>
|
|
<div class="flex flex-wrap items-center gap-3">
|
|
<h1 class="text-2xl font-bold">{{ inquiry.subject }}</h1>
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if inquiry.status == 'open' %}bg-yellow-100 text-yellow-700
|
|
{% elif inquiry.status == 'in_progress' %}bg-blue-100 text-blue-700
|
|
{% elif inquiry.status == 'resolved' %}bg-green-100 text-green-700
|
|
{% elif inquiry.status == 'closed' %}bg-slate-100 text-slate-600
|
|
{% elif inquiry.status == 'contacted' %}bg-purple-100 text-purple-700
|
|
{% elif inquiry.status == 'contacted_no_response' %}bg-slate-100 text-slate-600
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ inquiry.get_status_display }}
|
|
</span>
|
|
{% if inquiry.priority %}
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if inquiry.priority == 'critical' %}bg-red-500 text-white
|
|
{% elif inquiry.priority == 'high' %}bg-orange-100 text-orange-700
|
|
{% elif inquiry.priority == 'medium' %}bg-yellow-100 text-yellow-700
|
|
{% elif inquiry.priority == 'low' %}bg-green-100 text-green-700
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ inquiry.get_priority_display }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-blue-100 text-sm">
|
|
<i data-lucide="hash" class="w-3 h-3 inline-block mr-1"></i>{{ inquiry.reference_number|truncatechars:15 }}
|
|
<span class="mx-2">|</span>
|
|
<i data-lucide="calendar" class="w-3 h-3 inline-block mr-1"></i>{{ inquiry.created_at|date:"Y-m-d" }}
|
|
</span>
|
|
{% if can_respond %}
|
|
<button onclick="showRespondModal()" class="inline-flex items-center px-4 py-2.5 bg-white text-navy font-medium rounded-xl hover:bg-blue-50 transition text-sm">
|
|
<i data-lucide="message-square" class="w-4 h-4 me-2"></i>{% trans "Respond" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div class="lg:col-span-2 space-y-6">
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-cyan-500/10">
|
|
<i data-lucide="file-text" class="w-5 h-5 text-cyan-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Inquiry Details" %}</h5>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="prose prose-sm max-w-none">
|
|
<p class="text-slate-700 leading-relaxed">{{ inquiry.message|linebreaks }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-blue-500/20">
|
|
<i data-lucide="sparkles" class="w-5 h-5 text-blue-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-blue-800">{% trans "AI Analysis" %}</h5>
|
|
{% if can_respond and not inquiry.short_description_en %}
|
|
<div class="ml-auto">
|
|
<button id="reanalyzeBtn" onclick="reanalyzeAI()" class="inline-flex items-center gap-1 px-3 py-1.5 bg-white border border-slate-200 rounded-lg text-xs font-semibold text-navy hover:bg-slate-50 transition">
|
|
<i data-lucide="refresh-cw" class="w-3 h-3"></i>
|
|
{% trans "Re-analyze" %}
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="p-6" id="aiAnalysisContent">
|
|
{% if inquiry.short_description_en %}
|
|
<!-- Emotion Analysis -->
|
|
{% if inquiry.emotion and inquiry.emotion != 'neutral' %}
|
|
<div class="bg-light border border-blue-200 rounded-2xl p-5 mb-6">
|
|
<h4 class="font-bold text-navy mb-3 flex items-center gap-2">
|
|
<i data-lucide="cpu" class="w-5 h-5"></i> {% trans "Emotion Analysis" %}
|
|
</h4>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<span class="px-4 py-2 bg-navy text-white rounded-xl font-bold flex items-center gap-2">
|
|
<i data-lucide="frown" class="w-5 h-5"></i> {{ inquiry.get_emotion_display }}
|
|
</span>
|
|
<span class="text-slate">{% trans "Confidence" %}: {{ inquiry.emotion_confidence_percent|floatformat:0 }}%</span>
|
|
</div>
|
|
<div>
|
|
<div class="flex justify-between text-sm mb-1">
|
|
<span class="text-slate">{% trans "Intensity" %}</span>
|
|
<span class="font-semibold">{{ inquiry.emotion_intensity|floatformat:2 }} / 1.0</span>
|
|
</div>
|
|
<div class="h-2 bg-slate-200 rounded-full overflow-hidden">
|
|
<div class="h-full bg-navy rounded-full" style="width: {{ inquiry.emotion_intensity_percent }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- AI Summary English -->
|
|
{% if inquiry.short_description_en %}
|
|
<div class="bg-light/50 border border-slate-200 rounded-2xl p-5 mb-4">
|
|
<h4 class="font-bold text-navy mb-2">{% trans "AI Summary (English)" %}</h4>
|
|
<p class="text-slate">{{ inquiry.short_description_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- AI Summary Arabic -->
|
|
{% if inquiry.short_description_ar %}
|
|
<div class="bg-light/50 border border-slate-200 rounded-2xl p-5">
|
|
<h4 class="font-bold text-navy mb-2">{% trans "AI Summary (Arabic)" %}</h4>
|
|
<p class="text-slate" dir="rtl">{{ inquiry.short_description_ar }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% else %}
|
|
<div class="text-center py-8">
|
|
<i data-lucide="bot" class="w-12 h-12 mx-auto text-slate-300 mb-3"></i>
|
|
<p class="text-slate text-sm">{% trans "AI analysis is running. Please refresh the page shortly." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if inquiry.outgoing_department %}
|
|
<div class="section-card">
|
|
<div class="section-header {% if inquiry.department_responded_at %}bg-blue-50{% elif inquiry.dept_response_is_overdue %}bg-red-50{% else %}bg-amber-50{% endif %}">
|
|
<div class="section-icon {% if inquiry.department_responded_at %}bg-blue-500/20{% elif inquiry.dept_response_is_overdue %}bg-red-500/20{% else %}bg-amber-500/20{% endif %}">
|
|
<i data-lucide="building-2" class="w-5 h-5 {% if inquiry.department_responded_at %}text-blue-600{% elif inquiry.dept_response_is_overdue %}text-red-600{% else %}text-amber-600{% endif %}"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold {% if inquiry.department_responded_at %}text-blue-800{% elif inquiry.dept_response_is_overdue %}text-red-800{% else %}text-amber-800{% endif %}">
|
|
{% blocktrans with dept=inquiry.outgoing_department.get_localized_name %}Response from {{ dept }}{% endblocktrans %}
|
|
{% if inquiry.department_responded_at %}
|
|
<span class="text-xs font-normal text-navy ml-2">
|
|
<i data-lucide="check-circle" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "Received" %}
|
|
</span>
|
|
{% elif inquiry.dept_response_is_overdue %}
|
|
<span class="text-xs font-normal text-red-600 ml-2">
|
|
<i data-lucide="alert-triangle" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "OVERDUE" %}
|
|
</span>
|
|
{% else %}
|
|
<span class="text-xs font-normal text-amber-600 ml-2">
|
|
<i data-lucide="clock" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "Awaiting Response" %}
|
|
</span>
|
|
{% endif %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-6">
|
|
{% if inquiry.transferred_at %}
|
|
<div class="flex items-center gap-3 mb-4 text-sm text-slate-600">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="send" class="w-4 h-4"></i>
|
|
{% trans "Sent:" %} {{ inquiry.transferred_at|date:"Y-m-d H:i" }}
|
|
</span>
|
|
{% if inquiry.dept_response_sla_due_at and not inquiry.department_responded_at %}
|
|
<span>•</span>
|
|
<span class="flex items-center gap-1 {% if inquiry.dept_response_is_overdue %}text-red-600 font-semibold{% endif %}">
|
|
<i data-lucide="timer" class="w-4 h-4"></i>
|
|
{% trans "Deadline:" %} {{ inquiry.dept_response_sla_due_at|date:"Y-m-d H:i" }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if inquiry.department_responded_at %}
|
|
<div class="flex items-center gap-3 mb-4 text-sm text-slate-600">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="user" class="w-4 h-4"></i>
|
|
{{ inquiry.department_responded_by.get_full_name|default:"-" }}
|
|
</span>
|
|
<span>•</span>
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="calendar" class="w-4 h-4"></i>
|
|
{{ inquiry.department_responded_at|date:"Y-m-d H:i" }}
|
|
</span>
|
|
</div>
|
|
|
|
{% if inquiry.department_response_en %}
|
|
<div class="mb-4">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<p class="info-label">English</p>
|
|
</div>
|
|
<div class="prose prose-sm max-w-none bg-slate-50 rounded-xl p-4">
|
|
<p class="text-slate-700 leading-relaxed">{{ inquiry.department_response_en|linebreaks }}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if inquiry.department_response_ar %}
|
|
<div class="mb-4">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<p class="info-label">العربية</p>
|
|
</div>
|
|
<div class="prose prose-sm max-w-none bg-slate-50 rounded-xl p-4">
|
|
<p class="text-slate-700 leading-relaxed" dir="rtl">{{ inquiry.department_response_ar|linebreaks }}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- AI Summary of Department Response -->
|
|
{% if inquiry.department_response_summary_en %}
|
|
<div class="mt-4 bg-light border border-blue-200 rounded-xl p-4">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i data-lucide="sparkles" class="w-4 h-4 text-navy"></i>
|
|
<span class="text-sm font-bold text-navy">{% trans "AI Summary" %}</span>
|
|
</div>
|
|
<p class="text-sm text-slate-700 leading-relaxed">{{ inquiry.department_response_summary_en }}</p>
|
|
{% if inquiry.department_response_summary_ar %}
|
|
<p class="text-sm text-slate-700 leading-relaxed mt-2" dir="rtl">{{ inquiry.department_response_summary_ar }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Acceptance Review -->
|
|
<div class="mt-4 border rounded-xl p-4
|
|
{% if inquiry.dept_response_acceptance_status == 'acceptable' %}border-green-200 bg-green-50
|
|
{% elif inquiry.dept_response_acceptance_status == 'not_acceptable' %}border-red-200 bg-red-50
|
|
{% else %}border-yellow-200 bg-yellow-50{% endif %}">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-2">
|
|
{% if inquiry.dept_response_acceptance_status == 'acceptable' %}
|
|
<i data-lucide="check-circle" class="w-4 h-4 text-green-600"></i>
|
|
<span class="text-sm font-semibold text-green-700">{% trans "Accepted" %}</span>
|
|
{% elif inquiry.dept_response_acceptance_status == 'not_acceptable' %}
|
|
<i data-lucide="x-circle" class="w-4 h-4 text-red-600"></i>
|
|
<span class="text-sm font-semibold text-red-700">{% trans "Not Acceptable" %}</span>
|
|
{% else %}
|
|
<i data-lucide="clock" class="w-4 h-4 text-yellow-600"></i>
|
|
<span class="text-sm font-semibold text-yellow-700">{% trans "Pending Review" %}</span>
|
|
{% endif %}
|
|
{% if inquiry.dept_response_accepted_by %}
|
|
<span class="text-xs text-slate-500">by {{ inquiry.dept_response_accepted_by.get_full_name }}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if can_review_dept_response and inquiry.dept_response_acceptance_status == 'pending' %}
|
|
<div class="flex gap-2">
|
|
<form method="post" action="{% url 'inquiries:inquiry_review_dept_response' inquiry.pk %}">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="acceptance_status" value="acceptable">
|
|
<button type="submit" class="px-3 py-1.5 text-xs font-semibold text-white bg-green-600 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 'inquiries:inquiry_review_dept_response' inquiry.pk %}">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="acceptance_status" value="not_acceptable">
|
|
<button type="submit" class="px-3 py-1.5 text-xs font-semibold text-white bg-red-600 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>
|
|
{% if inquiry.dept_response_acceptance_notes %}
|
|
<p class="text-xs text-slate-600 mt-2">{{ inquiry.dept_response_acceptance_notes }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% else %}
|
|
<div class="text-center py-6">
|
|
<div class="inline-block w-8 h-8 border-4 border-amber-200 border-t-amber-500 rounded-full animate-spin mb-3"></div>
|
|
<p class="text-slate text-sm">{% trans "Waiting for department response..." %}</p>
|
|
{% if inquiry.dept_response_sla_due_at %}
|
|
<p class="text-xs text-slate-400 mt-1">
|
|
{% trans "Deadline:" %} {{ inquiry.dept_response_sla_due_at|date:"Y-m-d H:i" }}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
{% if can_send_reminder %}
|
|
<div class="mt-3 flex gap-2">
|
|
<form method="post" action="{% url 'inquiries:inquiry_send_dept_response_reminder' inquiry.pk %}" class="flex-1">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="reminder_type" value="first">
|
|
<button type="submit" class="w-full bg-amber-500 text-white px-4 py-2 rounded-xl font-semibold hover:bg-amber-600 transition flex items-center justify-center gap-2 text-sm">
|
|
<i data-lucide="bell" class="w-4 h-4"></i> {% trans "Send Reminder" %}
|
|
</button>
|
|
</form>
|
|
{% if inquiry.dept_response_reminder_sent_at %}
|
|
<form method="post" action="{% url 'inquiries:inquiry_send_dept_response_reminder' inquiry.pk %}" class="flex-1">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="reminder_type" value="second">
|
|
<button type="submit" class="w-full bg-red-500 text-white px-4 py-2 rounded-xl font-semibold hover:bg-red-600 transition flex items-center justify-center gap-2 text-sm">
|
|
<i data-lucide="alert-triangle" class="w-4 h-4"></i> {% trans "Urgent Reminder" %}
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if inquiry.response %}
|
|
<div class="section-card">
|
|
<div class="section-header bg-green-50">
|
|
<div class="section-icon bg-green-500/20">
|
|
<i data-lucide="circle-check" class="w-5 h-5 text-green-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-green-800">
|
|
{% trans "Response" %}
|
|
{% if inquiry.response_sent_at %}
|
|
<span class="text-xs font-normal text-green-600 ml-2">
|
|
<i data-lucide="send" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "Sent to inquirer" %}
|
|
</span>
|
|
{% endif %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="flex items-center gap-3 mb-4 text-sm text-slate-600">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="user" class="w-4 h-4"></i>
|
|
{% if inquiry.responded_by %}{{ inquiry.responded_by.get_full_name|default:inquiry.responded_by.email }}{% endif %}
|
|
</span>
|
|
<span>•</span>
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="calendar" class="w-4 h-4"></i>
|
|
{% if inquiry.response_sent_at %}{{ inquiry.response_sent_at|date:"Y-m-d H:i" }}{% else %}{{ inquiry.responded_at|date:"Y-m-d H:i" }}{% endif %}
|
|
</span>
|
|
</div>
|
|
{% if inquiry.response_en %}
|
|
<div class="mb-4">
|
|
<p class="info-label mb-1">English</p>
|
|
<div class="prose prose-sm max-w-none">
|
|
<p class="text-slate-700 leading-relaxed">{{ inquiry.response_en|linebreaks }}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if inquiry.response_ar %}
|
|
<div class="mb-2">
|
|
<p class="info-label mb-1">العربية</p>
|
|
<div class="prose prose-sm max-w-none">
|
|
<p class="text-slate-700 leading-relaxed" dir="rtl">{{ inquiry.response_ar|linebreaks }}</p>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if not inquiry.response_en and not inquiry.response_ar %}
|
|
<div class="prose prose-sm max-w-none">
|
|
<p class="text-slate-700 leading-relaxed">{{ inquiry.response|linebreaks }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-purple-500/10">
|
|
<i data-lucide="history" class="w-5 h-5 text-purple-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Timeline" %}</h5>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="timeline">
|
|
<!-- Created -->
|
|
<div class="timeline-item">
|
|
<p class="info-label">{% trans "Inquiry Created" %}</p>
|
|
<p class="text-sm text-slate-700">
|
|
{% trans "Created by" %} {% if inquiry.created_by %}{{ inquiry.created_by.get_full_name|default:inquiry.created_by.email }}{% endif %}
|
|
</p>
|
|
<p class="text-xs text-slate-500 mt-1">{{ inquiry.created_at|date:"Y-m-d H:i" }}</p>
|
|
</div>
|
|
|
|
<!-- Status Changes -->
|
|
{% for update in inquiry.updates.all %}
|
|
<div class="timeline-item {{ update.update_type }}">
|
|
<p class="info-label">
|
|
{% if update.update_type == 'status_change' %}{% trans "Status Changed" %}
|
|
{% elif update.update_type == 'response' %}{% trans "Response" %}
|
|
{% elif update.update_type == 'communication' %}{% trans "Communication" %}
|
|
{% else %}{% trans "Note" %}{% endif %}
|
|
</p>
|
|
<p class="text-sm text-slate-700">{{ update.message|truncatewords:30 }}</p>
|
|
{% if update.created_by %}
|
|
<p class="text-xs text-slate-500 mt-1">{% trans "by" %} {{ update.created_by.get_full_name|default:update.created_by.email }}</p>
|
|
{% endif %}
|
|
<p class="text-xs text-slate-500 mt-1">{{ update.created_at|date:"Y-m-d H:i" }}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="space-y-6">
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-green-500/10">
|
|
<i data-lucide="settings" class="w-5 h-5 text-green-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Actions" %}</h5>
|
|
</div>
|
|
<div class="p-4 space-y-2">
|
|
{% if can_respond %}
|
|
<button onclick="showRespondModal()" class="w-full px-4 py-2.5 bg-navy text-white rounded-xl font-semibold hover:bg-navy/90 transition text-sm inline-flex items-center justify-center gap-2">
|
|
<i data-lucide="message-square" class="w-4 h-4"></i>
|
|
{% trans "Send Response" %}
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if inquiry.status == 'resolved' or inquiry.status == 'closed' %}
|
|
{% if can_respond %}
|
|
<form method="post" action="{% url 'inquiries:inquiry_reopen' inquiry.pk %}">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="note" value="Inquiry reopened">
|
|
<button type="submit" class="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-amber-500 text-white rounded-xl font-bold hover:bg-amber-600 transition text-sm">
|
|
<i data-lucide="rotate-ccw" class="w-4 h-4"></i>
|
|
{% trans "Reopen" %}
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
{% if can_respond %}
|
|
<button onclick="showAssignModal()" class="w-full px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl font-semibold text-slate-600 hover:bg-slate-50 transition text-sm inline-flex items-center justify-center gap-2">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i>
|
|
{% if inquiry.assigned_to %}{% trans "Reassign" %}{% else %}{% trans "Assign" %}{% endif %}
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if can_edit and inquiry.status != 'closed' %}
|
|
<form method="post" action="{% url 'inquiries:inquiry_change_status' inquiry.pk %}" class="space-y-2">
|
|
{% csrf_token %}
|
|
<div>
|
|
<label class="text-[10px] font-bold text-slate uppercase tracking-wider mb-1 block">{% trans "Update Status" %}</label>
|
|
<select name="status" class="w-full border border-slate-200 rounded-xl p-2.5 text-sm focus:ring-2 focus:ring-navy/20 outline-none">
|
|
{% for val, label in status_choices %}
|
|
<option value="{{ val }}" {% if inquiry.status == val %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<button type="submit" class="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-orange-500 text-white rounded-xl font-bold hover:bg-orange-600 transition text-sm">
|
|
<i data-lucide="refresh-cw" class="w-4 h-4"></i>
|
|
{% trans "Change Status" %}
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
|
|
{% if can_edit and inquiry.status != 'closed' %}
|
|
<a href="{% url 'inquiries:inquiry_edit' inquiry.pk %}" class="flex items-center gap-2 p-3 rounded-lg hover:bg-slate-50 transition text-navy font-medium text-sm">
|
|
<i data-lucide="edit" class="w-4 h-4"></i>
|
|
{% trans "Edit Inquiry" %}
|
|
</a>
|
|
{% endif %}
|
|
|
|
<a href="{% url 'rca:rca_create' %}?related_model=inquiry&related_id={{ inquiry.pk }}"
|
|
class="flex items-center gap-2 p-3 rounded-lg hover:bg-blue-50 transition text-navy font-medium text-sm">
|
|
<i data-lucide="search" class="w-4 h-4"></i>
|
|
{% trans "Initiate RCA" %}
|
|
</a>
|
|
|
|
<a href="{% url 'projects:project_create' %}?related_model=inquiry&related_id={{ inquiry.pk }}"
|
|
class="flex items-center gap-2 p-3 rounded-lg hover:bg-teal-50 transition text-navy font-medium text-sm">
|
|
<i data-lucide="folder-plus" class="w-4 h-4"></i>
|
|
{% trans "Create QI Project" %}
|
|
</a>
|
|
|
|
<!-- Department Section -->
|
|
{% if can_respond %}
|
|
<div class="pt-3 mt-3 border-t border-slate-200">
|
|
<p class="text-[10px] font-bold text-slate uppercase tracking-wider mb-2">{% trans "Department Actions" %}</p>
|
|
|
|
{% if inquiry.outgoing_department %}
|
|
{% if not inquiry.department_responded_at %}
|
|
<a href="{% url 'inquiries:inquiry_department_response' inquiry.pk %}"
|
|
class="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-navy text-white rounded-xl font-bold hover:bg-blue transition text-sm mb-2">
|
|
<i data-lucide="message-square" class="w-4 h-4"></i>
|
|
{% trans "Respond as Department" %}
|
|
</a>
|
|
{% else %}
|
|
<div class="flex items-center gap-2 p-2 bg-green-50 rounded-lg text-green-700 text-xs font-semibold mb-2">
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
|
{% trans "Department has responded" %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="flex items-center gap-2 p-2 bg-green-50 rounded-lg text-green-700 text-xs font-semibold mb-2">
|
|
<i data-lucide="arrow-right-left" class="w-4 h-4"></i>
|
|
<span>{% trans "Transferred to" %} <strong>{{ inquiry.outgoing_department.get_localized_name }}</strong></span>
|
|
</div>
|
|
|
|
<button onclick="showSendModal('{{ inquiry.id }}', 'inquiry')" class="w-full inline-flex items-center justify-center gap-2 px-4 py-2 border-2 border-blue-300 text-navy rounded-xl font-semibold hover:bg-light transition text-sm">
|
|
<i data-lucide="repeat" class="w-4 h-4"></i>
|
|
{% trans "Send to Department" %}
|
|
</button>
|
|
{% else %}
|
|
<button onclick="showSendModal('{{ inquiry.id }}', 'inquiry')" class="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-navy text-white rounded-xl font-bold hover:bg-blue transition text-sm">
|
|
<i data-lucide="send" class="w-4 h-4"></i>
|
|
{% trans "Send to Department" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-navy/10">
|
|
<i data-lucide="user" class="w-5 h-5 text-navy"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Contact Information" %}</h5>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-24">{% trans "Name" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.contact_name|default:"-" }}</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-24">{% trans "Phone" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.contact_phone|default:"-" }}</span>
|
|
</div>
|
|
<div class="flex py-3">
|
|
<span class="text-sm font-semibold text-slate-500 w-24">{% trans "Email" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.contact_email|default:"-" }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Timeline -->
|
|
{% include "partials/stage_timeline.html" with stage_timeline=stage_timeline %}
|
|
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-blue-500/10">
|
|
<i data-lucide="building-2" class="w-5 h-5 text-blue-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Organization" %}</h5>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Hospital" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.hospital.name }}</span>
|
|
</div>
|
|
{% if inquiry.department %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Department" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.department.name }}</span>
|
|
</div>
|
|
{% endif %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Location" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{% if inquiry.location %}{{ inquiry.location.name }}{% else %}-{% endif %}</span>
|
|
</div>
|
|
{% if inquiry.main_section %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Section" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.main_section.name }}</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if inquiry.subsection %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Subsection" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.subsection.name }}</span>
|
|
</div>
|
|
{% endif %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Category" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ inquiry.get_category_display|default:"-" }}</span>
|
|
</div>
|
|
{% if inquiry.taxonomy_domain or inquiry.taxonomy_category or inquiry.taxonomy_subcategory or inquiry.taxonomy_classification %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "SHCT Taxonomy" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">
|
|
{{ inquiry.taxonomy_domain.name_en|default:"" }}
|
|
{% if inquiry.taxonomy_category %}> {{ inquiry.taxonomy_category.name_en }}{% endif %}
|
|
{% if inquiry.taxonomy_subcategory %}> {{ inquiry.taxonomy_subcategory.name_en }}{% endif %}
|
|
{% if inquiry.taxonomy_classification %}> {{ inquiry.taxonomy_classification.name_en }}{% endif %}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if inquiry.priority %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Priority" %}</span>
|
|
<span class="flex-1">
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if inquiry.priority == 'critical' %}bg-red-500 text-white
|
|
{% elif inquiry.priority == 'high' %}bg-orange-100 text-orange-700
|
|
{% elif inquiry.priority == 'medium' %}bg-yellow-100 text-yellow-700
|
|
{% elif inquiry.priority == 'low' %}bg-green-100 text-green-700
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ inquiry.get_priority_display }}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if inquiry.outgoing_department %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "Transferred Dept" %}</span>
|
|
<span class="flex-1 flex items-center gap-2">
|
|
<span class="text-sm text-gray-800">{{ inquiry.outgoing_department.get_localized_name }}</span>
|
|
{% if not inquiry.department_responded_at %}
|
|
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-amber-100 text-amber-700 uppercase">{% trans "Awaiting" %}</span>
|
|
{% else %}
|
|
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold bg-green-100 text-green-700 uppercase">{% trans "Responded" %}</span>
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if inquiry.timeline_sla %}
|
|
<div class="flex py-3">
|
|
<span class="text-sm font-semibold text-slate-500 w-28">{% trans "SLA" %}</span>
|
|
<span class="flex-1">
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if inquiry.timeline_sla == '24_hours' %}bg-green-100 text-green-700
|
|
{% elif inquiry.timeline_sla == '48_hours' %}bg-yellow-100 text-yellow-700
|
|
{% elif inquiry.timeline_sla == '72_hours' %}bg-orange-100 text-orange-700
|
|
{% else %}bg-red-100 text-red-700{% endif %}">
|
|
{{ inquiry.get_timeline_sla_display }}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-slate-500/10">
|
|
<i data-lucide="clipboard-list" class="w-5 h-5 text-slate-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Notes" %}</h5>
|
|
</div>
|
|
<div class="p-6 space-y-4">
|
|
{% if can_edit or inquiry.assigned_to == request.user %}
|
|
<form method="post" action="{% url 'inquiries:inquiry_update_contact_stage' pk=inquiry.pk %}" class="space-y-3">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="stage" value="notes">
|
|
<div>
|
|
<label class="info-label">{% trans "Staff Notes" %}</label>
|
|
<textarea name="staff_notes" rows="3" class="form-control text-sm" placeholder="{% trans 'Add staff notes...' %}">{{ inquiry.staff_notes }}</textarea>
|
|
</div>
|
|
{% if can_edit %}
|
|
<div>
|
|
<label class="info-label">{% trans "Supervisor Notes" %}</label>
|
|
<textarea name="supervisor_notes" rows="3" class="form-control text-sm" placeholder="{% trans 'Add supervisor notes...' %}">{{ inquiry.supervisor_notes }}</textarea>
|
|
</div>
|
|
{% endif %}
|
|
<button type="submit" class="w-full mt-2 px-4 py-2 bg-navy text-white text-sm font-semibold rounded-lg hover:bg-navy/90 transition">{% trans "Save Notes" %}</button>
|
|
</form>
|
|
{% else %}
|
|
{% if inquiry.staff_notes %}
|
|
<div>
|
|
<p class="info-label">{% trans "Staff Notes" %}</p>
|
|
<p class="text-sm text-slate-700 leading-relaxed">{{ inquiry.staff_notes|linebreaks }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% if inquiry.supervisor_notes %}
|
|
<div>
|
|
<p class="info-label">{% trans "Supervisor Notes" %}</p>
|
|
<p class="text-sm text-slate-700 leading-relaxed">{{ inquiry.supervisor_notes|linebreaks }}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if linked_rcas %}
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-purple-500/10">
|
|
<i data-lucide="search" class="w-5 h-5 text-purple-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Root Cause Analyses" %}</h5>
|
|
</div>
|
|
<div class="p-4 space-y-3">
|
|
{% for rca in linked_rcas %}
|
|
<a href="{% url 'rca:rca_detail' pk=rca.pk %}" class="block p-3 rounded-lg border border-slate-200 hover:border-blue-300 transition group">
|
|
<p class="text-sm font-bold text-navy group-hover:text-blue truncate">{{ rca.title }}</p>
|
|
<div class="flex items-center gap-2 mt-1">
|
|
<span class="px-2 py-0.5 rounded-full text-[10px] font-bold uppercase
|
|
{% if rca.status == 'draft' %}bg-slate-100 text-slate-600
|
|
{% elif rca.status == 'in_progress' %}bg-blue-100 text-blue-700
|
|
{% elif rca.status == 'review' %}bg-yellow-100 text-yellow-700
|
|
{% elif rca.status == 'approved' %}bg-green-100 text-green-700
|
|
{% elif rca.status == 'closed' %}bg-gray-100 text-gray-600{% endif %}">
|
|
{{ rca.get_status_display }}
|
|
</span>
|
|
<span class="text-[10px] text-slate">{{ rca.created_at|date:"M d, Y" }}</span>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Respond Modal -->
|
|
<div id="respondModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4">
|
|
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-3xl animate-in max-h-[90vh] overflow-y-auto">
|
|
<div class="p-6 border-b border-slate-200">
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-xl font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="message-square" class="w-5 h-5"></i>
|
|
{% trans "Send Response" %}
|
|
</h3>
|
|
<button type="button" onclick="closeRespondModal()" class="text-slate-400 hover:text-slate-600 transition">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<form method="post" action="{% url 'inquiries:inquiry_respond' inquiry.pk %}" id="respondForm">
|
|
{% csrf_token %}
|
|
<div class="p-6">
|
|
<!-- AI Summary Context -->
|
|
{% if inquiry.short_description_en or inquiry.short_description_ar or inquiry.ai_brief_en or inquiry.ai_brief_ar %}
|
|
<div class="bg-light/50 border border-slate-200 rounded-2xl p-4 mb-5">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i data-lucide="sparkles" class="w-4 h-4 text-navy"></i>
|
|
<span class="text-sm font-bold text-navy">{% trans "AI Summary" %}</span>
|
|
</div>
|
|
{% if inquiry.short_description_en or inquiry.ai_brief_en %}
|
|
<p class="text-sm text-slate-700 leading-relaxed">{% if inquiry.short_description_en %}{{ inquiry.short_description_en }}{% elif inquiry.ai_brief_en %}{{ inquiry.ai_brief_en }}{% endif %}</p>
|
|
{% endif %}
|
|
{% if inquiry.short_description_ar or inquiry.ai_brief_ar %}
|
|
<p class="text-sm text-slate-700 leading-relaxed mt-2" dir="rtl">{% if inquiry.short_description_ar %}{{ inquiry.short_description_ar }}{% elif inquiry.ai_brief_ar %}{{ inquiry.ai_brief_ar }}{% endif %}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Generate AI Response Button -->
|
|
<div class="mb-5">
|
|
<button type="button" id="generateAiBtn" onclick="generateAIResponse()" class="w-full inline-flex items-center justify-center gap-2 px-4 py-3 bg-navy text-white rounded-xl font-bold hover:bg-blue transition text-sm shadow-lg">
|
|
<i data-lucide="sparkles" class="w-4 h-4"></i>
|
|
{% trans "Generate AI Response" %}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- AI Suggestions (hidden by default) -->
|
|
<div id="aiSuggestions" class="hidden mb-5 space-y-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i data-lucide="sparkles" class="w-4 h-4 text-navy"></i>
|
|
<span class="text-sm font-semibold text-navy">{% trans "AI Generated Response (click to use)" %}</span>
|
|
</div>
|
|
<div id="aiSuggestionEn" onclick="useAISuggestion('en')" class="ai-suggestion-card">
|
|
<p class="info-label mb-1">English</p>
|
|
<p class="text-sm text-slate-700" id="aiSuggestionEnText"></p>
|
|
</div>
|
|
<div id="aiSuggestionAr" onclick="useAISuggestion('ar')" class="ai-suggestion-card">
|
|
<p class="info-label mb-1">العربية</p>
|
|
<p class="text-sm text-slate-700" dir="rtl" id="aiSuggestionArText"></p>
|
|
</div>
|
|
<button type="button" onclick="useBothAISuggestions()" class="w-full btn-primary justify-center mt-2">
|
|
<i data-lucide="check" class="w-4 h-4"></i>
|
|
{% trans "Use Both Suggestions" %}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- English Response -->
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Response (English)" %}
|
|
</label>
|
|
<textarea name="response_en" id="responseEn" rows="6"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none text-sm"
|
|
placeholder="{% trans 'Enter your response in English...' %}">{{ inquiry.response_en|default:"" }}</textarea>
|
|
</div>
|
|
|
|
<!-- Arabic Response -->
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Response (Arabic)" %}
|
|
</label>
|
|
<textarea name="response_ar" id="responseAr" rows="6" dir="rtl"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none text-sm"
|
|
placeholder="{% trans 'أدخل ردك باللغة العربية...' %}">{{ inquiry.response_ar|default:"" }}</textarea>
|
|
</div>
|
|
|
|
<p class="text-xs text-slate-400 mt-1">
|
|
<i data-lucide="info" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "At least one language is required. The response will be sent to the inquirer via SMS and Email." %}
|
|
</p>
|
|
</div>
|
|
<div class="p-6 border-t border-slate-200 flex gap-3">
|
|
<button type="submit" class="px-4 py-2.5 bg-navy text-white rounded-xl font-semibold hover:bg-navy/90 transition text-sm inline-flex items-center justify-center gap-2 flex-1 justify-center">
|
|
<i data-lucide="send" class="w-4 h-4"></i>
|
|
{% trans "Send Response" %}
|
|
</button>
|
|
<button type="button" onclick="closeRespondModal()" class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl font-semibold text-slate-600 hover:bg-slate-50 transition text-sm">
|
|
{% trans "Cancel" %}
|
|
</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 p-4">
|
|
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-md animate-in">
|
|
<div class="p-6 border-b border-slate-200">
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-xl font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="user-plus" class="w-5 h-5"></i>
|
|
{% if inquiry.assigned_to %}{% trans "Reassign Inquiry" %}{% else %}{% trans "Assign Inquiry" %}{% endif %}
|
|
</h3>
|
|
<button type="button" onclick="closeAssignModal()" class="text-slate-400 hover:text-slate-600 transition">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<form method="post" action="{% url 'inquiries:inquiry_assign' inquiry.pk %}">
|
|
{% csrf_token %}
|
|
<div class="p-6">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Assign To" %} <span class="text-red-500">*</span></label>
|
|
<select name="user_id" class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 text-sm" required>
|
|
<option value="">{% trans "Select User" %}</option>
|
|
{% for u in assignable_users %}
|
|
<option value="{{ u.id }}" {% if inquiry.assigned_to and inquiry.assigned_to.id == u.id %}selected{% endif %}>{{ u.get_full_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="p-6 border-t border-slate-200 flex gap-3">
|
|
<button type="submit" class="px-4 py-2.5 bg-navy text-white rounded-xl font-semibold hover:bg-navy/90 transition text-sm inline-flex items-center justify-center gap-2 flex-1 justify-center">
|
|
<i data-lucide="user-plus" class="w-4 h-4"></i>
|
|
{% if inquiry.assigned_to %}{% trans "Reassign" %}{% else %}{% trans "Assign" %}{% endif %}
|
|
</button>
|
|
<button type="button" onclick="closeAssignModal()" class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl font-semibold text-slate-600 hover:bg-slate-50 transition text-sm">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Transfer to Department Modal -->
|
|
<div id="sendToDeptModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4">
|
|
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg animate-in">
|
|
<div class="p-6 border-b border-slate-200">
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-xl font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="arrow-right-left" class="w-5 h-5"></i>
|
|
{% trans "Transfer to Department" %}
|
|
</h3>
|
|
<button type="button" onclick="closeSendToDeptModal()" class="text-slate-400 hover:text-slate-600 transition">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
<p class="text-sm text-slate mt-2">{% trans "Select a department to transfer this inquiry. Department champions will be notified to respond." %}</p>
|
|
</div>
|
|
<form method="post" action="{% url 'inquiries:inquiry_transfer_to_department' inquiry.pk %}">
|
|
{% csrf_token %}
|
|
<div class="p-6">
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Send To" %}</label>
|
|
<div class="flex gap-2">
|
|
<label class="flex-1 flex items-center gap-2 px-4 py-3 border-2 rounded-xl cursor-pointer transition-all border-navy bg-navy/5 text-navy font-semibold text-sm" id="inquiryRecipientLabelStaff" onclick="switchInquiryRecipientType('staff')">
|
|
<input type="radio" name="recipient_type" value="staff" checked class="accent-navy">
|
|
<i data-lucide="users" class="w-4 h-4"></i>
|
|
{% trans "Staff" %}
|
|
</label>
|
|
<label class="flex-1 flex items-center gap-2 px-4 py-3 border-2 rounded-xl cursor-pointer transition-all border-slate-200 text-slate-500 text-sm" id="inquiryRecipientLabelDeptEmail" onclick="switchInquiryRecipientType('department_email')">
|
|
<input type="radio" name="recipient_type" value="department_email" class="accent-navy">
|
|
<i data-lucide="building-2" class="w-4 h-4"></i>
|
|
{% trans "Department Email" %}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-4" id="inquiryDeptEmailHint" style="display:none;">
|
|
<div class="flex items-center gap-2 px-3 py-2 bg-amber-50 border border-amber-200 rounded-xl text-sm text-amber-700">
|
|
<i data-lucide="mail" class="w-4 h-4 shrink-0"></i>
|
|
<span>{% trans "The notification will be sent to the department's email address." %}</span>
|
|
</div>
|
|
</div>
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Department" %} <span class="text-red-500">*</span></label>
|
|
<select name="department_id" id="deptSelect" class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 text-sm" required>
|
|
<option value="">{% trans "Select Department" %}</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-4">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<label class="text-sm font-semibold text-navy">{% trans "Context / Note" %}</label>
|
|
{% if inquiry.short_description_en or inquiry.ai_brief_en %}
|
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 bg-light rounded-md text-[10px] font-bold text-navy uppercase">
|
|
<i data-lucide="sparkles" class="w-3 h-3"></i> {% trans "AI Summary" %}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="flex gap-1 mb-2">
|
|
<button type="button" class="lang-tab active" onclick="switchDeptNoteLang('en')" id="deptNoteTabEn">English</button>
|
|
<button type="button" class="lang-tab" onclick="switchDeptNoteLang('ar')" id="deptNoteTabAr">العربية</button>
|
|
</div>
|
|
<div id="deptNoteEn">
|
|
<textarea name="note_en" rows="4"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none text-sm"
|
|
placeholder="{% trans 'Add context or instructions for the department...' %}">{% if inquiry.short_description_en %}{{ inquiry.short_description_en }}{% elif inquiry.ai_brief_en %}{{ inquiry.ai_brief_en }}{% endif %}</textarea>
|
|
</div>
|
|
<div id="deptNoteAr" class="hidden">
|
|
<textarea name="note_ar" rows="4" dir="rtl"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl focus:outline-none focus:border-navy focus:ring-2 focus:ring-navy/20 resize-none text-sm"
|
|
placeholder="{% trans 'أضف سياقاً أو تعليمات للقسم...' %}">{% if inquiry.short_description_ar %}{{ inquiry.short_description_ar }}{% elif inquiry.ai_brief_ar %}{{ inquiry.ai_brief_ar }}{% endif %}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="p-6 border-t border-slate-200 flex gap-3">
|
|
<button type="submit" class="px-4 py-2.5 bg-navy text-white rounded-xl font-semibold hover:bg-navy/90 transition text-sm inline-flex items-center justify-center gap-2 flex-1 justify-center">
|
|
<i data-lucide="arrow-right-left" class="w-4 h-4"></i>
|
|
{% trans "Transfer to Department" %}
|
|
</button>
|
|
<button type="button" onclick="closeSendToDeptModal()" class="px-4 py-2.5 bg-white border-2 border-slate-200 rounded-xl font-semibold text-slate-600 hover:bg-slate-50 transition text-sm">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
lucide.createIcons();
|
|
});
|
|
|
|
function switchInquiryRecipientType(type) {
|
|
const staffLabel = document.getElementById('inquiryRecipientLabelStaff');
|
|
const deptEmailLabel = document.getElementById('inquiryRecipientLabelDeptEmail');
|
|
const hint = document.getElementById('inquiryDeptEmailHint');
|
|
|
|
if (type === 'staff') {
|
|
staffLabel.classList.add('border-navy', 'bg-navy/5', 'text-navy', 'font-semibold');
|
|
staffLabel.classList.remove('border-slate-200', 'text-slate-500');
|
|
deptEmailLabel.classList.remove('border-navy', 'bg-navy/5', 'text-navy', 'font-semibold');
|
|
deptEmailLabel.classList.add('border-slate-200', 'text-slate-500');
|
|
if (hint) hint.style.display = 'none';
|
|
} else {
|
|
deptEmailLabel.classList.add('border-navy', 'bg-navy/5', 'text-navy', 'font-semibold');
|
|
deptEmailLabel.classList.remove('border-slate-200', 'text-slate-500');
|
|
staffLabel.classList.remove('border-navy', 'bg-navy/5', 'text-navy', 'font-semibold');
|
|
staffLabel.classList.add('border-slate-200', 'text-slate-500');
|
|
if (hint) hint.style.display = '';
|
|
}
|
|
}
|
|
|
|
function getCSRFToken() {
|
|
const metaTag = document.querySelector('[name=csrfmiddlewaretoken]');
|
|
if (metaTag) return metaTag.value;
|
|
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, 9) === ('csrftoken' + '=')) {
|
|
cookieValue = decodeURIComponent(cookie.substring(9));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return cookieValue;
|
|
}
|
|
|
|
function showRespondModal() {
|
|
document.getElementById('respondModal').classList.remove('hidden');
|
|
}
|
|
|
|
function closeRespondModal() {
|
|
document.getElementById('respondModal').classList.add('hidden');
|
|
}
|
|
|
|
function showAssignModal() {
|
|
document.getElementById('assignModal').classList.remove('hidden');
|
|
}
|
|
|
|
function closeAssignModal() {
|
|
document.getElementById('assignModal').classList.add('hidden');
|
|
}
|
|
|
|
function showSendToDeptModal() {
|
|
document.getElementById('sendToDeptModal').classList.remove('hidden');
|
|
loadDepartments();
|
|
}
|
|
|
|
function closeSendToDeptModal() {
|
|
document.getElementById('sendToDeptModal').classList.add('hidden');
|
|
}
|
|
|
|
function switchDeptNoteLang(lang) {
|
|
const tabEn = document.getElementById('deptNoteTabEn');
|
|
const tabAr = document.getElementById('deptNoteTabAr');
|
|
const noteEn = document.getElementById('deptNoteEn');
|
|
const noteAr = document.getElementById('deptNoteAr');
|
|
|
|
if (lang === 'en') {
|
|
tabEn.classList.add('active');
|
|
tabAr.classList.remove('active');
|
|
noteEn.classList.remove('hidden');
|
|
noteAr.classList.add('hidden');
|
|
} else {
|
|
tabAr.classList.add('active');
|
|
tabEn.classList.remove('active');
|
|
noteAr.classList.remove('hidden');
|
|
noteEn.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
function loadDepartments() {
|
|
const sel = document.getElementById('deptSelect');
|
|
if (sel.options.length > 1) return;
|
|
const defaultDeptId = '{{ inquiry.department.id|default:"" }}';
|
|
fetch('/organizations/ajax/departments/?hospital_id={{ inquiry.hospital.id }}')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
sel.innerHTML = '<option value="">{% trans "Select Department" %}</option>';
|
|
(data.departments || data).forEach(d => {
|
|
sel.innerHTML += `<option value="${d.id}"${d.id === defaultDeptId ? ' selected' : ''}>${d.name}</option>`;
|
|
});
|
|
if (typeof TomSelect !== 'undefined' && sel.tomselect) {
|
|
sel.tomselect.sync();
|
|
if (defaultDeptId) {
|
|
sel.tomselect.setValue(defaultDeptId);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
closeRespondModal();
|
|
closeAssignModal();
|
|
closeSendToDeptModal();
|
|
}
|
|
});
|
|
|
|
function generateAIResponse() {
|
|
const btn = document.getElementById('generateAiBtn');
|
|
const suggestionsDiv = document.getElementById('aiSuggestions');
|
|
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner"></span> {% trans "Generating..." %}';
|
|
suggestionsDiv.classList.add('hidden');
|
|
|
|
fetch('/complaints/api/inquiries/{{ inquiry.pk }}/generate_ai_response/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCSRFToken(),
|
|
},
|
|
credentials: 'same-origin'
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
return response.json().then(data => {
|
|
throw new Error(data.error || 'Request failed');
|
|
});
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i data-lucide="sparkles" class="w-4 h-4"></i> {% trans "Generate AI Response" %}';
|
|
lucide.createIcons();
|
|
|
|
if (data.success) {
|
|
document.getElementById('aiSuggestionEnText').textContent = data.response_en;
|
|
document.getElementById('aiSuggestionArText').textContent = data.response_ar;
|
|
suggestionsDiv.classList.remove('hidden');
|
|
} else {
|
|
alert(data.error || '{% trans "Failed to generate response" %}');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i data-lucide="sparkles" class="w-4 h-4"></i> {% trans "Generate AI Response" %}';
|
|
lucide.createIcons();
|
|
alert(error.message || '{% trans "An error occurred while generating response" %}');
|
|
});
|
|
}
|
|
|
|
function useAISuggestion(lang) {
|
|
if (lang === 'en') {
|
|
document.getElementById('responseEn').value = document.getElementById('aiSuggestionEnText').textContent;
|
|
document.getElementById('aiSuggestionEn').classList.add('selected');
|
|
setTimeout(() => document.getElementById('aiSuggestionEn').classList.remove('selected'), 1500);
|
|
} else {
|
|
document.getElementById('responseAr').value = document.getElementById('aiSuggestionArText').textContent;
|
|
document.getElementById('aiSuggestionAr').classList.add('selected');
|
|
setTimeout(() => document.getElementById('aiSuggestionAr').classList.remove('selected'), 1500);
|
|
}
|
|
}
|
|
|
|
function useBothAISuggestions() {
|
|
useAISuggestion('en');
|
|
useAISuggestion('ar');
|
|
document.getElementById('aiSuggestionEn').classList.add('selected');
|
|
document.getElementById('aiSuggestionAr').classList.add('selected');
|
|
}
|
|
|
|
function reanalyzeAI() {
|
|
const btn = document.getElementById('reanalyzeBtn');
|
|
const content = document.getElementById('aiAnalysisContent');
|
|
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<span class="spinner spinner-dark"></span> {% trans "Analyzing..." %}';
|
|
content.innerHTML = '<div class="text-center py-8"><span class="spinner spinner-dark" style="width:2rem;height:2rem;border-width:3px;"></span><p class="text-slate text-sm mt-3">{% trans "Re-analyzing inquiry with AI..." %}</p></div>';
|
|
|
|
fetch('/complaints/api/inquiries/{{ inquiry.pk }}/reanalyze_ai/', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': getCSRFToken(),
|
|
},
|
|
credentials: 'same-origin'
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
return response.json().then(data => {
|
|
throw new Error(data.error || 'Request failed');
|
|
});
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i data-lucide="refresh-cw" class="w-3 h-3"></i> {% trans "Re-analyze" %}';
|
|
lucide.createIcons();
|
|
|
|
if (data.success) {
|
|
let html = '';
|
|
|
|
if (data.emotion && data.emotion !== 'neutral') {
|
|
html += '<div class="bg-light border border-blue-200 rounded-2xl p-5 mb-6">';
|
|
html += '<h4 class="font-bold text-navy mb-3 flex items-center gap-2"><i data-lucide="cpu" class="w-5 h-5"></i> {% trans "Emotion Analysis" %}</h4>';
|
|
html += '<div class="flex items-center justify-between mb-3">';
|
|
html += '<span class="px-4 py-2 bg-navy text-white rounded-xl font-bold flex items-center gap-2"><i data-lucide="frown" class="w-5 h-5"></i> ' + escapeHtml(data.emotion_display) + '</span>';
|
|
html += '<span class="text-slate">{% trans "Confidence" %}: ' + data.emotion_confidence_percent.toFixed(0) + '%</span>';
|
|
html += '</div>';
|
|
html += '<div>';
|
|
html += '<div class="flex justify-between text-sm mb-1"><span class="text-slate">{% trans "Intensity" %}</span><span class="font-semibold">' + data.emotion_intensity.toFixed(2) + ' / 1.0</span></div>';
|
|
html += '<div class="h-2 bg-slate-200 rounded-full overflow-hidden"><div class="h-full bg-navy rounded-full" style="width: ' + data.emotion_intensity_percent + '%"></div></div>';
|
|
html += '</div></div>';
|
|
}
|
|
|
|
if (data.short_description_en) {
|
|
html += '<div class="bg-light/50 border border-slate-200 rounded-2xl p-5 mb-4">';
|
|
html += '<h4 class="font-bold text-navy mb-2">{% trans "AI Summary (English)" %}</h4>';
|
|
html += '<p class="text-slate">' + escapeHtml(data.short_description_en) + '</p>';
|
|
html += '</div>';
|
|
}
|
|
|
|
if (data.short_description_ar) {
|
|
html += '<div class="bg-light/50 border border-slate-200 rounded-2xl p-5">';
|
|
html += '<h4 class="font-bold text-navy mb-2">{% trans "AI Summary (Arabic)" %}</h4>';
|
|
html += '<p class="text-slate" dir="rtl">' + escapeHtml(data.short_description_ar) + '</p>';
|
|
html += '</div>';
|
|
}
|
|
|
|
if (!html) {
|
|
html = '<div class="text-center py-8"><i data-lucide="check-circle" class="w-10 h-10 mx-auto text-green-500 mb-3"></i><p class="text-slate text-sm">{% trans "AI analysis complete" %}</p></div>';
|
|
}
|
|
|
|
content.innerHTML = html;
|
|
lucide.createIcons();
|
|
} else {
|
|
content.innerHTML = '<div class="text-center py-8"><i data-lucide="alert-circle" class="w-10 h-10 mx-auto text-red-500 mb-3"></i><p class="text-slate text-sm">' + escapeHtml(data.error || '{% trans "Analysis failed" %}') + '</p></div>';
|
|
lucide.createIcons();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i data-lucide="refresh-cw" class="w-3 h-3"></i> {% trans "Re-analyze" %}';
|
|
lucide.createIcons();
|
|
content.innerHTML = '<div class="text-center py-8"><i data-lucide="alert-circle" class="w-10 h-10 mx-auto text-red-500 mb-3"></i><p class="text-slate text-sm">' + escapeHtml(error.message || '{% trans "An error occurred" %}') + '</p></div>';
|
|
lucide.createIcons();
|
|
});
|
|
}
|
|
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
</script>
|
|
|
|
{% include "components/send_to_modal.html" with users=assignable_users departments=hospital_departments %}
|
|
|
|
{% endblock %}
|