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">
|
<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">
|
<form hx-post="{% url 'atlas:add_case_to_collection' collection.id %}" hx-swap="outerHTML">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<label for="case">Select Case to add:</label>
|
<label for="case">Select Case to add:</label>
|
||||||
|
|||||||
@@ -27,9 +27,7 @@
|
|||||||
|
|
||||||
<ul class="list-group mb-4" id="display-set-list">
|
<ul class="list-group mb-4" id="display-set-list">
|
||||||
{% for ds in case.display_sets.all %}
|
{% for ds in case.display_sets.all %}
|
||||||
<li class="list-group-item" id="displayset-{{ ds.pk }}">
|
|
||||||
{% include 'atlas/partials/displayset_row.html' %}
|
{% include 'atlas/partials/displayset_row.html' %}
|
||||||
</li>
|
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<li class="list-group-item text-muted">No display sets for this case.</li>
|
<li class="list-group-item text-muted">No display sets for this case.</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -47,7 +45,20 @@
|
|||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
importAnnotations_main_viewer({{ selected_displayset.annotations|safe }});
|
importAnnotations_main_viewer({{ selected_displayset.annotations|safe }});
|
||||||
importViewerState_main_viewer({{ selected_displayset.viewerstate|safe }});
|
importViewerState_main_viewer({{ selected_displayset.viewerstate|safe }});
|
||||||
}, 500);
|
// 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 %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,16 @@
|
|||||||
<h3>Cases</h3>
|
<h3>Cases</h3>
|
||||||
<ol id="full-question-list" class="sortable">
|
<ol id="full-question-list" class="sortable">
|
||||||
{% for casedetail in casesdetails %}
|
{% 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>
|
<li data-question_pk={{casedetail.case.pk}}>
|
||||||
: {{casedetail.case.title}}
|
|
||||||
|
|
||||||
|
<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>
|
(<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 %}
|
{% if casedetail.default_viewerstate %}
|
||||||
@@ -67,7 +75,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{% if can_edit %}
|
{% 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'
|
<p><button id='button-add-case'
|
||||||
title='click to add a case to this collection'
|
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">
|
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">
|
<div class="mb-2">
|
||||||
<button id="toggle-load-mode" class="btn btn-secondary">Switch to Load OHIF</button>
|
<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>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -24,14 +25,18 @@
|
|||||||
<ol id="" class="sortable">
|
<ol id="" class="sortable">
|
||||||
{% for casedetail in casedetails %}
|
{% for casedetail in casedetails %}
|
||||||
{% with case=casedetail.case %}
|
{% with case=casedetail.case %}
|
||||||
|
|
||||||
<li data-question_pk={{case.pk}}>
|
<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}}
|
<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}}>
|
||||||
{% 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 %}
|
Case {{forloop.counter}}: {{case.title}}
|
||||||
{% 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 %}
|
|
||||||
<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 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>
|
<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 />
|
<br />
|
||||||
<div class="pre-whitespace multi-image-block">
|
<div class="pre-whitespace multi-image-block">
|
||||||
<details class="series-detail"><summary>Series:</summary>
|
<details class="series-detail"><summary>Series:</summary>
|
||||||
@@ -42,10 +47,6 @@
|
|||||||
test
|
test
|
||||||
{{series.get_block}}
|
{{series.get_block}}
|
||||||
<br />
|
<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 %}">
|
<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
|
Load
|
||||||
</button>
|
</button>
|
||||||
@@ -56,6 +57,10 @@
|
|||||||
data-target="{{ series.pk }}">
|
data-target="{{ series.pk }}">
|
||||||
Load
|
Load
|
||||||
</button>
|
</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>
|
<br>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -83,6 +88,10 @@
|
|||||||
>
|
>
|
||||||
Load Display Set
|
Load Display Set
|
||||||
</button>
|
</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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
@@ -92,7 +101,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ol>
|
</ol>
|
||||||
@@ -120,10 +128,30 @@
|
|||||||
{% block js %}
|
{% block js %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var win2 = false;
|
var win2 = false;
|
||||||
|
let editMode = false;
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
let loadLocalMode = true;
|
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
|
// Update all "Load" buttons to reflect the current mode
|
||||||
function updateLoadButtons() {
|
function updateLoadButtons() {
|
||||||
if (loadLocalMode) {
|
if (loadLocalMode) {
|
||||||
@@ -335,5 +363,9 @@
|
|||||||
.title {
|
.title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.edit-link-case, .edit-link-series, .edit-link-displayset {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
@@ -66,10 +66,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<button
|
<button
|
||||||
|
id="displayset-{{ ds.pk }}-edit-btn"
|
||||||
hx-get="{% url 'atlas:case_displaysets_edit' ds.pk %}"
|
hx-get="{% url 'atlas:case_displaysets_edit' ds.pk %}"
|
||||||
hx-target="#displayset-edit-form-{{ ds.pk }}"
|
hx-target="#displayset-edit-form-{{ ds.pk }}"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
|
hx-after
|
||||||
class="btn btn-warning btn-sm"
|
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>
|
>Edit</button>
|
||||||
<button
|
<button
|
||||||
hx-delete="{% url 'atlas:case_displaysets_delete' ds.pk %}"
|
hx-delete="{% url 'atlas:case_displaysets_delete' ds.pk %}"
|
||||||
@@ -81,4 +104,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</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>
|
</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_POST
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.template.loader import render_to_string
|
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.models import User
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
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
|
# Get displayset id from GET parameter
|
||||||
displayset_id = request.GET.get("displayset")
|
displayset_id = request.GET.get("displayset")
|
||||||
|
edit = request.GET.get("edit", False)
|
||||||
selected_displayset = None
|
selected_displayset = None
|
||||||
if displayset_id:
|
if displayset_id:
|
||||||
try:
|
try:
|
||||||
@@ -223,6 +225,7 @@ def case_displaysets(request, pk):
|
|||||||
"atlas/case_displaysets.html",
|
"atlas/case_displaysets.html",
|
||||||
{
|
{
|
||||||
"case": case,
|
"case": case,
|
||||||
|
"edit": edit,
|
||||||
"can_edit": can_edit,
|
"can_edit": can_edit,
|
||||||
"selected_displayset": selected_displayset,
|
"selected_displayset": selected_displayset,
|
||||||
},
|
},
|
||||||
@@ -2029,8 +2032,12 @@ def collection_detail(request, pk):
|
|||||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||||
|
|
||||||
# cases = collection.cases.all().order_by("casedetail__sort_order")
|
# cases = collection.cases.all().order_by("casedetail__sort_order")
|
||||||
casedetails = CaseDetail.objects.filter(collection=collection).order_by(
|
casedetails = (
|
||||||
"sort_order"
|
CaseDetail.objects
|
||||||
|
.filter(collection=collection)
|
||||||
|
.select_related("case")
|
||||||
|
.annotate(series_count=Count("case__seriesdetail"))
|
||||||
|
.order_by("sort_order")
|
||||||
)
|
)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
|
|||||||
@@ -15,9 +15,13 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<h2>Edit Authors</h2>
|
<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>
|
<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">
|
<form action="" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@
|
|||||||
|
|
||||||
// Existing delete button logic
|
// Existing delete button logic
|
||||||
$(el).append($(
|
$(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(() => {
|
).click(() => {
|
||||||
el.remove();
|
el.remove();
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user