Enhance user experience by adding informative messages and edit mode functionality across various templates
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<span id="add-case-form">
|
||||
This form allows you to add an existing case to the collection. You may find it easier to do this from the individual case pages or the <a href="{% url 'atlas:case_view' %}">case overview view</a> which allows you to add multiple cases at once.
|
||||
<form hx-post="{% url 'atlas:add_case_to_collection' collection.id %}" hx-swap="outerHTML">
|
||||
{% csrf_token %}
|
||||
<label for="case">Select Case to add:</label>
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
|
||||
<ul class="list-group mb-4" id="display-set-list">
|
||||
{% for ds in case.display_sets.all %}
|
||||
<li class="list-group-item" id="displayset-{{ ds.pk }}">
|
||||
{% include 'atlas/partials/displayset_row.html' %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li class="list-group-item text-muted">No display sets for this case.</li>
|
||||
{% endfor %}
|
||||
@@ -42,14 +40,27 @@
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
window.mountDicomViewers();
|
||||
|
||||
|
||||
|
||||
{% if selected_displayset %}
|
||||
setTimeout(function() {
|
||||
importAnnotations_main_viewer({{ selected_displayset.annotations|safe }});
|
||||
importViewerState_main_viewer({{ selected_displayset.viewerstate|safe }});
|
||||
}, 500);
|
||||
{% endif %}
|
||||
setTimeout(function() {
|
||||
importAnnotations_main_viewer({{ selected_displayset.annotations|safe }});
|
||||
importViewerState_main_viewer({{ selected_displayset.viewerstate|safe }});
|
||||
// Highlight the selected displayset row
|
||||
var selectedRow = document.getElementById("displayset-{{ selected_displayset.pk }}");
|
||||
if (selectedRow) {
|
||||
selectedRow.style.border = "2px solid #ffc107";
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
{% if edit %}
|
||||
var editButton = document.getElementById("displayset-{{ selected_displayset.pk }}-edit-btn");
|
||||
if (editButton) {
|
||||
editButton.click();
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
});
|
||||
document.addEventListener("htmx:beforeRequest", function(evt) {
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
{% if collection.collection_type == "VIV" %}
|
||||
<p><a href='{% url "atlas:collection_viva" collection.pk %}'><button class="btn btn-primary">Start Viva</button></a>
|
||||
{% else %}
|
||||
<p>This collection will be available to view/take <a href='{{collection.get_take_url}}' id="take-url">here</a>{% if not collection.active %} (when active){% endif %} <button
|
||||
<p>This collection will be available to view/take <a href='{{collection.get_take_url}}' id="take-url">here</a>{% if not collection.active %} (when active){% endif %} <button
|
||||
_="on click
|
||||
writeText(#take-url's href) on navigator.clipboard
|
||||
put 'copied url!' into me
|
||||
wait 1s
|
||||
put 'copy url' into me"
|
||||
writeText(#take-url's href) on navigator.clipboard
|
||||
put 'copied url!' into me
|
||||
wait 1s
|
||||
put 'copy url' into me"
|
||||
class="btn btn-sm">copy link</button>
|
||||
<p>Review collection <a href='{% url "atlas:collection_viva" collection.pk %}'>here</a>
|
||||
{% endif %}
|
||||
@@ -33,30 +33,38 @@
|
||||
<h3>Cases</h3>
|
||||
<ol id="full-question-list" class="sortable">
|
||||
{% for casedetail in casesdetails %}
|
||||
<li data-question_pk={{casedetail.case.pk}}><a title="sort_order: {{casedetail.sort_order}}" href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=forloop.counter0 %}">Case {{forloop.counter}}</a>
|
||||
: {{casedetail.case.title}}
|
||||
<li data-question_pk={{casedetail.case.pk}}>
|
||||
|
||||
(<a href="{% url 'atlas:collection_case_displaysetup' casedetail.collection.pk casedetail.case.pk %}"><i class="bi bi-display" title="Setup default display"></i></a>
|
||||
|
||||
<a href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=forloop.counter0 %}">Case {{forloop.counter}}</a>
|
||||
:
|
||||
{% if casedetail.case.series.count == 0 %}
|
||||
<i class="bi bi-exclamation-circle text-warning" title="No series attached to this case"></i>
|
||||
{% endif %}
|
||||
{{casedetail.case.title}}
|
||||
|
||||
|
||||
(<a href="{% url 'atlas:collection_case_displaysetup' casedetail.collection.pk casedetail.case.pk %}"><i class="bi bi-display" title="Setup default display"></i></a>
|
||||
{% if casedetail.default_viewerstate %}
|
||||
<i class="bi bi-check text-success" title="This case has a default viewerstate defined"></i>
|
||||
{% endif %}
|
||||
)
|
||||
)
|
||||
|
||||
{% if collection.collection_type == "QUE" %}
|
||||
(<a href='{% url "atlas:collection_case_details" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
{% if casedetail.question_schema %}
|
||||
<i class="bi bi-question-square text-success" title="This case has questions defined."></i>
|
||||
{% else %}
|
||||
<i class="bi bi-question-square text-danger" title="This case has no questions defined."></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% if casedetail.question_schema %}
|
||||
<i class="bi bi-question-square text-success" title="This case has questions defined."></i>
|
||||
{% else %}
|
||||
<i class="bi bi-question-square text-danger" title="This case has no questions defined."></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
)
|
||||
{% endif %}
|
||||
|
||||
{% if casedetail.case.previous_case %}
|
||||
(<a href='{% url "atlas:collection_case_priors" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
<i class="bi bi-link-45deg" title="Manage priors"></i></a>)
|
||||
{% endif %}
|
||||
{% if casedetail.case.previous_case %}
|
||||
(<a href='{% url "atlas:collection_case_priors" casedetail.collection.pk casedetail.case.pk %}'>
|
||||
<i class="bi bi-link-45deg" title="Manage priors"></i></a>)
|
||||
{% endif %}
|
||||
|
||||
</li>
|
||||
|
||||
@@ -67,7 +75,7 @@
|
||||
|
||||
|
||||
{% if can_edit %}
|
||||
<p><button id='button-edit-order' title='click and drag questions to change order' data-posturl="{% url 'atlas:exam_json_edit' pk=collection.pk %}">Edit case order / Delete cases</button></p>
|
||||
<p><button id='button-edit-order' title='click and drag questions to change order' data-posturl="{% url 'atlas:exam_json_edit' pk=collection.pk %}">Edit case order / Remove cases</button></p>
|
||||
<p><button id='button-add-case'
|
||||
title='click to add a case to this collection'
|
||||
hx-get="{% url 'atlas:add_case_to_collection' collection.pk %}" hx-swap="innerHTML" hx-target="#case-list">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
<div class="mb-2">
|
||||
<button id="toggle-load-mode" class="btn btn-secondary">Switch to Load OHIF</button>
|
||||
<button id="toggle-edit-mode" class="btn btn-warning ms-2">Enable Edit Mode</button>
|
||||
</div>
|
||||
</details>
|
||||
<div class="container">
|
||||
@@ -24,14 +25,18 @@
|
||||
<ol id="" class="sortable">
|
||||
{% for casedetail in casedetails %}
|
||||
{% with case=casedetail.case %}
|
||||
|
||||
<li data-question_pk={{case.pk}}>
|
||||
<div class="case-item" data-title="{{case.title}}" data-case={{case.pk}} data-casejson='{{case.get_viva_details_json}}' data-images='{{case.get_series_images_nested}}' data-viewerstate={{casedetail.default_viewerstate_string}}>Case {{forloop.counter}}: {{case.title}}
|
||||
{% comment %} <button class="show-case no-padding" data-target="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}">Show</button> {% endcomment %}
|
||||
{% comment %} <button class="send-message no-padding" data-title="{{case.title}}" data-target="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}">Open</button> {% endcomment %}
|
||||
{% comment %} <button class="open-case no-padding" data-target="{% url 'atlas:collection_viva_case' collection.pk forloop.counter0 %}" data-type="case">Load</button> {% endcomment %}
|
||||
<div class="case-item" data-title="{{case.title}}" data-case={{case.pk}} data-casejson='{{case.get_viva_details_json}}' data-images='{{case.get_series_images_nested}}' data-viewerstate={{casedetail.default_viewerstate_string}}>
|
||||
Case {{forloop.counter}}: {{case.title}}
|
||||
<button class="open-case no-padding" data-target="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}" data-type="case">Load OHIF</button>
|
||||
<button class="open-case-local no-padding" data-target=case.pk data-type="case">Load</button>
|
||||
<!-- Edit mode link for case -->
|
||||
<button class="view-link-case btn btn-outline-primary btn-sm ms-2" style="display:none;">
|
||||
<a href="{% url 'atlas:case_detail' case.pk %}" style="color: inherit; text-decoration: none;">View Case</a>
|
||||
</button>
|
||||
<button class="edit-link-case btn btn-outline-warning btn-sm ms-2" style="display:none;">
|
||||
<a href="{% url 'atlas:case_update' case.pk %}" style="color: inherit; text-decoration: none;">Edit Case</a>
|
||||
</button>
|
||||
<br />
|
||||
<div class="pre-whitespace multi-image-block">
|
||||
<details class="series-detail"><summary>Series:</summary>
|
||||
@@ -42,10 +47,6 @@
|
||||
test
|
||||
{{series.get_block}}
|
||||
<br />
|
||||
{% comment %} <button class="show-series no-padding text-secondary border-secondary" data-case={{case.pk}} data-target="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:series_dicom_json' series.pk %}">
|
||||
show
|
||||
</button> {% endcomment %}
|
||||
{% comment %} <button class="open-series no-padding text-secondary border-secondary" data-type="series" data-series={{forloop.counter}} data-case={{case.pk}} data-target="{% url 'atlas:collection_viva_series' collection.pk series.pk %}"> {% endcomment %}
|
||||
<button class="open-series no-padding text-secondary border-secondary" data-type="series" data-series={{forloop.counter}} data-case={{case.pk}} data-target="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:series_dicom_json' series.pk %}">
|
||||
Load
|
||||
</button>
|
||||
@@ -56,6 +57,10 @@
|
||||
data-target="{{ series.pk }}">
|
||||
Load
|
||||
</button>
|
||||
<!-- Edit mode link for series -->
|
||||
<button class="edit-link-series btn btn-outline-warning btn-sm ms-2" style="display:none;">
|
||||
<a href="{% url 'atlas:series_update' series.pk %}" style="color: inherit; text-decoration: none;">Edit Series</a>
|
||||
</button>
|
||||
<br>
|
||||
</span>
|
||||
</span>
|
||||
@@ -76,13 +81,17 @@
|
||||
data-displayset="{{ ds.pk }}"
|
||||
data-case="{{ case.pk }}"
|
||||
data-title="{{ ds.name }}"
|
||||
data-casejson='{{case.get_viva_details_json}}'
|
||||
data-images='{{case.get_series_images_nested}}'
|
||||
data-casejson='{{case.get_viva_details_json}}'
|
||||
data-images='{{case.get_series_images_nested}}'
|
||||
data-viewerstate={{ds.viewerstate_string}}
|
||||
data-annotations='{{ds.annotations_string}}'
|
||||
>
|
||||
Load Display Set
|
||||
</button>
|
||||
<!-- Edit mode link for display set -->
|
||||
<button class="edit-link-displayset btn btn-outline-warning btn-sm ms-2" style="display:none;">
|
||||
<a href="{% url 'atlas:case_displaysets' case.pk %}?displayset={{ ds.pk }}&edit=true" style="color: inherit; text-decoration: none;">Edit Display Set</a>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
{% empty %}
|
||||
@@ -92,7 +101,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</ol>
|
||||
@@ -120,10 +128,30 @@
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
var win2 = false;
|
||||
let editMode = false;
|
||||
|
||||
$(document).ready(function() {
|
||||
let loadLocalMode = true;
|
||||
|
||||
|
||||
// Edit mode toggle
|
||||
$('#toggle-edit-mode').click(function() {
|
||||
editMode = !editMode;
|
||||
if (editMode) {
|
||||
$('.edit-link-case').show();
|
||||
$('.view-link-case').show();
|
||||
$('.edit-link-series').show();
|
||||
$('.edit-link-displayset').show();
|
||||
$(this).text('Disable Edit Mode').removeClass('btn-warning').addClass('btn-danger');
|
||||
} else {
|
||||
$('.edit-link-case').hide();
|
||||
$('.view-link-case').hide();
|
||||
$('.edit-link-series').hide();
|
||||
$('.edit-link-displayset').hide();
|
||||
$(this).text('Enable Edit Mode').removeClass('btn-danger').addClass('btn-warning');
|
||||
}
|
||||
});
|
||||
|
||||
// Update all "Load" buttons to reflect the current mode
|
||||
function updateLoadButtons() {
|
||||
if (loadLocalMode) {
|
||||
@@ -335,5 +363,9 @@
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.edit-link-case, .edit-link-series, .edit-link-displayset {
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
{% endblock css %}
|
||||
@@ -66,10 +66,33 @@
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button
|
||||
id="displayset-{{ ds.pk }}-edit-btn"
|
||||
hx-get="{% url 'atlas:case_displaysets_edit' ds.pk %}"
|
||||
hx-target="#displayset-edit-form-{{ ds.pk }}"
|
||||
hx-swap="innerHTML"
|
||||
hx-after
|
||||
class="btn btn-warning btn-sm"
|
||||
onclick="
|
||||
this.style.display='none';
|
||||
var li = this.closest('li');
|
||||
var ul = li.parentNode;
|
||||
ul.insertBefore(li, ul.firstChild);
|
||||
li.scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||
setTimeout(function() {
|
||||
var form = document.querySelector('#displayset-edit-form-{{ ds.pk }} form');
|
||||
if (form) {
|
||||
var cancelBtn = document.createElement('button');
|
||||
cancelBtn.type = 'button';
|
||||
cancelBtn.className = 'btn btn-secondary btn-sm ms-2';
|
||||
cancelBtn.textContent = 'Cancel';
|
||||
cancelBtn.onclick = function() {
|
||||
document.querySelector('#displayset-edit-form-{{ ds.pk }}').innerHTML = '';
|
||||
document.querySelector('#displayset-{{ ds.pk }}-edit-btn').style.display = '';
|
||||
};
|
||||
form.appendChild(cancelBtn);
|
||||
}
|
||||
}, 100);
|
||||
"
|
||||
>Edit</button>
|
||||
<button
|
||||
hx-delete="{% url 'atlas:case_displaysets_delete' ds.pk %}"
|
||||
@@ -80,5 +103,8 @@
|
||||
>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="displayset-edit-form-{{ ds.pk }}"></div>
|
||||
<div id="displayset-edit-form-{{ ds.pk }}"></div>
|
||||
<div class="align-self-start ms-3">
|
||||
<span class="text-muted small">ID: {{ ds.pk }}</span>
|
||||
</div>
|
||||
</li>
|
||||
+9
-2
@@ -11,6 +11,7 @@ from django.utils import timezone
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.template.loader import render_to_string
|
||||
from django.db.models import Count
|
||||
|
||||
# from django.contrib.auth.models import User
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
@@ -211,6 +212,7 @@ def case_displaysets(request, pk):
|
||||
|
||||
# Get displayset id from GET parameter
|
||||
displayset_id = request.GET.get("displayset")
|
||||
edit = request.GET.get("edit", False)
|
||||
selected_displayset = None
|
||||
if displayset_id:
|
||||
try:
|
||||
@@ -223,6 +225,7 @@ def case_displaysets(request, pk):
|
||||
"atlas/case_displaysets.html",
|
||||
{
|
||||
"case": case,
|
||||
"edit": edit,
|
||||
"can_edit": can_edit,
|
||||
"selected_displayset": selected_displayset,
|
||||
},
|
||||
@@ -2029,8 +2032,12 @@ def collection_detail(request, pk):
|
||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||
|
||||
# cases = collection.cases.all().order_by("casedetail__sort_order")
|
||||
casedetails = CaseDetail.objects.filter(collection=collection).order_by(
|
||||
"sort_order"
|
||||
casedetails = (
|
||||
CaseDetail.objects
|
||||
.filter(collection=collection)
|
||||
.select_related("case")
|
||||
.annotate(series_count=Count("case__seriesdetail"))
|
||||
.order_by("sort_order")
|
||||
)
|
||||
|
||||
return render(
|
||||
|
||||
@@ -15,9 +15,13 @@
|
||||
{% block content %}
|
||||
|
||||
<h2>Edit Authors</h2>
|
||||
<p>Authors have full access to access and edit the exam/collection (including the ability to add/remove other authors).</p>
|
||||
|
||||
<div class="alert alert-info" role="alert">
|
||||
<p>
|
||||
Authors have <b>full access</b> to access and edit the exam/collection (<b>including the ability to add/remove other authors</b>).
|
||||
</p>
|
||||
<p>If you just want a user to be able to mark questions add them as a marker instead.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
|
||||
// Existing delete button logic
|
||||
$(el).append($(
|
||||
"<span class='exam-question-delete flex-col'><button>DELETE</button></span>"
|
||||
"<span class='exam-question-delete flex-col'><button title='Remove this question from the exam/collection'>Remove</button></span>"
|
||||
).click(() => {
|
||||
el.remove();
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user