446 lines
21 KiB
HTML
446 lines
21 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ _("New Complaint")}} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.form-section {
|
|
background: #fff;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 25px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.form-section-title {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 2px solid #667eea;
|
|
}
|
|
.required-field::after {
|
|
content: " *";
|
|
color: #dc3545;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="mb-4">
|
|
<a href="{% url 'complaints:complaint_list' %}" class="btn btn-outline-secondary btn-sm mb-3">
|
|
<i class="bi bi-arrow-left me-1"></i> {{ _("Back to Complaints")}}
|
|
</a>
|
|
<h2 class="mb-1">
|
|
<i class="bi bi-plus-circle text-primary me-2"></i>
|
|
{{ _("Create New Complaint")}}
|
|
</h2>
|
|
<p class="text-muted mb-0">{{ _("File a new patient complaint with SLA tracking")}}</p>
|
|
</div>
|
|
|
|
<form method="post" action="{% url 'complaints:complaint_create' %}" id="complaintForm">
|
|
{% csrf_token %}
|
|
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<!-- Patient Information -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-person-fill me-2"></i>{{ _("Patient Information")}}
|
|
</h5>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label required-field">{% trans "Patient" %}</label>
|
|
<select name="patient_id" class="form-select" id="patientSelect" required>
|
|
<option value="">{{ _("Search and select patient")}}</option>
|
|
</select>
|
|
<small class="form-text text-muted">{{ _("Search by MRN or name")}}</small>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Encounter ID" %}</label>
|
|
<input type="text" name="encounter_id" class="form-control"
|
|
placeholder="{% trans 'Optional encounter/visit ID' %}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Organization Information -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-hospital me-2"></i>{{ _("Organization") }}
|
|
</h5>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label required-field">{% trans "Hospital" %}</label>
|
|
<select name="hospital_id" class="form-select" id="hospitalSelect" required>
|
|
<option value="">{{ _("Select hospital")}}</option>
|
|
{% for hospital in hospitals %}
|
|
<option value="{{ hospital.id }}">{{ hospital.name_en }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Department" %}</label>
|
|
<select name="department_id" class="form-select" id="departmentSelect">
|
|
<option value="">{{ _("Select department")}}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Staff" %}</label>
|
|
<select name="staff_id" class="form-select" id="staffSelect">
|
|
<option value="">{{ _("Select staff")}}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Classification Section -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-tags me-2"></i>Classification
|
|
</h5>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label required-field">{% trans "Category" %}</label>
|
|
<select name="category" class="form-select" id="categorySelect" required>
|
|
<option value="">Select hospital first...</option>
|
|
</select>
|
|
<small class="form-text text-muted">AI will analyze and suggest if needed</small>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Subcategory" %}</label>
|
|
<select name="subcategory" class="form-select" id="subcategorySelect">
|
|
<option value="">Select category first...</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Information -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-person-fill me-2"></i>Patient Information
|
|
</h5>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label required-field">{% trans "Patient" %}</label>
|
|
<select name="patient_id" class="form-select" id="patientSelect" required>
|
|
<option value="">Search and select patient...</option>
|
|
</select>
|
|
<small class="form-text text-muted">Search by MRN or name</small>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Encounter ID" %}</label>
|
|
<input type="text" name="encounter_id" class="form-control"
|
|
placeholder="{% trans 'Optional encounter/visit ID' %}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Complaint Details -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-file-text me-2"></i>{{ _("Complaint Details")}}
|
|
</h5>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required-field">{% trans "Description" %}</label>
|
|
<textarea name="description" class="form-control" rows="6"
|
|
placeholder="{% trans 'Detailed description of the complaint...' %}" required></textarea>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label required-field">{% trans "Category" %}</label>
|
|
<select name="category" class="form-select" required>
|
|
<option value="">{{ _("Select category")}}</option>
|
|
<option value="clinical_care">{{ _("Clinical Care")}}</option>
|
|
<option value="staff_behavior">{{ _("Staff Behavior")}}</option>
|
|
<option value="facility">{{ _("Facility & Environment")}}</option>
|
|
<option value="wait_time">{{ _("Wait Time")}}</option>
|
|
<option value="billing">{{ _("Billing") }}</option>
|
|
<option value="communication">{{ _("Communication") }}</option>
|
|
<option value="other">{{ _("Other") }}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Subcategory" %}</label>
|
|
<input type="text" name="subcategory" class="form-control"
|
|
placeholder="{% trans 'Optional subcategory' %}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Classification -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-tags me-2"></i>{{ _("Classification") }}
|
|
</h5>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required-field">{% trans "Severity" %}</label>
|
|
<select name="severity" class="form-select" required>
|
|
<option value="">{{ _("Select severity")}}</option>
|
|
<option value="low">{{ _("Low") }}</option>
|
|
<option value="medium" selected>{{ _("Medium") }}</option>
|
|
<option value="high">{{ _("High") }}</option>
|
|
<option value="critical">{{ _("Critical") }}</option>
|
|
</select>
|
|
<small class="form-text text-muted">
|
|
{{ _("Determines SLA deadline")}}
|
|
</small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required-field">{% trans "Priority" %}</label>
|
|
<select name="priority" class="form-select" required>
|
|
<option value="">{{ _("Select priority")}}</option>
|
|
<option value="low">{{ _("Low") }}</option>
|
|
<option value="medium" selected>{{ _("Medium") }}</option>
|
|
<option value="high">{{ _("High") }}</option>
|
|
<option value="urgent">{{ _("Urgent") }}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required-field">{% trans "Source" %}</label>
|
|
<select name="source" class="form-select" required>
|
|
<option value="">{{ _("Select source")}}</option>
|
|
<option value="patient">{{ _("Patient") }}</option>
|
|
<option value="family">{{ _("Family Member")}}</option>
|
|
<option value="staff">{{ _("Staff") }}</option>
|
|
<option value="survey">{{ _("Survey") }}</option>
|
|
<option value="social_media">{{ _("Social Media")}}</option>
|
|
<option value="call_center">{{ _("Call Center")}}</option>
|
|
<option value="moh">{{ _("Ministry of Health")}}</option>
|
|
<option value="chi">{{ _("Council of Health Insurance")}}</option>
|
|
<option value="other">{{ _("Other") }}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- AI Information -->
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">
|
|
<i class="bi bi-info-circle me-2"></i>{{ _("SLA Information")}}
|
|
</h6>
|
|
<p class="mb-0 small">
|
|
{{ _("SLA deadline will be automatically calculated based on severity")}}:
|
|
</p>
|
|
<ul class="mb-0 mt-2 small">
|
|
<li><strong>{{ _("Critical") }}:</strong> {{ _("4 hours")}}</li>
|
|
<li><strong>{{ _("High") }}:</strong> {{ _("24 hours")}}</li>
|
|
<li><strong>{{ _("Medium") }}:</strong> {{ _("72 hours")}}</li>
|
|
<li><strong>{{ _("Low") }}:</strong> {{ _("168 hours (7 days)")}}</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="d-grid gap-2">
|
|
<button type="submit" class="btn btn-primary btn-lg">
|
|
<i class="bi bi-check-circle me-2"></i>{{ _("Create Complaint")}}
|
|
</button>
|
|
<a href="{% url 'complaints:complaint_list' %}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-x-circle me-2"></i>{{ _("Cancel") }}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const hospitalSelect = document.getElementById('hospitalSelect');
|
|
const departmentSelect = document.getElementById('departmentSelect');
|
|
const staffSelect = document.getElementById('staffSelect');
|
|
const categorySelect = document.getElementById('categorySelect');
|
|
const subcategorySelect = document.getElementById('subcategorySelect');
|
|
const patientSelect = document.getElementById('patientSelect');
|
|
|
|
// Get current language
|
|
const currentLang = document.documentElement.lang || 'en';
|
|
|
|
// Hospital change handler
|
|
hospitalSelect.addEventListener('change', function() {
|
|
const hospitalId = this.value;
|
|
|
|
// Clear dependent dropdowns
|
|
departmentSelect.innerHTML = '<option value="">Select hospital first...</option>';
|
|
staffSelect.innerHTML = '<option value="">Select department first...</option>';
|
|
categorySelect.innerHTML = '<option value="">Loading categories...</option>';
|
|
subcategorySelect.innerHTML = '<option value="">Select category first...</option>';
|
|
|
|
if (hospitalId) {
|
|
// Load departments
|
|
fetch(`/api/organizations/departments/?hospital=${hospitalId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
departmentSelect.innerHTML = '<option value="">Select department...</option>';
|
|
data.results.forEach(dept => {
|
|
const option = document.createElement('option');
|
|
option.value = dept.id;
|
|
const deptName = currentLang === 'ar' && dept.name_ar ? dept.name_ar : dept.name_en;
|
|
option.textContent = deptName;
|
|
departmentSelect.appendChild(option);
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading departments:', error);
|
|
departmentSelect.innerHTML = '<option value="">Error loading departments</option>';
|
|
});
|
|
|
|
// Load categories (using public API endpoint)
|
|
fetch(`/complaints/public/api/load-categories/?hospital_id=${hospitalId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
categorySelect.innerHTML = '<option value="">Select category...</option>';
|
|
data.categories.forEach(cat => {
|
|
// Only show parent categories (no parent_id)
|
|
if (!cat.parent_id) {
|
|
const option = document.createElement('option');
|
|
option.value = cat.id;
|
|
option.dataset.code = cat.code;
|
|
const catName = currentLang === 'ar' && cat.name_ar ? cat.name_ar : cat.name_en;
|
|
option.textContent = catName;
|
|
categorySelect.appendChild(option);
|
|
}
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading categories:', error);
|
|
categorySelect.innerHTML = '<option value="">Error loading categories</option>';
|
|
});
|
|
} else {
|
|
categorySelect.innerHTML = '<option value="">Select hospital first...</option>';
|
|
}
|
|
});
|
|
|
|
// Department change handler - load staff
|
|
departmentSelect.addEventListener('change', function() {
|
|
const departmentId = this.value;
|
|
|
|
// Clear staff dropdown
|
|
staffSelect.innerHTML = '<option value="">Select department first...</option>';
|
|
|
|
if (departmentId) {
|
|
// Load staff (filtered by department)
|
|
fetch(`/complaints/ajax/get-staff-by-department/?department_id=${departmentId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
staffSelect.innerHTML = '<option value="">Select staff...</option>';
|
|
data.staff.forEach(staff => {
|
|
const option = document.createElement('option');
|
|
option.value = staff.id;
|
|
option.textContent = `${staff.first_name} ${staff.last_name} (${staff.job_title || staff.staff_type})`;
|
|
staffSelect.appendChild(option);
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading staff:', error);
|
|
staffSelect.innerHTML = '<option value="">Error loading staff</option>';
|
|
});
|
|
}
|
|
});
|
|
|
|
// Category change handler - load subcategories
|
|
categorySelect.addEventListener('change', function() {
|
|
const categoryId = this.value;
|
|
|
|
// Clear subcategory dropdown
|
|
subcategorySelect.innerHTML = '<option value="">Select category first...</option>';
|
|
|
|
if (categoryId) {
|
|
// Load categories again and filter for subcategories of selected parent
|
|
const hospitalId = hospitalSelect.value;
|
|
if (hospitalId) {
|
|
fetch(`/complaints/public/api/load-categories/?hospital_id=${hospitalId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
subcategorySelect.innerHTML = '<option value="">Select subcategory...</option>';
|
|
data.categories.forEach(cat => {
|
|
// Only show subcategories (has parent_id matching selected category)
|
|
if (cat.parent_id == categoryId) {
|
|
const option = document.createElement('option');
|
|
option.value = cat.id;
|
|
option.dataset.code = cat.code;
|
|
const catName = currentLang === 'ar' && cat.name_ar ? cat.name_ar : cat.name_en;
|
|
option.textContent = catName;
|
|
subcategorySelect.appendChild(option);
|
|
}
|
|
});
|
|
if (subcategorySelect.options.length <= 1) {
|
|
subcategorySelect.innerHTML = '<option value="">No subcategories available</option>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading subcategories:', error);
|
|
subcategorySelect.innerHTML = '<option value="">Error loading subcategories</option>';
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// Patient search
|
|
patientSelect.addEventListener('focus', function() {
|
|
if (this.options.length === 1) {
|
|
loadPatients('');
|
|
}
|
|
});
|
|
|
|
function loadPatients(searchTerm) {
|
|
const url = searchTerm
|
|
? `/api/organizations/patients/?search=${encodeURIComponent(searchTerm)}`
|
|
: '/api/organizations/patients/?page_size=50';
|
|
|
|
fetch(url)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
patientSelect.innerHTML = '<option value="">Search and select patient...</option>';
|
|
data.results.forEach(patient => {
|
|
const option = document.createElement('option');
|
|
option.value = patient.id;
|
|
option.textContent = `${patient.first_name} ${patient.last_name} (MRN: ${patient.mrn})`;
|
|
patientSelect.appendChild(option);
|
|
});
|
|
})
|
|
.catch(error => console.error('Error loading patients:', error));
|
|
}
|
|
|
|
// Form validation
|
|
const form = document.getElementById('complaintForm');
|
|
form.addEventListener('submit', function(e) {
|
|
if (!form.checkValidity()) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
form.classList.add('was-validated');
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|