Add case selection modal for importing series into existing cases
This commit is contained in:
@@ -130,6 +130,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
>Import {% if case %}into case{% endif %}</button>
|
>Import {% if case %}into case{% endif %}</button>
|
||||||
|
<button id="import-into-case-button" type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#caseSelectModal">Import into case…</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="import-status"></div>
|
<div id="import-status"></div>
|
||||||
@@ -167,7 +168,104 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Case select modal for importing into an existing case -->
|
||||||
|
<div class="modal fade" id="caseSelectModal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Import into case</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-2">
|
||||||
|
<label for="case-search-input-modal" class="form-label">Search cases</label>
|
||||||
|
<input id="case-search-input-modal" name="q" class="form-control" type="search"
|
||||||
|
placeholder="Type to search cases..."
|
||||||
|
hx-get="{% url 'atlas:case_search' %}"
|
||||||
|
hx-include="#case-search-input-modal"
|
||||||
|
hx-target="#case-search-results-modal"
|
||||||
|
hx-trigger="keyup changed delay:400ms"
|
||||||
|
autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div id="case-search-results-modal" class="mt-2">
|
||||||
|
<!-- initial empty state; results will be loaded by HTMX -->
|
||||||
|
<div class="text-muted small">Type to search cases to import into.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Handle selecting a case from the modal results to import selected series into it
|
||||||
|
document.body.addEventListener('click', async function (e) {
|
||||||
|
// Only handle clicks inside the case select modal results
|
||||||
|
const anchor = e.target.closest('#case-search-results-modal .list-group-item, #case-search-results-modal .list-group-item-action');
|
||||||
|
if (!anchor) return;
|
||||||
|
e.preventDefault();
|
||||||
|
// Determine case pk: prefer data-case-pk, else parse from href
|
||||||
|
let casePk = anchor.getAttribute('data-case-pk');
|
||||||
|
if (!casePk) {
|
||||||
|
const href = anchor.getAttribute('href') || '';
|
||||||
|
const m = href.match(/case\/(\d+)\/?$/);
|
||||||
|
if (m) casePk = m[1];
|
||||||
|
}
|
||||||
|
if (!casePk) {
|
||||||
|
console.error('Could not determine case id for import');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather selected series (same logic as import button)
|
||||||
|
const selected = Array.from(document.querySelectorAll('input[name="selection"]:checked'))
|
||||||
|
.filter(cb => !cb.disabled)
|
||||||
|
.map(cb => cb.value);
|
||||||
|
const allCheckboxes = Array.from(document.querySelectorAll('input[name="selection"]'));
|
||||||
|
const selectable = allCheckboxes.filter(cb => !cb.disabled).map(cb => cb.value);
|
||||||
|
const selection = selected.length ? selected : selectable;
|
||||||
|
if (!selection.length) {
|
||||||
|
alert('Please select at least one series to import into the case.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build import URL template and replace trailing 0 with casePk
|
||||||
|
const importTemplate = "{% url 'api-1:import_dicoms_case' 0 %}";
|
||||||
|
const importUrl = importTemplate.replace(/0\/?$/, casePk + '/');
|
||||||
|
|
||||||
|
const form = new URLSearchParams();
|
||||||
|
for (const s of selection) form.append('selection', s);
|
||||||
|
const orderSeriesInput = document.querySelector('input[name="order-series"]:checked');
|
||||||
|
if (orderSeriesInput) form.append('order-series', orderSeriesInput.value);
|
||||||
|
|
||||||
|
const csrfToken = '{{ csrf_token }}';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch(importUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
|
},
|
||||||
|
body: form.toString(),
|
||||||
|
credentials: 'same-origin'
|
||||||
|
});
|
||||||
|
const text = await resp.text();
|
||||||
|
// Close modal and show result in import-status
|
||||||
|
const modalEl = document.getElementById('caseSelectModal');
|
||||||
|
if (modalEl && window.bootstrap && bootstrap.Modal) {
|
||||||
|
bootstrap.Modal.getInstance(modalEl)?.hide();
|
||||||
|
}
|
||||||
|
const statusDiv = document.getElementById('import-status');
|
||||||
|
if (statusDiv) statusDiv.innerHTML = `<div>Imported into case ${casePk}: ${text}</div>`;
|
||||||
|
// Optionally reload to reflect imported state
|
||||||
|
setTimeout(function(){ location.reload(); }, 1200);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Import into case failed', err);
|
||||||
|
alert('Import failed — check console for details.');
|
||||||
|
}
|
||||||
|
});
|
||||||
// Update the selected count shown beside action buttons
|
// Update the selected count shown beside action buttons
|
||||||
function updateSelectionCount() {
|
function updateSelectionCount() {
|
||||||
const all = Array.from(document.querySelectorAll('input[name="selection"]'));
|
const all = Array.from(document.querySelectorAll('input[name="selection"]'));
|
||||||
|
|||||||
Reference in New Issue
Block a user