fix(admin): номера UTM и дата чтения с хостинга (#29)
This commit is contained in:
1 parent
00fbef9ede
commit
a22519e754
9 files changed
+49
-28
No files matched your search
@@ -87,33 +87,33 @@
|
||||
const matrix={entries:new Map(),structure:'',copySource:'',copyTargets:new Set(),copyBusy:false,copyEpoch:0,copyError:'',copyResults:[],operationTargets:new Set()};
|
||||
function matrixEntry(id){if(!matrix.entries.has(id))matrix.entries.set(id,{id,epoch:0,data:null,dirty:false,saving:false,error:'',conflict:false,cells:new Map(),groups:new Map()});return matrix.entries.get(id);}
|
||||
function matrixDirty(){return [...matrix.entries.values()].some(e=>e.dirty||e.saving)||[...utmNotes.entries.values()].some(e=>e.editing||e.saving);}
|
||||
function matrixChanged(entry){entry.epoch++;const snapshot=overview.entries.get(entry.id);if(snapshot?.status==='loading'&&snapshot.data)overview.entries.set(entry.id,{...snapshot,status:'success'});overview.pending.delete(entry.id);entry.dirty=true;entry.error='';entry.validation='';try{matrixChanges(entry);}catch(error){entry.validation=error.message;}updateMatrixStatus(entry);renderCopy();}
|
||||
function matrixChanged(entry){entry.epoch++;entry.dirty=true;entry.error='';entry.validation='';try{matrixChanges(entry);}catch(error){entry.validation=error.message;}updateMatrixStatus(entry);renderCopy();}
|
||||
function updateMatrixStatus(entry){
|
||||
if(!entry.statusNode)return;entry.statusNode.textContent=entry.saving?'Сохранение…':entry.error||entry.validation|| (entry.dirty?'Есть изменения':entry.data?'Сохранено':'Чтение…');entry.statusNode.className='matrix-status '+(entry.error||entry.validation?'overview-error':entry.dirty?'matrix-dirty':'text-muted');
|
||||
entry.save.disabled=!entry.dirty||!!entry.validation||entry.saving||entry.conflict||!entry.data||!state.targets.find(t=>t.id===entry.id)?.enabled||!state.targets.find(t=>t.id===entry.id)?.verified;entry.cancel.disabled=entry.saving||!entry.dirty;entry.reload.disabled=entry.saving;entry.reload.textContent=entry.conflict?'Загрузить сохранённое':'Обновить';
|
||||
if(!entry.statusNode)return;const reading=entry.reading;entry.statusNode.textContent=entry.saving?'Сохранение…':reading?'Чтение с хостинга…':entry.error||entry.validation|| (entry.dirty?'Есть изменения':entry.written?'Записано на хостинг':entry.data?'Прочитано с хостинга':'Чтение с хостинга…');if(entry.readAtNode)entry.readAtNode.textContent=entry.readAt?'Последнее чтение: '+new Intl.DateTimeFormat('ru-RU',{timeZone:'Europe/Moscow',day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false}).format(new Date(entry.readAt))+' МСК':'Последнее чтение: ещё не выполнено';entry.statusNode.className='matrix-status '+(entry.error||entry.validation?'overview-error':entry.dirty?'matrix-dirty':'text-muted');
|
||||
entry.save.disabled=reading||!entry.dirty||!!entry.validation||entry.saving||entry.conflict||!entry.data||!state.targets.find(t=>t.id===entry.id)?.enabled||!state.targets.find(t=>t.id===entry.id)?.verified;entry.cancel.disabled=entry.saving||!entry.dirty;entry.reload.disabled=entry.saving||reading;entry.reload.textContent=entry.conflict?'Загрузить сохранённое':'Обновить';
|
||||
if(entry.footerError){entry.footerError.textContent=entry.error||entry.validation||'';entry.footerError.hidden=!entry.footerError.textContent;}for(const cell of entry.cells.values())for(const input of cell.querySelectorAll('input,select,button'))input.disabled=entry.saving||input.dataset.exhausted==='true';
|
||||
}
|
||||
function matrixGroup(entry,marker,cell){
|
||||
cell.replaceChildren();const group=groupFor(entry.data.config,marker),raw=group.value;
|
||||
if(group.conflict.length){cell.append(el('p',{class:'overview-error',text:'Конфликт ключей: '+group.conflict.join(', ')}));return;}
|
||||
if(!group.key){cell.append(el('span',{class:'text-muted',text:'Группа отсутствует'}),button('+ Добавить группу',()=>{const draft={key:marker,original:null,rows:[],defaultRow:null,added:true};entry.groups.set(marker,draft);renderGroup(draft);addVariant(draft,'1',1);draft.defaultRow=draft.rows[0];draft.rows[0].radio.checked=true;draft.touched=true;matrixChanged(entry);}));return;}
|
||||
if(!group.key){cell.append(el('span',{class:'text-muted',text:'Группа отсутствует'}));return;}
|
||||
if(!raw||typeof raw!=='object'||Array.isArray(raw)||!raw.weights||typeof raw.weights!=='object'||Array.isArray(raw.weights)||!digitFor(marker,raw.default)||Object.entries(raw.weights).some(([key,w])=>!digitFor(marker,key)||typeof w!=='number'||!Number.isFinite(w)||w<0)||new Set(Object.keys(raw.weights).map(k=>digitFor(marker,k))).size!==Object.keys(raw.weights).length){cell.append(el('p',{class:'overview-error',text:'Некорректная группа. Редактирование недоступно.'}));return;}
|
||||
const draft={key:group.key,original:structuredClone(raw),rows:[],defaultRow:null,added:false};entry.groups.set(marker,draft);renderGroup(draft);for(const [key,w]of Object.entries(raw.weights))addVariant(draft,key,w);const defaultDigit=digitFor(marker,raw.default);if(!draft.rows.some(row=>digitFor(marker,row.variant.value)===defaultDigit))addVariant(draft,String(raw.default),0);draft.defaultRow=draft.rows.find(row=>digitFor(marker,row.variant.value)===defaultDigit);draft.defaultRow.radio.checked=true;percentages(draft);
|
||||
function renderGroup(draft){draft.marker=marker;cell.replaceChildren();draft.list=el('div',{class:'matrix-variants'});cell.append(el('div',{class:'matrix-labels','aria-hidden':'true'},el('span',{text:'Вариант'}),el('span',{text:'Вес'}),el('span',{text:'Доля'}),el('span',{text:'Default'})),draft.list,button('+ Вариант',()=>{const candidates=enums[marker]?Object.keys(enums[marker]):Array.from({length:9},(_,i)=>String(i+1));const next=candidates.find(v=>!draft.rows.some(r=>digitFor(marker,r.variant.value)===v));if(!next)return;const semantic=Object.keys(draft.original?.weights||{}).some(v=>Object.values(enums[marker]||{}).includes(v));addVariant(draft,semantic?enums[marker][next]:next,0);draft.touched=true;matrixChanged(entry);}));draft.addControl=cell.lastElementChild;}
|
||||
function addVariant(draft,key,weight){const wrapper=el('div',{class:'matrix-variant'}),values=enums[marker]?Object.keys(enums[marker]):Array.from({length:9},(_,i)=>String(i+1));const semantic=Object.values(enums[marker]||{}).includes(key);const variant=el('select',{class:'form-control','aria-label':marker+' вариант '+entry.id},...values.map(v=>el('option',{value:semantic?enums[marker][v]:v,text:v})));variant.value=key;const input=numberInput(weight,0);input.setAttribute('aria-label',marker+' вес '+key+' '+entry.id);const percent=el('span',{class:'matrix-percent'}),radio=el('input',{type:'radio',name:'default-'+entry.id+'-'+marker,'aria-label':marker+' default '+key+' '+entry.id});const row={wrapper,variant,weight:input,percent,radio};draft.rows.push(row);const remove=button('×',()=>{draft.rows.splice(draft.rows.indexOf(row),1);wrapper.remove();if(draft.defaultRow===row){draft.defaultRow=null;}percentages(draft);draft.touched=true;matrixChanged(entry);});remove.className='matrix-remove';remove.setAttribute('aria-label','Удалить вариант '+marker+' '+key+' '+entry.id);wrapper.append(variant,input,percent,el('label',{class:'matrix-default'},radio,el('span',{text:'default'})),remove);draft.list.append(wrapper);for(const control of [variant,input])control.addEventListener('input',()=>{percentages(draft);draft.touched=true;matrixChanged(entry);});radio.addEventListener('change',()=>{draft.defaultRow=row;draft.touched=true;matrixChanged(entry);});percentages(draft);}
|
||||
function renderGroup(draft){draft.marker=marker;cell.replaceChildren();draft.list=el('div',{class:'matrix-variants'});cell.append(el('div',{class:'matrix-labels','aria-hidden':'true'},el('span',{text:'Вариант'}),el('span',{text:'Вес'}),el('span',{text:'Доля'}),el('span',{text:'Default'})),draft.list);}
|
||||
function addVariant(draft,key,weight){const wrapper=el('div',{class:'matrix-variant'}),variant={value:key};const input=numberInput(weight,0);input.setAttribute('aria-label',marker+' вес '+key+' '+entry.id);const percent=el('span',{class:'matrix-percent'}),radio=el('input',{type:'radio',name:'default-'+entry.id+'-'+marker,'aria-label':marker+' default '+key+' '+entry.id});const row={wrapper,variant,weight:input,percent,radio};draft.rows.push(row);wrapper.append(el('span',{class:'matrix-variant-number',text:digitFor(marker,key)}),input,percent,el('label',{class:'matrix-default'},radio,el('span',{text:'default'})));draft.list.append(wrapper);input.addEventListener('input',()=>{percentages(draft);draft.touched=true;matrixChanged(entry);});radio.addEventListener('change',()=>{draft.defaultRow=row;draft.touched=true;matrixChanged(entry);});percentages(draft);}
|
||||
}
|
||||
function percentages(group){if(group.addControl){const values=enums[group.marker]?Object.keys(enums[group.marker]):Array.from({length:9},(_,i)=>String(i+1)),exhausted=values.every(v=>group.rows.some(r=>digitFor(group.marker,r.variant.value)===v));group.addControl.dataset.exhausted=String(exhausted);group.addControl.disabled=exhausted;}const max=Math.max(0,...group.rows.map(r=>Number(r.weight.value)||0)),sum=max?group.rows.reduce((n,r)=>n+(Number(r.weight.value)||0)/max,0):0;for(const row of group.rows){const p=sum?Math.round((Number(row.weight.value)||0)/max/sum*10000)/100:0;row.percent.textContent=p+'%';row.percent.classList.toggle('positive',p>0);}}
|
||||
function matrixChanges(entry){const changes=[];if(entry.hostTouched&&entry.host&&entry.host.value!==entry.data.config.host){if(!/^(?:https:\/\/)?[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::[0-9]{1,5})?$/i.test(entry.host.value)||entry.host.value.includes('..'))throw Error('Адрес сайта должен быть HTTPS-хостом без пути.');changes.push({path:['host'],value:entry.host.value});}for(const [marker,group]of entry.groups){if(!group.touched&&!group.added)continue;if(!group.rows.length)throw Error(marker+': добавьте хотя бы один вариант.');const weights={};for(const row of group.rows){const key=row.variant.value,w=Number(row.weight.value);if(!digitFor(marker,key)||Object.hasOwn(weights,key))throw Error(marker+': варианты должны быть уникальны.');if(row.weight.value===''||!Number.isFinite(w)||w<0)throw Error(marker+': вес должен быть неотрицательным числом.');weights[key]=w;}if(!group.defaultRow)throw Error(marker+': выберите default.');const digit=digitFor(marker,group.defaultRow.variant.value),original=group.original;const rawDefault=original&&digitFor(marker,original.default)===digit?original.default:typeof original?.default==='number'?Number(digit):Object.values(enums[marker]||{}).includes(String(original?.default))?enums[marker][digit]:digit;if(group.added){changes.push({path:['utm',marker],value:{default:rawDefault,weights}});}else{if(!equivalent(weights,original.weights))changes.push({path:['utm',group.key,'weights'],value:weights});if(!equivalent(rawDefault,original.default))changes.push({path:['utm',group.key,'default'],value:rawDefault});}}return changes;}
|
||||
function matrixChanges(entry){const changes=[];if(entry.hostTouched&&entry.host&&entry.host.value!==entry.data.config.host){if(!/^(?:https:\/\/)?[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::[0-9]{1,5})?$/i.test(entry.host.value)||entry.host.value.includes('..'))throw Error('Адрес сайта должен быть HTTPS-хостом без пути.');changes.push({path:['host'],value:entry.host.value});}for(const [marker,group]of entry.groups){if(!group.touched&&!group.added)continue;if(!group.rows.length)throw Error(marker+': добавьте хотя бы один вариант.');const weights={};for(const row of group.rows){const key=row.variant.value,w=Number(row.weight.value);if(!Object.hasOwn(group.original.weights,key)&&w===0)continue;if(!digitFor(marker,key)||Object.hasOwn(weights,key))throw Error(marker+': варианты должны быть уникальны.');if(row.weight.value===''||!Number.isFinite(w)||w<0)throw Error(marker+': вес должен быть неотрицательным числом.');weights[key]=w;}if(!group.defaultRow)throw Error(marker+': выберите default.');const digit=digitFor(marker,group.defaultRow.variant.value),original=group.original;const rawDefault=original&&digitFor(marker,original.default)===digit?original.default:typeof original?.default==='number'?Number(digit):Object.values(enums[marker]||{}).includes(String(original?.default))?enums[marker][digit]:digit;if(group.added){changes.push({path:['utm',marker],value:{default:rawDefault,weights}});}else{if(!equivalent(weights,original.weights))changes.push({path:['utm',group.key,'weights'],value:weights});if(!equivalent(rawDefault,original.default))changes.push({path:['utm',group.key,'default'],value:rawDefault});}}return changes;}
|
||||
function populateMatrix(entry){entry.groups.clear();entry.hostTouched=false;entry.validation='';for(const [key,cell]of entry.cells){cell.replaceChildren();if(key==='host'){entry.host=el('input',{class:'form-control',value:entry.data.config.host??'','data-host':'','aria-label':'Основной сайт '+entry.id});entry.host.addEventListener('input',()=>{entry.hostTouched=true;matrixChanged(entry);});cell.append(entry.host);}else matrixGroup(entry,key,cell);}updateMatrixStatus(entry);}
|
||||
function renderOverview(){
|
||||
const focused=document.activeElement,selection=focused&&'selectionStart'in focused?{start:focused.selectionStart,end:focused.selectionEnd}:null;const signature=state.targets.map(t=>t.id).join('|'),table=$('overview-table');table.style.minWidth=Math.max(760,110+state.targets.length*292)+'px';if(matrix.structure!==signature&&matrix.structure.split('|').slice().sort().join('|')===signature.split('|').slice().sort().join('|')&&table.querySelector('thead tr')){const ids=state.targets.map(t=>t.id);for(const row of table.querySelectorAll('tr')){if(row.hasAttribute('data-utm-note'))continue;const children=[...row.children];if(children.length===ids.length+1)for(const id of ids){const oldIndex=matrix.structure.split('|').indexOf(id);row.append(children[oldIndex+1]);}}matrix.structure=signature;if(focused?.isConnected){focused.focus({preventScroll:true});if(selection&&selection.start!==null)focused.setSelectionRange(selection.start,selection.end);}}if(matrix.structure!==signature||!table.querySelector('thead tr')){matrix.structure=signature;const head=el('tr',{},el('th',{scope:'col',text:'Параметр'})),body=table.querySelector('tbody');body.replaceChildren();table.querySelector('thead').replaceChildren(head);const hostrow=el('tr',{'data-overview-extra':'host'},el('th',{scope:'row',text:'Основной сайт'}));body.append(hostrow);for(const target of state.targets){const entry=matrixEntry(target.id);entry.statusNode=el('p',{class:'matrix-status',role:'status'});entry.reload=button('Обновить',()=>{if(entry.dirty&&!confirm('Загрузить сохранённые настройки и сбросить черновик '+target.domain+'?'))return;return refreshOverviewTarget(target.id,true);});entry.reload.setAttribute('aria-label','Обновить настройки '+target.id);head.append(el('th',{'data-overview-target':target.id},domainHeading(target),el('div',{class:'text-muted matrix-environment',text:target.environment+(target.enabled?'':' · отключено')+(target.verified?'':' · соединение не проверено')}),entry.statusNode,entry.reload));const cell=entry.cells.get('host')||el('td',{'data-matrix-target':target.id});entry.cells.set('host',cell);hostrow.append(cell);}
|
||||
const focused=document.activeElement,selection=focused&&'selectionStart'in focused?{start:focused.selectionStart,end:focused.selectionEnd}:null;const signature=state.targets.map(t=>t.id).join('|'),table=$('overview-table');table.style.minWidth=Math.max(760,110+state.targets.length*292)+'px';if(matrix.structure!==signature&&matrix.structure.split('|').slice().sort().join('|')===signature.split('|').slice().sort().join('|')&&table.querySelector('thead tr')){const ids=state.targets.map(t=>t.id);for(const row of table.querySelectorAll('tr')){if(row.hasAttribute('data-utm-note'))continue;const children=[...row.children];if(children.length===ids.length+1)for(const id of ids){const oldIndex=matrix.structure.split('|').indexOf(id);row.append(children[oldIndex+1]);}}matrix.structure=signature;if(focused?.isConnected){focused.focus({preventScroll:true});if(selection&&selection.start!==null)focused.setSelectionRange(selection.start,selection.end);}}if(matrix.structure!==signature||!table.querySelector('thead tr')){matrix.structure=signature;const head=el('tr',{},el('th',{scope:'col',text:'Параметр'})),body=table.querySelector('tbody');body.replaceChildren();table.querySelector('thead').replaceChildren(head);const hostrow=el('tr',{'data-overview-extra':'host'},el('th',{scope:'row',text:'Основной сайт'}));body.append(hostrow);for(const target of state.targets){const entry=matrixEntry(target.id);entry.statusNode=el('p',{class:'matrix-status',role:'status'});entry.readAtNode=el('p',{class:'matrix-read-at text-muted'});entry.reload=button('Обновить',()=>{if(entry.dirty&&!confirm('Загрузить сохранённые настройки и сбросить черновик '+target.domain+'?'))return;return refreshOverviewTarget(target.id,true);});entry.reload.setAttribute('aria-label','Обновить настройки '+target.id);head.append(el('th',{'data-overview-target':target.id},domainHeading(target),el('div',{class:'text-muted matrix-environment',text:target.environment+(target.enabled?'':' · отключено')+(target.verified?'':' · соединение не проверено')}),entry.statusNode,entry.readAtNode,entry.reload));const cell=entry.cells.get('host')||el('td',{'data-matrix-target':target.id});entry.cells.set('host',cell);hostrow.append(cell);}
|
||||
for(const marker of markers){const row=el('tr',{'data-overview-utm':marker},el('th',{'aria-hidden':'true'}));for(const target of state.targets){const cell=matrixEntry(target.id).cells.get(marker)||el('td',{'data-matrix-target':target.id,'data-marker':marker});matrixEntry(target.id).cells.set(marker,cell);row.append(cell);}body.append(noteEntry(marker).row,row);}
|
||||
const footer=el('tr',{class:'matrix-footer'},el('th',{scope:'row',text:'Сохранение'}));for(const target of state.targets){const entry=matrixEntry(target.id);entry.save=button('Сохранить',()=>saveMatrix(target.id),'primary');entry.save.setAttribute('aria-label','Сохранить настройки '+target.id);entry.cancel=button('Отмена',()=>{if(entry.conflict)return refreshOverviewTarget(target.id,true);entry.epoch++;entry.dirty=false;entry.error='';entry.validation='';populateMatrix(entry);renderCopy();});entry.cancel.setAttribute('aria-label','Отменить настройки '+target.id);entry.footerError=el('p',{class:'overview-error matrix-footer-error',role:'alert','aria-live':'polite',hidden:true});footer.append(el('td',{},el('div',{class:'toolbar'},entry.save,entry.cancel),entry.footerError));if(entry.data&&!entry.host)populateMatrix(entry);else updateMatrixStatus(entry);}body.append(footer);if(focused?.isConnected){focused.focus({preventScroll:true});if(selection&&selection.start!==null)focused.setSelectionRange(selection.start,selection.end);}
|
||||
}
|
||||
for(const target of state.targets){const entry=matrixEntry(target.id),snapshot=overview.entries.get(target.id);const title=entry.statusNode?.parentElement;if(title){title.querySelector('.overview-domain').textContent=target.domain;title.querySelector('.matrix-environment').textContent=target.environment+(target.enabled?'':' · отключено')+(target.verified?'':' · соединение не проверено');}if(snapshot?.data&&entry.data!==snapshot.data&&!entry.dirty&&!entry.saving){entry.data=snapshot.data;populateMatrix(entry);}if(snapshot?.status==='error'&&!entry.error){entry.error='Ошибка чтения: '+snapshot.error;}updateMatrixStatus(entry);}renderTargetControls();renderExtras();renderCopy();if(builder.target&&builder.entries.get(builder.target)?.ready)updateBuilderState();
|
||||
}
|
||||
function renderExtras(){const body=$('overview-table').querySelector('tbody'),values=new Map(),keys=new Map();for(const t of state.targets){const v=overviewExtra(matrixEntry(t.id).data?.config);v.delete('host');values.set(t.id,v);for(const [key,item]of v)keys.set(key,item.label);}body.querySelectorAll('[data-matrix-extra]').forEach(n=>n.remove());for(const [key,label]of keys){const row=el('tr',{'data-matrix-extra':key},el('th',{text:label}));for(const t of state.targets)row.append(el('td',{class:'overview-plain',text:values.get(t.id).get(key)?.text||'Поле отсутствует'}));body.append(row);}}
|
||||
function refreshOverviewTarget(id,force=false){if(!state.targets.some(t=>t.id===id))return Promise.resolve();const entry=matrixEntry(id),epoch=++entry.epoch,request=++overview.serial,previous=overview.entries.get(id);overview.entries.set(id,{...previous,status:'loading',request});renderOverview();const pending=(async()=>{try{const data=await api('config?target='+encodeURIComponent(id));if(entry.epoch!==epoch||matrix.entries.get(id)!==entry||!state.csrf)return;overview.entries.set(id,{status:'success',request,data});if(entry.dirty&&!force){if(entry.data?.version!==data.version){entry.conflict=true;entry.error='Настройки изменены на сервере. Черновик сохранён. Загрузите сохранённое или отмените изменения.';}}else{entry.data=data;entry.dirty=false;entry.conflict=false;entry.error='';populateMatrix(entry);}}catch(error){if(entry.epoch!==epoch||matrix.entries.get(id)!==entry||!state.csrf)return;overview.entries.set(id,{...previous,status:'error',request,error:error.message});entry.error=error.message;}finally{if(entry.epoch===epoch&&matrix.entries.get(id)===entry){overview.pending.delete(id);renderOverview();}}})();overview.pending.set(id,pending);return pending;}
|
||||
function refreshOverviewTarget(id,force=false){if(!state.targets.some(t=>t.id===id))return Promise.resolve();const entry=matrixEntry(id);if(entry.reading)return overview.pending.get(id)||Promise.resolve();const epoch=++entry.epoch,request=++overview.serial,previous=overview.entries.get(id);entry.reading=true;overview.entries.set(id,{...previous,status:'loading',request});renderOverview();const pending=(async()=>{try{const data=await api('config?target='+encodeURIComponent(id));if(matrix.entries.get(id)!==entry||!state.csrf)return;entry.readAt=data.read_at||entry.readAt;entry.written=false;entry.error='';overview.entries.set(id,{status:'success',request,data});if(entry.epoch!==epoch)return;if(entry.dirty&&!force){if(entry.data?.version!==data.version){entry.conflict=true;entry.error='Настройки изменены на сервере. Черновик сохранён. Загрузите сохранённое или отмените изменения.';}}else{entry.data=data;entry.dirty=false;entry.conflict=false;entry.error='';populateMatrix(entry);}}catch(error){if(matrix.entries.get(id)!==entry||!state.csrf)return;overview.entries.set(id,{...previous,status:'error',request,error:error.message});entry.error=error.message;}finally{if(matrix.entries.get(id)===entry){entry.reading=false;overview.pending.delete(id);renderOverview();}}})();overview.pending.set(id,pending);return pending;}
|
||||
async function refreshOverview(){const control=$('overview-refresh');control.disabled=true;run(loadNotes);try{await Promise.allSettled(state.targets.map(t=>refreshOverviewTarget(t.id)));}finally{control.disabled=false;}}
|
||||
async function waitMatrixJob(job,isCurrent){
|
||||
let current=typeof job==='object'?job:null,id=typeof job==='string'?job:job.id;
|
||||
@@ -125,7 +125,7 @@
|
||||
}
|
||||
async function saveMatrix(id){
|
||||
const entry=matrixEntry(id),generation=state.sessionGeneration,isCurrent=()=>generation===state.sessionGeneration&&!!state.csrf&&matrix.entries.get(id)===entry;
|
||||
if(entry.saving||entry.conflict||!entry.dirty)return;
|
||||
if(entry.reading||entry.saving||entry.conflict||!entry.dirty)return;
|
||||
try{
|
||||
const changes=matrixChanges(entry);if(!changes.length){entry.dirty=false;updateMatrixStatus(entry);renderCopy();return;}
|
||||
entry.epoch++;entry.saving=true;entry.error='';updateMatrixStatus(entry);renderCopy();
|
||||
@@ -133,7 +133,7 @@
|
||||
const job=await waitMatrixJob(result.job,isCurrent);if(!isCurrent()||!job)return;
|
||||
const outcome=job.results?.find(r=>(typeof r.target==='object'?r.target.id:r.target)===id);
|
||||
if(outcome?.status!=='success'){entry.conflict=outcome?.status==='conflict';throw Error(outcome?.message||'Сохранение не подтверждено. Проверьте журнал.');}
|
||||
entry.saving=false;if(!isCurrent())return;await refreshOverviewTarget(id,true);if(!isCurrent())return;
|
||||
entry.saving=false;if(!isCurrent())return;await refreshOverviewTarget(id,true);if(!isCurrent())return;if(overview.entries.get(id)?.status==='success'){entry.written=true;updateMatrixStatus(entry);}
|
||||
if(state.source===id){await fetchRead(id);if(!isCurrent())return;await loadSets();if(!isCurrent())return;await loadBackups();}
|
||||
}catch(error){if(isCurrent()){entry.error=error.message;entry.conflict=entry.conflict||error.status===409;}}
|
||||
finally{if(isCurrent()){entry.saving=false;updateMatrixStatus(entry);renderCopy();}}
|
||||
@@ -155,7 +155,7 @@
|
||||
matrix.copyResults=job.results||[];
|
||||
for(const outcome of job.results||[]){
|
||||
if(!isCurrent())return;const id=typeof outcome.target==='object'?outcome.target.id:outcome.target,entry=entries.get(id);if(!entry)continue;
|
||||
if(outcome.status==='success'){entry.saving=false;await refreshOverviewTarget(id);if(!isCurrent())return;}
|
||||
if(outcome.status==='success'){entry.saving=false;await refreshOverviewTarget(id);if(!isCurrent())return;if(overview.entries.get(id)?.status==='success')entry.written=true;}
|
||||
else{entry.error=outcome.message||'Не удалось скопировать настройки.';entry.conflict=outcome.status==='conflict';updateMatrixStatus(entry);}
|
||||
}
|
||||
if(!isCurrent())return;notice('Копирование завершено. Результат каждого домена показан в журнале.','info');
|
||||
|
||||
Reference in new issue
Block a user