Keep notification-read state personal to user/company, preserve server read-only sections during staff synchronization, and respect separate order-create/edit/delete rights. Add visible mobile header session actions and a sticky logout that survives profile RPC failure; scope asynchronous profile/branding to user and workspace. Targeted browser suites and isolated SQL recovery tests passed in run 35506145405, iPhone screenshots reviewed. Full main QA remains required before production promotion. Workspace branding RPC migration is included but has NOT been applied to production Supabase; older servers retain safe owner-only fallback. No live membership/business-data repair is claimed without identifying the reported employee.
49 lines
2.6 KiB
JavaScript
49 lines
2.6 KiB
JavaScript
(()=>{
|
|
'use strict';
|
|
const escapeHTML=value=>String(value??'').replace(/[&<>"']/g,ch=>({
|
|
'&':'&','<':'<','>':'>','"':'"',"'":'''
|
|
})[ch]);
|
|
const escapeAttr=escapeHTML;
|
|
// Personal UI preferences must never be included in a company payload.
|
|
const personalStorageKey=name=>{
|
|
const cloud=window.SunCloudV2,user=cloud?.getSession?.()?.user?.id,ws=cloud?.getWorkspace?.()?.id;
|
|
return user&&ws?'caterium:personal:'+encodeURIComponent(user)+':'+encodeURIComponent(ws)+':'+encodeURIComponent(name):'';
|
|
};
|
|
const idToken=value=>String(value??'').replace(/[^a-zA-Z0-9_-]/g,'');
|
|
const jsArg=value=>escapeHTML(JSON.stringify(String(value??'')));
|
|
// Existing trial databases and saved offers still reference the PNG originals.
|
|
// Resolve only our ten bundled demo assets; never rewrite customer photos.
|
|
const demoNames='berry-dessert|bruschetta-tomato|caprese|cheese-fruit|chicken-sandwich|meat-assortment|mushroom-tartlet|salmon-cream|turkey-wrap|vegetables-hummus';
|
|
const demoPath=new RegExp('^/(demo/images/(?:'+demoNames+'))\\.png$');
|
|
const imageAssetSrc=value=>{
|
|
const src=String(value??'').trim();
|
|
try{const url=new URL(src,document.baseURI);const match=url.pathname.match(demoPath);if(url.origin===location.origin&&match)return match[1]+'.webp'+url.search+url.hash;}catch(_){}
|
|
return src;
|
|
};
|
|
const safeImageSrc=value=>{
|
|
const s=imageAssetSrc(value);
|
|
if(!s)return '';
|
|
if(/^data:image\/(?:png|jpe?g|webp|gif);base64,[a-z0-9+/=\s]+$/i.test(s))return s;
|
|
if(/^(?:\.\/|\.\.\/|\/)?[a-z0-9_./-]+\.(?:png|jpe?g|webp|gif)(?:[?#][^\s]*)?$/i.test(s))return s;
|
|
if(/^blob:[a-z0-9-]+:/i.test(s))return s;
|
|
return '';
|
|
};
|
|
const setText=(node,value)=>{if(node)node.textContent=String(value??'');return node};
|
|
const insertBefore=(parent,node,reference=null)=>{
|
|
if(!parent||!node)return node;
|
|
if(reference&&reference.parentNode===parent)parent.insertBefore(node,reference);else parent.appendChild(node);
|
|
return node;
|
|
};
|
|
window.SunSafe=Object.freeze({escapeHTML,escapeAttr,personalStorageKey,idToken,jsArg,safeImageSrc,imageAssetSrc,setText,insertBefore});
|
|
|
|
// Small bootstrap for account/profile UI. Keeping it here makes the account
|
|
// center available on every Caterium screen without touching the legacy monolith.
|
|
if(!document.getElementById('cateriumAccountCenterV1780Script')){
|
|
const script=document.createElement('script');
|
|
script.id='cateriumAccountCenterV1780Script';
|
|
script.src='core/account-center-v1780.js?v=20260920-employee-session';
|
|
script.async=true;
|
|
document.head.appendChild(script);
|
|
}
|
|
})();
|