Add select/deselect all buttons for series in user uploads view and enhance styles for better visibility

This commit is contained in:
Ross
2026-03-02 10:36:21 +00:00
parent 0c6d33e26a
commit ed6418efad
+99 -39
View File
@@ -21,6 +21,10 @@
<summary> <summary>
<strong>{{ study.description|default:"(no description)" }}</strong> <strong>{{ study.description|default:"(no description)" }}</strong>
<small class="text-muted"> ({{ study_uid }}) — {{ study.series|length }} series</small> <small class="text-muted"> ({{ study_uid }}) — {{ study.series|length }} series</small>
<span class="ms-2">
<button type="button" class="btn btn-sm btn-outline-secondary select-all-btn" title="Select all in study">Select all</button>
<button type="button" class="btn btn-sm btn-outline-secondary deselect-all-btn" title="Deselect all in study">Deselect all</button>
</span>
</summary> </summary>
<ul class="study-series-list"> <ul class="study-series-list">
{% for series, n, tags, date in study.series %} {% for series, n, tags, date in study.series %}
@@ -98,7 +102,7 @@
<button id="import-dicoms-sequential-button" <button id="import-dicoms-sequential-button"
type="button" type="button"
class="btn btn-primary" class="btn btn-primary"
>Import {% if case %}into case{% endif %} (one at a time)</button> >Import {% if case %}into case{% endif %}</button>
<div id="import-status"></div> <div id="import-status"></div>
@@ -184,19 +188,62 @@
} }
</script> </script>
<script>
// Select / Deselect all series within a study block
document.addEventListener('click', function (e) {
const target = e.target;
if (target.matches('.select-all-btn')) {
const detail = target.closest('.study-block');
if (!detail) return;
const inputs = detail.querySelectorAll('input[name="selection"]');
inputs.forEach(cb => {
if (!cb.disabled) {
cb.checked = true;
const li = cb.closest('li');
if (li) li.classList.add('selected');
}
});
}
if (target.matches('.deselect-all-btn')) {
const detail = target.closest('.study-block');
if (!detail) return;
const inputs = detail.querySelectorAll('input[name="selection"]');
inputs.forEach(cb => {
if (!cb.disabled) {
cb.checked = false;
const li = cb.closest('li');
if (li) li.classList.remove('selected');
}
});
}
});
</script>
{% endblock %} {% endblock %}
{% block css %} {% block css %}
<style> <style>
.indicator { /* Dark theme friendly styles for uploads page */
display: none; :root {
--bg: #0f1115;
--panel: #111217;
--muted: #9aa0a6;
--border: #242629;
--accent: #7c3aed;
--success: #16a34a;
--danger: #dc2626;
--card: #0f1216;
} }
.indicator.htmx-request { body {
display: block; background: var(--bg);
color: #e6eef6;
} }
.indicator { display: none; }
.indicator.htmx-request { display: block; }
.loader { .loader {
width: 50px; width: 50px;
aspect-ratio: 1; aspect-ratio: 1;
@@ -206,7 +253,7 @@
.loader::after { .loader::after {
content: ""; content: "";
grid-area: 1/1; grid-area: 1/1;
--c:no-repeat radial-gradient(farthest-side,#25b09b 92%,#0000); --c: no-repeat radial-gradient(farthest-side, var(--accent) 92%, #0000);
background: background:
var(--c) 50% 0, var(--c) 50% 0,
var(--c) 50% 100%, var(--c) 50% 100%,
@@ -214,51 +261,64 @@
var(--c) 0 50%; var(--c) 0 50%;
background-size: 12px 12px; background-size: 12px 12px;
animation: l12 1s infinite; animation: l12 1s infinite;
opacity: 0.95;
} }
.loader::before { .loader::before { margin: 4px; filter: hue-rotate(45deg); background-size: 8px 8px; }
margin: 4px; @keyframes l12 { 100% { transform: rotate(.5turn) } }
filter: hue-rotate(45deg);
background-size: 8px 8px;
animation-timing-function: linear
}
@keyframes l12 { #series-list { user-select: none; }
100%{transform: rotate(.5turn)}
}
#series-list {
user-select: none;
cursor: pointer;
}
.study-block { .study-block {
margin: 8px 0; margin: 8px 0;
padding: 6px; padding: 8px;
border: 1px solid #e9ecef; border: 1px solid var(--border);
border-radius: 6px; border-radius: 8px;
background: #fafafa; background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
box-shadow: 0 1px 0 rgba(255,255,255,0.02) inset;
} }
.study-block summary { .study-block summary { font-size: 1rem; cursor: pointer; padding: 4px 6px; color: #e6eef6; }
font-size: 1rem;
cursor: pointer;
list-style: none;
padding: 4px 6px;
}
.study-series-list { .study-series-list { list-style: none; margin: 8px 0 0 0; padding: 0; }
list-style: none;
margin: 8px 0 0 0;
padding: 0;
}
.study-series-list li { .study-series-list li {
padding: 6px; padding: 10px;
border-bottom: 1px solid #eee; border-bottom: 1px solid rgba(255,255,255,0.03);
background: transparent;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
} }
.series-meta { font-weight: 600; } .study-series-list li:last-child { border-bottom: 0; }
.series-tags { font-size: 0.9rem; color: #666; }
.series-meta { font-weight: 600; color: #f1f5f9; }
.series-tags { font-size: 0.9rem; color: var(--muted); margin-top: 4px; }
.series-block-popup-link a { color: var(--muted); text-decoration: underline; }
/* Selected state */
#series-list .selected {
color: #fff;
background: linear-gradient(90deg, rgba(124,58,237,0.12), rgba(124,58,237,0.08));
border: 1px solid rgba(124,58,237,0.25);
border-radius: 6px;
padding: 6px;
}
/* Imported badge */
.badge.bg-success { background-color: var(--success); color: #000; }
/* Placeholder for tags */
.series-tags-placeholder { color: var(--muted); padding: 6px 0; }
/* Buttons */
button { background: transparent; color: #e6eef6; border: 1px solid var(--border); }
button.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
/* small helpers */
.text-muted { color: var(--muted) !important; }
#series-list li { #series-list li {
} }