diff --git a/public/app-runtime.js b/public/app-runtime.js index 28d12df..b73670d 100644 --- a/public/app-runtime.js +++ b/public/app-runtime.js @@ -461,6 +461,33 @@ window.SUN_LEGACY_CATALOG_V175=[{"id":"1","name":"Фуршетный бокс } function renderDefaultQr(){const root=$('sunDefaultQrPreview');if(!root)return;const src=defaultQr();root.innerHTML=src?`QR`:'QR не загружен';} + // ---------------- Backup to/from JSON file ---------------- + function backupFileState(){const storage={};for(let i=0;iURL.revokeObjectURL(url),2000);toast('Файл резервной копии скачан.','success');}catch(_){toast('Не удалось создать файл резервной копии.','warn')}} + async function restoreBackupFile(file){ + let state; + try{state=JSON.parse(await file.text());}catch(_){toast('Не удалось прочитать файл — это не JSON.','warn');return;} + if(!state||state.format!=='sun-catering-backup'||!state.storage||typeof state.storage!=='object'){toast('Неверный формат файла резервной копии.','warn');return;} + if(!confirm('Загрузить базу из файла? Текущие локальные данные будут заменены содержимым файла.'))return; + Object.entries(state.storage).forEach(([key,value])=>{if(key.startsWith('sun')){try{localStorage.setItem(key,String(value));}catch(_){}}}); + toast('База восстановлена из файла. Перезагрузка…','success'); + setTimeout(()=>location.reload(),600); + } + function ensureBackupFileCard(){ + const grid=document.querySelector('#enterprise-settings .enterprise-grid');if(!grid)return; + let card=$('sunBackupFileCard'); + if(!card){ + card=document.createElement('section');card.id='sunBackupFileCard';card.className='enterprise-card wide'; + card.innerHTML=`

Резервная копия базы (файл)

Скачайте базу в файл на компьютер или загрузите ранее сохранённый файл — например, для переноса на другой компьютер или работы без интернета.

`; + const audit=qa(':scope > .enterprise-card',grid).find(x=>(x.querySelector('h2')?.textContent||'').trim()==='История изменений'); + window.SunSafe.insertBefore(grid,card,audit||null); + $('sunBackupFileDownload').onclick=downloadBackupFile; + $('sunBackupFileUpload').onclick=()=>$('sunBackupFileInput').click(); + $('sunBackupFileInput').onchange=async()=>{const f=$('sunBackupFileInput').files?.[0];$('sunBackupFileInput').value='';if(f)await restoreBackupFile(f);}; + } + const audit=qa(':scope > .enterprise-card',grid).find(x=>(x.querySelector('h2')?.textContent||'').trim()==='История изменений');if(audit&&grid.lastElementChild!==audit)grid.appendChild(audit); + } + // ---------------- Discounts + promo tracking ---------------- const PROMO_KEY='sunPromoCodesV1'; const localDateIso=()=>{const d=new Date(),p=n=>String(n).padStart(2,'0');return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`}; @@ -566,7 +593,7 @@ window.SUN_LEGACY_CATALOG_V175=[{"id":"1","name":"Фуршетный бокс const previousStats=window.renderStats; window.renderStats=function(...args){const r=typeof previousStats==='function'?previousStats.apply(this,args):undefined;setTimeout(renderPromoStats,0);return r}; - function onSettings(){setTimeout(()=>{simplifySellerSettings();ensureDefaultQrSetting();},80);setTimeout(()=>{simplifySellerSettings();ensureDefaultQrSetting();},250)} + function onSettings(){setTimeout(()=>{simplifySellerSettings();ensureDefaultQrSetting();ensureBackupFileCard();},80);setTimeout(()=>{simplifySellerSettings();ensureDefaultQrSetting();ensureBackupFileCard();},250)} document.addEventListener('click',e=>{const nav=e.target.closest('header nav button');if(nav&&String(nav.dataset.navLabel||nav.textContent||'').trim()==='Настройки')onSettings();},true); (()=>{let queued=false;const run=()=>{if(queued)return;queued=true;setTimeout(()=>{queued=false;if($('sunReceiptSettingsCard'))simplifySellerSettings();patchDocButtons();},50);};[$('new'),$('enterprise-settings')].filter(Boolean).forEach(root=>new MutationObserver(run).observe(root,{childList:true,subtree:true}));})();