462 lines
20 KiB
HTML
462 lines
20 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ object.name }} - Equipment Details{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div>
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'radiology:dashboard' %}">Radiology</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'radiology:equipment_list' %}">Equipment</a></li>
|
|
<li class="breadcrumb-item active">{{ object.name }}</li>
|
|
</ol>
|
|
<h1 class="page-header mb-0">{{ object.name }}</h1>
|
|
</div>
|
|
<div class="ms-auto">
|
|
<a href="{% url 'radiology:equipment_update' object.pk %}" class="btn btn-primary">
|
|
<i class="fas fa-edit me-2"></i>Edit Equipment
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<!-- Equipment Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-x-ray me-2"></i>
|
|
Equipment Information
|
|
</h4>
|
|
<div class="card-toolbar">
|
|
<span class="badge bg-{% if object.status == 'operational' %}success{% elif object.status == 'maintenance' %}warning{% elif object.status == 'offline' %}danger{% else %}secondary{% endif %} fs-6">
|
|
{{ object.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Equipment Name:</td>
|
|
<td>{{ object.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Modality:</td>
|
|
<td>{{ object.get_modality_display }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Manufacturer:</td>
|
|
<td>{{ object.manufacturer|default:"Not specified" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Model:</td>
|
|
<td>{{ object.model|default:"Not specified" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Serial Number:</td>
|
|
<td>{{ object.serial_number|default:"Not specified" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold text-muted">Location:</td>
|
|
<td>{{ object.location|default:"Not specified" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Installation Date:</td>
|
|
<td>{{ object.installation_date|date:"M d, Y"|default:"Unknown" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Warranty Expires:</td>
|
|
<td>
|
|
{% if object.warranty_expiry %}
|
|
<span class="{% if object.warranty_expired %}text-danger{% elif object.warranty_expiring_soon %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.warranty_expiry|date:"M d, Y" }}
|
|
</span>
|
|
{% else %}
|
|
Not specified
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Asset Tag:</td>
|
|
<td>{{ object.asset_tag|default:"Not assigned" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold text-muted">Purchase Cost:</td>
|
|
<td>{{ object.purchase_cost|default:"Not specified" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Usage Statistics -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-chart-bar me-2"></i>
|
|
Usage Statistics
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row text-center mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h3 class="text-primary">{{ object.studies_today|default:0 }}</h3>
|
|
<p class="mb-0">Studies Today</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h3 class="text-info">{{ object.studies_this_week|default:0 }}</h3>
|
|
<p class="mb-0">This Week</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h3 class="text-success">{{ object.studies_this_month|default:0 }}</h3>
|
|
<p class="mb-0">This Month</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-light">
|
|
<div class="card-body">
|
|
<h3 class="text-warning">{{ object.utilization_rate|default:0 }}%</h3>
|
|
<p class="mb-0">Utilization</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Usage Chart Placeholder -->
|
|
<div class="text-center">
|
|
<canvas id="usageChart" width="400" height="200"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Maintenance History -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-wrench me-2"></i>
|
|
Maintenance History
|
|
</h4>
|
|
<div class="card-toolbar">
|
|
<button type="button" class="btn btn-sm btn-primary" onclick="scheduleService()">
|
|
<i class="fas fa-plus me-1"></i>Schedule Service
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if object.maintenance_records.all %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Type</th>
|
|
<th>Description</th>
|
|
<th>Technician</th>
|
|
<th>Status</th>
|
|
<th>Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for record in object.maintenance_records.all|slice:":5" %}
|
|
<tr>
|
|
<td>{{ record.date|date:"M d, Y" }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if record.type == 'preventive' %}info{% elif record.type == 'corrective' %}warning{% else %}secondary{% endif %}">
|
|
{{ record.get_type_display }}
|
|
</span>
|
|
</td>
|
|
<td>{{ record.description|truncatewords:10 }}</td>
|
|
<td>{{ record.technician|default:"Not specified" }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if record.status == 'completed' %}success{% elif record.status == 'in_progress' %}warning{% else %}secondary{% endif %}">
|
|
{{ record.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>{{ record.cost|default:"N/A" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% if object.maintenance_records.count > 5 %}
|
|
<div class="text-center">
|
|
<a href="#" class="btn btn-outline-primary btn-sm">View All Maintenance Records</a>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-wrench fa-3x text-muted mb-3"></i>
|
|
<p class="text-muted">No maintenance records found.</p>
|
|
<button type="button" class="btn btn-primary" onclick="scheduleService()">
|
|
<i class="fas fa-plus me-2"></i>Schedule First Service
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- Quick Actions -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Quick Actions</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
{% if object.status == 'operational' %}
|
|
<button type="button" class="btn btn-warning" onclick="takeOffline()">
|
|
<i class="fas fa-pause me-2"></i>Take Offline
|
|
</button>
|
|
{% elif object.status == 'offline' %}
|
|
<button type="button" class="btn btn-success" onclick="bringOnline()">
|
|
<i class="fas fa-play me-2"></i>Bring Online
|
|
</button>
|
|
{% endif %}
|
|
|
|
<button type="button" class="btn btn-outline-primary" onclick="viewSchedule()">
|
|
<i class="fas fa-calendar me-2"></i>View Schedule
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-outline-info" onclick="generateReport()">
|
|
<i class="fas fa-chart-bar me-2"></i>Usage Report
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-outline-secondary" onclick="printQRCode()">
|
|
<i class="fas fa-qrcode me-2"></i>Print QR Code
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Service Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Service Information</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Last Service Date</label>
|
|
<div class="fw-bold">
|
|
{% if object.last_service_date %}
|
|
{{ object.last_service_date|date:"M d, Y" }}
|
|
<small class="text-muted">({{ object.days_since_service }} days ago)</small>
|
|
{% else %}
|
|
<span class="text-warning">Never serviced</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Next Service Due</label>
|
|
<div class="fw-bold">
|
|
{% if object.next_service_date %}
|
|
<span class="{% if object.service_overdue %}text-danger{% elif object.service_due_soon %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.next_service_date|date:"M d, Y" }}
|
|
</span>
|
|
{% if object.service_overdue %}
|
|
<small class="text-danger">(Overdue)</small>
|
|
{% elif object.service_due_soon %}
|
|
<small class="text-warning">(Due Soon)</small>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="text-muted">Not scheduled</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Service Contract</label>
|
|
<div class="fw-bold">
|
|
{% if object.service_contract %}
|
|
<span class="text-success">Active</span>
|
|
<br><small class="text-muted">{{ object.service_provider }}</small>
|
|
{% else %}
|
|
<span class="text-warning">No Contract</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Downtime This Month</label>
|
|
<div class="fw-bold">
|
|
<span class="{% if object.downtime_hours > 24 %}text-danger{% elif object.downtime_hours > 8 %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.downtime_hours|default:0 }} hours
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Technical Specifications -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Technical Specifications</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if object.specifications %}
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Power Requirements</label>
|
|
<div>{{ object.specifications.power|default:"Not specified" }}</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Dimensions</label>
|
|
<div>{{ object.specifications.dimensions|default:"Not specified" }}</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Weight</label>
|
|
<div>{{ object.specifications.weight|default:"Not specified" }}</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Software Version</label>
|
|
<div>{{ object.specifications.software_version|default:"Not specified" }}</div>
|
|
</div>
|
|
{% else %}
|
|
<p class="text-muted">No technical specifications available.</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contact Information -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Support Contacts</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Service Provider</label>
|
|
<div class="fw-bold">{{ object.service_provider|default:"Not specified" }}</div>
|
|
{% if object.service_phone %}
|
|
<small class="text-muted">{{ object.service_phone }}</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Technical Support</label>
|
|
<div class="fw-bold">{{ object.tech_support_contact|default:"Not specified" }}</div>
|
|
{% if object.tech_support_phone %}
|
|
<small class="text-muted">{{ object.tech_support_phone }}</small>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label text-muted">Emergency Contact</label>
|
|
<div class="fw-bold">{{ object.emergency_contact|default:"Not specified" }}</div>
|
|
{% if object.emergency_phone %}
|
|
<small class="text-muted">{{ object.emergency_phone }}</small>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize usage chart
|
|
const ctx = document.getElementById('usageChart').getContext('2d');
|
|
|
|
// Sample data - replace with actual data from backend
|
|
const usageData = {
|
|
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
datasets: [{
|
|
label: 'Studies',
|
|
data: [12, 19, 15, 17, 14, 8, 6],
|
|
borderColor: 'rgb(75, 192, 192)',
|
|
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
|
tension: 0.1
|
|
}]
|
|
};
|
|
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: usageData,
|
|
options: {
|
|
responsive: true,
|
|
plugins: {
|
|
title: {
|
|
display: true,
|
|
text: 'Weekly Usage Pattern'
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
function takeOffline() {
|
|
if (confirm('Take this equipment offline? This will prevent new studies from being scheduled.')) {
|
|
updateEquipmentStatus('offline');
|
|
}
|
|
}
|
|
|
|
function bringOnline() {
|
|
if (confirm('Bring this equipment online? It will be available for scheduling.')) {
|
|
updateEquipmentStatus('operational');
|
|
}
|
|
}
|
|
|
|
function updateEquipmentStatus(status) {
|
|
fetch('{% url "radiology:equipment_detail" object.pk %}', {
|
|
method: 'PATCH',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
|
|
},
|
|
body: JSON.stringify({status: status})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error updating equipment status');
|
|
}
|
|
});
|
|
}
|
|
|
|
function scheduleService() {
|
|
window.location.href = '{% url "radiology:equipment_detail" object.pk %}?action=schedule_service';
|
|
}
|
|
|
|
function viewSchedule() {
|
|
window.open('/radiology/equipment/{{ object.pk }}/schedule/', '_blank');
|
|
}
|
|
|
|
function generateReport() {
|
|
window.open('/radiology/equipment/{{ object.pk }}/report/', '_blank');
|
|
}
|
|
|
|
function printQRCode() {
|
|
window.open('/radiology/equipment/{{ object.pk }}/qr-code/', '_blank');
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|