295 lines
17 KiB
HTML
295 lines
17 KiB
HTML
{% extends "layouts/public_base.html" %}
|
|
{% load i18n static %}
|
|
|
|
{% block title %}{% trans "Submit a Complaint" %} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.select-loading {
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24'%3E%3Cpath fill='%23005696' d='M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 18a8 8 0 1 1 8-8 8 8 0 0 1-8 8z' opacity='.25'/%3E%3Cpath fill='%23005696' d='M12 2a10 10 0 0 1 10 10h-2a8 8 0 0 0-8-8z'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='1s' repeatCount='indefinite'/%3E%3C/path%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 0.75rem center;
|
|
background-size: 1.25rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="complaint-form animate-fade-in">
|
|
<form id="public_complaint_form" method="post">
|
|
{% csrf_token %}
|
|
|
|
<div class="mb-6">
|
|
<h3 class="text-2xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
<i data-lucide="alert-circle" class="w-6 h-6 text-red-600"></i>
|
|
{% trans "Submit a Complaint" %}
|
|
</h3>
|
|
<p class="text-slate text-sm">{% trans "Report an issue with our services. We take all concerns seriously and will investigate thoroughly." %}</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-5">
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Name" %} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="complainant_name" required placeholder="{% trans 'Your full name' %}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Relation to Patient" %} <span class="text-red-500">*</span></label>
|
|
<select name="relation_to_patient" required
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 bg-white form-input">
|
|
<option value="">{% trans "Select Relation" %}</option>
|
|
<option value="patient">{% trans "Patient" %}</option>
|
|
<option value="relative">{% trans "Relative" %}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-5">
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Email" %}</label>
|
|
<input type="email" name="email" placeholder="your@email.com"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Phone Number" %} <span class="text-red-500">*</span></label>
|
|
<input type="tel" name="mobile_number" maxlength="20" required placeholder="{% trans 'Your phone number' %}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 form-input">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-5">
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Patient Name" %} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="patient_name" required placeholder="{% trans 'Name of patient involved' %}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "National ID / Iqama No." %} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="national_id" maxlength="20" required placeholder="{% trans '10-digit National ID or Iqama number' %}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 form-input">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Incident Date" %} <span class="text-red-500">*</span></label>
|
|
<input type="date" id="id_incident_date" name="incident_date" max="{% now 'Y-m-d' %}" required
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 form-input">
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-5">
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Hospital" %} <span class="text-red-500">*</span></label>
|
|
<select name="hospital" id="id_hospital" required
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 bg-white form-input">
|
|
<option value="">{% trans "Select Hospital" %}</option>
|
|
{% for hospital in hospitals %}
|
|
<option value="{{ hospital.id }}">{{ hospital.get_localized_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Location Type" %} <span class="text-red-500">*</span></label>
|
|
<select name="location_type" id="id_location_type" required
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 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>
|
|
</div>
|
|
|
|
<div id="area_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Area" %}</label>
|
|
<select id="id_area" name="area"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 bg-white form-input">
|
|
<option value="">{% trans "Select Area (optional)" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Category" %} <span class="text-red-500">*</span></label>
|
|
<select id="id_category" name="category" required
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 bg-white form-input">
|
|
<option value="">{% trans "Select Category" %}</option>
|
|
<option value="medical">{% trans "Medical" %}</option>
|
|
<option value="nursing">{% trans "Nursing" %}</option>
|
|
<option value="administrative">{% trans "Administrative" %}</option>
|
|
<option value="support_services">{% trans "Support Services" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="department_container" style="display: none;" class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-5">
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Department" %} <span class="text-red-500">*</span></label>
|
|
<select id="id_department" name="department" required
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 bg-white form-input">
|
|
<option value="">{% trans "Select Department" %}</option>
|
|
</select>
|
|
</div>
|
|
<div id="section_container" style="display: none;">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Section" %}</label>
|
|
<select id="id_section" name="section"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 bg-white form-input">
|
|
<option value="">{% trans "Select Section" %}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">{% trans "Complaint Description" %} <span class="text-red-500">*</span></label>
|
|
<textarea name="complaint_details" rows="5" required placeholder="{% trans 'Please describe your complaint in detail...' %}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-red/20 resize-none form-input"></textarea>
|
|
</div>
|
|
|
|
<div class="text-center pt-4 border-t border-slate-100">
|
|
<button type="submit" class="bg-gradient-to-r from-red-500 to-red-600 text-white px-8 py-3.5 rounded-xl font-semibold hover:shadow-xl transition inline-flex items-center gap-2 btn-hover" id="submit_btn">
|
|
<i data-lucide="send" class="w-5 h-5"></i> {% trans "Submit Complaint" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script src="{% static 'vendor/sweetalert2/sweetalert2.all.min.js' %}"></script>
|
|
{% get_current_language as LANGUAGE_CODE %}
|
|
<script>
|
|
const CURRENT_LANG = '{{ LANGUAGE_CODE }}';
|
|
function localizedLabel(o) { if (!o) return ''; if (CURRENT_LANG === 'ar' && o.name_ar) return o.name_ar; return o.name_en || o.name || o.display_name || ''; }
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
|
|
var incidentDateInput = document.querySelector('input[name="incident_date"]');
|
|
if (incidentDateInput) {
|
|
incidentDateInput.max = new Date().toISOString().split('T')[0];
|
|
}
|
|
|
|
var categorySel = document.getElementById('id_category');
|
|
var deptSel = document.getElementById('id_department');
|
|
var sectionSel = document.getElementById('id_section');
|
|
var locationTypeSel = document.getElementById('id_location_type');
|
|
var hospitalSel = document.getElementById('id_hospital');
|
|
var areaSel = document.getElementById('id_area');
|
|
|
|
function loadAreas(hospitalId, locationType) {
|
|
var container = document.getElementById('area_container');
|
|
areaSel.innerHTML = '<option value="">{% trans "Select Area (optional)" %}</option>';
|
|
if (!hospitalId) { container.style.display = 'none'; return; }
|
|
areaSel.classList.add('select-loading');
|
|
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 = localizedLabel(a);
|
|
areaSel.appendChild(opt);
|
|
});
|
|
container.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(function(err) { console.error(err); })
|
|
.finally(function() { areaSel.classList.remove('select-loading'); });
|
|
}
|
|
|
|
locationTypeSel.addEventListener('change', function() {
|
|
loadAreas(hospitalSel.value, this.value);
|
|
});
|
|
|
|
hospitalSel.addEventListener('change', function() {
|
|
loadAreas(this.value, locationTypeSel.value);
|
|
if (categorySel.value) {
|
|
loadDepartments(categorySel.value);
|
|
} else {
|
|
deptSel.innerHTML = '<option value="">{% trans "Select Department" %}</option>';
|
|
sectionSel.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
document.getElementById('department_container').style.display = 'none';
|
|
document.getElementById('section_container').style.display = 'none';
|
|
}
|
|
});
|
|
|
|
categorySel.addEventListener('change', function() {
|
|
loadDepartments(this.value);
|
|
});
|
|
|
|
deptSel.addEventListener('change', function() {
|
|
loadSections(this.value);
|
|
});
|
|
|
|
function loadDepartments(category) {
|
|
var container = document.getElementById('department_container');
|
|
deptSel.innerHTML = '<option value="">{% trans "Select Department" %}</option>';
|
|
sectionSel.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
document.getElementById('section_container').style.display = 'none';
|
|
if (!category || !hospitalSel.value) { container.style.display = 'none'; return; }
|
|
var url = '/organizations/dropdowns/departments-by-category/?category=' + encodeURIComponent(category) + '&hospital=' + encodeURIComponent(hospitalSel.value);
|
|
deptSel.classList.add('select-loading');
|
|
fetch(url)
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
(data || []).forEach(function(d) {
|
|
var opt = document.createElement('option');
|
|
opt.value = d.id;
|
|
opt.textContent = localizedLabel(d);
|
|
deptSel.appendChild(opt);
|
|
});
|
|
container.style.display = (data && data.length) ? 'grid' : 'none';
|
|
})
|
|
.catch(function(err) { console.error(err); })
|
|
.finally(function() { deptSel.classList.remove('select-loading'); });
|
|
}
|
|
|
|
function loadSections(deptId) {
|
|
var container = document.getElementById('section_container');
|
|
sectionSel.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
if (!deptId) { container.style.display = 'none'; return; }
|
|
sectionSel.classList.add('select-loading');
|
|
fetch('/organizations/dropdowns/sections/' + deptId + '/')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
(data || []).forEach(function(s) {
|
|
var opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = localizedLabel(s);
|
|
sectionSel.appendChild(opt);
|
|
});
|
|
container.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(function(err) { console.error(err); })
|
|
.finally(function() { sectionSel.classList.remove('select-loading'); });
|
|
}
|
|
|
|
document.getElementById('public_complaint_form').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
var btn = document.getElementById('submit_btn');
|
|
var originalHTML = btn.innerHTML;
|
|
btn.disabled = true;
|
|
btn.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..." %}';
|
|
|
|
fetch('{% url "complaints:public_complaint_submit" %}', {
|
|
method: 'POST',
|
|
body: new FormData(this),
|
|
headers: { 'X-CSRFToken': document.querySelector('[name="csrfmiddlewaretoken"]').value }
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) {
|
|
Swal.fire({ icon: 'success', title: '{% trans "Submitted Successfully!" %}', text: '{% trans "Your reference number is: " %}' + data.reference_number, confirmButtonColor: '#005696' });
|
|
e.target.reset();
|
|
document.getElementById('department_container').style.display = 'none';
|
|
document.getElementById('section_container').style.display = 'none';
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: '{% trans "Error" %}', text: data.message || data.errors?.join('\\n') || '{% trans "Failed to submit. Please try again." %}' });
|
|
}
|
|
})
|
|
.catch(function() {
|
|
Swal.fire({ icon: 'error', title: '{% trans "Error" %}', text: '{% trans "Failed to submit. Please try again." %}' });
|
|
})
|
|
.finally(function() { btn.disabled = false; btn.innerHTML = originalHTML; });
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|