{#
SENT-HUB (part 1) — the "Report an issue" widget. Included once in the cp-shell so every logged-in
user (testers especially) can file a defect from wherever they are. AJAX POST → /sentinel/report →
a source=user_report defect_ticket (current route + user-agent auto-captured; title/description
scrubbed of secrets server-side). No dependency beyond the cp-shell's own jQuery-less vanilla JS.
#}
<style>
#si-fab { position:fixed; left:18px; bottom:18px; z-index:1200; background:var(--cp-primary,#b8860b); color:var(--cp-on-primary,#fff);
border:none; border-radius:999px; padding:9px 15px; font-size:var(--cp-fs-sm); font-weight:700; cursor:pointer; box-shadow:0 2px 10px rgba(0,0,0,.2); }
#si-modal { position:fixed; inset:0; z-index:1300; background:rgba(0,0,0,.35); display:none; align-items:center; justify-content:center; }
#si-modal .si-box { background:var(--cp-surface-white,#fff); border-radius:12px; width:min(520px,92vw); max-height:90vh; overflow:auto; padding:18px 20px; box-shadow:0 8px 40px rgba(0,0,0,.3); }
#si-modal h3 { margin:0 0 4px; font-size:var(--cp-fs-lg); color:var(--cp-text,#222); }
#si-modal .si-sub { color:var(--cp-text-faint,#777); font-size:var(--cp-fs-sm); margin-bottom:12px; }
#si-modal label { display:block; font-size:var(--cp-fs-xs); font-weight:600; color:var(--cp-text-faint,#666); margin:8px 0 3px; }
#si-modal input, #si-modal textarea, #si-modal select { width:100%; box-sizing:border-box; border:1px solid var(--cp-outline-variant,#ccc); border-radius:6px; padding:7px 9px; font-size:var(--cp-fs-base); font-family:inherit; }
#si-modal textarea { resize:vertical; }
#si-actions { display:flex; gap:8px; justify-content:flex-end; margin-top:14px; }
#si-actions button { border:none; border-radius:6px; padding:8px 15px; font-size:var(--cp-fs-base); font-weight:700; cursor:pointer; }
.si-go { background:var(--cp-primary,#b8860b); color:#fff; } .si-cancel { background:transparent; color:var(--cp-text-faint,#777); }
#si-msg { font-size:var(--cp-fs-base); margin-top:8px; }
</style>
<button id="si-fab" onclick="siOpen()" title="Report an issue">🐛 Report an issue</button>
<div id="si-modal" onclick="if(event.target.id==='si-modal')siClose()">
<div class="si-box">
<h3>Report an issue</h3>
<div class="si-sub">We auto-capture the page you're on. Please don't paste passwords or tokens — they're stripped, but best not to.</div>
<label>Title *</label>
<input type="text" id="si-title" maxlength="180" placeholder="e.g. Save button does nothing on the PO form">
<label>What happened?</label>
<textarea id="si-desc" rows="4" placeholder="Steps, what you expected, what you got…"></textarea>
<label>Console log / notes (optional)</label>
<textarea id="si-log" rows="3" placeholder="Paste any browser console error here (optional)"></textarea>
<label>How bad is it?</label>
<select id="si-sev">
<option value="P2" selected>Normal — something's wrong</option>
<option value="P1">High — blocks my work</option>
<option value="P0">Critical — data/money at risk</option>
<option value="P3">Minor — cosmetic</option>
</select>
<div id="si-msg"></div>
<div id="si-actions">
<button class="si-cancel" onclick="siClose()">Cancel</button>
<button class="si-go" id="si-submit" onclick="siSubmit()">Send report</button>
</div>
</div>
</div>
<script>
function siOpen(){ document.getElementById('si-modal').style.display='flex'; document.getElementById('si-msg').textContent=''; }
function siClose(){ document.getElementById('si-modal').style.display='none'; }
function siSubmit(){
var title=(document.getElementById('si-title').value||'').trim();
var msg=document.getElementById('si-msg');
if(!title){ msg.style.color='#c00'; msg.textContent='Please add a short title.'; return; }
var btn=document.getElementById('si-submit'); btn.disabled=true;
var body=new URLSearchParams();
body.append('title',title);
body.append('description',document.getElementById('si-desc').value||'');
body.append('log',document.getElementById('si-log').value||'');
body.append('severity',document.getElementById('si-sev').value||'P2');
body.append('route',window.location.pathname+window.location.search);
fetch('{{ path('sentinel_report_submit') }}',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body.toString()})
.then(function(r){return r.json();})
.then(function(d){
msg.style.color=d.ok?'#2e7d32':'#c00'; msg.textContent=d.message||(d.ok?'Sent.':'Failed.');
if(d.ok){ document.getElementById('si-title').value=''; document.getElementById('si-desc').value=''; document.getElementById('si-log').value=''; setTimeout(siClose,1600); }
btn.disabled=false;
})
.catch(function(e){ msg.style.color='#c00'; msg.textContent='Could not send: '+e; btn.disabled=false; });
}
</script>