Files
SGF/resources/js/context-transition.js
T
2026-11-02 08:06:45 +02:00

33 lines
2.0 KiB
JavaScript

// A single coordinator for page, module, company and unit transitions.
// Saving uses the existing form and its existing command/validation path.
export class ContextTransition {
constructor() { this.dirty = new Set(); this.pending = 0; this.saved = 0; this.failed = 0; this.versions = new WeakMap(); this.submitting = null; }
changed(form) { if (form) { this.dirty.add(form); this.versions.set(form,(this.versions.get(form)||0)+1); } }
prune() { for (const form of this.dirty) if (!form.isConnected) this.dirty.delete(form); }
async command(operation) {
this.pending++;
const form=this.submitting, version=form&&this.versions.get(form);
try { const result = await operation(); this.saved++; if(form&&version===this.versions.get(form))this.dirty.delete(form); return result; }
catch (e) { this.failed++; throw e; }
finally { this.pending--; }
}
async save() {
this.prune();
for (const form of [...this.dirty]) {
if (!form.requestSubmit) throw new Error('Este editor exige gravação explícita no próprio percurso. Cancele a mudança, guarde e tente novamente.');
if (!form.reportValidity()) throw new Error('Corrija os campos antes de mudar de contexto.');
const saved = this.saved, failed = this.failed;
const version=this.versions.get(form);
this.submitting=form;
form.requestSubmit();
// React handlers start commands synchronously or after a short validation step.
await new Promise(resolve => setTimeout(resolve, 100));
while (this.pending) await new Promise(resolve => setTimeout(resolve, 40));
if (this.failed !== failed || this.saved === saved) throw new Error('Não foi possível guardar. O contexto foi mantido.');
if(version!==this.versions.get(form))throw new Error('Existem alterações posteriores à gravação. O contexto foi mantido.');
this.dirty.delete(form);
}
}
discard() { this.dirty.clear(); }
}