429 lines
20 KiB
HTML
429 lines
20 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% trans "Create Complaint" %} - {% trans "Call Center" %} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.form-section {
|
|
background: #fff;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 25px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
}
|
|
.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;
|
|
}
|
|
.patient-search-result {
|
|
padding: 10px;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
.patient-search-result:hover {
|
|
background: #f8f9fa;
|
|
border-color: #667eea;
|
|
}
|
|
.patient-search-result.selected {
|
|
background: #e7f3ff;
|
|
border-color: #667eea;
|
|
}
|
|
.badge-severity-critical { background: #dc3545; }
|
|
.badge-severity-high { background: #fd7e14; }
|
|
.badge-severity-medium { background: #ffc107; color: #000; }
|
|
.badge-severity-low { background: #28a745; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="mb-4">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h2 class="mb-1">
|
|
<i class="bi bi-telephone-fill text-primary me-2"></i>
|
|
{% trans "Create Complaint" %}
|
|
</h2>
|
|
<p class="text-muted mb-0">{% trans "File a complaint on behalf of a patient or caller" %}</p>
|
|
</div>
|
|
<a href="{% url 'callcenter:complaint_list' %}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left me-1"></i> {% trans "Back to List" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" action="{% url 'callcenter:create_complaint' %}" id="complaintForm">
|
|
{% csrf_token %}
|
|
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<!-- Caller Information -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-person-circle me-2"></i>{% trans "Caller Information" %}
|
|
</h5>
|
|
|
|
<div class="alert alert-info mb-3">
|
|
<i class="bi bi-info-circle me-2"></i>
|
|
{% trans "Search for an existing patient or enter caller details manually" %}
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 mb-3">
|
|
<label class="form-label">{% trans "Search Patient" %}</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="patientSearch"
|
|
placeholder="{% trans 'Search by MRN, name, phone, or national ID...' %}">
|
|
<button class="btn btn-outline-secondary" type="button" id="searchBtn">
|
|
<i class="bi bi-search"></i> {% trans "Search" %}
|
|
</button>
|
|
</div>
|
|
<div id="patientResults" class="mt-2"></div>
|
|
<input type="hidden" name="patient_id" id="patientId">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label required-field">{% trans "Caller Name" %}</label>
|
|
<input type="text" name="caller_name" id="callerName" class="form-control"
|
|
placeholder="{% trans 'Full name' %}" required>
|
|
</div>
|
|
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label required-field">{% trans "Caller Phone" %}</label>
|
|
<input type="tel" name="caller_phone" id="callerPhone" class="form-control"
|
|
placeholder="{% trans 'Phone number' %}" required>
|
|
</div>
|
|
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label required-field">{% trans "Relationship" %}</label>
|
|
<select name="caller_relationship" class="form-select" required>
|
|
<option value="patient">{% trans "Patient" %}</option>
|
|
<option value="family">{% trans "Family Member" %}</option>
|
|
<option value="other">{% trans "Other" %}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Organization Information -->
|
|
<div class="form-section">
|
|
<h5 class="form-section-title">
|
|
<i class="bi bi-hospital me-2"></i>{% trans "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="">{% trans "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="">{% trans "Select department..." %}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">{% trans "Physician" %}</label>
|
|
<select name="physician_id" class="form-select" id="physicianSelect">
|
|
<option value="">{% trans "Select physician..." %}</option>
|
|
</select>
|
|
</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>{% trans "Complaint Details" %}
|
|
</h5>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required-field">{% trans "Title" %}</label>
|
|
<input type="text" name="title" class="form-control"
|
|
placeholder="{% trans 'Brief summary of the complaint' %}" required maxlength="500">
|
|
</div>
|
|
|
|
<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. Include all relevant information provided by the caller...' %}" 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="">{% trans "Select category..." %}</option>
|
|
<option value="clinical_care">{% trans "Clinical Care" %}</option>
|
|
<option value="staff_behavior">{% trans "Staff Behavior" %}</option>
|
|
<option value="facility">{% trans "Facility & Environment" %}</option>
|
|
<option value="wait_time">{% trans "Wait Time" %}</option>
|
|
<option value="billing">{% trans "Billing" %}</option>
|
|
<option value="communication">{% trans "Communication" %}</option>
|
|
<option value="other">{% trans "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>{% trans "Classification" %}
|
|
</h5>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label required-field">{% trans "Severity" %}</label>
|
|
<select name="severity" class="form-select" id="severitySelect" required>
|
|
<option value="">{% trans "Select severity..." %}</option>
|
|
<option value="low">{% trans "Low" %}</option>
|
|
<option value="medium" selected>{% trans "Medium" %}</option>
|
|
<option value="high">{% trans "High" %}</option>
|
|
<option value="critical">{% trans "Critical" %}</option>
|
|
</select>
|
|
<small class="form-text text-muted">
|
|
{% trans "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="">{% trans "Select priority..." %}</option>
|
|
<option value="low">{% trans "Low" %}</option>
|
|
<option value="medium" selected>{% trans "Medium" %}</option>
|
|
<option value="high">{% trans "High" %}</option>
|
|
<option value="urgent">{% trans "Urgent" %}</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SLA Information -->
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">
|
|
<i class="bi bi-clock-history me-2"></i>{% trans "SLA Information" %}
|
|
</h6>
|
|
<p class="mb-0 small">
|
|
{% trans "SLA deadline will be automatically calculated based on severity:" %}
|
|
</p>
|
|
<ul class="mb-0 mt-2 small">
|
|
<li><strong>{% trans "Critical:" %}</strong> 4 {% trans "hours" %}</li>
|
|
<li><strong>{% trans "High:" %}</strong> 24 {% trans "hours" %}</li>
|
|
<li><strong>{% trans "Medium:" %}</strong> 72 {% trans "hours" %}</li>
|
|
<li><strong>{% trans "Low:" %}</strong> 168 {% trans "hours" %} (7 {% trans "days" %})</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Call Center Info -->
|
|
<div class="alert alert-secondary">
|
|
<h6 class="alert-heading">
|
|
<i class="bi bi-info-circle me-2"></i>{% trans "Call Center Note" %}
|
|
</h6>
|
|
<p class="mb-0 small">
|
|
{% trans "This complaint will be marked as received via Call Center. A call center interaction record will be automatically created." %}
|
|
</p>
|
|
</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>{% trans "Create Complaint" %}
|
|
</button>
|
|
<a href="{% url 'callcenter:complaint_list' %}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-x-circle me-2"></i>{% trans "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 physicianSelect = document.getElementById('physicianSelect');
|
|
const patientSearch = document.getElementById('patientSearch');
|
|
const searchBtn = document.getElementById('searchBtn');
|
|
const patientResults = document.getElementById('patientResults');
|
|
const patientIdInput = document.getElementById('patientId');
|
|
const callerNameInput = document.getElementById('callerName');
|
|
const callerPhoneInput = document.getElementById('callerPhone');
|
|
|
|
// Hospital change handler - load departments and physicians
|
|
hospitalSelect.addEventListener('change', function() {
|
|
const hospitalId = this.value;
|
|
|
|
// Clear department and physician
|
|
departmentSelect.innerHTML = '<option value="">{% trans "Select department..." %}</option>';
|
|
physicianSelect.innerHTML = '<option value="">{% trans "Select physician..." %}</option>';
|
|
|
|
if (hospitalId) {
|
|
// Load departments
|
|
fetch(`/callcenter/ajax/departments/?hospital_id=${hospitalId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
data.departments.forEach(dept => {
|
|
const option = document.createElement('option');
|
|
option.value = dept.id;
|
|
option.textContent = dept.name_en;
|
|
departmentSelect.appendChild(option);
|
|
});
|
|
})
|
|
.catch(error => console.error('Error loading departments:', error));
|
|
|
|
// Load physicians
|
|
fetch(`/callcenter/ajax/physicians/?hospital_id=${hospitalId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
data.physicians.forEach(physician => {
|
|
const option = document.createElement('option');
|
|
option.value = physician.id;
|
|
option.textContent = `${physician.name} (${physician.specialty})`;
|
|
physicianSelect.appendChild(option);
|
|
});
|
|
})
|
|
.catch(error => console.error('Error loading physicians:', error));
|
|
}
|
|
});
|
|
|
|
// Patient search
|
|
function searchPatients() {
|
|
const query = patientSearch.value.trim();
|
|
const hospitalId = hospitalSelect.value;
|
|
|
|
if (query.length < 2) {
|
|
patientResults.innerHTML = '<div class="alert alert-warning small">{% trans "Please enter at least 2 characters to search" %}</div>';
|
|
return;
|
|
}
|
|
|
|
patientResults.innerHTML = '<div class="text-center"><div class="spinner-border spinner-border-sm" role="status"></div> {% trans "Searching..." %}</div>';
|
|
|
|
let url = `/callcenter/ajax/patients/?q=${encodeURIComponent(query)}`;
|
|
if (hospitalId) {
|
|
url += `&hospital_id=${hospitalId}`;
|
|
}
|
|
|
|
fetch(url)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.patients.length === 0) {
|
|
patientResults.innerHTML = '<div class="alert alert-info small">{% trans "No patients found. Please enter caller details manually." %}</div>';
|
|
return;
|
|
}
|
|
|
|
let html = '<div class="mb-2"><strong>{% trans "Select Patient:" %}</strong></div>';
|
|
data.patients.forEach(patient => {
|
|
html += `
|
|
<div class="patient-search-result" data-patient-id="${patient.id}"
|
|
data-patient-name="${patient.name}" data-patient-phone="${patient.phone}">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<strong>${patient.name}</strong><br>
|
|
<small class="text-muted">
|
|
MRN: ${patient.mrn} |
|
|
Phone: ${patient.phone || 'N/A'} |
|
|
ID: ${patient.national_id || 'N/A'}
|
|
</small>
|
|
</div>
|
|
<div>
|
|
<i class="bi bi-check-circle text-success" style="font-size: 1.5rem; display: none;"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
patientResults.innerHTML = html;
|
|
|
|
// Add click handlers
|
|
document.querySelectorAll('.patient-search-result').forEach(result => {
|
|
result.addEventListener('click', function() {
|
|
// Remove previous selection
|
|
document.querySelectorAll('.patient-search-result').forEach(r => {
|
|
r.classList.remove('selected');
|
|
r.querySelector('.bi-check-circle').style.display = 'none';
|
|
});
|
|
|
|
// Mark as selected
|
|
this.classList.add('selected');
|
|
this.querySelector('.bi-check-circle').style.display = 'block';
|
|
|
|
// Set hidden input
|
|
patientIdInput.value = this.dataset.patientId;
|
|
|
|
// Auto-fill caller info
|
|
callerNameInput.value = this.dataset.patientName;
|
|
callerPhoneInput.value = this.dataset.patientPhone;
|
|
});
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error searching patients:', error);
|
|
patientResults.innerHTML = '<div class="alert alert-danger small">{% trans "Error searching patients. Please try again." %}</div>';
|
|
});
|
|
}
|
|
|
|
searchBtn.addEventListener('click', searchPatients);
|
|
patientSearch.addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
searchPatients();
|
|
}
|
|
});
|
|
|
|
// 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 %}
|