hospital-management/templates/radiology/series/imaging_series_form.html
Marwan Alwali 0c980b4706 update
2025-08-13 19:41:02 +03:00

566 lines
25 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}{% if form.instance.pk %}Edit{% else %}Add{% endif %} Imaging Series - Hospital Management{% endblock %}
{% block content %}
<div class="content">
<div class="container-fluid">
<!-- Page Header -->
<div class="row">
<div class="col-12">
<div class="page-header">
<div class="page-title">
<h4>{% if form.instance.pk %}Edit{% else %}Add{% endif %} Imaging Series</h4>
<h6>{% if study %}Study: {{ study.study_description|default:"Imaging Study" }}{% endif %}</h6>
</div>
<div class="page-btn">
<a href="{% if study %}{% url 'radiology:imaging_series_list' study.pk %}{% else %}{% url 'radiology:imaging_series_list' form.instance.study.pk %}{% endif %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i>Back to Series List
</a>
</div>
</div>
</div>
</div>
<form method="post" enctype="multipart/form-data" id="series-form">
{% csrf_token %}
<!-- Basic Information -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-info-circle me-2"></i>Basic Information
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.series_number.id_for_label }}">
Series Number <span class="text-danger">*</span>
</label>
{{ form.series_number }}
{% if form.series_number.errors %}
<div class="text-danger">
{% for error in form.series_number.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Unique series number within the study</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.modality.id_for_label }}">
Modality <span class="text-danger">*</span>
</label>
{{ form.modality }}
{% if form.modality.errors %}
<div class="text-danger">
{% for error in form.modality.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label class="form-label" for="{{ form.series_description.id_for_label }}">
Series Description
</label>
{{ form.series_description }}
{% if form.series_description.errors %}
<div class="text-danger">
{% for error in form.series_description.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Descriptive name for this imaging series</small>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.protocol_name.id_for_label }}">
Protocol Name
</label>
{{ form.protocol_name }}
{% if form.protocol_name.errors %}
<div class="text-danger">
{% for error in form.protocol_name.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.series_instance_uid.id_for_label }}">
Series Instance UID <span class="text-danger">*</span>
</label>
{{ form.series_instance_uid }}
{% if form.series_instance_uid.errors %}
<div class="text-danger">
{% for error in form.series_instance_uid.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">DICOM Series Instance UID</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Date and Time Information -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-calendar-alt me-2"></i>Date and Time Information
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label class="form-label" for="{{ form.series_date.id_for_label }}">
Series Date <span class="text-danger">*</span>
</label>
{{ form.series_date }}
{% if form.series_date.errors %}
<div class="text-danger">
{% for error in form.series_date.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label" for="{{ form.series_time.id_for_label }}">
Series Time <span class="text-danger">*</span>
</label>
{{ form.series_time }}
{% if form.series_time.errors %}
<div class="text-danger">
{% for error in form.series_time.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label" for="{{ form.series_datetime.id_for_label }}">
Series DateTime <span class="text-danger">*</span>
</label>
{{ form.series_datetime }}
{% if form.series_datetime.errors %}
<div class="text-danger">
{% for error in form.series_datetime.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Combined date and time</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Technical Parameters -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-cogs me-2"></i>Technical Parameters
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.slice_thickness.id_for_label }}">
Slice Thickness (mm)
</label>
{{ form.slice_thickness }}
{% if form.slice_thickness.errors %}
<div class="text-danger">
{% for error in form.slice_thickness.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.spacing_between_slices.id_for_label }}">
Spacing Between Slices (mm)
</label>
{{ form.spacing_between_slices }}
{% if form.spacing_between_slices.errors %}
<div class="text-danger">
{% for error in form.spacing_between_slices.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.pixel_spacing.id_for_label }}">
Pixel Spacing
</label>
{{ form.pixel_spacing }}
{% if form.pixel_spacing.errors %}
<div class="text-danger">
{% for error in form.pixel_spacing.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Format: row spacing\column spacing</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label" for="{{ form.image_orientation.id_for_label }}">
Image Orientation
</label>
{{ form.image_orientation }}
{% if form.image_orientation.errors %}
<div class="text-danger">
{% for error in form.image_orientation.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">DICOM Image Orientation Patient</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Additional Information -->
{% if form.instance.pk %}
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-info me-2"></i>Additional Information
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="info-group">
<label class="form-label">Series ID:</label>
<p class="text-monospace">{{ form.instance.series_id }}</p>
</div>
</div>
<div class="col-md-6">
<div class="info-group">
<label class="form-label">Number of Images:</label>
<p>{{ form.instance.number_of_images|default:0 }}</p>
</div>
</div>
</div>
{% if form.instance.created_at %}
<div class="row">
<div class="col-md-6">
<div class="info-group">
<label class="form-label">Created:</label>
<p>{{ form.instance.created_at|date:"M d, Y H:i" }}</p>
</div>
</div>
<div class="col-md-6">
<div class="info-group">
<label class="form-label">Last Modified:</label>
<p>{{ form.instance.updated_at|date:"M d, Y H:i" }}</p>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- Form Actions -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div class="form-actions">
<button type="submit" class="btn btn-primary me-2">
<i class="fas fa-save me-1"></i>
{% if form.instance.pk %}Update{% else %}Create{% endif %} Series
</button>
<button type="button" class="btn btn-outline-secondary me-2" onclick="resetForm()">
<i class="fas fa-undo me-1"></i>Reset
</button>
<a href="{% if study %}{% url 'radiology:imaging_series_list' study.pk %}{% else %}{% url 'radiology:imaging_series_list' form.instance.study.pk %}{% endif %}"
class="btn btn-outline-danger">
<i class="fas fa-times me-1"></i>Cancel
</a>
</div>
{% if form.instance.pk %}
<div class="danger-actions">
<a href="{% url 'radiology:imaging_series_delete' form.instance.pk %}"
class="btn btn-outline-danger"
onclick="return confirm('Are you sure you want to delete this series? This action cannot be undone.')">
<i class="fas fa-trash me-1"></i>Delete Series
</a>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<script>
$(document).ready(function() {
// Initialize form
initializeForm();
// Auto-generate Series Instance UID if empty
if (!$('#{{ form.series_instance_uid.id_for_label }}').val()) {
generateSeriesInstanceUID();
}
// Sync date and time with datetime field
$('#{{ form.series_date.id_for_label }}, #{{ form.series_time.id_for_label }}').on('change', function() {
syncDateTime();
});
// Form validation
$('#series-form').on('submit', function(e) {
if (!validateForm()) {
e.preventDefault();
}
});
});
function initializeForm() {
// Add form classes
$('.form-control, .form-select').addClass('form-control');
// Set current date/time if creating new series
{% if not form.instance.pk %}
const now = new Date();
const today = now.toISOString().split('T')[0];
const currentTime = now.toTimeString().split(' ')[0].substring(0, 5);
const currentDateTime = now.toISOString().slice(0, 16);
if (!$('#{{ form.series_date.id_for_label }}').val()) {
$('#{{ form.series_date.id_for_label }}').val(today);
}
if (!$('#{{ form.series_time.id_for_label }}').val()) {
$('#{{ form.series_time.id_for_label }}').val(currentTime);
}
if (!$('#{{ form.series_datetime.id_for_label }}').val()) {
$('#{{ form.series_datetime.id_for_label }}').val(currentDateTime);
}
{% endif %}
}
function generateSeriesInstanceUID() {
// Generate a DICOM-compliant Series Instance UID
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000000);
const uid = `1.2.826.0.1.3680043.8.498.${timestamp}.${random}`;
$('#{{ form.series_instance_uid.id_for_label }}').val(uid);
}
function syncDateTime() {
const date = $('#{{ form.series_date.id_for_label }}').val();
const time = $('#{{ form.series_time.id_for_label }}').val();
if (date && time) {
const datetime = `${date}T${time}`;
$('#{{ form.series_datetime.id_for_label }}').val(datetime);
}
}
function validateForm() {
let isValid = true;
const requiredFields = [
'{{ form.series_number.id_for_label }}',
'{{ form.modality.id_for_label }}',
'{{ form.series_instance_uid.id_for_label }}',
'{{ form.series_date.id_for_label }}',
'{{ form.series_time.id_for_label }}',
'{{ form.series_datetime.id_for_label }}'
];
// Clear previous error states
$('.is-invalid').removeClass('is-invalid');
// Validate required fields
requiredFields.forEach(function(fieldId) {
const field = $('#' + fieldId);
if (!field.val() || field.val().trim() === '') {
field.addClass('is-invalid');
isValid = false;
}
});
// Validate Series Instance UID format
const uid = $('#{{ form.series_instance_uid.id_for_label }}').val();
if (uid && !isValidUID(uid)) {
$('#{{ form.series_instance_uid.id_for_label }}').addClass('is-invalid');
alert('Please enter a valid DICOM Series Instance UID');
isValid = false;
}
// Validate numeric fields
const numericFields = [
'{{ form.slice_thickness.id_for_label }}',
'{{ form.spacing_between_slices.id_for_label }}'
];
numericFields.forEach(function(fieldId) {
const field = $('#' + fieldId);
const value = field.val();
if (value && (isNaN(value) || parseFloat(value) < 0)) {
field.addClass('is-invalid');
isValid = false;
}
});
if (!isValid) {
alert('Please correct the highlighted fields before submitting.');
}
return isValid;
}
function isValidUID(uid) {
// Basic DICOM UID validation
const uidPattern = /^[0-9]+(\.[0-9]+)*$/;
return uidPattern.test(uid) && uid.length <= 64;
}
function resetForm() {
if (confirm('Are you sure you want to reset the form? All unsaved changes will be lost.')) {
document.getElementById('series-form').reset();
$('.is-invalid').removeClass('is-invalid');
initializeForm();
}
}
// Auto-save functionality (optional)
let autoSaveTimer;
function enableAutoSave() {
$('#series-form input, #series-form select, #series-form textarea').on('input change', function() {
clearTimeout(autoSaveTimer);
autoSaveTimer = setTimeout(function() {
// Auto-save logic here
console.log('Auto-saving form...');
}, 30000); // Auto-save after 30 seconds of inactivity
});
}
</script>
<style>
.info-group {
margin-bottom: 15px;
}
.info-group .form-label {
font-weight: 600;
color: #495057;
margin-bottom: 5px;
}
.info-group p {
margin-bottom: 0;
color: #6c757d;
padding: 8px 12px;
background: #f8f9fa;
border-radius: 4px;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
font-weight: 600;
color: #495057;
margin-bottom: 8px;
}
.text-danger {
color: #dc3545 !important;
}
.is-invalid {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
.form-actions .btn {
margin-right: 10px;
}
.danger-actions {
margin-left: auto;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.d-flex.justify-content-between {
flex-direction: column;
gap: 15px;
}
.form-actions {
display: flex;
flex-direction: column;
gap: 10px;
}
.form-actions .btn {
margin-right: 0;
}
}
</style>
{% endblock %}