325 lines
15 KiB
HTML
325 lines
15 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% trans "New Observation" %} - PX360{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="mb-6">
|
|
<a href="{% url 'observations:observation_list' %}" class="text-slate hover:text-navy text-sm font-medium inline-flex items-center gap-1 mb-4 transition">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i>
|
|
{% trans "Back to Observations" %}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="max-w-3xl mx-auto">
|
|
<!-- Header -->
|
|
<div class="text-center mb-8">
|
|
<div class="w-16 h-16 bg-gradient-to-br from-navy to-blue rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-lg">
|
|
<i data-lucide="eye" class="w-8 h-8 text-white"></i>
|
|
</div>
|
|
<h1 class="text-2xl font-bold text-navy mb-2">{% trans "Report an Observation" %}</h1>
|
|
<p class="text-slate">{% trans "Help us improve by reporting issues you notice" %}</p>
|
|
</div>
|
|
|
|
{% if messages %}
|
|
{% for message in messages %}
|
|
<div class="bg-{% if message.tags == 'error' %}red{% elif message.tags == 'warning' %}yellow{% else %}green{% endif %}-50 border border-{% if message.tags == 'error' %}red{% elif message.tags == 'warning' %}yellow{% else %}green{% endif %}-200 rounded-xl p-4 mb-4 text-sm text-{% if message.tags == 'error' %}red{% elif message.tags == 'warning' %}yellow{% else %}green{% endif %}-800">
|
|
{{ message }}
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<!-- Form Card -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-slate-100 p-8">
|
|
<form method="post" enctype="multipart/form-data" id="observationForm">
|
|
{% csrf_token %}
|
|
{% if communication_request %}
|
|
<input type="hidden" name="comm_req" value="{{ communication_request.id }}">
|
|
{% endif %}
|
|
|
|
<!-- Hospital -->
|
|
<div class="mb-6">
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="building-2" class="w-4 h-4"></i>
|
|
{% trans "Hospital" %}
|
|
<span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.hospital }}
|
|
{% if form.hospital.errors %}<p class="text-xs text-red-500 mt-1">{{ form.hospital.errors.0 }}</p>{% endif %}
|
|
</div>
|
|
|
|
<!-- Location Type -->
|
|
<div class="mb-6">
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="map-pin" class="w-4 h-4"></i>
|
|
{% trans "Location Type" %}
|
|
<span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.location_type }}
|
|
{% if form.location_type.errors %}<p class="text-xs text-red-500 mt-1">{{ form.location_type.errors.0 }}</p>{% endif %}
|
|
</div>
|
|
|
|
<!-- Area -->
|
|
<div class="mb-6">
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="map" class="w-4 h-4"></i>
|
|
{% trans "Area" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
{{ form.area }}
|
|
{% if form.area.errors %}<p class="text-xs text-red-500 mt-1">{{ form.area.errors.0 }}</p>{% endif %}
|
|
</div>
|
|
|
|
<!-- Department + Section (2-col) -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<div>
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="building" class="w-4 h-4"></i>
|
|
{% trans "Department" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
{{ form.assigned_department }}
|
|
</div>
|
|
<div>
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="layers" class="w-4 h-4"></i>
|
|
{% trans "Section" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
{{ form.section }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Title -->
|
|
<div class="mb-6">
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="type" class="w-4 h-4"></i>
|
|
{% trans "Title" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
{{ form.title }}
|
|
</div>
|
|
|
|
<!-- Description -->
|
|
<div class="mb-6">
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="file-text" class="w-4 h-4"></i>
|
|
{% trans "Description" %}
|
|
<span class="text-red-500">*</span>
|
|
</label>
|
|
{{ form.description }}
|
|
<p class="text-xs text-slate mt-1.5">{% trans "Please describe what you observed in detail." %}</p>
|
|
{% if form.description.errors %}<p class="text-xs text-red-500 mt-1">{{ form.description.errors.0 }}</p>{% endif %}
|
|
</div>
|
|
|
|
<!-- Location Details + Incident Datetime (2-col) -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<div>
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="map-pin" class="w-4 h-4"></i>
|
|
{% trans "Location Details" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
{{ form.location_text }}
|
|
</div>
|
|
<div>
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="calendar" class="w-4 h-4"></i>
|
|
{% trans "When did this occur?" %}
|
|
</label>
|
|
{{ form.incident_datetime }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Department + Source (2-col) -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<div>
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-3">
|
|
<i data-lucide="source" class="w-4 h-4"></i>
|
|
{% trans "Source" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
{{ form.px_source }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Information -->
|
|
<div class="mb-8 pt-6 border-t border-slate-100">
|
|
<label class="flex items-center gap-2 text-sm font-bold text-navy mb-2">
|
|
<i data-lucide="user" class="w-4 h-4"></i>
|
|
{% trans "Patient Information" %}
|
|
<span class="text-xs font-normal text-slate">({% trans "optional" %})</span>
|
|
</label>
|
|
<p class="text-xs text-slate mb-4">{% trans "Fill in if this observation involves a specific patient." %}</p>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate mb-1.5">{% trans "Patient Name" %}</label>
|
|
{{ form.patient_name }}
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate mb-1.5">{% trans "National ID/Iqama" %}</label>
|
|
{{ form.patient_national_id }}
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate mb-1.5">{% trans "Phone" %}</label>
|
|
{{ form.patient_phone }}
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate mb-1.5">{% trans "Email" %}</label>
|
|
{{ form.patient_email }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submit -->
|
|
<div class="flex gap-3">
|
|
<a href="{% url 'observations:observation_list' %}" class="px-5 py-3 border border-slate-200 text-slate-600 rounded-xl font-semibold text-sm hover:bg-slate-50 transition flex items-center gap-2">
|
|
{% trans "Cancel" %}
|
|
</a>
|
|
<button type="submit" class="flex-1 bg-navy text-white py-3 rounded-xl font-bold text-sm hover:bg-blue transition shadow-lg shadow-navy/20 flex items-center justify-center gap-2">
|
|
<i data-lucide="send" class="w-5 h-5"></i>
|
|
{% trans "Create Observation" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
{% trans "Select assignee (optional)" as trans_select_assignee %}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const selAssignee = '{{ trans_select_assignee|escapejs }}';
|
|
const locationTypeSelect = document.getElementById('locationTypeSelect');
|
|
const areaSelect = document.getElementById('areaSelect');
|
|
const deptSelect = document.getElementById('id_assigned_department');
|
|
const assigneeSelect = document.getElementById('id_assigned_to');
|
|
const sectionSelect = document.getElementById('sectionSelect');
|
|
|
|
function getHospitalId() {
|
|
const sel = document.getElementById('hospitalSelect');
|
|
return sel ? sel.value : null;
|
|
}
|
|
|
|
function loadAreas(locationType) {
|
|
if (!areaSelect) return;
|
|
areaSelect.innerHTML = '<option value="">{% trans "Loading..." %}</option>';
|
|
var hospitalId = getHospitalId();
|
|
if (!hospitalId) {
|
|
areaSelect.innerHTML = '<option value="">{% trans "Select Area (optional)" %}</option>';
|
|
return;
|
|
}
|
|
var url = '/organizations/dropdowns/areas/?hospital=' + encodeURIComponent(hospitalId);
|
|
if (locationType) url += '&location_type=' + encodeURIComponent(locationType);
|
|
fetch(url)
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
areaSelect.innerHTML = '<option value="">{% trans "Select Area (optional)" %}</option>';
|
|
(data || []).forEach(function(a) {
|
|
var opt = document.createElement('option');
|
|
opt.value = a.id;
|
|
opt.textContent = a.name_en;
|
|
areaSelect.appendChild(opt);
|
|
});
|
|
})
|
|
.catch(function(err) {
|
|
console.error('Error loading areas:', err);
|
|
areaSelect.innerHTML = '<option value="">{% trans "Error loading areas" %}</option>';
|
|
});
|
|
}
|
|
|
|
function loadDepartments(hospitalId) {
|
|
if (!deptSelect) return;
|
|
deptSelect.innerHTML = '<option value="">{% trans "Loading..." %}</option>';
|
|
if (!hospitalId) {
|
|
deptSelect.innerHTML = '<option value="">{% trans "Select Department" %}</option>';
|
|
return;
|
|
}
|
|
fetch('/organizations/dropdowns/departments-by-category/?hospital=' + encodeURIComponent(hospitalId))
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
deptSelect.innerHTML = '<option value="">{% trans "Select Department" %}</option>';
|
|
(data || []).forEach(function(d) {
|
|
var opt = document.createElement('option');
|
|
opt.value = d.id;
|
|
opt.textContent = d.name_en || d.name;
|
|
deptSelect.appendChild(opt);
|
|
});
|
|
})
|
|
.catch(function(err) {
|
|
console.error('Error loading departments:', err);
|
|
deptSelect.innerHTML = '<option value="">{% trans "Error loading departments" %}</option>';
|
|
});
|
|
}
|
|
|
|
if (locationTypeSelect) {
|
|
locationTypeSelect.addEventListener('change', function() {
|
|
loadAreas(this.value);
|
|
});
|
|
}
|
|
|
|
if (deptSelect) {
|
|
deptSelect.addEventListener('change', function() {
|
|
var deptId = this.value;
|
|
if (!deptId) {
|
|
if (assigneeSelect) assigneeSelect.innerHTML = '<option value="">' + selAssignee + '</option>';
|
|
if (sectionSelect) sectionSelect.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
return;
|
|
}
|
|
|
|
if (assigneeSelect) {
|
|
fetch("{% url 'observations:get_users_by_department' %}?department_id=" + deptId)
|
|
.then(function(response) { return response.json(); })
|
|
.then(function(data) {
|
|
var html = '<option value="">' + selAssignee + '</option>';
|
|
data.users.forEach(function(user) {
|
|
html += '<option value="' + user.id + '">' + user.name + '</option>';
|
|
});
|
|
assigneeSelect.innerHTML = html;
|
|
})
|
|
.catch(function() { console.error('Failed to load users'); });
|
|
}
|
|
|
|
if (sectionSelect) {
|
|
sectionSelect.innerHTML = '<option value="">{% trans "Loading..." %}</option>';
|
|
fetch('/organizations/dropdowns/sections/' + deptId + '/')
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
sectionSelect.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
(data || []).forEach(function(s) {
|
|
var opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = s.name_en || s.name;
|
|
sectionSelect.appendChild(opt);
|
|
});
|
|
})
|
|
.catch(function(err) { console.error(err); });
|
|
}
|
|
});
|
|
}
|
|
|
|
var initHospitalId = getHospitalId();
|
|
if (initHospitalId) {
|
|
loadDepartments(initHospitalId);
|
|
loadAreas(null);
|
|
}
|
|
|
|
var hospitalSelect = document.getElementById('hospitalSelect');
|
|
if (hospitalSelect) {
|
|
hospitalSelect.addEventListener('change', function() {
|
|
loadDepartments(this.value);
|
|
loadAreas(null);
|
|
if (sectionSelect) sectionSelect.innerHTML = '<option value="">{% trans "Select Section" %}</option>';
|
|
if (assigneeSelect) assigneeSelect.innerHTML = '<option value="">' + selAssignee + '</option>';
|
|
});
|
|
}
|
|
|
|
if (typeof lucide !== 'undefined') lucide.createIcons();
|
|
});
|
|
</script>
|
|
{% endblock %}
|