(function(){
const table=document.querySelector('#dataset-table');
if(!table)return;
const panel=table.closest('.panel');
const search=document.createElement('div');
search.innerHTML='
Find lookout Search
';
panel.insertBefore(search,table);
const modal=document.createElement('dialog');
modal.style.cssText='max-width:620px;width:calc(100% - 32px);background:#151f28;color:#edf5f5;border:1px solid #2b3d47;border-radius:10px;padding:24px';
document.body.appendChild(modal);
const close=()=>modal.close();
const attr=value=>esc(value).replace(/"/g,'"');
function message(text,title='Admin message'){
modal.innerHTML=''+esc(title)+' '+esc(text)+'
OK ';
modal.querySelector('#modal-ok').onclick=close;modal.showModal();
}
function confirmAction(text){
return new Promise(resolve=>{modal.innerHTML='Confirm action '+esc(text)+'
Yes Cancel ';modal.querySelector('#modal-yes').onclick=()=>{close();resolve(true)};modal.querySelector('#modal-no').onclick=()=>{close();resolve(false)};modal.addEventListener('cancel',()=>resolve(false),{once:true});modal.showModal()});
}
window.alert=text=>message(text);
async function details(id){
const response=await fetch('/api/admin/lookouts/'+encodeURIComponent(id),{headers:headers()});
const body=await response.json().catch(()=>({}));
if(!response.ok)throw Error(body.detail||'Could not load lookout');
return body;
}
function showMetadata(item){
modal.innerHTML='Lookout metadata Lookout ID: '+esc(item.id)+'
Added by Telegram ID: '+esc(item.telegram_id||'unknown')+'Submitted: '+esc(item.created_at||'')+'Status: '+esc(item.status||'')+'
Name: '+esc(item.name||'')+'
Reason: '+esc(item.reason||'')+'
Edit Remove Close ';
modal.querySelector('#edit-lookout').onclick=()=>showEdit(item);
modal.querySelector('#delete-lookout').onclick=()=>remove(item.id);
modal.querySelector('#close-lookout').onclick=close;
modal.showModal();
}
function showEdit(item){
modal.innerHTML='Edit lookout Name Reason
Save Cancel ';
modal.querySelector('#save-lookout').onclick=async()=>{const error=modal.querySelector('#edit-error');error.textContent='Saving...';try{const response=await fetch('/api/admin/lookouts/'+encodeURIComponent(item.id),{method:'PUT',headers:{...headers(),'Content-Type':'application/json'},body:JSON.stringify({name:modal.querySelector('#edit-name').value,reason:modal.querySelector('#edit-reason').value})});const body=await response.json().catch(()=>({}));if(!response.ok)throw Error(body.detail||'Could not save lookout');showMetadata(body);await renderDatasetView()}catch(e){error.textContent=e.message}};
modal.querySelector('#cancel-edit').onclick=()=>showMetadata(item);
}
async function remove(id){
if(!await confirmAction('Remove this lookout and its stored image?'))return;
try{const response=await fetch('/api/admin/lookouts/'+encodeURIComponent(id),{method:'DELETE',headers:headers()});const body=await response.json().catch(()=>({}));if(!response.ok)throw Error(body.detail||'Could not remove lookout');close();await renderDatasetView();searchResult.textContent='Lookout removed.'}catch(e){message(e.message,'Could not remove lookout')}
}
const searchResult=search.querySelector('#lookout-search-result');
search.querySelector('#lookout-id-search-button').onclick=async()=>{const id=search.querySelector('#lookout-id-search').value.trim();if(!id)return;searchResult.textContent='Searching...';try{showMetadata(await details(id));searchResult.textContent=''}catch(e){searchResult.textContent=e.message}};
search.querySelector('#lookout-id-search').onkeydown=e=>{if(e.key==='Enter')search.querySelector('#lookout-id-search-button').click()};
function enhance(){
table.querySelectorAll('.stat').forEach(card=>{
if(card.dataset.lookoutActions||!card.textContent.includes('ID:'))return;
card.dataset.lookoutActions='1';
const idText=[...card.querySelectorAll('div')].find(x=>x.textContent.trim().startsWith('ID:'));
const id=idText?idText.textContent.trim().slice(3).trim():'';
const actions=document.createElement('div');actions.style.cssText='display:flex;gap:6px;margin-top:10px';actions.innerHTML='View metadata Remove ';
actions.children[0].onclick=async()=>{try{showMetadata(await details(id))}catch(e){message(e.message,'Could not load lookout')}};
actions.children[1].onclick=()=>remove(id);card.appendChild(actions);
});
}
const sentLink=document.createElement('a');sentLink.href='#sent-reports';sentLink.dataset.view='sent-reports';sentLink.textContent='Sent Reports';document.querySelector('.side').insertBefore(sentLink,document.querySelector('#logout'));
const sentPanel=document.createElement('div');sentPanel.id='sent-reports';sentPanel.dataset.panel='';sentPanel.className='hidden';sentPanel.innerHTML='';
document.querySelector('.content').appendChild(sentPanel);
const sentTable=sentPanel.querySelector('#sent-table');
function sentParams(page){const q=new URLSearchParams({page,per_page:25});[['from_date','sent-from'],['to_date','sent-to'],['search_id','sent-search-id'],['report_id','sent-report-id'],['lookup_id','sent-lookout-id'],['telegram_id','sent-user-id'],['organisation','sent-org'],['status','sent-status']].forEach(([key,id])=>{const value=sentPanel.querySelector('#'+id).value.trim();if(value)q.set(key,value)});[['similarity_min','sent-min'],['similarity_max','sent-max']].forEach(([key,id])=>{const value=sentPanel.querySelector('#'+id).value;if(value)q.set(key,value)});return q}
async function loadSent(page=1){sentTable.innerHTML='Loading sent reports...
';try{const d=await api('/admin/sent-reports?'+sentParams(page));if(!d.items.length){sentTable.innerHTML='No sent reports found.
';return}let html='Timestamp Report ID Search ID Telegram Lookout Similarity Status Channel Actions ';for(const r of d.items)html+=''+esc(r.sent_at||r.generated_at)+' '+esc(r.report_id)+' '+esc(r.search_id||'')+' '+esc(r.telegram_id||'')+' '+esc(r.lookout_id||'')+' '+esc(r.similarity==null?'':Math.round(r.similarity*100)+'%')+' '+esc(r.status)+' '+esc(r.channel)+' More Info ';html+='
Prev Page '+d.page+' of '+(d.pages||1)+' ยท '+d.total+' report(s) Next
';sentTable.innerHTML=html;sentTable.querySelector('#sent-prev').disabled=page<=1;sentTable.querySelector('#sent-next').disabled=page>=d.pages;sentTable.querySelector('#sent-prev').onclick=()=>loadSent(page-1);sentTable.querySelector('#sent-next').onclick=()=>loadSent(page+1);sentTable.querySelectorAll('[data-sent-detail]').forEach(b=>b.onclick=()=>showSentDetail(b.dataset.sentDetail))}catch(e){sentTable.innerHTML='Could not load sent reports: '+esc(e.message)+'
'}}
async function showSentDetail(id){try{const r=await api('/admin/sent-reports/'+encodeURIComponent(id));modal.innerHTML='Sent report details Report ID: '+esc(r.id)+'Search ID: '+esc(r.search_id||'')+'Lookout ID: '+esc(r.report_id||'')+'Lookup ID: '+esc(r.lookup_id||'')+'Telegram user: '+esc(r.telegram_id||'')+'Candidate similarity: '+esc(r.similarity==null?'':Math.round(r.similarity*100)+'%')+'Threshold used: '+esc(r.threshold==null?'':Math.round(r.threshold*100)+'%')+'Generated: '+esc(r.generated_at||'')+'Sent: '+esc(r.sent_at||'')+'Status: '+esc(r.status||'')+'Channel: '+esc(r.channel||'')+'Report version: '+esc(r.report_version||'')+'
Lookout name: '+esc(r.name||'')+'Delivery attempts: '+esc((r.delivery_attempts||[]).length)+'
Close ';modal.querySelector('#close-sent-detail').onclick=close;modal.showModal()}catch(e){message(e.message,'Could not load sent report')}}
const dashboard=document.querySelector('#dashboard .panel');if(dashboard){const thresholdBox=document.createElement('div');thresholdBox.innerHTML='Candidate similarity threshold Enter a score from 0 to 100. This is not identity certainty.
Save
';dashboard.appendChild(thresholdBox);thresholdBox.querySelector('#admin-threshold-save').onclick=async()=>{const value=Number(thresholdBox.querySelector('#admin-threshold').value);try{const response=await fetch('/api/admin/config/threshold',{method:'PUT',headers:{...headers(),'Content-Type':'application/json'},body:JSON.stringify({threshold:value,reason:thresholdBox.querySelector('#admin-threshold-reason').value})});const body=await response.json();if(!response.ok)throw Error(body.detail||'Could not update threshold');message('Threshold saved at '+body.threshold+' candidate similarity score.','Configuration updated')}catch(e){message(e.message,'Could not update threshold')}}}
sentPanel.querySelector('#sent-filter').onclick=()=>loadSent(1);sentPanel.querySelector('#sent-clear').onclick=()=>{sentPanel.querySelectorAll('input').forEach(x=>x.value='');loadSent(1)};sentLink.onclick=e=>{e.preventDefault();document.querySelectorAll('[data-panel]').forEach(x=>x.classList.add('hidden'));sentPanel.classList.remove('hidden');document.querySelectorAll('[data-view]').forEach(x=>x.classList.remove('active'));sentLink.classList.add('active');document.querySelector('#title').textContent='Sent Reports';loadSent(1)};
new MutationObserver(enhance).observe(table,{childList:true,subtree:true});
enhance();
})();