fix: lock company owner and simplify employee accounts

This commit is contained in:
pavlov346346-source 2026-09-12 08:48:39 +03:00
parent 0e1a7c98e8
commit 9c868aa99b

View File

@ -1,7 +1,7 @@
(()=>{ (()=>{
'use strict'; 'use strict';
if(window.CateriumAccountCenterV1780)return; if(window.CateriumAccountCenterV1780)return;
const VERSION='17.8.0-account-center-v1'; const VERSION='17.8.0-account-center-v2';
const $=(s,r=document)=>r.querySelector(s); const $=(s,r=document)=>r.querySelector(s);
const qa=(s,r=document)=>[...r.querySelectorAll(s)]; const qa=(s,r=document)=>[...r.querySelectorAll(s)];
const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??''); const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??'');
@ -38,10 +38,12 @@ function installStyle(){
#cateriumAccountCenter button.primary{border:0;border-radius:11px;background:linear-gradient(100deg,#d9aa42,#efca69);color:#171512;padding:12px 16px;font-weight:900} #cateriumAccountCenter button.primary{border:0;border-radius:11px;background:linear-gradient(100deg,#d9aa42,#efca69);color:#171512;padding:12px 16px;font-weight:900}
#cateriumAccountCenter button.outline{border:1px solid #c9c1b6;border-radius:11px;background:#fffaf2;color:#37332d;padding:11px 15px;font-weight:800} #cateriumAccountCenter button.outline{border:1px solid #c9c1b6;border-radius:11px;background:#fffaf2;color:#37332d;padding:11px 15px;font-weight:800}
#cateriumAccountCenter .cac-note{font-size:12px;color:#8a8379;line-height:1.55;margin-top:9px} #cateriumAccountCenter .cac-note{font-size:12px;color:#8a8379;line-height:1.55;margin-top:9px}
#cateriumAccountCenter .cac-owner{display:flex;align-items:center;gap:10px;padding:11px 13px;background:#fbf2d9;border:1px solid #ead39a;border-radius:12px;margin-bottom:12px}.cac-owner b{color:#725317}.cac-owner span{color:#8a6e36;font-size:12px} #cateriumAccountCenter .cac-owner{display:flex;align-items:center;gap:10px;padding:11px 13px;background:#fbf2d9;border:1px solid #ead39a;border-radius:12px;margin-bottom:12px}.cac-owner b{display:block;color:#725317}.cac-owner span{color:#8a6e36;font-size:12px}
#cateriumAccountCenter .cac-company-actions{display:flex;gap:10px;flex-wrap:wrap} #cateriumAccountCenter .cac-company-actions{display:flex;gap:10px;flex-wrap:wrap}
#sunCurrentUserLabel{cursor:pointer} #sunCurrentUserLabel{cursor:pointer}
#sunCurrentUserLabel:hover{text-decoration:underline;text-underline-offset:3px} #sunCurrentUserLabel:hover{text-decoration:underline;text-underline-offset:3px}
#sun-cloud-users-modal [data-caterium-owner-row="1"]{background:#fff9e8;border:1px solid #ead39a;border-radius:10px;padding:10px}
#sun-cloud-users-modal .caterium-owner-chip{display:inline-flex;align-items:center;border-radius:999px;background:#f4dfad;color:#6d5118;padding:5px 9px;font-size:11px;font-weight:900}
@media(max-width:720px){#cateriumAccountCenter{padding:0;place-items:stretch}#cateriumAccountCenter .cac-shell{width:100%;max-height:none;height:100dvh;border-radius:0}#cateriumAccountCenter .cac-grid{grid-template-columns:1fr}#cateriumAccountCenter .cac-card.wide{grid-column:auto}#cateriumAccountCenter .cac-head,#cateriumAccountCenter .cac-body{padding-left:18px;padding-right:18px}} @media(max-width:720px){#cateriumAccountCenter{padding:0;place-items:stretch}#cateriumAccountCenter .cac-shell{width:100%;max-height:none;height:100dvh;border-radius:0}#cateriumAccountCenter .cac-grid{grid-template-columns:1fr}#cateriumAccountCenter .cac-card.wide{grid-column:auto}#cateriumAccountCenter .cac-head,#cateriumAccountCenter .cac-body{padding-left:18px;padding-right:18px}}
`;document.head.appendChild(s); `;document.head.appendChild(s);
} }
@ -65,7 +67,7 @@ function render(){
body.innerHTML=`<div class="cac-grid"> body.innerHTML=`<div class="cac-grid">
<section class="cac-card"> <section class="cac-card">
<h3>Профиль</h3> <h3>Профиль</h3>
${owner?'<div class="cac-owner"><div><b>Главный аккаунт компании</b><span>Только этот аккаунт управляет сотрудниками и их правами.</span></div></div>':''} ${owner?'<div class="cac-owner"><div><b>Главный аккаунт компании</b><span>Только этот аккаунт создаёт сотрудников и распределяет права.</span></div></div>':''}
<div class="cac-meta"><div><span>Компания</span><b>${esc(s.company_name||'')} · ${esc(companyNo)}</b></div><div><span>Статус</span><b>${esc(owner?'Владелец':ROLE_LABELS[s.role]||s.role||'Сотрудник')}</b></div></div> <div class="cac-meta"><div><span>Компания</span><b>${esc(s.company_name||'')} · ${esc(companyNo)}</b></div><div><span>Статус</span><b>${esc(owner?'Владелец':ROLE_LABELS[s.role]||s.role||'Сотрудник')}</b></div></div>
<label>Имя<input id="cacName" value="${esc(s.display_name||ss?.user?.user_metadata?.name||'')}"></label> <label>Имя<input id="cacName" value="${esc(s.display_name||ss?.user?.user_metadata?.name||'')}"></label>
<div class="cac-actions"><button class="primary" id="cacSaveName" type="button">Сохранить имя</button></div> <div class="cac-actions"><button class="primary" id="cacSaveName" type="button">Сохранить имя</button></div>
@ -75,7 +77,7 @@ function render(){
<label>Текущий email<input value="${esc(ss?.user?.email||s.email||'')}" disabled></label> <label>Текущий email<input value="${esc(ss?.user?.email||s.email||'')}" disabled></label>
<label>Новый email<input id="cacEmail" type="email" autocomplete="email" placeholder="new@email.com"></label> <label>Новый email<input id="cacEmail" type="email" autocomplete="email" placeholder="new@email.com"></label>
<div class="cac-actions"><button class="primary" id="cacSaveEmail" type="button">Изменить email</button></div> <div class="cac-actions"><button class="primary" id="cacSaveEmail" type="button">Изменить email</button></div>
<div class="cac-note">После смены Supabase отправит письмо подтверждения на новый адрес.</div> <div class="cac-note">На новый адрес придёт письмо подтверждения.</div>
</section> </section>
<section class="cac-card"> <section class="cac-card">
<h3>Пароль</h3> <h3>Пароль</h3>
@ -83,16 +85,15 @@ function render(){
<label>Повторите пароль<input id="cacPassword2" type="password" autocomplete="new-password" minlength="8"></label> <label>Повторите пароль<input id="cacPassword2" type="password" autocomplete="new-password" minlength="8"></label>
<div class="cac-actions"><button class="primary" id="cacSavePassword" type="button">Сменить пароль</button></div> <div class="cac-actions"><button class="primary" id="cacSavePassword" type="button">Сменить пароль</button></div>
</section> </section>
${owner?`<section class="cac-card"><h3>Компания и пользователи</h3><p class="cac-note">Создавайте сотрудников, назначайте роли и выдавайте доступ к нужным разделам Caterium.</p><div class="cac-company-actions"><button class="primary" id="cacOpenUsers" type="button">Пользователи и права</button></div></section>`:''} ${owner?`<section class="cac-card"><h3>Компания и пользователи</h3><p class="cac-note">Создавайте сотрудников, назначайте им рабочую роль и включайте только нужные права.</p><div class="cac-company-actions"><button class="primary" id="cacOpenUsers" type="button">Пользователи и права</button></div></section>`:''}
<section class="cac-card wide"><h3>Сеанс</h3><div class="cac-actions"><button class="outline" id="cacLogout" type="button">Выйти из аккаунта</button></div></section> <section class="cac-card wide"><h3>Сеанс</h3><div class="cac-actions"><button class="outline" id="cacLogout" type="button">Выйти из аккаунта</button></div></section>
</div>`; </div>`;
$('#cacSaveName').onclick=saveName;$('#cacSaveEmail').onclick=saveEmail;$('#cacSavePassword').onclick=savePassword;$('#cacLogout').onclick=logout; $('#cacSaveName').onclick=saveName;$('#cacSaveEmail').onclick=saveEmail;$('#cacSavePassword').onclick=savePassword;$('#cacLogout').onclick=logout;
const users=$('#cacOpenUsers');if(users)users.onclick=()=>{close();window.SunAdminRBACV3?.openUsers?.();}; const users=$('#cacOpenUsers');if(users)users.onclick=()=>{close();window.SunAdminRBACV3?.openUsers?.();setTimeout(sanitizeRbac,100);};
} }
async function saveName(){ async function saveName(){
const name=String($('#cacName')?.value||'').trim();if(!name)return toast('Введите имя.','warn'); const name=String($('#cacName')?.value||'').trim();if(!name)return toast('Введите имя.','warn');
const c=client();if(!c)return; const c=client();if(!c)return;const btn=$('#cacSaveName');btn.disabled=true;
const btn=$('#cacSaveName');btn.disabled=true;
try{const {data,error}=await c.rpc('caterium_update_my_name',{p_display_name:name});if(error)throw error;const r=await c.auth.updateUser({data:{name:data||name}});if(r.error)throw r.error;await cloud()?.reloadMemberships?.();await loadSnapshot();render();window.SunAdminRBACV3?.apply?.();toast('Имя изменено.','success');} try{const {data,error}=await c.rpc('caterium_update_my_name',{p_display_name:name});if(error)throw error;const r=await c.auth.updateUser({data:{name:data||name}});if(r.error)throw r.error;await cloud()?.reloadMemberships?.();await loadSnapshot();render();window.SunAdminRBACV3?.apply?.();toast('Имя изменено.','success');}
catch(e){toast(e?.message||String(e),'error',6500)}finally{btn.disabled=false} catch(e){toast(e?.message||String(e),'error',6500)}finally{btn.disabled=false}
} }
@ -114,9 +115,20 @@ async function open(){
try{await loadSnapshot();render()}catch(e){body.innerHTML=`<p class="cac-note">${esc(e?.message||e||'Не удалось загрузить аккаунт.')}</p>`} try{await loadSnapshot();render()}catch(e){body.innerHTML=`<p class="cac-note">${esc(e?.message||e||'Не удалось загрузить аккаунт.')}</p>`}
} }
function close(){modal?.classList.remove('on')} function close(){modal?.classList.remove('on')}
function sanitizeRbac(){
const body=$('#sunRbacBody');if(!body)return;
qa('select option[value="admin"]',body).forEach(o=>o.remove());
qa('[data-rbac-perm="users.manage"]',body).forEach(i=>{i.checked=false;i.disabled=true;const row=i.closest('label');if(row)row.title='Это право принадлежит только главному аккаунту компании';});
if(snapshot?.is_owner&&snapshot?.user_id){
const btn=body.querySelector(`[data-rbac-edit="${CSS.escape(String(snapshot.user_id))}"]`);
if(btn){const row=btn.closest('.sun-rbac-member');if(row){row.dataset.cateriumOwnerRow='1';const role=row.querySelector('.role-chip');if(role)role.textContent='Владелец';btn.outerHTML='<span class="caterium-owner-chip">Главный аккаунт</span>';}}
}
const addRole=$('#sunRbacEmployeeRole',body);if(addRole)qa('option[value="admin"]',addRole).forEach(o=>o.remove());
}
function bindEntry(){ function bindEntry(){
const label=$('#sunCurrentUserLabel');if(label&&!label.dataset.cateriumAccountBound){label.dataset.cateriumAccountBound='1';label.title='Открыть личный кабинет';label.addEventListener('click',open)} const label=$('#sunCurrentUserLabel');if(label&&!label.dataset.cateriumAccountBound){label.dataset.cateriumAccountBound='1';label.title='Открыть личный кабинет';label.addEventListener('click',open)}
const ss=session();const uid=ss?.user?.id||'';if(uid&&uid!==lastUserId){lastUserId=uid;snapshot=null;} const ss=session();const uid=ss?.user?.id||'';if(uid&&uid!==lastUserId){lastUserId=uid;snapshot=null;}
sanitizeRbac();
} }
installStyle();ensureModal();bindEntry(); installStyle();ensureModal();bindEntry();
const obs=new MutationObserver(bindEntry);obs.observe(document.documentElement,{childList:true,subtree:true}); const obs=new MutationObserver(bindEntry);obs.observe(document.documentElement,{childList:true,subtree:true});