Enhance finding and display set widgets with explicit case filtering and badge indicators

This commit is contained in:
Ross
2026-03-23 14:18:25 +00:00
parent 13d73020ee
commit a792f0c7cd
3 changed files with 115 additions and 93 deletions
+8 -92
View File
@@ -39,9 +39,8 @@
toolbar.querySelectorAll('.markup-insert-button').forEach(function(btn){
btn.addEventListener('click', function(){
const t = btn.dataset.type;
// determine a sensible default case id from the DOM context
const detected = detectCurrentCase(textarea);
const detectedCase = detected && detected.id ? detected.id : null;
// Respect explicit dataset provided by the widget or page meta only
var detectedCase = textarea && textarea.dataset && textarea.dataset.currentCase ? textarea.dataset.currentCase : (document.getElementById('current-case-meta') ? document.getElementById('current-case-meta').getAttribute('data-current-case') : null);
if(t === 'case'){
openCasePicker(textarea);
} else if(t === 'finding'){
@@ -198,25 +197,9 @@
Object.assign(overlay.style, {position:'fixed',left:0,top:0,right:0,bottom:0,background:'rgba(0,0,0,0.4)',zIndex:1050,display:'flex',alignItems:'center',justifyContent:'center'});
const dialog = document.createElement('div'); dialog.className='markup-case-picker-dialog card'; Object.assign(dialog.style, {width:'min(900px,95%)',maxHeight:'80vh',overflow:'auto'});
const header = document.createElement('div'); header.className='card-header d-flex justify-content-between align-items-center'; header.innerHTML = '<strong>Select finding</strong>';
var detectedF = detectCurrentCase(textarea) || {};
// Determine case filter from explicit dataset on the textarea or provided caseId
var detectedF = (textarea && textarea.dataset && textarea.dataset.currentCase) ? { id: textarea.dataset.currentCase, title: textarea.dataset.currentCaseTitle } : {};
var currentCaseF = caseId || detectedF.id || null;
var currentTitleF = detectedF.title || null;
var badgeContainerF = document.createElement('div'); badgeContainerF.className = 'd-flex align-items-center';
var badgeTextF = document.createElement('span'); badgeTextF.className = 'badge bg-secondary ms-2'; badgeTextF.style.marginLeft='8px';
function updateBadgeF(){
if(currentCaseF){
badgeTextF.textContent = 'Filtering: ' + (currentTitleF || ('#'+currentCaseF));
removeBtnF.style.display = '';
} else {
badgeTextF.textContent = 'No case filter';
removeBtnF.style.display = 'none';
}
}
var removeBtnF = document.createElement('button'); removeBtnF.type='button'; removeBtnF.className='btn btn-sm btn-outline-light ms-2'; removeBtnF.style.padding='0 .4rem'; removeBtnF.textContent='✕';
removeBtnF.addEventListener('click', function(){ currentCaseF = null; currentTitleF = null; updateBadgeF(); fetchInitialFindings(); });
badgeContainerF.appendChild(badgeTextF); badgeContainerF.appendChild(removeBtnF);
updateBadgeF();
header.appendChild(badgeContainerF);
const closeBtn = document.createElement('button'); closeBtn.type='button'; closeBtn.className='btn-close'; closeBtn.setAttribute('aria-label','Close'); closeBtn.addEventListener('click', function(){ document.body.removeChild(overlay); }); header.appendChild(closeBtn);
const body = document.createElement('div'); body.className='card-body';
const input = document.createElement('input'); input.type='search'; input.className='form-control mb-2'; input.placeholder='Type to search findings...';
@@ -242,25 +225,9 @@
const dialog = document.createElement('div'); dialog.className='markup-case-picker-dialog card'; Object.assign(dialog.style, {width:'min(900px,95%)',maxHeight:'80vh',overflow:'auto'});
const header = document.createElement('div'); header.className='card-header d-flex justify-content-between align-items-center';
header.innerHTML = '<strong>Select display set</strong>';
var detectedD = detectCurrentCase(textarea) || {};
// Determine case filter from explicit dataset on the textarea or provided caseId
var detectedD = (textarea && textarea.dataset && textarea.dataset.currentCase) ? { id: textarea.dataset.currentCase, title: textarea.dataset.currentCaseTitle } : {};
var currentCaseD = caseId || detectedD.id || null;
var currentTitleD = detectedD.title || null;
var badgeContainerD = document.createElement('div'); badgeContainerD.className = 'd-flex align-items-center';
var badgeTextD = document.createElement('span'); badgeTextD.className = 'badge bg-secondary ms-2'; badgeTextD.style.marginLeft='8px';
function updateBadgeD(){
if(currentCaseD){
badgeTextD.textContent = 'Filtering: ' + (currentTitleD || ('#'+currentCaseD));
removeBtnD.style.display = '';
} else {
badgeTextD.textContent = 'No case filter';
removeBtnD.style.display = 'none';
}
}
var removeBtnD = document.createElement('button'); removeBtnD.type='button'; removeBtnD.className='btn btn-sm btn-outline-light ms-2'; removeBtnD.style.padding='0 .4rem'; removeBtnD.textContent='✕';
removeBtnD.addEventListener('click', function(){ currentCaseD = null; currentTitleD = null; updateBadgeD(); fetchInitialDisplaysets(); });
badgeContainerD.appendChild(badgeTextD); badgeContainerD.appendChild(removeBtnD);
updateBadgeD();
header.appendChild(badgeContainerD);
const closeBtn = document.createElement('button'); closeBtn.type='button'; closeBtn.className='btn-close'; closeBtn.setAttribute('aria-label','Close'); closeBtn.addEventListener('click', function(){ document.body.removeChild(overlay); }); header.appendChild(closeBtn);
const body = document.createElement('div'); body.className='card-body';
const input = document.createElement('input'); input.type='search'; input.className='form-control mb-2'; input.placeholder='Type to search display sets...';
@@ -277,58 +244,7 @@
}catch(err){ console.error('openDisplaysetPicker error', err); }
}
function detectCurrentCase(contextEl){
try{
var result = { id: null, title: null };
var root = contextEl && contextEl.closest ? contextEl.closest('form,body') : document;
var inp = root.querySelector('input[name="case"]') || root.querySelector('input[name="case_id"]') || document.querySelector('input[name="case"]') || document.querySelector('input[name="case_id"]');
if(inp && inp.value) result.id = inp.value;
// Look for explicit metadata element
var meta = document.querySelector('#current-case-meta') || document.querySelector('[data-current-case]');
if(meta){
if(!result.id) result.id = meta.getAttribute('data-current-case') || meta.getAttribute('data-case-pk') || meta.value || null;
result.title = meta.getAttribute('data-current-case-title') || meta.getAttribute('data-case-title') || null;
}
// Fallback: look for ancestor attributes
if(!result.id){
var anc = contextEl;
while(anc){
if(anc.getAttribute && (anc.getAttribute('data-case-pk') || anc.getAttribute('data-case-id'))){
result.id = anc.getAttribute('data-case-pk') || anc.getAttribute('data-case-id');
break;
}
anc = anc.parentElement;
}
}
// Fallback: check URL query string for ?case= or ?case_id=
if(!result.id){
try{
var params = new URLSearchParams(window.location.search);
if(params.has('case')) result.id = params.get('case');
else if(params.has('case_id')) result.id = params.get('case_id');
}catch(e){}
}
// Fallback: try to parse a /case/<id>/ segment from the pathname
if(!result.id){
try{
var m = window.location.pathname.match(/\/case\/(\d+)(?:\/|$)/i) || window.location.pathname.match(/\/atlas\/case\/(\d+)(?:\/|$)/i);
if(m) result.id = m[1];
}catch(e){}
}
// If there's an H2 like 'Display Sets: TITLE' try to use it as title
if(!result.title){
var h2 = document.querySelector('h2');
if(h2 && /Display Sets:/i.test(h2.textContent || '')){
result.title = (h2.textContent || '').replace(/Display Sets:\s*/i, '').trim();
}
}
return result;
}catch(e){ return { id: null, title: null }; }
}
// Simplified detection: rely on explicit dataset attributes set by the widget
// When a textarea includes `data-current-case` and optionally `data-current-case-title`, that will be used as the default filter.
})(window);