Replace native confirm() with a branded modal on every delete action, both tools
This commit is contained in:
@@ -164,6 +164,13 @@
|
||||
.toast{position:fixed; bottom:24px; left:50%; transform:translateX(-50%); background:var(--text); color:#fff; padding:12px 22px; font-size:14px; opacity:0; pointer-events:none; transition:opacity .2s; z-index:50;}
|
||||
.toast.show{opacity:1;}
|
||||
#jsonFile{display:none;}
|
||||
.btn.danger{background:var(--danger); color:#fff; border:none;}
|
||||
.btn.danger:hover{background:#b3121d;}
|
||||
.confirm-backdrop{position:fixed; inset:0; background:rgba(22,22,22,.5); display:flex; align-items:center; justify-content:center; z-index:70; padding:20px;}
|
||||
.confirm-card{background:var(--layer); border:1px solid var(--border); border-left:4px solid var(--danger); padding:22px 26px; max-width:420px; width:100%;}
|
||||
.confirm-card h3{font-size:16px; font-weight:600; margin-bottom:8px;}
|
||||
.confirm-card p{font-size:14px; color:var(--text-secondary); line-height:1.5; margin-bottom:20px;}
|
||||
.confirm-actions{display:flex; justify-content:flex-end; gap:10px;}
|
||||
@media (max-width:1200px){ .bc-top{grid-template-columns:1fr;} .bc-out{grid-template-columns:1fr 1fr;} }
|
||||
@media (prefers-reduced-motion: reduce){ *{transition:none !important;} }
|
||||
</style>
|
||||
@@ -503,10 +510,16 @@ document.getElementById('panel-map').addEventListener('click', e=>{
|
||||
}
|
||||
if(t.dataset.cdel){
|
||||
const c = clusterById(t.dataset.cdel);
|
||||
if(c && confirm(`Remove cluster "${c.name}"? Its issues return to Unclustered.`)){
|
||||
S.problems.forEach(p=>{ if(p.cluster===c.id) p.cluster=''; });
|
||||
S.clusters = S.clusters.filter(x=>x.id!==c.id);
|
||||
save(); renderMap();
|
||||
if(c){
|
||||
confirmModal({
|
||||
title: 'Remove cluster?',
|
||||
message: `Remove cluster "${c.name}"? Its issues return to Unclustered.`,
|
||||
confirmLabel: 'Remove cluster'
|
||||
}, ()=>{
|
||||
S.problems.forEach(p=>{ if(p.cluster===c.id) p.cluster=''; });
|
||||
S.clusters = S.clusters.filter(x=>x.id!==c.id);
|
||||
save(); renderMap();
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -530,9 +543,16 @@ document.getElementById('panel-map').addEventListener('click', e=>{
|
||||
return;
|
||||
}
|
||||
if(t.dataset.pdel){
|
||||
S.problems = S.problems.filter(p=>p.id !== t.dataset.pdel);
|
||||
editingItems.delete(t.dataset.pdel);
|
||||
save(); renderMap();
|
||||
const pid = t.dataset.pdel;
|
||||
confirmModal({
|
||||
title: 'Remove issue?',
|
||||
message: 'Remove this issue from the problem map? This cannot be undone.',
|
||||
confirmLabel: 'Remove issue'
|
||||
}, ()=>{
|
||||
S.problems = S.problems.filter(p=>p.id !== pid);
|
||||
editingItems.delete(pid);
|
||||
save(); renderMap();
|
||||
});
|
||||
return;
|
||||
}
|
||||
const head = t.closest('[data-toggle]');
|
||||
@@ -685,8 +705,15 @@ document.getElementById('bcDetail').addEventListener('click', e=>{
|
||||
}
|
||||
if(e.target.dataset.aibc){ aiDraftBreadcrumb(e.target.dataset.aibc, e.target); return; }
|
||||
if(e.target.dataset.bdel){
|
||||
S.breadcrumbs = S.breadcrumbs.filter(b=>b.id!==e.target.dataset.bdel);
|
||||
save(); renderBc();
|
||||
const bid = e.target.dataset.bdel;
|
||||
confirmModal({
|
||||
title: 'Remove breadcrumb?',
|
||||
message: 'Remove this breadcrumb? Its fields will be lost unless you have already saved or exported this session.',
|
||||
confirmLabel: 'Remove breadcrumb'
|
||||
}, ()=>{
|
||||
S.breadcrumbs = S.breadcrumbs.filter(b=>b.id!==bid);
|
||||
save(); renderBc();
|
||||
});
|
||||
}
|
||||
});
|
||||
document.getElementById('btnAddBc').addEventListener('click', ()=>{
|
||||
@@ -762,7 +789,14 @@ function renderAct(){
|
||||
}
|
||||
document.getElementById('actList').addEventListener('click', e=>{
|
||||
if(e.target.dataset.done){ const a=S.actions.find(x=>x.id===e.target.dataset.done); if(a){a.done=!a.done; save(); renderAct();} }
|
||||
if(e.target.dataset.adel){ S.actions=S.actions.filter(a=>a.id!==e.target.dataset.adel); save(); renderAct(); }
|
||||
if(e.target.dataset.adel){
|
||||
const aid = e.target.dataset.adel;
|
||||
confirmModal({
|
||||
title: 'Remove action?',
|
||||
message: 'Remove this action item? This cannot be undone.',
|
||||
confirmLabel: 'Remove action'
|
||||
}, ()=>{ S.actions=S.actions.filter(a=>a.id!==aid); save(); renderAct(); });
|
||||
}
|
||||
});
|
||||
document.getElementById('actList').addEventListener('change', e=>{
|
||||
const t = e.target;
|
||||
@@ -864,10 +898,14 @@ document.getElementById('btnExport').addEventListener('click', ()=>{
|
||||
toast('Summary downloaded');
|
||||
});
|
||||
document.getElementById('btnReset').addEventListener('click', ()=>{
|
||||
if(confirm('Reset to the clustered July 21 baseline? Anything entered since will be lost unless saved to JSON.')){
|
||||
confirmModal({
|
||||
title: 'Reset to baseline?',
|
||||
message: 'Reset to the clustered July 21 baseline? Anything entered since will be lost unless saved to JSON.',
|
||||
confirmLabel: 'Reset'
|
||||
}, ()=>{
|
||||
S = JSON.parse(JSON.stringify(DEFAULT_STATE));
|
||||
save(); renderAll(); toast('Reset to baseline');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let toastTimer;
|
||||
@@ -879,6 +917,27 @@ function toast(msg){
|
||||
toastTimer = setTimeout(()=>t.classList.remove('show'), 2200);
|
||||
}
|
||||
|
||||
function confirmModal(opts, onConfirm){
|
||||
const backdrop = document.createElement('div');
|
||||
backdrop.className = 'confirm-backdrop';
|
||||
backdrop.innerHTML = `<div class="confirm-card" role="alertdialog" aria-modal="true" aria-labelledby="confirmTitle">
|
||||
<h3 id="confirmTitle">${esc(opts.title || 'Remove this?')}</h3>
|
||||
<p>${esc(opts.message || 'This cannot be undone.')}</p>
|
||||
<div class="confirm-actions">
|
||||
<button class="btn ghost" data-cc="cancel">Cancel</button>
|
||||
<button class="btn danger" data-cc="ok">${esc(opts.confirmLabel || 'Delete')}</button>
|
||||
</div>
|
||||
</div>`;
|
||||
document.body.appendChild(backdrop);
|
||||
function close(){ document.removeEventListener('keydown', onKey); backdrop.remove(); }
|
||||
function onKey(e){ if(e.key==='Escape') close(); }
|
||||
document.addEventListener('keydown', onKey);
|
||||
backdrop.addEventListener('click', e=>{ if(e.target===backdrop) close(); });
|
||||
backdrop.querySelector('[data-cc="cancel"]').addEventListener('click', close);
|
||||
backdrop.querySelector('[data-cc="ok"]').addEventListener('click', ()=>{ close(); onConfirm(); });
|
||||
backdrop.querySelector('[data-cc="ok"]').focus();
|
||||
}
|
||||
|
||||
function renderAll(){ renderMap(); renderBc(); renderAct(); }
|
||||
renderAll();
|
||||
</script>
|
||||
|
||||
@@ -214,6 +214,13 @@
|
||||
opacity:0; pointer-events:none; transition:opacity .2s;
|
||||
}
|
||||
.toast.show{opacity:1;}
|
||||
.btn.danger{background:var(--danger); color:#fff; border:none;}
|
||||
.btn.danger:hover{background:#b3121d;}
|
||||
.confirm-backdrop{position:fixed; inset:0; background:rgba(22,22,22,.5); display:flex; align-items:center; justify-content:center; z-index:70; padding:20px;}
|
||||
.confirm-card{background:var(--layer); border:1px solid var(--border); border-left:4px solid var(--danger); padding:22px 26px; max-width:420px; width:100%;}
|
||||
.confirm-card h3{font-size:16px; font-weight:600; margin-bottom:8px;}
|
||||
.confirm-card p{font-size:14px; color:var(--text-secondary); line-height:1.5; margin-bottom:20px;}
|
||||
.confirm-actions{display:flex; justify-content:flex-end; gap:10px;}
|
||||
@media (max-width:1100px){ .board{grid-template-columns:repeat(2,1fr);} .reg-form{grid-template-columns:1fr 1fr;} }
|
||||
@media (prefers-reduced-motion: reduce){ *{transition:none !important;} }
|
||||
</style>
|
||||
@@ -502,8 +509,12 @@ document.getElementById('panel-board').addEventListener('click', e=>{
|
||||
if(item){ item.b = (item.b === t.dataset.b) ? '' : t.dataset.b; save(); renderScope(); }
|
||||
}
|
||||
if(t.dataset && t.dataset.del){
|
||||
S.scope = S.scope.filter(i=>i.id!==t.dataset.del);
|
||||
save(); renderScope();
|
||||
const iid = t.dataset.del;
|
||||
confirmModal({
|
||||
title: 'Remove item?',
|
||||
message: 'Remove this item from the Scope Boundary Board? This cannot be undone.',
|
||||
confirmLabel: 'Remove item'
|
||||
}, ()=>{ S.scope = S.scope.filter(i=>i.id!==iid); save(); renderScope(); });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -553,9 +564,16 @@ document.getElementById('rankList').addEventListener('click', e=>{
|
||||
[S.rank[i], S.rank[i+1]] = [S.rank[i+1], S.rank[i]];
|
||||
save(); renderRank();
|
||||
} else if(t.dataset.rdel !== undefined){
|
||||
S.rank.splice(+t.dataset.rdel, 1);
|
||||
if(S.cutAfter > S.rank.length) S.cutAfter = S.rank.length;
|
||||
save(); renderRank();
|
||||
const idx = +t.dataset.rdel;
|
||||
confirmModal({
|
||||
title: 'Remove item?',
|
||||
message: 'Remove this item from the priority ranking? This cannot be undone.',
|
||||
confirmLabel: 'Remove item'
|
||||
}, ()=>{
|
||||
S.rank.splice(idx, 1);
|
||||
if(S.cutAfter > S.rank.length) S.cutAfter = S.rank.length;
|
||||
save(); renderRank();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -609,8 +627,12 @@ document.getElementById('btnAddDecision').addEventListener('click', ()=>{
|
||||
|
||||
document.getElementById('regTableWrap').addEventListener('click', e=>{
|
||||
if(e.target.dataset.ddel !== undefined){
|
||||
S.decisions.splice(+e.target.dataset.ddel, 1);
|
||||
save(); renderRegistry();
|
||||
const idx = +e.target.dataset.ddel;
|
||||
confirmModal({
|
||||
title: 'Remove decision?',
|
||||
message: 'Remove this decision from the registry? This cannot be undone.',
|
||||
confirmLabel: 'Remove decision'
|
||||
}, ()=>{ S.decisions.splice(idx, 1); save(); renderRegistry(); });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -719,10 +741,14 @@ document.getElementById('btnExport').addEventListener('click', ()=>{
|
||||
|
||||
/* ================= RESET ================= */
|
||||
document.getElementById('btnReset').addEventListener('click', ()=>{
|
||||
if(confirm('Reset all four tools to their starting state? This clears everything entered in this meeting.')){
|
||||
confirmModal({
|
||||
title: 'Reset everything?',
|
||||
message: 'Reset all four tools to their starting state? This clears everything entered in this meeting.',
|
||||
confirmLabel: 'Reset'
|
||||
}, ()=>{
|
||||
S = JSON.parse(JSON.stringify(DEFAULT_STATE));
|
||||
save(); renderAll(); toast('Reset complete');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ================= TOAST ================= */
|
||||
@@ -735,6 +761,27 @@ function toast(msg){
|
||||
toastTimer = setTimeout(()=>t.classList.remove('show'), 2000);
|
||||
}
|
||||
|
||||
function confirmModal(opts, onConfirm){
|
||||
const backdrop = document.createElement('div');
|
||||
backdrop.className = 'confirm-backdrop';
|
||||
backdrop.innerHTML = `<div class="confirm-card" role="alertdialog" aria-modal="true" aria-labelledby="confirmTitle">
|
||||
<h3 id="confirmTitle">${esc(opts.title || 'Remove this?')}</h3>
|
||||
<p>${esc(opts.message || 'This cannot be undone.')}</p>
|
||||
<div class="confirm-actions">
|
||||
<button class="btn ghost" data-cc="cancel">Cancel</button>
|
||||
<button class="btn danger" data-cc="ok">${esc(opts.confirmLabel || 'Delete')}</button>
|
||||
</div>
|
||||
</div>`;
|
||||
document.body.appendChild(backdrop);
|
||||
function close(){ document.removeEventListener('keydown', onKey); backdrop.remove(); }
|
||||
function onKey(e){ if(e.key==='Escape') close(); }
|
||||
document.addEventListener('keydown', onKey);
|
||||
backdrop.addEventListener('click', e=>{ if(e.target===backdrop) close(); });
|
||||
backdrop.querySelector('[data-cc="cancel"]').addEventListener('click', close);
|
||||
backdrop.querySelector('[data-cc="ok"]').addEventListener('click', ()=>{ close(); onConfirm(); });
|
||||
backdrop.querySelector('[data-cc="ok"]').focus();
|
||||
}
|
||||
|
||||
function renderAll(){ renderScope(); renderRank(); renderRegistry(); renderReady(); }
|
||||
renderAll();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user