494 lines
22 KiB
HTML
494 lines
22 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Incident Report Details - Quality Management{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- BEGIN breadcrumb -->
|
|
<ol class="breadcrumb float-xl-end">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'quality:dashboard' %}">Quality</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'quality:incident_report_list' %}">Incident Reports</a></li>
|
|
<li class="breadcrumb-item active">{{ object.report_number }}</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
Incident Report Details
|
|
<small>{{ object.report_number }}</small>
|
|
</h1>
|
|
<!-- END page-header -->
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">
|
|
<i class="fa fa-file-alt me-2"></i>
|
|
{{ object.title }}
|
|
</h4>
|
|
<div class="panel-heading-btn">
|
|
<a href="{% url 'quality:incident_report_edit' object.pk %}" class="btn btn-sm btn-primary">
|
|
<i class="fa fa-edit me-1"></i>Edit
|
|
</a>
|
|
<a href="{% url 'quality:incident_report_print' object.pk %}" class="btn btn-sm btn-secondary" target="_blank">
|
|
<i class="fa fa-print me-1"></i>Print
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<!-- Report Status and Priority -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="d-flex align-items-center mb-2">
|
|
<strong class="me-2">Status:</strong>
|
|
{% if object.status == 'draft' %}
|
|
<span class="badge bg-secondary">Draft</span>
|
|
{% elif object.status == 'submitted' %}
|
|
<span class="badge bg-warning text-dark">Submitted</span>
|
|
{% elif object.status == 'under_review' %}
|
|
<span class="badge bg-info">Under Review</span>
|
|
{% elif object.status == 'approved' %}
|
|
<span class="badge bg-success">Approved</span>
|
|
{% elif object.status == 'rejected' %}
|
|
<span class="badge bg-danger">Rejected</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="d-flex align-items-center mb-2">
|
|
<strong class="me-2">Priority:</strong>
|
|
{% if object.priority == 'critical' %}
|
|
<span class="badge bg-danger">Critical</span>
|
|
{% elif object.priority == 'high' %}
|
|
<span class="badge bg-warning text-dark">High</span>
|
|
{% elif object.priority == 'medium' %}
|
|
<span class="badge bg-info">Medium</span>
|
|
{% elif object.priority == 'low' %}
|
|
<span class="badge bg-success">Low</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-2">
|
|
<strong>Report Number:</strong> {{ object.report_number }}
|
|
</div>
|
|
<div class="mb-2">
|
|
<strong>Created:</strong> {{ object.created_at|date:"M d, Y H:i" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Report Content -->
|
|
<div class="card border-primary mb-4">
|
|
<div class="card-header bg-primary text-white">
|
|
<h6 class="card-title mb-0">Report Content</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<strong>Description:</strong>
|
|
<div class="mt-2">{{ object.description|linebreaks }}</div>
|
|
</div>
|
|
|
|
{% if object.findings %}
|
|
<div class="mb-3">
|
|
<strong>Findings:</strong>
|
|
<div class="mt-2">{{ object.findings|linebreaks }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if object.recommendations %}
|
|
<div class="mb-3">
|
|
<strong>Recommendations:</strong>
|
|
<div class="mt-2">{{ object.recommendations|linebreaks }}</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Related Incidents -->
|
|
{% if object.related_incidents.exists %}
|
|
<div class="card border-info mb-4">
|
|
<div class="card-header bg-info text-white">
|
|
<h6 class="card-title mb-0">Related Incidents</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Incident Number</th>
|
|
<th>Title</th>
|
|
<th>Status</th>
|
|
<th>Severity</th>
|
|
<th>Date</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for incident in object.related_incidents.all %}
|
|
<tr>
|
|
<td>{{ incident.incident_number }}</td>
|
|
<td>{{ incident.title|truncatechars:40 }}</td>
|
|
<td>
|
|
{% if incident.status == 'open' %}
|
|
<span class="badge bg-warning text-dark">Open</span>
|
|
{% elif incident.status == 'investigating' %}
|
|
<span class="badge bg-info">Investigating</span>
|
|
{% elif incident.status == 'resolved' %}
|
|
<span class="badge bg-success">Resolved</span>
|
|
{% elif incident.status == 'closed' %}
|
|
<span class="badge bg-secondary">Closed</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if incident.severity == 'critical' %}
|
|
<span class="badge bg-danger">Critical</span>
|
|
{% elif incident.severity == 'high' %}
|
|
<span class="badge bg-warning text-dark">High</span>
|
|
{% elif incident.severity == 'medium' %}
|
|
<span class="badge bg-info">Medium</span>
|
|
{% elif incident.severity == 'low' %}
|
|
<span class="badge bg-success">Low</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ incident.occurred_at|date:"M d, Y" }}</td>
|
|
<td>
|
|
<a href="{% url 'quality:incident_detail' incident.pk %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="fa fa-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Attachments -->
|
|
{% if object.attachments.exists %}
|
|
<div class="card border-secondary mb-4">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h6 class="card-title mb-0">Attachments</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
{% for attachment in object.attachments.all %}
|
|
<div class="col-md-6 mb-3">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">{{ attachment.name }}</h6>
|
|
<p class="card-text small text-muted">
|
|
Size: {{ attachment.file.size|filesizeformat }}<br>
|
|
Uploaded: {{ attachment.uploaded_at|date:"M d, Y H:i" }}
|
|
</p>
|
|
<a href="{{ attachment.file.url }}" class="btn btn-sm btn-primary" target="_blank">
|
|
<i class="fa fa-download me-1"></i>Download
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Review History -->
|
|
{% if object.review_history.exists %}
|
|
<div class="card border-warning mb-4">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h6 class="card-title mb-0">Review History</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="timeline">
|
|
{% for review in object.review_history.all %}
|
|
<div class="timeline-item">
|
|
<div class="timeline-marker"></div>
|
|
<div class="timeline-content">
|
|
<h6 class="timeline-title">
|
|
{{ review.action }} by {{ review.reviewer.get_full_name }}
|
|
</h6>
|
|
<p class="timeline-text">{{ review.comments }}</p>
|
|
<small class="text-muted">{{ review.created_at|date:"M d, Y H:i" }}</small>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Report Information</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="mb-3">
|
|
<strong>Author:</strong><br>
|
|
{{ object.author.get_full_name }}<br>
|
|
<small class="text-muted">{{ object.author.email }}</small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<strong>Department:</strong><br>
|
|
{{ object.department.name|default:"Not specified" }}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<strong>Report Type:</strong><br>
|
|
{{ object.get_report_type_display }}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<strong>Reporting Period:</strong><br>
|
|
{{ object.period_start|date:"M d, Y" }} - {{ object.period_end|date:"M d, Y" }}
|
|
</div>
|
|
|
|
{% if object.reviewer %}
|
|
<div class="mb-3">
|
|
<strong>Reviewer:</strong><br>
|
|
{{ object.reviewer.get_full_name }}<br>
|
|
<small class="text-muted">{{ object.reviewer.email }}</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if object.approved_at %}
|
|
<div class="mb-3">
|
|
<strong>Approved:</strong><br>
|
|
{{ object.approved_at|date:"M d, Y H:i" }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-3">
|
|
<strong>Last Modified:</strong><br>
|
|
{{ object.updated_at|date:"M d, Y H:i" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Quick Actions</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="d-grid gap-2">
|
|
{% if object.status == 'draft' %}
|
|
<button type="button" class="btn btn-primary" onclick="submitReport()">
|
|
<i class="fa fa-paper-plane me-2"></i>Submit for Review
|
|
</button>
|
|
{% elif object.status == 'submitted' and user.has_perm:'quality.review_incident_report' %}
|
|
<button type="button" class="btn btn-info" onclick="startReview()">
|
|
<i class="fa fa-search me-2"></i>Start Review
|
|
</button>
|
|
{% elif object.status == 'under_review' and user.has_perm:'quality.approve_incident_report' %}
|
|
<button type="button" class="btn btn-success" onclick="approveReport()">
|
|
<i class="fa fa-check me-2"></i>Approve Report
|
|
</button>
|
|
<button type="button" class="btn btn-danger" onclick="rejectReport()">
|
|
<i class="fa fa-times me-2"></i>Reject Report
|
|
</button>
|
|
{% endif %}
|
|
|
|
<a href="{% url 'quality:incident_report_edit' object.pk %}" class="btn btn-outline-primary">
|
|
<i class="fa fa-edit me-2"></i>Edit Report
|
|
</a>
|
|
|
|
<button type="button" class="btn btn-outline-secondary" onclick="duplicateReport()">
|
|
<i class="fa fa-copy me-2"></i>Duplicate Report
|
|
</button>
|
|
|
|
<a href="{% url 'quality:incident_report_export' object.pk %}" class="btn btn-outline-info">
|
|
<i class="fa fa-download me-2"></i>Export PDF
|
|
</a>
|
|
|
|
{% if user.has_perm:'quality.delete_incident_report' %}
|
|
<a href="{% url 'quality:incident_report_delete' object.pk %}" class="btn btn-outline-danger">
|
|
<i class="fa fa-trash me-2"></i>Delete Report
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Statistics</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="mb-2">
|
|
<div class="h4 mb-0">{{ object.related_incidents.count }}</div>
|
|
<div class="small text-muted">Related Incidents</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="mb-2">
|
|
<div class="h4 mb-0">{{ object.attachments.count }}</div>
|
|
<div class="small text-muted">Attachments</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row text-center mt-3">
|
|
<div class="col-6">
|
|
<div class="mb-2">
|
|
<div class="h4 mb-0">{{ object.review_history.count }}</div>
|
|
<div class="small text-muted">Reviews</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="mb-2">
|
|
<div class="h4 mb-0">{{ object.days_since_created }}</div>
|
|
<div class="small text-muted">Days Old</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Contact Information</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="small">
|
|
<div class="mb-2">
|
|
<strong>Quality Manager:</strong><br>
|
|
<i class="fa fa-phone me-1"></i> (555) 123-4567<br>
|
|
<i class="fa fa-envelope me-1"></i> quality@hospital.com
|
|
</div>
|
|
<div class="mb-2">
|
|
<strong>Report Author:</strong><br>
|
|
<i class="fa fa-user me-1"></i> {{ object.author.get_full_name }}<br>
|
|
<i class="fa fa-envelope me-1"></i> {{ object.author.email }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
function submitReport() {
|
|
if (confirm('Submit this report for review? You will not be able to edit it after submission.')) {
|
|
$.ajax({
|
|
url: '{% url "quality:submit_incident_report" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
success: function(response) {
|
|
toastr.success('Report submitted for review');
|
|
location.reload();
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to submit report');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function startReview() {
|
|
if (confirm('Start reviewing this report?')) {
|
|
$.ajax({
|
|
url: '{% url "quality:start_review_incident_report" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
success: function(response) {
|
|
toastr.success('Review started');
|
|
location.reload();
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to start review');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function approveReport() {
|
|
var comments = prompt('Enter approval comments (optional):');
|
|
if (comments !== null) {
|
|
$.ajax({
|
|
url: '{% url "quality:approve_incident_report" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
|
'comments': comments
|
|
},
|
|
success: function(response) {
|
|
toastr.success('Report approved');
|
|
location.reload();
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to approve report');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function rejectReport() {
|
|
var reason = prompt('Enter rejection reason:');
|
|
if (reason && reason.trim()) {
|
|
$.ajax({
|
|
url: '{% url "quality:reject_incident_report" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}',
|
|
'reason': reason
|
|
},
|
|
success: function(response) {
|
|
toastr.success('Report rejected');
|
|
location.reload();
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to reject report');
|
|
}
|
|
});
|
|
} else {
|
|
toastr.warning('Rejection reason is required');
|
|
}
|
|
}
|
|
|
|
function duplicateReport() {
|
|
if (confirm('Create a duplicate of this report?')) {
|
|
$.ajax({
|
|
url: '{% url "quality:duplicate_incident_report" object.pk %}',
|
|
method: 'POST',
|
|
data: {
|
|
'csrfmiddlewaretoken': '{{ csrf_token }}'
|
|
},
|
|
success: function(response) {
|
|
toastr.success('Report duplicated');
|
|
window.location.href = response.redirect_url;
|
|
},
|
|
error: function() {
|
|
toastr.error('Failed to duplicate report');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|