const definitions={requests:['request'],suppliers:['supplier'],sourcing:['request'],orders:['award'],receipts:['order','receipt','movement'],invoices:['order','invoice'],obligations:['invoice'],dossiers:['request']}; export function readFocus(stage,search){ const keys=definitions[stage];if(!keys)throw Error('Etapa de consulta inválida.');const query=new URLSearchParams(search);if(!query.size)return null;const result={}; for(const [key,value]of query){if(!keys.includes(key)||query.getAll(key).length!==1||!/^[1-9][0-9]*$/.test(value)||!Number.isSafeInteger(Number(value)))throw Error('Atalho inválido. Seleccione o registo na lista autorizada.');result[key]=Number(value);} if(stage==='receipts'&&result.movement){if(Object.keys(result).length!==1)throw Error('Atalho inválido. Seleccione o registo na lista autorizada.');return result;} if(!result[keys[0]])throw Error('O atalho não identifica a origem. Seleccione o registo na lista autorizada.');return result; } export function focusHref(stage,ids){const query=new URLSearchParams(Object.entries(ids).map(([k,v])=>[k,String(v)]));readFocus(stage,query);return '/purchasing/'+stage+'?'+query;} /** Keep the currently authorised document when explicitly choosing another QA function. */ export function rememberFocus(stage,ids){ const entry=document.querySelector('[data-purchasing-entry]');if(!entry)return; const href=focusHref(stage,ids);globalThis.history?.replaceState(null,'',href);entry.href='/compras?'+new URLSearchParams({return_to:href}); } export function continuationHref(stage,ids){ const href=focusHref(stage,ids); if(document.querySelector('nav[aria-label="Etapas de Compras"] a[href="/purchasing/'+stage+'"]'))return href; return document.querySelector('[data-purchasing-entry]')?'/compras?'+new URLSearchParams({return_to:href}):null; } export function appendProcessLinks(target,links,allowed=()=>true){ for(const link of links??[]){try{const match=/^\/purchasing\/([a-z]+)\?([^#]+)$/.exec(link.href);if(!match||focusHref(match[1],Object.fromEntries(new URLSearchParams(match[2])))!==link.href)continue;readFocus(match[1],match[2]);if(!allowed(match[1]))continue;const a=document.createElement('a');a.href=link.href;a.textContent=link.label;target.append(a);}catch{/* An invalid navigation hint must never become an external or ambiguous link. */}} } /** Menu visibility is a presentation hint. Destination APIs remain the authority. */ export function appendStageLinks(root,target,links){ if(root.dataset.continuityEnabled!=='true')return; appendProcessLinks(target,links,stage=>Boolean(document.querySelector('nav[aria-label="Etapas de Compras"] a[href="/purchasing/'+stage+'"]'))); if(document.querySelector('[data-purchasing-entry]'))for(const link of links??[]){try{const match=/^\/purchasing\/([a-z]+)\?([^#]+)$/.exec(link.href);if(!match||focusHref(match[1],Object.fromEntries(new URLSearchParams(match[2])))!==link.href||document.querySelector('nav[aria-label="Etapas de Compras"] a[href="/purchasing/'+match[1]+'"]'))continue;const a=document.createElement('a');a.href='/compras?'+new URLSearchParams({return_to:link.href});a.textContent='Mudar função para '+link.label;target.append(a);}catch{/* Invalid hints never become navigation. */}} } /** Startup does reads only; it never calls a preview/confirmation or changes company/module. */ export async function beginWorkspace({stage,root,load,open,current,error,search=globalThis.location?.search??''}){ let focus=null,problem=null;if(root.dataset.focusEnabled==='true'){try{focus=readFocus(stage,search);}catch(e){problem=e.message;}} const pending=load(),generation=current();await pending;if(current()!==generation)return;if(problem){error(problem);return;}if(focus)await open(focus); }