caterium-app/public/core/trial-promo-developer-v181.js
2026-09-13 18:44:18 +03:00

21 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(()=>{
'use strict';
if(window.CateriumTrialPromoDeveloperV181)return;
const VERSION='18.1-trial-promo-developer';
const $=id=>document.getElementById(id),qa=(s,r=document)=>[...r.querySelectorAll(s)];
const client=()=>window.SunCloudV2?.getClient?.()||null;
const dev=()=>window.SunDeveloperV22||null;
const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const toast=(t,type='info')=>window.SunEnterprise?.toast?.(t,type,5000);
async function rpc(name,args={}){const c=client();if(!c)throw new Error('Supabase не подключён.');const {data,error}=await c.rpc(name,args);if(error)throw error;return data;}
function fmt(v){if(!v)return'—';try{return new Date(v).toLocaleString('ru-RU',{dateStyle:'short',timeStyle:'short'})}catch(_){return String(v)}}
function ensureStyle(){if($('cateriumPromoDevStyle'))return;const s=document.createElement('style');s.id='cateriumPromoDevStyle';s.textContent=`.ctm-promo-tools{display:grid;grid-template-columns:1.2fr 1.2fr .65fr .65fr .65fr auto;gap:8px;align-items:end;margin-bottom:14px}.ctm-promo-tools label{display:grid;gap:5px;font-size:11px;font-weight:800;color:#686e76}.ctm-promo-tools input,.ctm-promo-tools select{min-height:39px;border:1px solid #d8dbe0;border-radius:9px;padding:7px 9px;background:#fff}.ctm-promo-code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:900;letter-spacing:.04em}.ctm-promo-copy{cursor:pointer}.ctm-promo-note{margin:0 0 12px;color:#747b83;font-size:12px}@media(max-width:1000px){.ctm-promo-tools{grid-template-columns:1fr 1fr}.ctm-promo-tools button{grid-column:1/-1}}`;document.head.appendChild(s);}
function addTab(){const root=$('sun-developer-console-v22');if(!root)return false;const tabs=root.querySelector('[data-dev-tab]')?.parentElement;if(!tabs)return false;if(!tabs.querySelector('[data-dev-tab="promos"]')){const b=document.createElement('button');b.type='button';b.dataset.devTab='promos';b.textContent='Промокоды';tabs.appendChild(b);}return true;}
async function render(){const root=$('sunDevBody');if(!root)return;qa('#sun-developer-console-v22 [data-dev-tab]').forEach(b=>b.classList.toggle('on',b.dataset.devTab==='promos'));try{localStorage.setItem('sunDeveloperActiveTabV22','promos')}catch(_){}root.innerHTML='<div class="sun-dev-empty">Загрузка промокодов…</div>';
try{const rows=await rpc('sun_dev_list_trial_promos',{p_limit:300})||[];root.innerHTML=`<div class="sun-dev-card"><h2 style="margin-top:0">Промокоды пробной версии</h2><p class="ctm-promo-note">Сгенерируйте код автоматически или напишите свой. Email можно оставить пустым для универсального кода. По умолчанию код одноразовый.</p><div class="ctm-promo-tools"><label>Промокод<input id="ctmPromoCode" maxlength="32" placeholder="Оставьте пустым для генерации"></label><label>Email клиента<input id="ctmPromoEmail" type="email" placeholder="client@example.com"></label><label>Trial, дней<input id="ctmPromoTrial" type="number" min="1" max="365" value="14"></label><label>Код действует, дней<input id="ctmPromoValid" type="number" min="1" max="365" value="7"></label><label>Активаций<input id="ctmPromoUses" type="number" min="1" max="10000" value="1"></label><button class="primary" id="ctmPromoCreate" type="button">Создать промокод</button></div><div id="ctmPromoCreated"></div></div><div class="sun-dev-table-wrap" style="margin-top:12px"><table class="sun-dev-table"><thead><tr><th>Код</th><th>Клиент</th><th>Trial</th><th>Действует до</th><th>Использования</th><th>Последняя активация</th><th>Статус</th><th></th></tr></thead><tbody>${rows.map(r=>`<tr><td><button class="outline ctm-promo-code ctm-promo-copy" data-copy="${esc(r.code)}" title="Скопировать">${esc(r.code)}</button></td><td>${esc(r.client_email||'Любой email')}</td><td>${Number(r.trial_days||0)} дн.</td><td>${fmt(r.valid_until)}</td><td>${Number(r.use_count||0)} / ${Number(r.max_uses||0)}</td><td>${r.last_redeemed_at?`${fmt(r.last_redeemed_at)}<div class="sun-dev-muted">${esc(r.last_workspace_name||'')} ${esc(r.last_redeemed_email||'')}</div>`:'—'}</td><td><span class="sun-dev-pill ${r.is_active?'ok':'bad'}">${r.is_active?'Активен':'Отключён'}</span></td><td><button class="outline" data-toggle="${esc(r.promo_id)}" data-active="${r.is_active?'1':'0'}">${r.is_active?'Отключить':'Включить'}</button></td></tr>`).join('')||'<tr><td colspan="8" class="sun-dev-empty">Промокодов пока нет.</td></tr>'}</tbody></table></div>`;
$('ctmPromoCode').oninput=e=>e.target.value=e.target.value.toUpperCase().replace(/\s+/g,'');$('ctmPromoCreate').onclick=create;qa('[data-copy]',root).forEach(b=>b.onclick=async()=>{try{await navigator.clipboard.writeText(b.dataset.copy);toast('Промокод скопирован.','success')}catch(_){toast('Не удалось скопировать код.','error')}});qa('[data-toggle]',root).forEach(b=>b.onclick=async()=>{b.disabled=true;try{await rpc('sun_dev_set_trial_promo_active',{p_promo:b.dataset.toggle,p_active:b.dataset.active!=='1'});toast('Статус промокода изменён.','success');await render()}catch(e){toast(e?.message||String(e),'error');b.disabled=false}});
}catch(e){root.innerHTML=`<div class="sun-dev-card"><h3>Не удалось загрузить промокоды</h3><p>${esc(e?.message||e)}</p></div>`;}}
async function create(){const btn=$('ctmPromoCreate');btn.disabled=true;try{const data=await rpc('sun_dev_create_trial_promo',{p_code:String($('ctmPromoCode')?.value||'').trim()||null,p_email:String($('ctmPromoEmail')?.value||'').trim()||null,p_trial_days:Number($('ctmPromoTrial')?.value||14),p_valid_days:Number($('ctmPromoValid')?.value||7),p_max_uses:Number($('ctmPromoUses')?.value||1),p_plan:'full',p_note:null});const code=data?.code||'';try{await navigator.clipboard.writeText(code)}catch(_){}toast(`Промокод ${code} создан${code?' и скопирован':''}.`,'success');await render()}catch(e){toast(e?.message||String(e),'error')}finally{if(btn?.isConnected)btn.disabled=false}}
function bind(){ensureStyle();if(!addTab())return;const root=$('sun-developer-console-v22');if(root&&!root.dataset.promoV181){root.dataset.promoV181='1';root.addEventListener('click',e=>{const b=e.target.closest('[data-dev-tab="promos"]');if(!b)return;e.preventDefault();e.stopImmediatePropagation();render();},true);}const d=dev();if(d&&!d.__promoV181&&typeof d.open==='function'){const original=d.open.bind(d);d.open=async source=>{const r=await original(source);addTab();if(localStorage.getItem('sunDeveloperActiveTabV22')==='promos')await render();return r};Object.defineProperty(d,'__promoV181',{value:true,configurable:true});}}
const obs=new MutationObserver(bind);obs.observe(document.documentElement,{childList:true,subtree:true});bind();setInterval(()=>{if(!document.hidden)bind()},3000);window.CateriumTrialPromoDeveloperV181=Object.freeze({VERSION,render,bind});
})();