1523 lines
76 KiB
HTML
1523 lines
76 KiB
HTML
{% extends "layouts/public_base.html" %}
|
|
{% load i18n static %}
|
|
|
|
{% block title %}{% trans "Submit Feedback" %} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
/* Hide the header on this page */
|
|
header.glass-card {
|
|
display: none !important;
|
|
}
|
|
|
|
/* Language switcher styles */
|
|
.lang-switcher {
|
|
position: fixed;
|
|
top: 1rem;
|
|
right: 1rem;
|
|
z-index: 100;
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.lang-switcher a {
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.75rem;
|
|
border: 2px solid rgba(255,255,255,0.3);
|
|
background: rgba(255,255,255,0.1);
|
|
backdrop-filter: blur(10px);
|
|
color: white;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
transition: all 0.2s ease;
|
|
text-decoration: none;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.lang-switcher a:hover {
|
|
background: rgba(255,255,255,0.2);
|
|
border-color: rgba(255,255,255,0.5);
|
|
}
|
|
|
|
.lang-switcher a.active {
|
|
background: white;
|
|
color: #005696;
|
|
border-color: white;
|
|
}
|
|
|
|
[dir="rtl"] .lang-switcher {
|
|
right: auto;
|
|
left: 1rem;
|
|
}
|
|
/* PX360 Theme */
|
|
.selection-card {
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.selection-card:hover {
|
|
transform: translateY(-8px);
|
|
box-shadow: 0 20px 40px -10px rgba(0, 86, 150, 0.3);
|
|
}
|
|
|
|
.form-container.active {
|
|
animation: slideUp 0.4s ease-out;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from { opacity: 0; transform: translateY(30px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.severity-option {
|
|
transition: all 0.2s ease;
|
|
cursor: pointer;
|
|
}
|
|
.severity-option:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
.severity-option.selected {
|
|
border-color: #007bbd !important;
|
|
background-color: #eef6fb !important;
|
|
box-shadow: 0 4px 12px rgba(0, 123, 189, 0.2);
|
|
}
|
|
|
|
/* Custom scrollbar for form content */
|
|
#formContent::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
#formContent::-webkit-scrollbar-track {
|
|
background: #f1f5f9;
|
|
}
|
|
#formContent::-webkit-scrollbar-thumb {
|
|
background: #cbd5e1;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
/* Complaint Form Styles */
|
|
.complaint-form {
|
|
max-width: 900px;
|
|
margin: 0 auto;
|
|
padding: 1rem 0;
|
|
}
|
|
|
|
.form-section {
|
|
background: white;
|
|
border-radius: 1.5rem;
|
|
padding: 2rem;
|
|
margin-bottom: 1.5rem;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
|
border: 1px solid #e2e8f0;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.form-section:hover {
|
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.form-section h3 {
|
|
color: #005696;
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 2px solid #eef6fb;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
font-size: 1.25rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
padding: 0.875rem 1.125rem;
|
|
border: 2px solid #cbd5e1;
|
|
border-radius: 0.875rem;
|
|
font-size: 0.95rem;
|
|
transition: all 0.2s;
|
|
background: white;
|
|
color: #1e293b;
|
|
font-family: 'Inter', 'Noto Kufi Arabic', sans-serif;
|
|
}
|
|
|
|
.form-control:focus {
|
|
outline: none;
|
|
border-color: #007bbd;
|
|
box-shadow: 0 0 0 4px rgba(0, 123, 189, 0.1);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
margin-bottom: 0.625rem;
|
|
font-weight: 600;
|
|
color: #1e293b;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.form-text {
|
|
display: block;
|
|
margin-top: 0.5rem;
|
|
font-size: 0.825rem;
|
|
color: #64748b;
|
|
}
|
|
|
|
.required-mark {
|
|
color: #ef4444;
|
|
}
|
|
|
|
.alert-box {
|
|
padding: 1.5rem 1.75rem;
|
|
border-radius: 1.25rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.alert-box.info {
|
|
background: linear-gradient(135deg, #eef6fb 0%, #e0f2fe 100%);
|
|
color: #005696;
|
|
border: 2px solid #bae6fd;
|
|
}
|
|
|
|
.location-hierarchy-help {
|
|
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
|
|
border-left: 4px solid #f59e0b;
|
|
padding: 1.25rem;
|
|
margin: 1.25rem 0;
|
|
border-radius: 1rem;
|
|
font-size: 0.9rem;
|
|
color: #4b5563;
|
|
}
|
|
|
|
[dir="rtl"] .location-hierarchy-help {
|
|
border-left: none;
|
|
border-right: 4px solid #f59e0b;
|
|
}
|
|
|
|
.btn-submit {
|
|
background: linear-gradient(135deg, #005696 0%, #007bbd 100%);
|
|
border: none;
|
|
padding: 1rem 2.5rem;
|
|
border-radius: 1rem;
|
|
color: white;
|
|
font-weight: 700;
|
|
font-size: 1.05rem;
|
|
cursor: pointer;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
box-shadow: 0 8px 16px rgba(0, 86, 150, 0.25);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.btn-submit:hover {
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 12px 24px rgba(0, 86, 150, 0.35);
|
|
}
|
|
|
|
.btn-submit:disabled {
|
|
background: #94a3b8;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.spinner {
|
|
display: inline-block;
|
|
width: 1.125rem;
|
|
height: 1.125rem;
|
|
border: 2px solid rgba(255,255,255,0.3);
|
|
border-radius: 50%;
|
|
border-top-color: white;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
.form-input {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.form-input:focus {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.btn-hover {
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.btn-hover:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 20px rgba(0, 86, 150, 0.2);
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Language Switcher -->
|
|
<div class="lang-switcher">
|
|
{% get_available_languages as LANGUAGES %}
|
|
{% get_current_language as LANGUAGE_CODE %}
|
|
{% for lang_code, lang_name in LANGUAGES %}
|
|
<a href="{% url 'core:set_language' %}?language={{ lang_code }}"
|
|
class="{% if LANGUAGE_CODE == lang_code %}active{% endif %}">
|
|
<span>{% if lang_code == 'en' %}🇬🇧{% elif lang_code == 'ar' %}🇸🇦{% endif %}</span>
|
|
<span>{{ lang_name }}</span>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="max-w-6xl mx-auto">
|
|
<!-- Welcome Card -->
|
|
<div id="welcomeCard" class="rounded-3xl shadow-2xl overflow-hidden mb-8 text-center animate-fade-in">
|
|
<!-- Logo Banner - Full Width White -->
|
|
<div class="bg-white w-full py-8 px-6 flex items-center justify-center">
|
|
<img src="{% static 'img/hh-logo.png' %}" alt="Al Hammadi Hospital" class="max-h-16 w-auto object-contain">
|
|
</div>
|
|
<!-- Text Content Below - White Background -->
|
|
<div class="bg-white p-8">
|
|
<h1 class="text-2xl font-bold text-navy mb-3">{% trans "We Value Your Feedback" %}</h1>
|
|
<p class="text-slate text-base max-w-xl mx-auto">
|
|
{% trans "Your feedback helps us improve our services and provide better care for everyone. Choose a category below to get started." %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Selection Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-6 mb-8" id="selectionCards">
|
|
<!-- Complaint Card -->
|
|
<div class="selection-card glass-card rounded-2xl shadow-xl border-2 border-transparent p-8 text-center cursor-pointer relative overflow-hidden group"
|
|
data-type="complaint">
|
|
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-red-400 to-red-600"></div>
|
|
<div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-red-50 to-red-100 mx-auto mb-4 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
|
<i data-lucide="alert-circle" class="w-8 h-8 text-red-600"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy mb-3">{% trans "Complaint" %}</h3>
|
|
<p class="text-slate text-sm leading-relaxed">
|
|
{% trans "Report an issue with our services. We take all concerns seriously and will investigate thoroughly." %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Observation Card -->
|
|
<div class="selection-card glass-card rounded-2xl shadow-xl border-2 border-transparent p-8 text-center cursor-pointer relative overflow-hidden group"
|
|
data-type="observation">
|
|
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-purple-400 to-purple-600"></div>
|
|
<div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 mx-auto mb-4 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
|
<i data-lucide="eye" class="w-8 h-8 text-purple-600"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy mb-3">{% trans "Observation" %}</h3>
|
|
<p class="text-slate text-sm leading-relaxed">
|
|
{% trans "Help us improve safety by sharing what you've noticed. Anonymous submissions are welcome." %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Inquiry Card -->
|
|
<div class="selection-card glass-card rounded-2xl shadow-xl border-2 border-transparent p-8 text-center cursor-pointer relative overflow-hidden group"
|
|
data-type="inquiry">
|
|
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-amber-400 to-amber-600"></div>
|
|
<div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-amber-50 to-amber-100 mx-auto mb-4 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
|
<i data-lucide="help-circle" class="w-8 h-8 text-amber-600"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy mb-3">{% trans "Inquiry" %}</h3>
|
|
<p class="text-slate text-sm leading-relaxed">
|
|
{% trans "Have questions? We're here to help with appointments, services, or general information." %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Appreciation Card -->
|
|
<div class="selection-card glass-card rounded-2xl shadow-xl border-2 border-transparent p-8 text-center cursor-pointer relative overflow-hidden group"
|
|
data-type="appreciation">
|
|
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-green-400 to-emerald-600"></div>
|
|
<div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-green-50 to-green-100 mx-auto mb-4 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
|
<i data-lucide="heart" class="w-8 h-8 text-green-600"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy mb-3">{% trans "Appreciation" %}</h3>
|
|
<p class="text-slate text-sm leading-relaxed">
|
|
{% trans "Recognize a staff member or team who made a positive difference in your experience." %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Suggestion Card -->
|
|
<div class="selection-card glass-card rounded-2xl shadow-xl border-2 border-transparent p-8 text-center cursor-pointer relative overflow-hidden group"
|
|
data-type="suggestion">
|
|
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-cyan-400 to-blue-600"></div>
|
|
<div class="w-16 h-16 rounded-2xl bg-gradient-to-br from-cyan-50 to-cyan-100 mx-auto mb-4 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
|
|
<i data-lucide="lightbulb" class="w-8 h-8 text-cyan-600"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-navy mb-3">{% trans "Suggestion" %}</h3>
|
|
<p class="text-slate text-sm leading-relaxed">
|
|
{% trans "Share your ideas to help us improve our services and facilities for everyone." %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Container -->
|
|
<div class="form-container glass-card rounded-3xl shadow-2xl p-8 mb-8 hidden animate-slide-up" id="publicFormContainer">
|
|
<!-- Back Button -->
|
|
<div class="mb-6">
|
|
<button class="inline-flex items-center gap-2 px-6 py-3 bg-white text-navy border-2 border-navy font-bold rounded-xl hover:bg-navy hover:text-white transition-all duration-200 shadow-md hover:shadow-lg"
|
|
onclick="resetToSelection()">
|
|
<i data-lucide="arrow-left" class="w-5 h-5"></i>
|
|
{% trans "Back to Selection" %}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Loading Spinner -->
|
|
<div class="loading-spinner text-center py-16 hidden" id="loadingSpinner">
|
|
<div class="inline-block w-14 h-14 border-4 border-slate-200 border-t-blue rounded-full animate-spin mb-4"></div>
|
|
<p class="text-slate text-sm font-medium">{% trans "Loading form..." %}</p>
|
|
</div>
|
|
|
|
<!-- Form Content -->
|
|
<div id="formContent" class="pr-2"></div>
|
|
</div>
|
|
|
|
<!-- Track Submission Links -->
|
|
<div id="trackSubmission" class="glass-card rounded-2xl shadow-xl p-8 text-center">
|
|
<p class="text-slate mb-4 font-medium">{% trans "Already submitted something?" %}</p>
|
|
<a href="{% url 'core:public_track' %}"
|
|
class="inline-flex items-center gap-2 px-8 py-3.5 bg-navy text-white rounded-xl hover:bg-blue transition font-semibold shadow-lg shadow-navy/30 btn-hover text-lg">
|
|
<i data-lucide="search" class="w-5 h-5"></i>
|
|
{% trans "Track Your Submission" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Success Modal -->
|
|
<div id="successModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
|
|
<div class="bg-white rounded-3xl shadow-2xl w-full max-w-sm p-8 text-center animate-slide-up">
|
|
<div class="inline-flex items-center justify-center w-20 h-20 bg-gradient-to-br from-emerald-400 to-emerald-600 rounded-full mb-6 shadow-xl">
|
|
<i data-lucide="check" class="w-10 h-10 text-white"></i>
|
|
</div>
|
|
<h3 class="text-2xl font-bold text-navy mb-3">{% trans "Submitted Successfully!" %}</h3>
|
|
<p class="text-slate text-sm mb-4">{% trans "Your submission has been received and will be reviewed." %}</p>
|
|
<div class="bg-light rounded-xl p-4 mb-6">
|
|
<p class="text-slate text-xs font-semibold mb-1">{% trans "Reference Number:" %}</p>
|
|
<span id="referenceNumber" class="text-2xl font-bold text-navy tracking-wide"></span>
|
|
</div>
|
|
<button onclick="closeSuccessModal()" class="w-full bg-gradient-to-r from-navy to-blue text-white px-6 py-3.5 rounded-xl font-semibold hover:shadow-xl transition btn-hover">
|
|
{% trans "Done" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script src="{% static 'vendor/sweetalert2/sweetalert2.all.min.js' %}"></script>{% trans "Error" as t_error %}
|
|
{% trans "Failed to load form." as t_failed_to_load_form %}
|
|
{% trans "Select Category" as t_select_category %}
|
|
{% trans "optional" as t_optional %}
|
|
{% trans "Report an Observation" as t_report_an_observation %}
|
|
{% trans "Help us improve by reporting issues you notice. Your input is valuable." as t_help_us_improve_by_reporting_issues_you_notice_you %}
|
|
{% trans "Category" as t_category %}
|
|
{% trans "Severity" as t_severity %}
|
|
{% trans "Low" as t_low %}
|
|
{% trans "Medium" as t_medium %}
|
|
{% trans "High" as t_high %}
|
|
{% trans "Critical" as t_critical %}
|
|
{% trans "Title" as t_title %}
|
|
{% trans "Brief descriptive title" as t_brief_descriptive_title %}
|
|
{% trans "Description" as t_description %}
|
|
{% trans "Describe what you observed in detail..." as t_describe_what_you_observed_in_detail %}
|
|
{% trans "Location" as t_location %}
|
|
{% trans "Where did this occur?" as t_where_did_this_occur %}
|
|
{% trans "When" as t_when %}
|
|
{% trans "Attachments" as t_attachments %}
|
|
{% trans "Click to upload files" as t_click_to_upload_files %}
|
|
{% trans "Images, PDF, Word (max 10MB)" as t_images_pdf_word_max_10mb %}
|
|
{% trans "Your Info" as t_your_info %}
|
|
{% trans "optional - leave blank for anonymous" as t_optional_leave_blank_for_anonymous %}
|
|
{% trans "Staff ID" as t_staff_id %}
|
|
{% trans "Name" as t_name %}
|
|
{% trans "Phone" as t_phone %}
|
|
{% trans "Email" as t_email %}
|
|
{% trans "Submit Observation" as t_submit_observation %}
|
|
{% trans "Submitting..." as t_submitting %}
|
|
{% trans "Failed to submit" as t_failed_to_submit %}
|
|
{% trans "Select Hospital" as t_select_hospital %}
|
|
{% trans "Inquiry Details" as t_inquiry_details %}
|
|
{% trans "Your full name" as t_your_full_name %}
|
|
{% trans "Email Address" as t_email_address %}
|
|
{% trans "Phone Number" as t_phone_number %}
|
|
{% trans "Your phone number" as t_your_phone_number %}
|
|
{% trans "Hospital" as t_hospital %}
|
|
{% trans "General Inquiry" as t_general_inquiry %}
|
|
{% trans "Services Information" as t_services_information %}
|
|
{% trans "Appointments" as t_appointments %}
|
|
{% trans "Billing & Insurance" as t_billing_insurance %}
|
|
{% trans "Medical Records" as t_medical_records %}
|
|
{% trans "Other" as t_other %}
|
|
{% trans "Subject" as t_subject %}
|
|
{% trans "Brief subject of your inquiry" as t_brief_subject_of_your_inquiry %}
|
|
{% trans "Message" as t_message %}
|
|
{% trans "Please describe your inquiry in detail..." as t_please_describe_your_inquiry_in_detail %}
|
|
{% trans "Submit Inquiry" as t_submit_inquiry %}
|
|
{% trans "Failed" as t_failed %}
|
|
{% trans "Select Location" as t_select_location %}
|
|
{% trans "Main Section" as t_main_section %}
|
|
{% trans "Subsection" as t_subsection %}
|
|
{% trans "Select Main Section" as t_select_main_section %}
|
|
{% trans "Select Subsection" as t_select_subsection %}
|
|
{% trans "Select Domain" as t_select_domain %}
|
|
{% trans "Select Subcategory" as t_select_subcategory %}
|
|
{% trans "Select Classification" as t_select_classification %}
|
|
{% trans "Failed to submit. Please try again." as t_failed_to_submit_please_try_again %}
|
|
{% trans "Share Your Appreciation" as t_share_your_appreciation %}
|
|
{% trans "Recognize a staff member or team who made a difference in your experience." as t_recognize_a_staff_member_or_team_who_made_a_differ %}
|
|
{% trans "Your Name" as t_your_name %}
|
|
{% trans "Your Phone" as t_your_phone %}
|
|
{% trans "Staff / Department Name" as t_staff_department_name %}
|
|
{% trans "Name of staff or department" as t_name_of_staff_or_department %}
|
|
{% trans "Your Appreciation" as t_your_appreciation %}
|
|
{% trans "Share what this person or team did that made your experience special..." as t_share_what_this_person_or_team_did_that_made_your_ %}
|
|
{% trans "Submit Appreciation" as t_submit_appreciation %}
|
|
{% trans "Thank You!" as t_thank_you %}
|
|
{% trans "Thank you for your appreciation! Your kind words will be reviewed and shared with the staff member or department you recognized." as t_thank_you_for_your_appreciation_your_kind_words_wi %}
|
|
{% trans "Share Your Suggestion" as t_share_your_suggestion %}
|
|
{% trans "Your ideas help us improve. Share how we can make our services better for everyone." as t_your_ideas_help_us_improve_share_how_we_can_make_o %}
|
|
{% trans "Suggestion Area" as t_suggestion_area %}
|
|
{% trans "General" as t_general %}
|
|
{% trans "Clinical Care" as t_clinical_care %}
|
|
{% trans "Facility & Environment" as t_facility_environment %}
|
|
{% trans "Staff Service" as t_staff_service %}
|
|
{% trans "Communication" as t_communication %}
|
|
{% trans "Technology & Systems" as t_technology_systems %}
|
|
{% trans "Food Service" as t_food_service %}
|
|
{% trans "Appointment & Scheduling" as t_appointment_scheduling %}
|
|
{% trans "Suggestion Title" as t_suggestion_title %}
|
|
{% trans "Brief title for your suggestion" as t_brief_title_for_your_suggestion %}
|
|
{% trans "Your Suggestion" as t_your_suggestion %}
|
|
{% trans "Describe your suggestion in detail. What would you like to see improved or changed?" as t_describe_your_suggestion_in_detail_what_would_you_ %}
|
|
{% trans "Submit Suggestion" as t_submit_suggestion %}
|
|
{% trans "Thank you for your suggestion! We appreciate your input and will review it carefully." as t_thank_you_for_your_suggestion_we_appreciate_your_i %}
|
|
{% trans "Failed to load hospitals." as t_failed_to_load_hospitals %}
|
|
{% trans "Ask a question. We'll respond within 24-48 hours." as t_ask_a_question_we_ll_respond_within_24_48_hours %}
|
|
{% trans "Medical" as t_medical %}
|
|
{% trans "Nursing" as t_nursing %}
|
|
{% trans "Administrative" as t_administrative %}
|
|
{% trans "Support Services" as t_support_services %}
|
|
{% trans "Department" as t_department %}
|
|
{% trans "Select Department" as t_select_department %}
|
|
{% trans "Section" as t_section %}
|
|
{% trans "Select Section" as t_select_section %}
|
|
{% trans "Sub-Section" as t_sub_section %}
|
|
{% trans "Select Sub-Section" as t_select_sub_section %}
|
|
{% trans "Location Type" as t_location_type %}
|
|
{% trans "Select Location Type" as t_select_location_type %}
|
|
{% trans "Area" as t_area %}
|
|
{% trans "Select Area (optional)" as t_select_area %}
|
|
{% trans "Outpatient (OP)" as t_outpatient %}
|
|
{% trans "Inpatient (IP)" as t_inpatient %}
|
|
{% trans "Emergency (ER)" as t_emergency %}
|
|
{% trans "General" as t_government %}
|
|
<script>
|
|
var T = {
|
|
error: '{{ t_error|escapejs }}',
|
|
failed_to_load_form: '{{ t_failed_to_load_form|escapejs }}',
|
|
select_category: '{{ t_select_category|escapejs }}',
|
|
optional: '{{ t_optional|escapejs }}',
|
|
report_an_observation: '{{ t_report_an_observation|escapejs }}',
|
|
help_us_improve_by_reporting_issues_you_notice_you: '{{ t_help_us_improve_by_reporting_issues_you_notice_you|escapejs }}',
|
|
category: '{{ t_category|escapejs }}',
|
|
severity: '{{ t_severity|escapejs }}',
|
|
low: '{{ t_low|escapejs }}',
|
|
medium: '{{ t_medium|escapejs }}',
|
|
high: '{{ t_high|escapejs }}',
|
|
critical: '{{ t_critical|escapejs }}',
|
|
title: '{{ t_title|escapejs }}',
|
|
brief_descriptive_title: '{{ t_brief_descriptive_title|escapejs }}',
|
|
description: '{{ t_description|escapejs }}',
|
|
describe_what_you_observed_in_detail: '{{ t_describe_what_you_observed_in_detail|escapejs }}',
|
|
location: '{{ t_location|escapejs }}',
|
|
where_did_this_occur: '{{ t_where_did_this_occur|escapejs }}',
|
|
when: '{{ t_when|escapejs }}',
|
|
attachments: '{{ t_attachments|escapejs }}',
|
|
click_to_upload_files: '{{ t_click_to_upload_files|escapejs }}',
|
|
images_pdf_word_max_10mb: '{{ t_images_pdf_word_max_10mb|escapejs }}',
|
|
your_info: '{{ t_your_info|escapejs }}',
|
|
optional_leave_blank_for_anonymous: '{{ t_optional_leave_blank_for_anonymous|escapejs }}',
|
|
staff_id: '{{ t_staff_id|escapejs }}',
|
|
name: '{{ t_name|escapejs }}',
|
|
phone: '{{ t_phone|escapejs }}',
|
|
email: '{{ t_email|escapejs }}',
|
|
submit_observation: '{{ t_submit_observation|escapejs }}',
|
|
submitting: '{{ t_submitting|escapejs }}',
|
|
failed_to_submit: '{{ t_failed_to_submit|escapejs }}',
|
|
select_hospital: '{{ t_select_hospital|escapejs }}',
|
|
inquiry_details: '{{ t_inquiry_details|escapejs }}',
|
|
your_full_name: '{{ t_your_full_name|escapejs }}',
|
|
email_address: '{{ t_email_address|escapejs }}',
|
|
phone_number: '{{ t_phone_number|escapejs }}',
|
|
your_phone_number: '{{ t_your_phone_number|escapejs }}',
|
|
hospital: '{{ t_hospital|escapejs }}',
|
|
general_inquiry: '{{ t_general_inquiry|escapejs }}',
|
|
services_information: '{{ t_services_information|escapejs }}',
|
|
appointments: '{{ t_appointments|escapejs }}',
|
|
billing_insurance: '{{ t_billing_insurance|escapejs }}',
|
|
medical_records: '{{ t_medical_records|escapejs }}',
|
|
other: '{{ t_other|escapejs }}',
|
|
subject: '{{ t_subject|escapejs }}',
|
|
brief_subject_of_your_inquiry: '{{ t_brief_subject_of_your_inquiry|escapejs }}',
|
|
message: '{{ t_message|escapejs }}',
|
|
please_describe_your_inquiry_in_detail: '{{ t_please_describe_your_inquiry_in_detail|escapejs }}',
|
|
submit_inquiry: '{{ t_submit_inquiry|escapejs }}',
|
|
failed: '{{ t_failed|escapejs }}',
|
|
select_location: '{{ t_select_location|escapejs }}',
|
|
main_section: '{{ t_main_section|escapejs }}',
|
|
subsection: '{{ t_subsection|escapejs }}',
|
|
select_main_section: '{{ t_select_main_section|escapejs }}',
|
|
select_subsection: '{{ t_select_subsection|escapejs }}',
|
|
select_domain: '{{ t_select_domain|escapejs }}',
|
|
select_subcategory: '{{ t_select_subcategory|escapejs }}',
|
|
select_classification: '{{ t_select_classification|escapejs }}',
|
|
failed_to_submit_please_try_again: '{{ t_failed_to_submit_please_try_again|escapejs }}',
|
|
share_your_appreciation: '{{ t_share_your_appreciation|escapejs }}',
|
|
recognize_a_staff_member_or_team_who_made_a_differ: '{{ t_recognize_a_staff_member_or_team_who_made_a_differ|escapejs }}',
|
|
your_name: '{{ t_your_name|escapejs }}',
|
|
your_phone: '{{ t_your_phone|escapejs }}',
|
|
staff_department_name: '{{ t_staff_department_name|escapejs }}',
|
|
name_of_staff_or_department: '{{ t_name_of_staff_or_department|escapejs }}',
|
|
your_appreciation: '{{ t_your_appreciation|escapejs }}',
|
|
share_what_this_person_or_team_did_that_made_your_: '{{ t_share_what_this_person_or_team_did_that_made_your_|escapejs }}',
|
|
submit_appreciation: '{{ t_submit_appreciation|escapejs }}',
|
|
thank_you: '{{ t_thank_you|escapejs }}',
|
|
thank_you_for_your_appreciation_your_kind_words_wi: '{{ t_thank_you_for_your_appreciation_your_kind_words_wi|escapejs }}',
|
|
share_your_suggestion: '{{ t_share_your_suggestion|escapejs }}',
|
|
your_ideas_help_us_improve_share_how_we_can_make_o: '{{ t_your_ideas_help_us_improve_share_how_we_can_make_o|escapejs }}',
|
|
suggestion_area: '{{ t_suggestion_area|escapejs }}',
|
|
general: '{{ t_general|escapejs }}',
|
|
clinical_care: '{{ t_clinical_care|escapejs }}',
|
|
facility_environment: '{{ t_facility_environment|escapejs }}',
|
|
staff_service: '{{ t_staff_service|escapejs }}',
|
|
communication: '{{ t_communication|escapejs }}',
|
|
technology_systems: '{{ t_technology_systems|escapejs }}',
|
|
food_service: '{{ t_food_service|escapejs }}',
|
|
appointment_scheduling: '{{ t_appointment_scheduling|escapejs }}',
|
|
suggestion_title: '{{ t_suggestion_title|escapejs }}',
|
|
brief_title_for_your_suggestion: '{{ t_brief_title_for_your_suggestion|escapejs }}',
|
|
your_suggestion: '{{ t_your_suggestion|escapejs }}',
|
|
describe_your_suggestion_in_detail_what_would_you_: '{{ t_describe_your_suggestion_in_detail_what_would_you_|escapejs }}',
|
|
submit_suggestion: '{{ t_submit_suggestion|escapejs }}',
|
|
thank_you_for_your_suggestion_we_appreciate_your_i: '{{ t_thank_you_for_your_suggestion_we_appreciate_your_i|escapejs }}',
|
|
failed_to_load_hospitals: '{{ t_failed_to_load_hospitals|escapejs }}',
|
|
ask_a_question_we_ll_respond_within_24_48_hours: '{{ t_ask_a_question_we_ll_respond_within_24_48_hours|escapejs }}',
|
|
medical: '{{ t_medical|escapejs }}',
|
|
nursing: '{{ t_nursing|escapejs }}',
|
|
administrative: '{{ t_administrative|escapejs }}',
|
|
support_services: '{{ t_support_services|escapejs }}',
|
|
department: '{{ t_department|escapejs }}',
|
|
select_department: '{{ t_select_department|escapejs }}',
|
|
section: '{{ t_section|escapejs }}',
|
|
select_section: '{{ t_select_section|escapejs }}',
|
|
sub_section: '{{ t_sub_section|escapejs }}',
|
|
select_sub_section: '{{ t_select_sub_section|escapejs }}',
|
|
location_type: '{{ t_location_type|escapejs }}',
|
|
select_location_type: '{{ t_select_location_type|escapejs }}',
|
|
area: '{{ t_area|escapejs }}',
|
|
select_area: '{{ t_select_area|escapejs }}',
|
|
outpatient: '{{ t_outpatient|escapejs }}',
|
|
inpatient: '{{ t_inpatient|escapejs }}',
|
|
emergency: '{{ t_emergency|escapejs }}',
|
|
government: '{{ t_government|escapejs }}'
|
|
};
|
|
var CSRF_INPUT = '<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token|escapejs }}">';
|
|
</script>
|
|
|
|
<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() {
|
|
lucide.createIcons();
|
|
let currentFormType = null;
|
|
|
|
// Card selection
|
|
document.querySelectorAll('.selection-card').forEach(function(card) {
|
|
card.addEventListener('click', function() {
|
|
const formType = this.getAttribute('data-type');
|
|
currentFormType = formType;
|
|
document.getElementById('selectionCards').style.display = 'none';
|
|
document.getElementById('welcomeCard').style.display = 'none';
|
|
document.getElementById('trackSubmission').style.display = 'none';
|
|
const formContainer = document.getElementById('publicFormContainer');
|
|
formContainer.classList.remove('hidden');
|
|
formContainer.classList.add('active');
|
|
loadForm(formType);
|
|
});
|
|
});
|
|
|
|
function loadForm(formType) {
|
|
document.getElementById('loadingSpinner').classList.remove('hidden');
|
|
document.getElementById('formContent').classList.add('hidden');
|
|
|
|
fetch('/core/api/hospitals/')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
const hospitals = data.hospitals || [];
|
|
|
|
if (formType === 'complaint') {
|
|
fetch('{% url "complaints:public_complaint_submit" %}')
|
|
.then(r => r.text())
|
|
.then(html => {
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(html, 'text/html');
|
|
const formHtml = doc.querySelector('.complaint-form')?.innerHTML || '';
|
|
document.getElementById('formContent').innerHTML = formHtml;
|
|
|
|
setTimeout(() => {
|
|
lucide.createIcons();
|
|
initComplaintFormInLanding();
|
|
}, 100);
|
|
|
|
showForm();
|
|
})
|
|
.catch((error) => {
|
|
console.error('Error loading complaint form:', error);
|
|
document.getElementById('loadingSpinner').classList.add('hidden');
|
|
Swal.fire({ icon: 'error', title: T.error, text: T.failed_to_load_form });
|
|
});
|
|
} else if (formType === 'observation') {
|
|
fetchCategoriesAndRenderObservationForm(hospitals);
|
|
} else if (formType === 'inquiry') {
|
|
renderInquiryForm(hospitals);
|
|
} else if (formType === 'appreciation') {
|
|
renderAppreciationForm(hospitals);
|
|
} else if (formType === 'suggestion') {
|
|
renderSuggestionForm(hospitals);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
document.getElementById('loadingSpinner').classList.add('hidden');
|
|
Swal.fire({ icon: 'error', title: T.error, text: T.failed_to_load_hospitals });
|
|
});
|
|
}
|
|
|
|
function fetchCategoriesAndRenderObservationForm(hospitals) {
|
|
renderObservationForm(hospitals);
|
|
}
|
|
|
|
function renderObservationForm(hospitals) {
|
|
let hospOpts = '<option value="">' + T.select_hospital + '</option>';
|
|
hospitals.forEach(h => hospOpts += `<option value="${h.id}">${localizedLabel(h)}</option>`);
|
|
|
|
const formHtml = `
|
|
<form id="observationForm" enctype="multipart/form-data">
|
|
${CSRF_INPUT}
|
|
<div class="mb-6">
|
|
<h3 class="text-2xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
<i data-lucide="eye" class="w-6 h-6 text-purple-600"></i>
|
|
${T.report_an_observation}
|
|
</h3>
|
|
<p class="text-slate text-sm">${T.help_us_improve_by_reporting_issues_you_notice_you}</p>
|
|
</div>
|
|
|
|
<input type="hidden" name="severity" id="severityInput" value="medium">
|
|
|
|
<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">${T.name} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="reporter_name" required placeholder="${T.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-purple-500/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.phone_number} <span class="text-red-500">*</span></label>
|
|
<input type="tel" name="reporter_phone" maxlength="20" required pattern="(\+?966|0)?5[0-9]{8}" inputmode="tel" title="05XXXXXXXX or +9665XXXXXXXX" placeholder="${T.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-purple-500/20 form-input">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.hospital} <span class="text-red-500">*</span></label>
|
|
<select name="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-purple-500/20 bg-white form-input">
|
|
${hospOpts}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.location_type} <span class="text-red-500">*</span></label>
|
|
<select name="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-purple-500/20 bg-white form-input">
|
|
<option value="">${T.select_location_type}</option>
|
|
<option value="OP">${T.outpatient}</option>
|
|
<option value="IP">${T.inpatient}</option>
|
|
<option value="ER">${T.emergency}</option>
|
|
<option value="GENERAL">${T.government}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="area_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-purple-500/20 bg-white form-input">
|
|
<option value="">${T.select_area}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-purple-500/20 bg-white form-input">
|
|
<option value="">${T.select_category}</option>
|
|
<option value="medical">${T.medical}</option>
|
|
<option value="nursing">${T.nursing}</option>
|
|
<option value="administrative">${T.administrative}</option>
|
|
<option value="support_services">${T.support_services}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="department_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.department} <span class="text-red-500">*</span></label>
|
|
<select id="id_department" name="department" class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-purple-500/20 bg-white form-input">
|
|
<option value="">${T.select_department}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="section_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-purple-500/20 bg-white form-input">
|
|
<option value="">${T.select_section}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.description} <span class="text-red-500">*</span></label>
|
|
<textarea name="description" rows="5" required placeholder="${T.describe_what_you_observed_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-purple-500/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-purple-600 to-purple-700 text-white px-8 py-3.5 rounded-xl font-semibold hover:shadow-xl transition inline-flex items-center gap-2 btn-hover" id="obsSubmitBtn">
|
|
<i data-lucide="send" class="w-5 h-5"></i> ${T.submit_observation}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
document.getElementById('formContent').innerHTML = formHtml;
|
|
lucide.createIcons();
|
|
|
|
document.getElementById('observationForm').addEventListener('submit', handleObservationSubmit);
|
|
initHierarchyCascade();
|
|
showForm();
|
|
}
|
|
|
|
function handleObservationSubmit(e) {
|
|
e.preventDefault();
|
|
const btn = document.getElementById('obsSubmitBtn');
|
|
const original = 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>' + T.submitting;
|
|
|
|
fetch('/core/public/observation/submit/', {
|
|
method: 'POST',
|
|
body: new FormData(this),
|
|
headers: { 'X-CSRFToken': getCSRFToken() }
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
document.getElementById('referenceNumber').textContent = data.tracking_code;
|
|
document.getElementById('successModal').classList.remove('hidden');
|
|
this.reset();
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: T.error, text: data.errors?.join('\\n') || T.failed_to_submit });
|
|
}
|
|
})
|
|
.catch(() => Swal.fire({ icon: 'error', title: T.error, text: T.failed_to_submit }))
|
|
.finally(() => { btn.disabled = false; btn.innerHTML = original; });
|
|
}
|
|
|
|
function renderInquiryForm(hospitals) {
|
|
let opts = '<option value="">' + T.select_hospital + '</option>';
|
|
hospitals.forEach(h => opts += `<option value="${h.id}">${localizedLabel(h)}</option>`);
|
|
|
|
const formHtml = `
|
|
<form id="inquiryForm">
|
|
${CSRF_INPUT}
|
|
<div class="mb-6">
|
|
<h3 class="text-2xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
<i data-lucide="help-circle" class="w-6 h-6 text-amber-600"></i>
|
|
${T.inquiry_details}
|
|
</h3>
|
|
<p class="text-slate text-sm">${T.ask_a_question_we_ll_respond_within_24_48_hours}</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">${T.name} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="name" required placeholder="${T.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-amber/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.email_address}</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-amber/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">${T.phone_number} <span class="text-red-500">*</span></label>
|
|
<input type="tel" name="phone" maxlength="20" required pattern="(\+?966|0)?5[0-9]{8}" inputmode="tel" title="05XXXXXXXX or +9665XXXXXXXX" placeholder="${T.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-amber/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.hospital} <span class="text-red-500">*</span></label>
|
|
<select name="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-amber/20 bg-white form-input">
|
|
${opts}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.location_type} <span class="text-red-500">*</span></label>
|
|
<select name="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-amber/20 bg-white form-input">
|
|
<option value="">${T.select_location_type}</option>
|
|
<option value="OP">${T.outpatient}</option>
|
|
<option value="IP">${T.inpatient}</option>
|
|
<option value="ER">${T.emergency}</option>
|
|
<option value="GENERAL">${T.government}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="area_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-amber/20 bg-white form-input">
|
|
<option value="">${T.select_area}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-amber/20 bg-white form-input">
|
|
<option value="">${T.select_category}</option>
|
|
<option value="medical">${T.medical}</option>
|
|
<option value="nursing">${T.nursing}</option>
|
|
<option value="administrative">${T.administrative}</option>
|
|
<option value="support_services">${T.support_services}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="department_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-amber/20 bg-white form-input">
|
|
<option value="">${T.select_department}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="section_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-amber/20 bg-white form-input">
|
|
<option value="">${T.select_section}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.message} <span class="text-red-500">*</span></label>
|
|
<textarea name="message" rows="5" required placeholder="${T.please_describe_your_inquiry_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-amber/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-amber-600 to-amber-700 text-white px-8 py-3.5 rounded-xl font-semibold hover:shadow-xl transition inline-flex items-center gap-2 btn-hover" id="inqSubmitBtn">
|
|
<i data-lucide="send" class="w-5 h-5"></i> ${T.submit_inquiry}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
document.getElementById('formContent').innerHTML = formHtml;
|
|
lucide.createIcons();
|
|
document.getElementById('inquiryForm').addEventListener('submit', handleInquirySubmit);
|
|
initHierarchyCascade();
|
|
showForm();
|
|
}
|
|
|
|
function handleInquirySubmit(e) {
|
|
e.preventDefault();
|
|
const btn = document.getElementById('inqSubmitBtn');
|
|
const original = 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>' + T.submitting;
|
|
|
|
fetch('{% url "core:public_inquiry_submit" %}', {
|
|
method: 'POST',
|
|
body: new FormData(this),
|
|
headers: { 'X-CSRFToken': getCSRFToken() }
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
document.getElementById('referenceNumber').textContent = data.reference_number;
|
|
document.getElementById('successModal').classList.remove('hidden');
|
|
this.reset();
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: T.error, text: data.message || T.failed });
|
|
}
|
|
})
|
|
.catch(() => Swal.fire({ icon: 'error', title: T.error, text: T.failed }))
|
|
.finally(() => { btn.disabled = false; btn.innerHTML = original; });
|
|
}
|
|
|
|
function renderAppreciationForm(hospitals) {
|
|
let opts = '<option value="">' + T.select_hospital + '</option>';
|
|
hospitals.forEach(h => opts += `<option value="${h.id}">${localizedLabel(h)}</option>`);
|
|
|
|
const formHtml = `
|
|
<form id="appreciationForm">
|
|
${CSRF_INPUT}
|
|
<div class="mb-6">
|
|
<h3 class="text-2xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
<i data-lucide="heart" class="w-6 h-6 text-green-600"></i>
|
|
${T.share_your_appreciation}
|
|
</h3>
|
|
<p class="text-slate text-sm">${T.recognize_a_staff_member_or_team_who_made_a_differ}</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">${T.your_name} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="contact_name" required placeholder="${T.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-green/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.your_phone} <span class="text-red-500">*</span></label>
|
|
<input type="tel" name="contact_phone" required pattern="(\+?966|0)?5[0-9]{8}" inputmode="tel" title="05XXXXXXXX or +9665XXXXXXXX" placeholder="${T.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-green/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">${T.hospital} <span class="text-red-500">*</span></label>
|
|
<select name="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-green/20 bg-white form-input">
|
|
${opts}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.staff_department_name} <span class="text-xs font-normal text-slate">(${T.optional})</span></label>
|
|
<input type="text" name="staff_name" placeholder="${T.name_of_staff_or_department}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-green/20 form-input">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.location_type} <span class="text-red-500">*</span></label>
|
|
<select name="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-green/20 bg-white form-input">
|
|
<option value="">${T.select_location_type}</option>
|
|
<option value="OP">${T.outpatient}</option>
|
|
<option value="IP">${T.inpatient}</option>
|
|
<option value="ER">${T.emergency}</option>
|
|
<option value="GENERAL">${T.government}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="area_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-green/20 bg-white form-input">
|
|
<option value="">${T.select_area}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-green/20 bg-white form-input">
|
|
<option value="">${T.select_category}</option>
|
|
<option value="medical">${T.medical}</option>
|
|
<option value="nursing">${T.nursing}</option>
|
|
<option value="administrative">${T.administrative}</option>
|
|
<option value="support_services">${T.support_services}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="department_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-green/20 bg-white form-input">
|
|
<option value="">${T.select_department}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="section_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-green/20 bg-white form-input">
|
|
<option value="">${T.select_section}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.your_appreciation} <span class="text-red-500">*</span></label>
|
|
<textarea name="message" rows="5" required placeholder="${T.share_what_this_person_or_team_did_that_made_your_}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-green/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-green-500 to-emerald-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="apprSubmitBtn">
|
|
<i data-lucide="heart" class="w-5 h-5"></i> ${T.submit_appreciation}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
document.getElementById('formContent').innerHTML = formHtml;
|
|
lucide.createIcons();
|
|
document.getElementById('appreciationForm').addEventListener('submit', handleAppreciationSubmit);
|
|
initHierarchyCascade();
|
|
showForm();
|
|
}
|
|
|
|
function handleAppreciationSubmit(e) {
|
|
e.preventDefault();
|
|
const btn = document.getElementById('apprSubmitBtn');
|
|
const original = 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>' + T.submitting;
|
|
|
|
const formData = new FormData(this);
|
|
const data = Object.fromEntries(formData.entries());
|
|
|
|
fetch('/appreciation/public/submit/', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
headers: { 'X-CSRFToken': getCSRFToken(), 'Content-Type': 'application/json' }
|
|
})
|
|
.then(r => {
|
|
if (!r.ok) return r.text().then(t => { throw new Error('HTTP ' + r.status + ': ' + t); });
|
|
return r.text().then(t => { try { return JSON.parse(t); } catch(e) { throw new Error('Invalid JSON: ' + t.substring(0, 200)); } });
|
|
})
|
|
.then(data => {
|
|
if (data.success) {
|
|
var refBlock = document.getElementById('referenceNumber').closest('.bg-light');
|
|
if (refBlock) refBlock.style.display = 'none';
|
|
document.querySelector('#successModal h3').textContent = T.thank_you;
|
|
document.querySelector('#successModal p').textContent = T.thank_you_for_your_appreciation_your_kind_words_wi;
|
|
document.getElementById('successModal').classList.remove('hidden');
|
|
this.reset();
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: T.error, text: data.message || T.failed });
|
|
}
|
|
})
|
|
.catch(err => Swal.fire({ icon: 'error', title: T.error, text: err.message || T.failed }))
|
|
.finally(() => { btn.disabled = false; btn.innerHTML = original; });
|
|
}
|
|
|
|
function renderSuggestionForm(hospitals) {
|
|
let opts = '<option value="">' + T.select_hospital + '</option>';
|
|
hospitals.forEach(h => opts += `<option value="${h.id}">${localizedLabel(h)}</option>`);
|
|
|
|
const formHtml = `
|
|
<form id="suggestionForm">
|
|
${CSRF_INPUT}
|
|
<div class="mb-6">
|
|
<h3 class="text-2xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
<i data-lucide="lightbulb" class="w-6 h-6 text-cyan-600"></i>
|
|
${T.share_your_suggestion}
|
|
</h3>
|
|
<p class="text-slate text-sm">${T.your_ideas_help_us_improve_share_how_we_can_make_o}</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">${T.your_name} <span class="text-red-500">*</span></label>
|
|
<input type="text" name="contact_name" required placeholder="${T.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-cyan/20 form-input">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.your_phone} <span class="text-red-500">*</span></label>
|
|
<input type="tel" name="contact_phone" required pattern="(\+?966|0)?5[0-9]{8}" inputmode="tel" title="05XXXXXXXX or +9665XXXXXXXX" placeholder="${T.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-cyan/20 form-input">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.hospital} <span class="text-red-500">*</span></label>
|
|
<select name="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-cyan/20 bg-white form-input">
|
|
${opts}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.location_type} <span class="text-red-500">*</span></label>
|
|
<select name="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-cyan/20 bg-white form-input">
|
|
<option value="">${T.select_location_type}</option>
|
|
<option value="OP">${T.outpatient}</option>
|
|
<option value="IP">${T.inpatient}</option>
|
|
<option value="ER">${T.emergency}</option>
|
|
<option value="GENERAL">${T.government}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="area_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-cyan/20 bg-white form-input">
|
|
<option value="">${T.select_area}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-cyan/20 bg-white form-input">
|
|
<option value="">${T.select_category}</option>
|
|
<option value="medical">${T.medical}</option>
|
|
<option value="nursing">${T.nursing}</option>
|
|
<option value="administrative">${T.administrative}</option>
|
|
<option value="support_services">${T.support_services}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="department_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-cyan/20 bg-white form-input">
|
|
<option value="">${T.select_department}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="section_container" style="display: none;" class="mb-5">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.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-cyan/20 bg-white form-input">
|
|
<option value="">${T.select_section}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<label class="block text-sm font-semibold text-navy mb-2">${T.your_suggestion} <span class="text-red-500">*</span></label>
|
|
<textarea name="message" rows="5" required placeholder="${T.describe_your_suggestion_in_detail_what_would_you_}"
|
|
class="w-full px-4 py-3 border border-slate-200 rounded-xl text-navy focus:outline-none focus:ring-2 focus:ring-cyan/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-cyan-500 to-blue-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="suggSubmitBtn">
|
|
<i data-lucide="lightbulb" class="w-5 h-5"></i> ${T.submit_suggestion}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
`;
|
|
|
|
document.getElementById('formContent').innerHTML = formHtml;
|
|
lucide.createIcons();
|
|
document.getElementById('suggestionForm').addEventListener('submit', handleSuggestionSubmit);
|
|
initHierarchyCascade();
|
|
showForm();
|
|
}
|
|
|
|
function handleSuggestionSubmit(e) {
|
|
e.preventDefault();
|
|
const btn = document.getElementById('suggSubmitBtn');
|
|
const original = 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>' + T.submitting;
|
|
|
|
const formData = new FormData(this);
|
|
const data = Object.fromEntries(formData.entries());
|
|
|
|
fetch('/suggestions/public/suggestion/', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
headers: { 'X-CSRFToken': getCSRFToken(), 'Content-Type': 'application/json' }
|
|
})
|
|
.then(r => {
|
|
if (!r.ok) return r.text().then(t => { throw new Error('HTTP ' + r.status + ': ' + t); });
|
|
return r.text().then(t => { try { return JSON.parse(t); } catch(e) { throw new Error('Invalid JSON: ' + t.substring(0, 200)); } });
|
|
})
|
|
.then(data => {
|
|
if (data.success) {
|
|
var refBlock = document.getElementById('referenceNumber').closest('.bg-light');
|
|
if (refBlock) refBlock.style.display = 'none';
|
|
document.querySelector('#successModal h3').textContent = T.thank_you;
|
|
document.querySelector('#successModal p').textContent = T.thank_you_for_your_suggestion_we_appreciate_your_i;
|
|
document.getElementById('successModal').classList.remove('hidden');
|
|
this.reset();
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: T.error, text: data.message || T.failed });
|
|
}
|
|
})
|
|
.catch(err => Swal.fire({ icon: 'error', title: T.error, text: err.message || T.failed }))
|
|
.finally(() => { btn.disabled = false; btn.innerHTML = original; });
|
|
}
|
|
|
|
|
|
function showForm() {
|
|
document.getElementById('loadingSpinner').classList.add('hidden');
|
|
document.getElementById('formContent').classList.remove('hidden');
|
|
}
|
|
|
|
window.resetToSelection = function() {
|
|
document.getElementById('publicFormContainer').classList.add('hidden');
|
|
document.getElementById('formContent').innerHTML = '';
|
|
document.getElementById('selectionCards').style.display = 'grid';
|
|
document.getElementById('welcomeCard').style.display = '';
|
|
document.getElementById('trackSubmission').style.display = '';
|
|
currentFormType = null;
|
|
};
|
|
|
|
window.closeSuccessModal = function() {
|
|
document.getElementById('successModal').classList.add('hidden');
|
|
var refBlock = document.getElementById('referenceNumber').closest('.bg-light');
|
|
if (refBlock) refBlock.style.display = '';
|
|
resetToSelection();
|
|
};
|
|
|
|
// Location hierarchy loading functions for complaint form
|
|
window.initHierarchyCascade = function() {
|
|
const catSel = document.getElementById('id_category');
|
|
const areaSel = document.getElementById('id_area');
|
|
const deptSel = document.getElementById('id_department');
|
|
const secSel = document.getElementById('id_section');
|
|
|
|
if (!catSel) return;
|
|
|
|
function getSelectedHospital() {
|
|
const hSel = document.querySelector('select[name="hospital"]');
|
|
return hSel ? hSel.value : '';
|
|
}
|
|
|
|
// Location Type → Area cascade
|
|
const locTypeSel = document.querySelector('select[name="location_type"]');
|
|
if (locTypeSel && areaSel) {
|
|
locTypeSel.addEventListener('change', function() {
|
|
areaSel.innerHTML = '<option value="">' + T.select_area + '</option>';
|
|
const areaContainer = document.getElementById('area_container');
|
|
const hId = getSelectedHospital();
|
|
if (!hId || !this.value) { if (areaContainer) areaContainer.style.display = 'none'; return; }
|
|
fetch('/organizations/dropdowns/areas/?hospital=' + encodeURIComponent(hId) + '&location_type=' + encodeURIComponent(this.value))
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
(data || []).forEach(a => {
|
|
const opt = document.createElement('option');
|
|
opt.value = a.id;
|
|
opt.textContent = localizedLabel(a);
|
|
areaSel.appendChild(opt);
|
|
});
|
|
if (areaContainer) areaContainer.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(err => console.error(err));
|
|
});
|
|
}
|
|
|
|
// Hospital change → refresh areas + departments
|
|
const hospSel = document.querySelector('select[name="hospital"]');
|
|
if (hospSel) {
|
|
hospSel.addEventListener('change', function() {
|
|
// Refresh areas
|
|
if (locTypeSel && areaSel && locTypeSel.value) {
|
|
locTypeSel.dispatchEvent(new Event('change'));
|
|
} else {
|
|
const areaContainer = document.getElementById('area_container');
|
|
if (areaContainer) areaContainer.style.display = 'none';
|
|
}
|
|
// Refresh departments if category already selected
|
|
if (catSel && catSel.value) {
|
|
catSel.dispatchEvent(new Event('change'));
|
|
} else {
|
|
const deptContainer = document.getElementById('department_container');
|
|
if (deptContainer) deptContainer.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
catSel.addEventListener('change', function() {
|
|
const container = document.getElementById('department_container');
|
|
deptSel.innerHTML = '<option value="">' + T.select_department + '</option>';
|
|
secSel.innerHTML = '<option value="">' + T.select_section + '</option>';
|
|
document.getElementById('section_container').style.display = 'none';
|
|
if (!this.value) { container.style.display = 'none'; return; }
|
|
var hId = getSelectedHospital();
|
|
if (!hId) { container.style.display = 'none'; return; }
|
|
var url = '/organizations/dropdowns/departments-by-category/?category=' + encodeURIComponent(this.value) + '&hospital=' + encodeURIComponent(hId);
|
|
fetch(url)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
(data || []).forEach(d => {
|
|
const opt = document.createElement('option');
|
|
opt.value = d.id;
|
|
opt.textContent = localizedLabel(d);
|
|
deptSel.appendChild(opt);
|
|
});
|
|
container.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(err => console.error(err));
|
|
});
|
|
|
|
deptSel.addEventListener('change', function() {
|
|
const container = document.getElementById('section_container');
|
|
secSel.innerHTML = '<option value="">' + T.select_section + '</option>';
|
|
if (!this.value) { container.style.display = 'none'; return; }
|
|
fetch('/organizations/dropdowns/sections/' + this.value + '/')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
(data || []).forEach(s => {
|
|
const opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = localizedLabel(s);
|
|
secSel.appendChild(opt);
|
|
});
|
|
container.style.display = (data && data.length) ? 'block' : 'none';
|
|
})
|
|
.catch(err => console.error(err));
|
|
});
|
|
};
|
|
|
|
// Global variable to store all categories for taxonomy (legacy)
|
|
window.allComplaintCategories = [];
|
|
|
|
// Load taxonomy data
|
|
window.loadTaxonomy = function(hospitalId) {
|
|
fetch('{% url "complaints:api_load_categories" %}?hospital_id=' + (hospitalId || ''))
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
window.allComplaintCategories = data.categories || [];
|
|
const domainSelect = document.getElementById('id_domain');
|
|
if (!domainSelect) return;
|
|
domainSelect.innerHTML = '<option value="">' + T.select_domain + '</option>';
|
|
window.allComplaintCategories.filter(c => c.level === 1).forEach(cat => {
|
|
const opt = document.createElement('option');
|
|
opt.value = cat.id;
|
|
opt.textContent = localizedLabel(cat);
|
|
domainSelect.appendChild(opt);
|
|
});
|
|
})
|
|
.catch(err => console.error('Load taxonomy error:', err));
|
|
};
|
|
|
|
// Load categories for selected domain
|
|
window.loadCategories = function(domainId) {
|
|
const catSelect = document.getElementById('id_category');
|
|
const catContainer = document.getElementById('category_container');
|
|
if (!catSelect) return;
|
|
catSelect.innerHTML = '<option value="">' + T.select_category + '</option>';
|
|
if (!domainId) {
|
|
if (catContainer) catContainer.style.display = 'none';
|
|
window.loadSubcategories('');
|
|
return;
|
|
}
|
|
const categories = window.allComplaintCategories.filter(c => c.level === 2 && c.parent_id == domainId);
|
|
categories.forEach(cat => {
|
|
const opt = document.createElement('option');
|
|
opt.value = cat.id;
|
|
opt.textContent = localizedLabel(cat);
|
|
catSelect.appendChild(opt);
|
|
});
|
|
if (catContainer) catContainer.style.display = categories.length ? 'block' : 'none';
|
|
};
|
|
|
|
// Load subcategories for selected category
|
|
window.loadSubcategories = function(categoryId) {
|
|
const subSelect = document.getElementById('id_subcategory');
|
|
const subContainer = document.getElementById('subcategory_container');
|
|
if (!subSelect) return;
|
|
subSelect.innerHTML = '<option value="">' + T.select_subcategory + '</option>';
|
|
if (!categoryId) {
|
|
if (subContainer) subContainer.style.display = 'none';
|
|
window.loadClassifications('');
|
|
return;
|
|
}
|
|
const subcategories = window.allComplaintCategories.filter(c => c.level === 3 && c.parent_id == categoryId);
|
|
subcategories.forEach(cat => {
|
|
const opt = document.createElement('option');
|
|
opt.value = cat.id;
|
|
opt.textContent = localizedLabel(cat);
|
|
subSelect.appendChild(opt);
|
|
});
|
|
if (subContainer) subContainer.style.display = subcategories.length ? 'block' : 'none';
|
|
};
|
|
|
|
// Load classifications for selected subcategory
|
|
window.loadClassifications = function(subcategoryId) {
|
|
const clsSelect = document.getElementById('id_classification');
|
|
const clsContainer = document.getElementById('classification_container');
|
|
if (!clsSelect) return;
|
|
clsSelect.innerHTML = '<option value="">' + T.select_classification + '</option>';
|
|
if (!subcategoryId) {
|
|
if (clsContainer) clsContainer.style.display = 'none';
|
|
return;
|
|
}
|
|
const classifications = window.allComplaintCategories.filter(c => c.level === 4 && c.parent_id == subcategoryId);
|
|
classifications.forEach(cat => {
|
|
const opt = document.createElement('option');
|
|
opt.value = cat.id;
|
|
opt.textContent = localizedLabel(cat);
|
|
clsSelect.appendChild(opt);
|
|
});
|
|
if (clsContainer) clsContainer.style.display = classifications.length ? 'block' : 'none';
|
|
};
|
|
|
|
// Initialize complaint form when loaded in landing page
|
|
window.initComplaintFormInLanding = function() {
|
|
var incidentDateInput = document.getElementById('id_incident_date');
|
|
if (incidentDateInput) {
|
|
incidentDateInput.max = new Date().toISOString().split('T')[0];
|
|
}
|
|
|
|
window.initHierarchyCascade();
|
|
|
|
// Add form submission handler
|
|
const complaintForm = document.getElementById('public_complaint_form');
|
|
if (complaintForm) {
|
|
complaintForm.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const submitBtn = complaintForm.querySelector('button[type="submit"]');
|
|
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>' + T.submitting;
|
|
|
|
const formData = new FormData(complaintForm);
|
|
|
|
fetch('/complaints/public/submit/', {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
'X-CSRFToken': getCSRFToken()
|
|
}
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
document.getElementById('referenceNumber').textContent = data.reference_number;
|
|
document.getElementById('successModal').classList.remove('hidden');
|
|
complaintForm.reset();
|
|
// Hide cascading dropdowns
|
|
const deptContainer = document.getElementById('department_container');
|
|
const secContainer = document.getElementById('section_container');
|
|
if (deptContainer) deptContainer.style.display = 'none';
|
|
if (secContainer) secContainer.style.display = 'none';
|
|
} else {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: T.error,
|
|
text: data.errors ? data.errors.join('\n') : (data.message || T.failed_to_submit_please_try_again)
|
|
});
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error('Submit error:', err);
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: T.error,
|
|
text: T.failed_to_submit_please_try_again
|
|
});
|
|
})
|
|
.finally(() => {
|
|
submitBtn.disabled = false;
|
|
submitBtn.innerHTML = originalHTML;
|
|
});
|
|
});
|
|
|
|
// Initialize icons
|
|
lucide.createIcons();
|
|
}
|
|
};
|
|
});
|
|
</script>
|
|
{% endblock %}
|