50 lines
3.2 KiB
JavaScript
50 lines
3.2 KiB
JavaScript
// SGF host wrapper only; the shared purchasing package remains unchanged.
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const dirty = new Set();
|
|
let pending=0;
|
|
for(const form of document.querySelectorAll('form[data-action]')){
|
|
const submit=form.onsubmit;
|
|
if(typeof submit!=='function')continue;
|
|
form.onsubmit=async e=>{
|
|
pending++;
|
|
try{await submit.call(form,e);if(!document.querySelector('#error')?.textContent.trim())dirty.delete(form);}
|
|
finally{pending--;}
|
|
};
|
|
}
|
|
document.addEventListener('input', e => { const form=e.target.closest('form');if(form)dirty.add(form); });
|
|
window.addEventListener('beforeunload', e => { if(dirty.size||pending){e.preventDefault();e.returnValue='';} });
|
|
const back=document.querySelector('[data-sgf-module-back]');
|
|
back?.addEventListener('click', e => {
|
|
if(pending){e.preventDefault();document.querySelector('#error').textContent='Aguarde a conclusão da operação antes de sair.';return;}
|
|
if(!dirty.size)return;
|
|
e.preventDefault();
|
|
const dialog=document.createElement('dialog');
|
|
dialog.setAttribute('aria-label','Alterações por guardar');
|
|
dialog.innerHTML='<h2>Alterações por guardar</h2><p>Guarde, descarte ou cancele a saída deste percurso.</p><p role="alert"></p><button data-save>Guardar e continuar</button> <button data-discard>Descartar e continuar</button> <button data-cancel>Cancelar</button>';
|
|
document.body.append(dialog);dialog.showModal();
|
|
const close=()=>{dialog.close();dialog.remove();back.focus();};
|
|
dialog.querySelector('[data-cancel]').onclick=close;
|
|
dialog.addEventListener('cancel',e=>{e.preventDefault();close();});
|
|
dialog.querySelector('[data-discard]').onclick=()=>{dirty.clear();location.assign(back.href);};
|
|
dialog.querySelector('[data-save]').onclick=async()=>{
|
|
for(const b of dialog.querySelectorAll('button'))b.disabled=true;
|
|
try{
|
|
if(dirty.size>1)throw Error('Guarde cada formulário no percurso original antes de sair. O contexto foi mantido.');
|
|
for(const form of [...dirty]){
|
|
if(!form.isConnected){dirty.delete(form);continue;}
|
|
if(!form.reportValidity())throw Error('Corrija os campos. O contexto foi mantido.');
|
|
if(typeof form.onsubmit!=='function')throw Error('Guarde no formulário original antes de sair.');
|
|
// Existing package handlers validate, post and report errors in #error.
|
|
// Successful handlers reload their own page; this destination supersedes
|
|
// that reload only after the original promise has completed successfully.
|
|
await form.onsubmit({preventDefault(){}});
|
|
if(document.querySelector('#error')?.textContent.trim())throw Error('A gravação falhou. O contexto foi mantido.');
|
|
dirty.delete(form);
|
|
}
|
|
location.assign(back.href);
|
|
}catch(error){dialog.querySelector('[role=alert]').textContent=error.message;}
|
|
finally{for(const b of dialog.querySelectorAll('button'))b.disabled=false;}
|
|
};
|
|
});
|
|
});
|