440 lines
20 KiB
HTML
440 lines
20 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% if inquiry %}{% trans "Edit Inquiry" %}{% else %}{% trans "Create New Inquiry" %}{% endif %} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.page-header {
|
|
background: linear-gradient(135deg, #005696 0%, #0069a8 50%, #007bbd 100%);
|
|
color: white;
|
|
padding: 2rem 2.5rem;
|
|
border-radius: 1rem;
|
|
margin-bottom: 2rem;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 86, 150, 0.2);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.page-header::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 200px;
|
|
height: 200px;
|
|
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
}
|
|
.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;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header">
|
|
<div class="flex justify-between items-center">
|
|
<div>
|
|
<div class="flex items-center gap-2 text-blue-100 text-sm mb-2">
|
|
<a href="{% url 'inquiries:inquiry_list' %}" class="hover:text-white transition">{% trans "Inquiries" %}</a>
|
|
<i data-lucide="chevron-right" class="w-3 h-3"></i>
|
|
{% if inquiry %}
|
|
<a href="{% url 'inquiries:inquiry_detail' inquiry.pk %}" class="hover:text-white transition">{{ inquiry.subject|truncatechars:30 }}</a>
|
|
<i data-lucide="chevron-right" class="w-3 h-3"></i>
|
|
<span class="text-white">{% trans "Edit" %}</span>
|
|
{% else %}
|
|
<span class="text-white">{% trans "New Inquiry" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
<h1 class="text-2xl font-bold">{% if inquiry %}{% trans "Edit Inquiry" %}{% else %}{% trans "Create New Inquiry" %}{% endif %}</h1>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" {% if inquiry %}action="{% url 'inquiries:inquiry_edit' inquiry.pk %}"{% else %}action="{% url 'inquiries:inquiry_create' %}"{% endif %} id="inquiryForm" data-loading data-loading-text="{% trans 'Saving...' %}">
|
|
{% csrf_token %}
|
|
{% if communication_request %}
|
|
<input type="hidden" name="comm_req" value="{{ communication_request.id }}">
|
|
{% endif %}
|
|
<input type="hidden" name="patient_id" id="patientId" value="">
|
|
|
|
<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">{% if inquiry %}{% trans "Edit Inquiry Information" %}{% else %}{% trans "Inquiry Information" %}{% endif %}</h5>
|
|
</div>
|
|
|
|
<div class="p-6 space-y-8">
|
|
<div>
|
|
<h6 class="text-xs font-bold text-slate uppercase mb-4 flex items-center gap-2">
|
|
<i data-lucide="building" class="w-4 h-4"></i>{% trans "Organization" %}
|
|
</h6>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
{% if not form.hospital.is_hidden %}
|
|
<div>
|
|
<label for="{{ form.hospital.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">
|
|
{{ form.hospital.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.hospital }}
|
|
{% for error in form.hospital.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
{{ form.hospital }}
|
|
{% endif %}
|
|
|
|
<div>
|
|
<label for="{{ form.department.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.department.label }}</label>
|
|
{{ form.department }}
|
|
{% for error in form.department.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div>
|
|
<label for="{{ form.source.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.source.label }}</label>
|
|
{{ form.source }}
|
|
{% for error in form.source.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border-t border-slate-200 pt-6">
|
|
<h6 class="text-xs font-bold text-slate uppercase mb-4 flex items-center gap-2">
|
|
<i data-lucide="user" class="w-4 h-4"></i>{% trans "Contact Information" %}
|
|
</h6>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label for="{{ form.contact_name.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">
|
|
{{ form.contact_name.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.contact_name }}
|
|
{% for error in form.contact_name.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div>
|
|
<label for="{{ form.contact_phone.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">
|
|
{{ form.contact_phone.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.contact_phone }}
|
|
{% for error in form.contact_phone.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
<div id="patientFoundBanner" class="hidden bg-green-50 border border-green-200 rounded-lg p-2 mt-2">
|
|
<div class="flex items-center gap-2">
|
|
<i data-lucide="check-circle" class="w-4 h-4 text-green-500"></i>
|
|
<span class="text-sm text-green-700">{% trans "Patient linked:" %} <strong id="patientFoundName"></strong> <span id="patientFoundMrn" class="text-green-600"></span></span>
|
|
<button type="button" onclick="clearPatientLink()" class="ml-auto text-green-600 hover:text-red-500 transition" title="{% trans 'Remove link' %}">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="md:col-span-2">
|
|
<label for="{{ form.contact_email.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.contact_email.label }}</label>
|
|
{{ form.contact_email }}
|
|
{% for error in form.contact_email.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border-t border-slate-200 pt-6">
|
|
<h6 class="text-xs font-bold text-slate uppercase mb-4 flex items-center gap-2">
|
|
<i data-lucide="help-circle" class="w-4 h-4"></i>{% trans "Inquiry Details" %}
|
|
</h6>
|
|
|
|
<input type="hidden" name="is_outgoing" value="false">
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label for="{{ form.category.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">
|
|
{{ form.category.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.category }}
|
|
{% for error in form.category.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div>
|
|
<label for="{{ form.priority.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.priority.label }}</label>
|
|
{{ form.priority }}
|
|
{% for error in form.priority.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-5 mt-5">
|
|
<div>
|
|
<label for="{{ form.location.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.location.label }}</label>
|
|
{{ form.location }}
|
|
{% for error in form.location.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div>
|
|
<label for="{{ form.main_section.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.main_section.label }}</label>
|
|
{{ form.main_section }}
|
|
{% for error in form.main_section.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div>
|
|
<label for="{{ form.subsection.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">{{ form.subsection.label }}</label>
|
|
{{ form.subsection }}
|
|
{% for error in form.subsection.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-5">
|
|
<label for="{{ form.subject.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">
|
|
{{ form.subject.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.subject }}
|
|
{% for error in form.subject.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="mt-5">
|
|
<label for="{{ form.message.id_for_label }}" class="block text-sm font-semibold text-slate-700 mb-1.5">
|
|
{{ form.message.label }} <span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.message }}
|
|
{% for error in form.message.errors %}
|
|
<p class="text-red-500 text-xs mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-3 h-3"></i>{{ error }}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="p-6 bg-slate-50 border-t border-slate-200 flex gap-3 rounded-b-xl">
|
|
<button type="submit" class="inline-flex items-center gap-2 px-6 py-2.5 bg-navy text-white rounded-xl font-semibold hover:bg-navy/90 transition text-sm">
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
|
{% if inquiry %}{% trans "Save Changes" %}{% else %}{% trans "Create Inquiry" %}{% endif %}
|
|
</button>
|
|
{% if inquiry %}
|
|
<a href="{% url 'inquiries:inquiry_detail' inquiry.pk %}" class="px-6 py-2.5 border-2 border-slate-200 rounded-xl font-semibold text-slate-600 hover:bg-slate-50 transition text-sm">{% trans "Cancel" %}</a>
|
|
{% else %}
|
|
<a href="{% url 'inquiries:inquiry_list' %}" class="px-6 py-2.5 border-2 border-slate-200 rounded-xl font-semibold text-slate-600 hover:bg-slate-50 transition text-sm">{% trans "Cancel" %}</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
{% trans "Select Location" as trans_select_location %}
|
|
{% trans "Select Section" as trans_select_section %}
|
|
{% trans "Select Subsection" as trans_select_subsection %}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const selLocation = '{{ trans_select_location|escapejs }}';
|
|
const selSection = '{{ trans_select_section|escapejs }}';
|
|
const selSubsection = '{{ trans_select_subsection|escapejs }}';
|
|
|
|
document.querySelectorAll('.text-red-500').forEach(function(errorEl) {
|
|
const input = errorEl.parentElement.querySelector('input, select, textarea');
|
|
if (input) {
|
|
input.classList.add('border-red-300');
|
|
}
|
|
});
|
|
|
|
const outgoingSection = document.getElementById('outgoingDepartmentSection');
|
|
const isOutgoingField = document.getElementById('isOutgoing');
|
|
document.querySelectorAll('input[name="is_outgoing"]').forEach(function(radio) {
|
|
radio.addEventListener('change', function() {
|
|
if (this.value === 'true') {
|
|
outgoingSection.classList.remove('hidden');
|
|
isOutgoingField.value = 'true';
|
|
} else {
|
|
outgoingSection.classList.add('hidden');
|
|
isOutgoingField.value = 'false';
|
|
}
|
|
});
|
|
});
|
|
|
|
const locationSelect = document.getElementById('locationSelect');
|
|
const sectionSelect = document.getElementById('mainSectionSelect');
|
|
const subsectionSelect = document.getElementById('subsectionSelect');
|
|
|
|
function loadLocations() {
|
|
if (!locationSelect) return;
|
|
locationSelect.innerHTML = '<option value="">' + selLocation + '</option>';
|
|
fetch('/organizations/dropdowns/locations/')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (Array.isArray(data) && data.length > 0) {
|
|
data.forEach(function(loc) {
|
|
const opt = document.createElement('option');
|
|
opt.value = loc.id;
|
|
opt.textContent = loc.name;
|
|
locationSelect.appendChild(opt);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadSections(locationId) {
|
|
sectionSelect.innerHTML = '<option value="">' + selSection + '</option>';
|
|
subsectionSelect.innerHTML = '<option value="">' + selSubsection + '</option>';
|
|
if (!locationId) return;
|
|
fetch('{% url "organizations:ajax_main_sections" %}?location_id=' + locationId)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.sections) {
|
|
data.sections.forEach(function(s) {
|
|
const opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = s.name;
|
|
sectionSelect.appendChild(opt);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadSubsections(locationId, sectionId) {
|
|
subsectionSelect.innerHTML = '<option value="">' + selSubsection + '</option>';
|
|
if (!locationId || !sectionId) return;
|
|
fetch('{% url "organizations:ajax_subsections" %}?location_id=' + locationId + '&main_section_id=' + sectionId)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.subsections) {
|
|
data.subsections.forEach(function(s) {
|
|
const opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = s.name;
|
|
subsectionSelect.appendChild(opt);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
loadLocations();
|
|
|
|
if (locationSelect) {
|
|
locationSelect.addEventListener('change', function() {
|
|
loadSections(this.value);
|
|
});
|
|
}
|
|
if (sectionSelect) {
|
|
sectionSelect.addEventListener('change', function() {
|
|
loadSubsections(locationSelect.value, this.value);
|
|
});
|
|
}
|
|
|
|
const phoneField = document.getElementById('id_contact_phone');
|
|
const patientIdField = document.getElementById('patientId');
|
|
const patientFoundBanner = document.getElementById('patientFoundBanner');
|
|
const patientFoundName = document.getElementById('patientFoundName');
|
|
const patientFoundMrn = document.getElementById('patientFoundMrn');
|
|
const contactNameField = document.getElementById('id_contact_name');
|
|
|
|
let patientLookupTimer = null;
|
|
|
|
function clearPatientLink() {
|
|
patientIdField.value = '';
|
|
patientFoundBanner.classList.add('hidden');
|
|
}
|
|
window.clearPatientLink = clearPatientLink;
|
|
|
|
if (phoneField) {
|
|
phoneField.addEventListener('input', function () {
|
|
clearTimeout(patientLookupTimer);
|
|
const val = this.value.trim().replace(/\D/g, '');
|
|
if (val.length < 7) {
|
|
clearPatientLink();
|
|
return;
|
|
}
|
|
patientLookupTimer = setTimeout(function () {
|
|
fetch(`/complaints/api/lookup-patient/?phone=${encodeURIComponent(val)}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.found) {
|
|
patientIdField.value = data.id;
|
|
patientFoundName.textContent = data.name;
|
|
patientFoundMrn.textContent = `(MRN: ${data.mrn})`;
|
|
patientFoundBanner.classList.remove('hidden');
|
|
if (contactNameField && !contactNameField.value.trim()) {
|
|
contactNameField.value = data.name;
|
|
}
|
|
} else {
|
|
clearPatientLink();
|
|
}
|
|
})
|
|
.catch(error => console.error('Patient lookup error:', error));
|
|
}, 500);
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|