Imrpvoe the case links
This commit is contained in:
@@ -426,21 +426,40 @@
|
||||
</div>
|
||||
</h2>
|
||||
{% if case.previous_case or case.next_case %}
|
||||
<div class="alert alert-info" role="alert">
|
||||
This case is part of a <a href='{% url "atlas:linked_cases_overview" case.pk %}'>series.</a><br/>
|
||||
<div class="row">
|
||||
{% if case.previous_case %}
|
||||
<div class="col-sm-6 mb-3 mb-sm-0">
|
||||
<h5 class="card-title">Previous Case</h5>
|
||||
{{ case.previous_case.get_link }}
|
||||
<div class="card border-info bg-info-subtle mb-3">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex justify-content-between align-items-center border-bottom border-info-subtle pb-2 mb-2">
|
||||
<span class="fw-semibold text-info-emphasis"><i class="bi bi-layers-half me-1"></i> Part of a Case Series</span>
|
||||
<a href='{% url "atlas:linked_cases_overview" case.pk %}' class="btn btn-sm btn-outline-info text-decoration-none py-0 px-2" style="font-size: 0.8rem;">View Series Overview</a>
|
||||
</div>
|
||||
<div class="row text-center text-sm-start g-2">
|
||||
<div class="col-6 border-end border-info-subtle d-flex flex-column justify-content-center">
|
||||
{% if case.previous_case %}
|
||||
<div>
|
||||
<a href="{{ case.previous_case.get_absolute_url }}" class="text-decoration-none fw-semibold link-info" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-delay='{"show":500,"hide":100}' title="{{ case.previous_case.description|striptags|default:'No description' }}">
|
||||
<i class="bi bi-arrow-left me-1"></i> Previous Case: {{ case.previous_case.title|default:'Untitled Case' }}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="small text-muted italic">
|
||||
<i class="bi bi-arrow-left me-1"></i> No previous case
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if case.next_case %}
|
||||
<div class="col-sm-6">
|
||||
<h5 class="card-title">Next Case</h5>
|
||||
{{ case.next_case.get_link }}
|
||||
<div class="col-6 d-flex flex-column justify-content-center text-sm-end align-items-sm-end">
|
||||
{% if case.next_case %}
|
||||
<div>
|
||||
<a href="{{ case.next_case.get_absolute_url }}" class="text-decoration-none fw-semibold link-info" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-delay='{"show":500,"hide":100}' title="{{ case.next_case.description|striptags|default:'No description' }}">
|
||||
Next Case: {{ case.next_case.title|default:'Untitled Case' }} <i class="bi bi-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="small text-muted italic">
|
||||
No follow-up case <i class="bi bi-arrow-right ms-1"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -1123,6 +1142,16 @@
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Initialize tooltips
|
||||
function initAlertTooltips() {
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function(el) {
|
||||
try { new bootstrap.Tooltip(el); } catch (e) {}
|
||||
});
|
||||
}
|
||||
initAlertTooltips();
|
||||
document.body.addEventListener('htmx:afterSwap', function() {
|
||||
initAlertTooltips();
|
||||
});
|
||||
|
||||
const dicomDetails = document.getElementById("dicom-viewer-details");
|
||||
let dicomViewerLoaded = false;
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
{% if show_study_dates_editor %}
|
||||
<a class="btn btn-outline-secondary" href="{% url 'atlas:linked_cases_overview' current_case.pk %}">Hide study dates editor</a>
|
||||
{% endif %}
|
||||
{% if can_edit_chain and not show_reorder_editor %}
|
||||
<a class="btn btn-outline-warning" href="{% url 'atlas:linked_cases_overview' current_case.pk %}?show_reorder=1">Open case reorder editor</a>
|
||||
{% endif %}
|
||||
{% if show_reorder_editor %}
|
||||
<a class="btn btn-outline-secondary" href="{% url 'atlas:linked_cases_overview' current_case.pk %}">Hide case reorder editor</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -104,6 +110,39 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_edit_chain and show_reorder_editor %}
|
||||
<div class="card border-warning-subtle shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<h2 class="h5 mb-1 text-warning-emphasis">Reorder Cases</h2>
|
||||
<p class="text-muted mb-3">Drag and drop cases to reorder them, or use the Up/Down buttons, then click Save.</p>
|
||||
|
||||
<form method="post" id="reorder-cases-form">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="form_action" value="reorder_cases">
|
||||
<ul id="cases-reorder-list" class="list-group mb-3">
|
||||
{% for linked_case in chain_cases %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center {% if linked_case.pk == current_case.pk %}border-primary{% endif %}" data-case-id="{{ linked_case.pk }}">
|
||||
<span>
|
||||
<i class="bi bi-grip-vertical me-2 text-muted drag-handle" style="cursor: grab;"></i>
|
||||
<span class="fw-semibold">Case {{ linked_case.pk }}:</span> {{ linked_case.title|default:"Untitled case" }}
|
||||
{% if linked_case.pk == current_case.pk %}<span class="badge bg-primary ms-1">Current</span>{% endif %}
|
||||
</span>
|
||||
<span>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary move-up">↑</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary move-down">↓</button>
|
||||
</span>
|
||||
<input type="hidden" name="case_order" value="{{ linked_case.pk }}">
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-warning">Save Case Order</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if show_case_editor and case_editor_formset %}
|
||||
<div class="card border-info-subtle shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
@@ -273,4 +312,92 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style>
|
||||
#cases-reorder-list .dragging {
|
||||
opacity: 0.55;
|
||||
box-shadow: 0 4px 16px rgba(255, 193, 7, 0.35);
|
||||
}
|
||||
#cases-reorder-list .drag-over {
|
||||
border: 2px dashed #ffc107 !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Move up/down logic
|
||||
document.querySelectorAll("#cases-reorder-list .move-up, #cases-reorder-list .move-down").forEach(function(btn) {
|
||||
btn.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
const li = btn.closest("li");
|
||||
if (btn.classList.contains("move-up") && li.previousElementSibling) {
|
||||
li.parentNode.insertBefore(li, li.previousElementSibling);
|
||||
}
|
||||
if (btn.classList.contains("move-down") && li.nextElementSibling) {
|
||||
li.parentNode.insertBefore(li.nextElementSibling, li);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// On submit, update the hidden inputs to match the new order
|
||||
const reorderCasesForm = document.getElementById("reorder-cases-form");
|
||||
if (reorderCasesForm) {
|
||||
reorderCasesForm.addEventListener("submit", function(e) {
|
||||
const lis = document.querySelectorAll("#cases-reorder-list li");
|
||||
lis.forEach((li, idx) => {
|
||||
const input = li.querySelector("input[name='case_order']");
|
||||
if (input) {
|
||||
input.setAttribute("name", "case_order_" + idx);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Drag and drop reordering for cases list
|
||||
const reorderList = document.getElementById("cases-reorder-list");
|
||||
let draggedItem = null;
|
||||
|
||||
if (reorderList) {
|
||||
reorderList.querySelectorAll("li").forEach(function(li) {
|
||||
li.setAttribute("draggable", "true");
|
||||
|
||||
li.addEventListener("dragstart", function(e) {
|
||||
draggedItem = li;
|
||||
li.classList.add("dragging");
|
||||
e.dataTransfer.effectAllowed = "move";
|
||||
});
|
||||
|
||||
li.addEventListener("dragend", function() {
|
||||
draggedItem = null;
|
||||
li.classList.remove("dragging");
|
||||
reorderList.querySelectorAll("li").forEach(el => el.classList.remove("drag-over"));
|
||||
});
|
||||
|
||||
li.addEventListener("dragover", function(e) {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = "move";
|
||||
li.classList.add("drag-over");
|
||||
});
|
||||
|
||||
li.addEventListener("dragleave", function() {
|
||||
li.classList.remove("drag-over");
|
||||
});
|
||||
|
||||
li.addEventListener("drop", function(e) {
|
||||
e.preventDefault();
|
||||
li.classList.remove("drag-over");
|
||||
if (draggedItem && draggedItem !== li) {
|
||||
const rect = li.getBoundingClientRect();
|
||||
const next = (e.clientY - rect.top) / (rect.bottom - rect.top) > 0.5;
|
||||
li.parentNode.insertBefore(draggedItem, next ? li.nextElementSibling : li);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user