All checks were successful
Build and Push Docker Image / build (push) Successful in 4m14s
Reference numbers (unified scheme PREFIX-YYYYMM-HOSP-NNNN, e.g. CMP-202606-HHN-0001): - new ReferenceSequence model + generate_reference() helper (apps/core) - Complaint/Inquiry/Observation/Appreciation/Suggestion emit unified refs via save() - prefix-based auto-routing in public track API (CMP/INQ/OBS trackable; APR/SGT internal-only) - removed legacy CMP-/INQ- generators in ui_views, integrations, px_sources - migrations: core.0003_referencesequence, appreciation.0006, feedback.0008, observations.0012 - unit tests (format, sanitization, monthly reset, 40-thread concurrency) QA audit: - isolated E2E hospital sandbox mirroring HH-N + 10 role users (create_e2e_isolated_env) - feedback-modules-audit.spec.ts + audit helper (headed, run-to-completion) - reports/feedback-modules-qa-report.md Also bundles accumulated in-progress work across complaints, observations, organizations, templates, and other modules.
287 lines
15 KiB
HTML
287 lines
15 KiB
HTML
{% load i18n %}
|
|
|
|
<!-- Inquiry Form - PX360 Styled -->
|
|
<div class="inquiry-form" id="inquiryFormContainer">
|
|
<form id="inquiryForm" data-loading data-loading-text="{% trans 'Submitting...' %}">
|
|
{% csrf_token %}
|
|
|
|
<!-- Header -->
|
|
<div class="mb-8 p-6 bg-gradient-to-br from-blue-50 to-light rounded-2xl border-2 border-blue/10">
|
|
<h3 class="text-2xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
<i data-lucide="help-circle" class="w-7 h-7 text-blue-600"></i>
|
|
{% trans "Inquiry Details" %}
|
|
</h3>
|
|
<p class="text-slate text-sm leading-relaxed">{% trans "Ask a question or request information. We'll get back to you within 24-48 hours." %}</p>
|
|
</div>
|
|
|
|
<!-- Contact Information -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
|
|
<div>
|
|
<label for="inquiry_name" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Name" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="text" id="inquiry_name" name="name" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition form-input"
|
|
placeholder="{% trans 'Your full name' %}">
|
|
</div>
|
|
<div>
|
|
<label for="inquiry_email" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Email Address" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="email" id="inquiry_email" name="email" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition form-input"
|
|
placeholder="your@email.com">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
|
|
<div>
|
|
<label for="inquiry_phone" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Phone Number" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="tel" id="inquiry_phone" name="phone" maxlength="20" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition form-input"
|
|
placeholder="{% trans 'Your phone number' %}">
|
|
</div>
|
|
<div>
|
|
<label for="inquiry_hospital" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Hospital" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<select id="inquiry_hospital" name="hospital" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition bg-white form-input">
|
|
<option value="">{% trans "Select Hospital" %}</option>
|
|
{% for hospital in hospitals %}
|
|
<option value="{{ hospital.id }}">{{ hospital.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Location Type -->
|
|
<div class="mb-5">
|
|
<label for="inquiry_location_type" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Location Type" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<select id="inquiry_location_type" name="location_type" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition bg-white form-input">
|
|
<option value="">{% trans "Select Location Type" %}</option>
|
|
<option value="OP">{% trans "Outpatient (OP)" %}</option>
|
|
<option value="IP">{% trans "Inpatient (IP)" %}</option>
|
|
<option value="ER">{% trans "Emergency (ER)" %}</option>
|
|
<option value="GENERAL">{% trans "General" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Area (cascading from hospital + location_type) -->
|
|
<div id="inquiry_area_container" style="display: none;" class="mb-5">
|
|
<label for="inquiry_area" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Area" %}
|
|
</label>
|
|
<select id="inquiry_area" name="area"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition bg-white form-input">
|
|
<option value="">{% trans "Select Area (optional)" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Department & Section (cascading) -->
|
|
<div id="inquiry_dept_container" style="display: none;" class="grid grid-cols-1 md:grid-cols-2 gap-5 mb-5">
|
|
<div>
|
|
<label for="inquiry_department" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Department" %}
|
|
</label>
|
|
<select id="inquiry_department" name="department"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition bg-white form-input">
|
|
<option value="">{% trans "Select Department" %}</option>
|
|
</select>
|
|
</div>
|
|
<div id="inquiry_section_container" style="display: none;">
|
|
<label for="inquiry_section" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Section" %}
|
|
</label>
|
|
<select id="inquiry_section" name="section"
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition bg-white form-input">
|
|
<option value="">{% trans "Select Section" %}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Category -->
|
|
<div class="mb-5">
|
|
<label for="inquiry_category" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Category" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<select id="inquiry_category" name="category" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition bg-white form-input">
|
|
<option value="">{% trans "Select Category" %}</option>
|
|
<option value="general">{% trans "General Inquiry" %}</option>
|
|
<option value="appointment">{% trans "Appointments" %}</option>
|
|
<option value="billing">{% trans "Billing & Insurance" %}</option>
|
|
<option value="medical_records">{% trans "Medical Records" %}</option>
|
|
<option value="pharmacy">{% trans "Pharmacy" %}</option>
|
|
<option value="insurance">{% trans "Insurance" %}</option>
|
|
<option value="feedback">{% trans "Feedback" %}</option>
|
|
<option value="other">{% trans "Other" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Subject -->
|
|
<div class="mb-5">
|
|
<label for="inquiry_subject" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Subject" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<input type="text" id="inquiry_subject" name="subject" maxlength="200" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition form-input"
|
|
placeholder="{% trans 'Brief subject of your inquiry' %}">
|
|
</div>
|
|
|
|
<!-- Message -->
|
|
<div class="mb-6">
|
|
<label for="inquiry_message" class="block text-sm font-semibold text-navy mb-2">
|
|
{% trans "Message" %} <span class="text-red-500">*</span>
|
|
</label>
|
|
<textarea id="inquiry_message" name="message" rows="6" required
|
|
class="w-full px-4 py-3 border-2 border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-blue/20 focus:border-blue transition resize-none form-input"
|
|
placeholder="{% trans 'Please describe your inquiry in detail...' %}"></textarea>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<div class="text-center pt-6 border-t border-slate-100">
|
|
<button type="submit" id="inquirySubmitBtn"
|
|
class="bg-gradient-to-r from-blue-600 to-blue-700 text-white px-10 py-4 rounded-xl font-bold hover:shadow-xl transition inline-flex items-center gap-2 shadow-lg shadow-blue/30 btn-hover">
|
|
<i data-lucide="send" class="w-5 h-5"></i>
|
|
{% trans "Submit Inquiry" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
// Hospital -> Location Type -> Area -> Department -> Section cascade for inquiry form
|
|
(function() {
|
|
var hospitalSel = document.getElementById('inquiry_hospital');
|
|
var locationTypeSel = document.getElementById('inquiry_location_type');
|
|
var areaSel = document.getElementById('inquiry_area');
|
|
var deptSel = document.getElementById('inquiry_department');
|
|
var sectionSel = document.getElementById('inquiry_section');
|
|
var areaContainer = document.getElementById('inquiry_area_container');
|
|
var deptContainer = document.getElementById('inquiry_dept_container');
|
|
var sectionContainer = document.getElementById('inquiry_section_container');
|
|
|
|
function loadAreas(hospitalId, locationType) {
|
|
areaSel.innerHTML = '<option value="">{% trans "Select Area (optional)" %}</option>';
|
|
if (!hospitalId) { areaContainer.style.display = 'none'; return; }
|
|
var url = '/organizations/dropdowns/areas/?hospital=' + encodeURIComponent(hospitalId);
|
|
if (locationType) url += '&location_type=' + encodeURIComponent(locationType);
|
|
fetch(url)
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
(data || []).forEach(function(a) {
|
|
var opt = document.createElement('option');
|
|
opt.value = a.id;
|
|
opt.textContent = a.name_en;
|
|
areaSel.appendChild(opt);
|
|
});
|
|
areaContainer.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(function(err) { console.error(err); });
|
|
}
|
|
|
|
locationTypeSel.addEventListener('change', function() {
|
|
loadAreas(hospitalSel.value, this.value);
|
|
});
|
|
|
|
hospitalSel.addEventListener('change', function() {
|
|
loadAreas(this.value, locationTypeSel.value);
|
|
deptSel.innerHTML = '<option value="">{% trans "Select Department" %}</option>';
|
|
sectionSel.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
sectionContainer.style.display = 'none';
|
|
if (!this.value) { deptContainer.style.display = 'none'; return; }
|
|
fetch('/organizations/dropdowns/departments-by-category/?hospital=' + encodeURIComponent(this.value))
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
(data || []).forEach(function(d) {
|
|
var opt = document.createElement('option');
|
|
opt.value = d.id;
|
|
opt.textContent = d.name_en || d.name;
|
|
deptSel.appendChild(opt);
|
|
});
|
|
deptContainer.style.display = (data && data.length) ? 'grid' : 'none';
|
|
})
|
|
.catch(function(err) { console.error(err); });
|
|
});
|
|
|
|
deptSel.addEventListener('change', function() {
|
|
sectionSel.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
if (!this.value) { sectionContainer.style.display = 'none'; return; }
|
|
fetch('/organizations/dropdowns/sections/' + this.value + '/')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
(data || []).forEach(function(s) {
|
|
var opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = s.name_en || s.name;
|
|
sectionSel.appendChild(opt);
|
|
});
|
|
sectionContainer.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(function(err) { console.error(err); });
|
|
});
|
|
})();
|
|
|
|
// Inquiry form submission handler
|
|
document.getElementById('inquiryForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const submitBtn = document.getElementById('inquirySubmitBtn');
|
|
const originalHTML = submitBtn.innerHTML;
|
|
|
|
submitBtn.disabled = true;
|
|
submitBtn.innerHTML = '<span class="inline-block w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"></span>{% trans "Submitting..." %}';
|
|
|
|
const formData = new FormData(this);
|
|
|
|
fetch('{% url "inquiries:public_inquiry_submit" %}', {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name="csrfmiddlewaretoken"]').value
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: '{% trans "Submitted Successfully!" %}',
|
|
html: '{% trans "Your reference number is: " %}<br><strong class="text-2xl text-navy mt-2">' + data.reference_number + '</strong>',
|
|
confirmButtonColor: '#005696'
|
|
});
|
|
}
|
|
this.reset();
|
|
} else {
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '{% trans "Error" %}',
|
|
text: data.message || '{% trans "Failed to submit. Please try again." %}'
|
|
});
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '{% trans "Error" %}',
|
|
text: '{% trans "Failed to submit. Please try again." %}'
|
|
});
|
|
}
|
|
})
|
|
.finally(() => {
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = originalHTML;
|
|
});
|
|
});
|
|
</script>
|