PageSourceSearch

https://app.auroravpn.org/assets/Admin-WPZcu_88.js

js auroravpn.org collected 2026-10-08 08:29:46 UTC 351,656 bytes, 89 lines download raw bytes

1import{r,j as e,k as _s,V as Ft,a9 as nn,aa as kn,a1 as rs,ab as Cn,ac as Wt,ad as zn,ae as Mt,C as _n,x as Ts,af as $s,X as Rs,M as Tn,ag as $n,F as Ws,ah as Rn,ai as Wn,aj as rn,ak as an,al as An,B as In,am as Dn,an as ln,ao as Bn,z as Ln,ap as En,aq as Fn,ar as us,as as Un,at as on,au as Ot,av as dn,y as Pn,aw as Mn,I as Ut,ax as Pt,ay as qt,az as cn,aA as ps,U as On,J as As,aB as qn,aC as mn,G as Hn,m as Vn,aD as Gn,aE as Kn,aF as Xn,aG as Yn,aH as Jn,aI as Qn,aJ as Zn,aK as At,l as It,aL as Dt,aM as ei,aN as Is}from"./vendor-B2roeKE5.js";import{O as ti,c as si,u as ni,T as Ds,B as Xe,L as Bs,A as hn}from"./index-B6DZdGhn.js";const yt={violet:"#7C5CFF",cyan:"#22D3EE",green:"#34D399"},un=1180,pn=130,ii=s=>{const t=String(s||"").match(/^(\d{4})-(\d{2})-(\d{2})$/);if(t)return`${t[3]}.${t[2]}`;const m=String(s||"").match(/^(\d{4})-(\d{2})$/);return m?`${m[2]}.${m[1].slice(2)}`:String(s||"")};function xn({data:s,days:t,color:m=yt.violet,fmtValue:n,fmtDay:x=ii,height:i=120,width:C=600}){const u=C,k=30,g=i,I=2,[E,X]=r.useState(null),D=r.useRef(null),W=(Array.isArray(s)?s:[]).map(U=>Number.isFinite(Number(U))?Math.max(0,Number(U)):0),o=Array.isArray(t)?t:[],N=Math.max(1,W.length),v=U=>n?n(U):String(U),_=Math.max(1,...W),B=_/3,Q=Math.pow(10,Math.floor(Math.log10(B))),A=Math.max(1,[1,2,5,10].map(U=>U*Q).find(U=>U>=B)||10*Q),$=[];for(let U=0;U<=_+A*.999;U+=A)$.push(Math.round(U));$.length||$.push(0,1);const V=$[$.length-1]||1,T=Math.max(...$.map(U=>v(U).length))*5.6+10,y=(u-T)/N,q="bg-"+m.replace("#",""),P=U=>{var ee;const ne=(ee=D.current)==null?void 0:ee.getBoundingClientRect();if(!ne)return;const le=(U.clientX-ne.left)/ne.width*u-T;X(Math.max(0,Math.min(N-1,Math.floor(le/y))))},oe=E==null?"":`${x(o[E]||"")} · ${v(W[E]||0)}`,L=E==null?0:T+E*y+y/2,se=Math.max(1,Math.ceil(N/Math.max(1,Math.floor((u-T)/55))));return e.jsxs("div",{style:{position:"relative",width:"100%"},children:[e.jsxs("svg",{ref:D,viewBox:`0 0 ${u} ${k+g+18}`,style:{width:"100%",height:"auto",display:"block",touchAction:"none"},onPointerDown:P,onPointerMove:P,onPointerLeave:()=>X(null),onPointerUp:()=>X(null),children:[e.jsx("defs",{children:e.jsxs("linearGradient",{id:q,x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"0",stopColor:m,stopOpacity:"1"}),e.jsx("stop",{offset:"1",stopColor:m,stopOpacity:"0.45"})]})}),$.map(U=>{const ne=k+g-U/V*g;return e.jsxs("g",{pointerEvents:"none",children:[e.jsx("line",{x1:T,y1:ne,x2:u,y2:ne,stroke:"rgba(128,128,140,0.18)",strokeWidth:"1",strokeDasharray:U?"3 3":void 0}),e.jsx("text",{x:T-6,y:ne+3,textAnchor:"end",fontSize:"9",fill:"#7B8494",children:v(U)})]},"t"+U)}),E!=null&&e.jsx("line",{x1:L,y1:k,x2:L,y2:k+g,stroke:"rgba(255,255,255,0.14)",strokeWidth:"1",pointerEvents:"none"}),W.map((U,ne)=>{const le=Math.max(U>0?3:1,U/V*g),ee=E===ne;return e.jsxs("g",{pointerEvents:"none",children:[e.jsx("rect",{x:T+ne*y+I,y:k+g-le,width:Math.max(1,y-I*2),height:le,rx:2.5,fill:U>0?`url(#${q})`:"rgba(255,255,255,0.06)",opacity:E==null||ee?1:.4}),ee&&U>0&&e.jsx("circle",{cx:L,cy:k+g-le,r:3.5,fill:"#fff"}),(ne%se===0||ne===N-1)&&e.jsx("text",{x:T+ne*y+y/2,y:k+g+13,textAnchor:"middle",fontSize:"9",fill:"#7B8494",children:x(o[ne]||"")})]},ne)})]}),E!=null&&e.jsxs("div",{style:{position:"absolute",top:0,left:`${L/u*100}%`,transform:`translateX(${L>u*.75?"-100%":L<u*.25?"0":"-50%"})`,pointerEvents:"none",display:"inline-flex",alignItems:"center",gap:6,whiteSpace:"nowrap",background:"#151821",border:"1px solid rgba(255,255,255,0.22)",borderRadius:8,padding:"5px 10px",fontSize:13,fontWeight:600,color:"#EEF1F7",boxShadow:"0 6px 18px rgba(0,0,0,0.4)",zIndex:2},children:[e.jsx("span",{style:{width:8,height:8,borderRadius:3,background:m,flex:"none"}}),oe]})]})}function Ls(s,t){return s==="stripe"?"Счёт Stripe живёт сутки — потом Stripe закрывает его сам, и мы помечаем строку тем же часом.":s==="yookassa"?"Ссылка ЮKassa живёт дольше суток: у нас есть оплаты через 29 часов после выставления. Хороним неоплаченный счёт на вторые сутки.":s==="coingate"?"Крипто-счёт ждёт подтверждения сети — хороÐ
1½Ð¸Ð¼ неоплаченный на вторые сутки.":t==="sbp"||t==="sberpay"?"QR держится до часа, дальше человек платит заново. Неоплаченный счёт хороним через сутки.":"Неоплаченный счёт хороним через сутки — денег за ним нет ни до, ни после."}const ri=["[email protected]"];function Yt(s){var m;const t=String(s||"");return(((m=t.match(/<([^>]+)>/))==null?void 0:m[1])||t).trim().toLowerCase()}const ai=/\b(?:[a-z]{2,12}_[0-9a-f]{6,}_[0-9a-z]{6,}|(?:ep|etpguest|etpimp|etp|recurrent)[0-9a-z]*_[0-9a-z][0-9a-z_-]{3,})\b/gi;function li(s){const t=String(s.html||""),m=t.search(/<blockquote|class="?gmail_quote/i),n=(m>=0?t.slice(0,m):t).replace(/<[^>]+>/g," "),x=String(s.body||""),i=x.search(/\n\s*(>|-{2,}\s*(Исходное|Original)|\d{1,2}[./]\d{1,2}[./]\d{2,4}[^\n]{0,60}(писал|wrote))/i);return`${i>=0?x.slice(0,i):x} ${n}`}const gn={matrix:"[email protected]",wisp:"[email protected]",babka:"[email protected]",petya:"[email protected]",brat:"[email protected]",inside:"[email protected]"},oi={babka:"статьёй 32 Закона Российской Федерации «О защите прав потребителей»",petya:"статьёй 30 Закона Республики Казахстан «О защите прав потребителей»"},fn=s=>s.map(t=>`— ${t}`).join(`
2`),di=["адрес электронной почты аккаунта в сервисе;","дату и сумму списания;","последние четыре цифры карты либо номер операции (для оплаты через СБП — идентификатор платежа);","реквизиты для возврата — те же, с которых производилась оплата."],ci=["фамилию, имя и отчество полностью;","адрес электронной почты аккаунта в сервисе;","дату, время и точную сумму списания;","последние четыре цифры карты либо номер операции (для оплаты через СБП — идентификатор платежа);","реквизиты для возврата — они должны совпадать с реквизитами, с которых производилась оплата;","основание требования со ссылкой на пункт оферты;","подтверждение оплаты — чек или выписку банка файлом;","дату составления заявления и вашу подпись (допускается скан или фото подписанного документа);","согласие на обработку персональных данных в целях рассмотрения заявления."];function mi(s){const t=`https://${s.registrable}/offer`,m=`https://app.${s.registrable}`,n=gn[s.id];return["Здравствуйте!",`Мы получили ваше обращение о возврате денежных средств и готовы рассмотреть его в порядке, установленном публичной офертой сервиса ${s.name}. Оферта размещена по адресу ${t}; вы приняли её условия при оплате, и именно они определяют, когда и как возможен возврат. Ниже — порядок по пунктам, чтобы ваше заявление было рассмотрено без задержек.`,`1. Основания для возврата
3Согласно п. 8.1 оферты услуга считается оказанной в течение 3 календарных дней с момента поступления оплаты, а моментом оказания считается предоставление доступа к сервису. Пункт 8.2 предусматривает право потребовать возврат, если услуга не была окаÐ
3·Ð°Ð½Ð° в этот срок.
4Если вы сообщаете, что доступ не работает, оферта (п. 8.2) предусматривает, что вы даёте службе поддержки возможность это проверить: сообщаете устройство, приложение и способ подключения, присылаете снимки экрана с ошибкой, выполняете рекомендации поддержки по настройке, а при необходимости предоставляете доступ к устройству для удалённой проверки. Если такая возможность не предоставлена, а по данным сервиса доступ был выдан и использовался, услуга считается оказанной.`,`2. Срок подачи заявления
5Заявление на возврат подаётся в течение 7 календарных дней с даты истечения срока оказания услуги (п. 8.3 оферты).`,`3. Форма и способ подачи
6Согласно п. 8.4 оферты заявление составляется в письменной форме на имя Администрации сервиса: устной просьбы или короткого сообщения «верните деньги» для этого недостаточно, нужен документ со сведениями ниже. Направить его можно ответом в это обращение (раздел «Поддержка» в личном кабинете ${m}, файлы прикладываются к сообщению) либо письмом на адрес ${n} с темой «Заявление о возврате».
7В заявлении необходимо указать:
8${fn(ci)}`,`4. Рассмотрение
9Заявление, в котором нет перечисленных сведений, к рассмотрению не принимается — в этом случае мы сообщим, чего не хватает (п. 8.5). Надлежаще оформленное заявление рассматривается в течение 30 (тридцати) календарных дней с даты его регистрации. Решение принимается по результатам проверки: сведений о платеже, данных о выдаче и использовании доступа, переписки со службой поддержки.`,`5. Возврат средств
10При положительном решении денежные средства возвращаются в течение 30 (тридцати) календарных дней на те же реквизиты, с которых производилась оплата. Перевод на реквизиты третьих лиц не осуществляется (п. 8.6).`,`6. Автоматическое продление
11Списания в рамках включённого автопродления производятся на основании вашего согласия и сами по себе не являются основанием для возврата (п. 9.1). Отключить автопродление можно в любой момент в личном кабинете — после этого новые списания прекращаются (п. 9.2).`,"Параллельно с этим мы по-прежнему готовы помочь с подключением: если что-то не работает, опишите, на каком устройстве и в каком приложении возникает проблема, и пришлите снимок экрана — разберёмся."].join(`
12
13`)}function hi(s){const t=`https://${s.registrable}/offer`,m=`https://app.${s.registrable}`,n=gn[s.id];return["Здравствуйте!",`Мы получили ваше обращение о возврате денежных средств. Порядок отказа от услуги и возврата установлен разделом 6 публичной оферты сервиса ${s.name} (${t}), условия которой вы приняли при оплате. Ниже — как он работает, чтобы ваше требование было рассмотрено без задержек.`,`1. Право на отказ
14Вы вправе отказаться от исполнения договора в любое время, уведомив нас через личный кабинет или по электронной почте (п. 6.1 оферты). При отказе оплачиваются фактически понесённые исполнителем расходы, связанные с исполнением обязательств, — это прямо предусмотрено ${oi[s.id]} (п. 6.2 оферты).`,`2. Если услуга не была окаÐ
14·Ð°Ð½Ð°
15Если доступ не был предоставлен или сервис был недоступен по нашей вине, уплаченная сумма возвращается в полном объёме (п. 6.3). Заявляя, что доступ не работает, вы даёте службе поддержки возможность это проверить: сообщаете устройство, приложение и способ подключения, присылаете снимки экрана с ошибкой, выполняете рекомендации по настройке, а при необходимости предоставляете доступ к устройству для удалённой проверки. Если такая возможность не предоставлена, а по данным сервиса доступ был выдан и использовался, услуга считается оказанной.`,`3. Как подать требование
16Требование направляется через раздел «Поддержка» в личном кабинете (${m}) — можно ответом в это обращение — либо на адрес ${n} с темой «Заявление о возврате» (п. 6.5). Чтобы мы могли однозначно найти платёж и вернуть деньги на верные реквизиты, пожалуйста, укажите:
17${fn(di)}`,`4. Сроки и способ возврата
18Возврат, если он положен, производится тем же способом, которым была произведена оплата, в срок не более 30 (тридцати) календарных дней с даты получения требования (п. 6.4). Размер суммы к возврату определяется с учётом фактически понесённых расходов по п. 6.2 и данных об использовании доступа.`,`5. Автоматическое продление
19Чтобы избежать следующих списаний, автопродление можно отключить в личном кабинете в любой момент.`,"Параллельно с этим мы по-прежнему готовы помочь с подключением: если что-то не работает, опишите, на каком устройстве и в каком приложении возникает проблема, и пришлите снимок экрана — разберёмся."].join(`
20
21`)}function ui(s){return s.id==="babka"||s.id==="petya"?hi(s):s.id==="matrix"||s.id==="wisp"||s.id==="brat"||s.id==="inside"?mi(s):null}const jn=Symbol("cancelled");function pi(s,t){return s===jn?{msg:"Отменено — ничего не изменилось",changed:!1}:{msg:typeof s=="string"&&s?s:t,changed:!0}}function Ge({icon:s,children:t}){return e.jsxs("span",{className:"dim mu__lbl",children:[e.jsx(s,{size:15,strokeWidth:1.9})," ",t]})}function xt({icon:s,title:t}){return e.jsx("span",{className:"dim mu__flbl",title:t,children:e.jsx(s,{size:15,strokeWidth:1.9})})}const xi={only_subscription:"действующая подписка только у него",no_subscriptions:"действующей подписки нет ни у одного",autorenew:"у него включено автопродление",usage:"этим аккаунтом пользовались",longer_term:"у него дольше оплаченный срок"},gi={purchase:"покупка",renewal:"продление",trial:"пробный",trial_convert:"после пробного",extra_devices:"устройства",refund:"возврат",upgrade:"смена тарифа"},fi={pending:"ждёт",failed:"не прошёл",canceled:"отменён",refunded:"возврат",chargeback:"чарджбек"};function Es(s,t){const m=Math.round((s||0)/100),n=String(t||"RUB").toUpperCase();
21return m.toLocaleString("ru-RU")+(n==="RUB"?" ₽":n==="USD"?" $":n==="EUR"?" €":" "+n)}function xs(s){if(!s||s<0)return"0";const t=s/1073741824;return t>=1?t.toFixed(t>=10?0:1)+" ГБ":Math.max(1,Math.round(s/1048576))+" МБ"}const ji={active:"активна",suspended:"приостановлена",pending:"ожидает оплаты",cancelled:"отменена",expired:"истекла"};function Ht({api:s,userId:t,email:m,onClose:n,onChanged:x}){var Ct,ot,zt,_t,dt,st,ct;const[i,C]=r.useState(null),[u,k]=r.useState(""),g=r.useRef(!1),I=r.useRef({}),E=(j,J)=>{var Ue;const ie=JSON.stringify(J),ge=I.current[j];if(ge&&ge.sig===ie)return ge.id;const ye=((Ue=crypto.randomUUID)==null?void 0:Ue.call(crypto))||Date.now().toString(36)+"-"+Math.random().toString(36).slice(2,12);return I.current[j]={sig:ie,id:ye},ye},X=j=>{delete I.current[j]},[D,W]=r.useState(""),[o,N]=r.useState(""),[v,_]=r.useState(""),[B,Q]=r.useState(""),[A,$]=r.useState(""),[V,T]=r.useState(null),[y,q]=r.useState(null),[P,oe]=r.useState(!1),[L,se]=r.useState(""),[U,ne]=r.useState(null),[le,ee]=r.useState(""),[be,w]=r.useState(""),[d,b]=r.useState(""),[F,Z]=r.useState("months"),[xe,ue]=r.useState("start"),[Ne,fe]=r.useState(""),[ce,Be]=r.useState("add"),[Ae,Ce]=r.useState(""),[R,ze]=r.useState(""),[ke,Le]=r.useState(!1),[Ee,Fe]=r.useState(""),[l,a]=r.useState(null),[M,f]=r.useState(!1),S=()=>s(`/admin/user/${t}/manage`).then(C).catch(j=>N((j==null?void 0:j.message)||"не открылось")),H=()=>s(`/admin/user/${t}/payments`).then(j=>a({list:(j==null?void 0:j.payments)||[],ltvCents:(j==null?void 0:j.ltvCents)||0,currency:(j==null?void 0:j.currency)||"RUB"})).catch(()=>a(null)),p=()=>{const j=be.trim();if(j)return pe("tk",()=>s(`/admin/user/${t}/ticket`,{subject:le.trim()||void 0,body:j}).then(()=>{ee(""),w("")}),"обращение создано ✓")},K=j=>F==="days"?{days:j}:{months:j},re=()=>{if(u)return;const j=Math.floor(Number(d));if(!j||j<=0)return;const J=Math.floor(Number(Ne)),ie={plan:xe,...K(j)};J>0&&(ie.devices=J);const ge=E("grant-new",ie);pe("grant",()=>s(`/admin/user/${t}/grant-new`,{...ie,requestId:ge}).then(()=>{X("grant-new"),b(""),fe("")}),`выдан ${xe} на ${j} ${F==="days"?"дн.":"мес."}`)},je=()=>{var j,J;return((j=i==null?void 0:i.panel)==null?void 0:j.deviceLimit)||((J=i==null?void 0:i.subscription)==null?void 0:J.deviceLimit)||(i==null?void 0:i.devices.length)||1},c=()=>{if(u)return;const j=Math.floor(Number(d)),J=Math.floor(Number(Ae));(!j||j<=0)&&(!J||J<=0)||pe("grant",async()=>{j>0&&await s(`/admin/user/${t}/grant-time`,{...K(j),requestId:E("grant-time",K(j))}),J>0&&await s(`/admin/user/${t}/set-devices`,{total:je()+J}),X("grant-time"),b(""),Ce("")},"Добавлено")},te=()=>{if(u)return;const j=Math.floor(Number(d)),J=Math.floor(Number(Ae));(!j||j<=0)&&(!J||J<=0)||pe("grant",async()=>{const ie=[];if(J>0){const ge=Math.max(1,je()-J),ye=await s(`/admin/user/${t}/set-devices`,{total:ge}),Ue=ye&&typeof ye.deviceLimit=="number"?ye.deviceLimit:null;Ue!==null&&Ue>ge&&ie.push(Ue===je()?`Убирать нечего: все ${Ue} устройств входят в тариф, докупленных сверху нет. Меньше — только сменой тарифа.`:`Осталось ${Ue} устройств: сняты только докупленные, пакет тарифа кнопкой не уменьшить.`)}if(j>0){const ge=await s(`/admin/user/${t}/trim-time`,K(j));ge&&typeof ge.trimmedDays=="number"&&ge.trimmedDays<ge.requestedDays&&ie.push(`Срезано ${ge.trimmedDays} дн. из ${ge.requestedDays} — активный период не гасим (для полного — «Отменить подписку»).`)}return b(""),Ce(""),ie.join(" ")},"Убрано")},ve=()=>{if(u||!R||R===(me==null?void 0:me.plan))return;const j=ke?`
22
23Остаток оплаченного периода будет пересчитан СРОКОМ: дешевле тариф — дней станет больше, дороже — меньше.`:`
24
25Срок останется прежним.`;confirm(`Сменить тариф ${me==null?void 0:me.plan} → ${R}?${j}
26
27Изменятся лимит устройств и сумма следующего автопродления. Деньги сейчас не списываются и не возвращаются.`)&&pe("plan",async()=>{const J={plan:R,prorate:ke||void 0},ie=await s(`/admin/user/${t}/set-plan`,{...J,requestId:E("set-plan",J)});X("set-plan"),ze("");const ge=[`Тариф: ${(ie==null?void 0:ie.prevPlan)||(me==null?void 0:me.plan)} → ${(ie==null?void 0:ie.plan)||R}`,`устройств ${(ie==null?void 0:ie.deviceLimit)??"—"}`],ye=Number(ie==null?void 0:ie.proratedDays)||0;return ye?ge.push(`срок ${ye>0?"+":"−"}${Math.abs(ye)} дн.`):ke&&ge.push("пересчёт не потребовался"),ie&&ie.panelOk===!1&&ge.push("ПАНЕЛЬ НЕ ОБНОВИЛАСЬ — проверьте лимит устройств вручную"),ie&&ie.renewAmountUnknown&&ge.push("СУММА АВТОПРОДЛЕНИЯ НЕ ПЕРЕСЧИТАНА (нет цены в каталоге) — проверьте вручную"),ge.join(", ")+"."},"Тариф изменён")},Se=()=>{u||confirm("Отменить подписку? Доступ будет отключён. Действие для полного гашения — не для сокращения срока.")&&pe("revoke",()=>s("/admin/revoke",{userId:t}),"Подписка отменена")};r.useEffect(()=>{S(),H(),f(!1)},[t]);const pe=async(j,J,ie)=>{if(!g.current){g.current=!0,k(j),N(""),W("");try{const ge=pi(await J(),ie);W(ge.msg),ge.changed&&(await S(),x==null||x())}catch(ge){N((ge==null?void 0:ge.message)||"ошибка")}finally{g.current=!1,k("")}}};r.useEffect(()=>
27{i&&(Q(String(i.user.tgId??"")),$(String(i.user.tg??"")))},[(Ct=i==null?void 0:i.user)==null?void 0:Ct.tgId,(ot=i==null?void 0:i.user)==null?void 0:ot.tg]);const Re=()=>{const j=B.replace(/[^0-9]/g,"");confirm(j?`Привязать Telegram id ${j}${A?" (@"+A.replace(/^@/,"")+")":""} к этому аккаунту?`:"Отвязать Telegram от аккаунта?")&&(T(null),pe("tg",async()=>{var J;try{return await s(`/admin/user/${t}/telegram`,{telegramId:j,username:A.replace(/^@/,"")||null}),j?"Telegram привязан":"Telegram отвязан"}catch(ie){throw((J=ie==null?void 0:ie.data)==null?void 0:J.error)==="telegram_taken"?(T({otherId:String(ie.data.otherId),otherEmail:ie.data.otherEmail??null}),new Error("telegram_taken")):ie}},"Готово"))},Ie=j=>{if(!(y!=null&&y.otherId))return;const J=y.otherId,ie=j==="in"?J:t,ge=j==="in"?t:J;pe("email",async()=>{var pt;const ye=await s(`/admin/users/merge-plan?from=${encodeURIComponent(ie)}&to=${encodeURIComponent(ge)}`);if((pt=ye==null?void 0:ye.blockers)!=null&&pt.length)throw new Error("Слить нельзя: "+ye.blockers.join("; "));const Ue=Object.entries((ye==null?void 0:ye.counts)||{}).filter(([,nt])=>nt>0).map(([nt,$t])=>`${nt}: ${$t}`).join(", ")||"ничего, кроме самого аккаунта",mt=(ye==null?void 0:ye.fromEmail)||"без почты",Tt=(ye==null?void 0:ye.toEmail)||"без почты",ht=prompt(`Перенести с ${mt} на ${Tt}.
28Переедет: ${Ue}.
29Аккаунт ${mt} станет закрытым.
30
31Для подтверждения напечатайте адрес, с которого переносим (или оставьте пустым, если почты нет):`);if(ht===null)return jn;await s("/admin/users/merge",{from:ie,to:ge,confirmEmail:ht});const ut=(j==="in"&&y.otherEmail||"").trim().toLowerCase();if(ut)try{await s(`/admin/user/${t}/email`,{email:ut})}catch{}q(null),j==="out"&&n()},"Аккаунты слиты")},rt=()=>{if(!V)return;const j=V.otherEmail||"без почты (Telegram)";confirm(`Слить аккаунт ${j} (с этим Telegram) в текущий? Его подписка и платежи переедут сюда.`)&&pe("tg",async()=>{await s("/admin/users/merge",{from:V.otherId,to:t,confirmEmail:V.otherEmail||""});const J=B.replace(/[^0-9]/g,"");if(J)try{await s(`/admin/user/${t}/telegram`,{telegramId:J})}catch{}return T(null),"Аккаунты объединены, Telegram привязан"},"Готово")},et=()=>{const j=v.trim().toLowerCase();j&&confirm(`Сменить адрес
32${(i==null?void 0:i.user.email)||"—"}
33на
34${j}?
35
36Все сессии человека будут завершены.`)&&(q(null),pe("email",async()=>{var J;try{return await s(`/admin/user/${t}/email`,{email:j})}catch(ie){throw((J=ie==null?void 0:ie.data)==null?void 0:J.error)==="email_taken"?(q({otherId:String(ie.data.otherId||""),otherEmail:ie.data.otherEmail??j}),new Error("email_taken")):ie}},"Адрес изменён"),_(""))},at=()=>{const j=L.trim().toLowerCase();j&&(ne(null),pe("alias",async()=>{const J=await s(`/admin/user/${t}/alias-plan?email=${encodeURIComponent(j)}`);if(ne(J),J.blockers.length)throw new Error(J.blockers.join("; "));return J.mode==="free"?"Адрес свободен — можно добавить":"Адрес занят: понадобится объединение"},""))},Ye=j=>{var ge;const J=U;if(!J)return;const ie=J.loserId===J.targetId?J.target:J.owner;if(J.mode==="merge"){const ye=j==="add_days"?`остаток (${J.loserDaysLeft} дн.) добавится к выжившей`:j==="close_now"?"остаток второй подписки сгорит":"вторая подписка закроется";if(!confirm(`Объединить аккаунты?
37
38Остаётся: ${((ge=J.winnerId===J.targetId?J.target:J.owner)==null?void 0:ge.email)||"—"}
39Закрывается: ${(ie==null?void 0:ie.email)||"—"}
40
41${ye}.
42
43Отменить это будет нельзя.`))return}pe("alias",async()=>{const ye=await s(`/admin/user/${t}/alias`,{email:J.email,choice:j??null,confirmEmail:(ie==null?void 0:ie.email)||J.loserId||""});return ne(null),se(""),ye!=null&&ye.merged?`Аккаунты объединены${ye.carriedDays?`, перенесено ${ye.carriedDays} дн.`:""}`:"Адрес добавлен"},"Готово")},lt=j=>{confirm(`Убрать адрес ${j}?
44
45Входить по нему станет нельзя. Аккаунт и подписка не тронуты.`)&&pe("alias",async()=>(await s(`/admin/user/${t}/alias/remove`,{email:j}),"Адрес убран"),"Готово")},Gt=()=>{confirm("Убрать пароль? Человек будет входить по коду на почту — это наш основной способ входа.")&&pe("pw",()=>s(`/admin/user/${t}/password/reset`,{}),"Пароль убран, вход по коду")},Je=()=>{confirm(`Снять двухфакторную защиту?
46
47Сделайте это, только если уверены, что пишет владелец (письмо с адреса аккаунта). Вход снова станет по почте/паролю без кода из приложения.`)&&pe("2fa",()=>s(`/admin/user/${t}/2fa/reset`,{}),"Двухфакторная защита снята")},St=async()=>
47{if(confirm("Сгенерировать пароль? Человек сможет входить почтой и паролем. Пароль сохраняется зашифрованным — позже его можно показать снова кнопкой «Показать пароль».")){k("setpw"),N(""),W(""),Fe("");try{const j=await s(`/admin/user/${t}/password/set`,{});j!=null&&j.password&&(Fe(j.password),W(`Новый пароль: ${j.password} — скопируйте и передайте`)),await S(),x==null||x()}catch(j){N((j==null?void 0:j.message)||"ошибка")}finally{k("")}}},tt=async()=>{var j;k("revpw"),N(""),W(""),Fe("");try{const J=await s(`/admin/user/${t}/password/reveal`,{});J!=null&&J.password&&(Fe(J.password),W(`Пароль: ${J.password}`))}catch(J){N((((j=J==null?void 0:J.data)==null?void 0:j.error)||(J==null?void 0:J.message))==="no_stored"?"Сохранённого пароля нет — клиент задал свой":(J==null?void 0:J.message)||"ошибка"),await S()}finally{k("")}},Ve=async()=>{if(confirm("Отвязать все устройства? Лимит тарифа не меняется — освобождаются занятые места.")){k("dev"),N(""),W("");try{const j=await s(`/admin/user/${t}/devices/reset`,{});W(j!=null&&j.total?`Снято устройств: ${j.removed??0} из ${j.total}`:"Устройства отвязаны"),await S(),x==null||x()}catch(j){N((j==null?void 0:j.message)||"ошибка")}finally{k("")}}},Kt=()=>{confirm(`Выдать новую ссылку-подписку?
48
49Старая перестанет работать немедленно. Все устройства клиента отключатся, пока он не вставит новую ссылку в приложение — отправьте её ему сразу.`)&&pe("rot",async()=>{var J;const j=await s(`/admin/user/${t}/subscription/rotate`,{});if(j!=null&&j.url)try{await((J=navigator.clipboard)==null?void 0:J.writeText(String(j.url)))}catch{}return await S(),"Новая ссылка выдана и скопирована"},"Готово")},wt=j=>{j&&confirm("Отвязать это устройство? Если оно сейчас подключено, слот освободится, но при следующем соединении устройство вернётся.")&&pe("dev1:"+j,()=>s(`/admin/user/${t}/device/remove`,{hwid:j}),"Устройство отвязано")},Nt=async()=>{if(i!=null&&i.subUrl)try{await navigator.clipboard.writeText(i.subUrl),oe(!0),setTimeout(()=>oe(!1),1500)}catch{}},Xt=()=>pe("imp",async()=>{const j=await s("/admin/impersonate",{userId:t});if(!(j!=null&&j.url))throw new Error("нет адреса почты — вход под пользователем недоступен");window.open(j.url,"_blank","noopener")},"Открыл его кабинет в новой вкладке"),me=i==null?void 0:i.subscription,kt=o==="email_taken"?"Этот адрес уже занят другим аккаунтом":o==="bad_email"?"Адрес не похож на почтовый":o==="no_subscription"?"У человека нет подписки — отвязывать нечего":o==="telegram_taken"?"Этот Telegram уже привязан к другому аккаунту — это слияние, не перепривязка (используйте мерж)":o;return e.jsx("div",{className:"mu__back",onClick:j=>{j.target===j.currentTarget&&n()},children:e.jsxs("div",{className:"mu",children:[e.jsxs("div",{className:"mu__head",children:[e.jsx("b",{children:"Управление пользователем"}),e.jsx("span",{className:"dim",style:{overflowWrap:"anywhere"},children:(i==null?void 0:i.user.email)||m||t.slice(0,8)}),e.jsx("button",{className:"btn btn--ghost btn--xs",style:{marginLeft:"auto"},onClick:n,children:"Закрыть"})]}),!i&&!o&&e.jsx("p",{className:"dim",children:"Загружаю…"}),kt&&e.jsx("p",{className:"err",style:{margin:"4px 0"},children:kt}),D&&e.jsx("p",{style:{color:"var(--green)",margin:"4px 0"},children:D}),i&&e.jsxs(e.Fragment,{children:[i.user.blockedAt?e.jsxs("div",{style:{margin:"0 0 12px",padding:"10px 12px",borderRadius:10,background:"rgba(255,90,80,0.12)",border:"1px solid rgba(255,90,80,0.45)",display:"flex",gap:12,alignItems:"center",flexWrap:"wrap"},children:[e.jsxs("div",{style:{flex:"1 1 260px",minWidth:0},children:[e.jsx("b",{children:"Доступ заблокирован"}),e.jsxs("div",{className:"dim",style:{fontSize:12,marginTop:2}
49,children:[new Date(i.user.blockedAt).toLocaleString("ru-RU"),i.user.blockedReason?` · ${i.user.blockedReason}`:""]})]}),e.jsx("button",{className:"btn btn--sm",disabled:!!u,onClick:()=>pe("block",()=>s(`/admin/user/${t}/block`,{undo:!0}),"Блокировка снята. Доступ к сервису вернёт продление — автоматически он не включается"),children:"Разблокировать"})]}):null,e.jsxs("div",{className:"mu__contacts",children:[e.jsxs("span",{className:"mu__contact",children:[e.jsx(_s,{size:14,strokeWidth:1.9}),i.user.email||e.jsx("span",{className:"dim",children:"почты нет"})]}),i.user.tg?e.jsxs("span",{className:"mu__contact",children:[e.jsx(Ft,{size:14,strokeWidth:1.9}),"@",i.user.tg]}):i.user.tgId?e.jsxs("span",{className:"mu__contact",title:"Telegram привязан, без @username — бот пишет по chat_id",children:[e.jsx(Ft,{size:14,strokeWidth:1.9}),"id ",i.user.tgId]}):null]}),e.jsxs("div",{className:"mu__facts",children:[e.jsxs("div",{children:[e.jsx(xt,{icon:nn,title:"Подписка"}),e.jsx("b",{children:(me==null?void 0:me.plan)||"нет"}),me!=null&&me.status?e.jsx("div",{className:"dim",style:{fontSize:11,marginTop:2},children:ji[me.status]||me.status}):null]}),e.jsxs("div",{children:[e.jsx(xt,{icon:kn,title:"Действует до"}),e.jsx("b",{children:me!=null&&me.expiresAt?new Date(me.expiresAt).toLocaleDateString("ru-RU"):"—"}),me!=null&&me.paidUntil&&me.expiresAt&&me.paidUntil>me.expiresAt?e.jsxs("div",{className:"dim",style:{fontSize:11,marginTop:2},title:"с учётом очереди будущих периодов",children:["до ",new Date(me.paidUntil).toLocaleDateString("ru-RU")]}):null]}),e.jsxs("div",{children:[e.jsx(xt,{icon:rs,title:"Устройств"}),e.jsxs("b",{children:[i.devices.length,(zt=i.panel)!=null&&zt.deviceLimit||me!=null&&me.deviceLimit?` из ${((_t=i.panel)==null?void 0:_t.deviceLimit)||(me==null?void 0:me.deviceLimit)}`:""]})]}),i.panel&&e.jsxs("div",{children:[e.jsx(xt,{icon:Cn,title:"Скачано"}),e.jsx("b",{title:i.panel.onlineAt?"Последний онлайн: "+new Date(i.panel.onlineAt).toLocaleString("ru-RU"):"Ни разу не подключался",children:xs(i.panel.lifetimeBytes||i.panel.usedBytes)})]}),e.jsxs("div",{children:[e.jsx(xt,{icon:Wt,title:"Пароль"}),e.jsxs("b",{children:[i.user.genPass?"выдан нами":i.user.hasPassword?"задан клиентом":"по коду",i.user.twoFactor?" + 2FA":""]})]})]}),e.jsxs("div",{className:"mu__row",children:[e.jsx(Ge,{icon:zn,children:"Ссылка подписки"}),i.subUrl?e.jsxs(e.Fragment,{children:[e.jsx("code",{className:"mu__code",children:i.subUrl}),e.jsx("button",{className:"btn btn--ghost btn--xs mu__iconbtn",onClick:Kt,disabled:u==="rot",title:"Выдать новую ссылку, старая перестанет работать","aria-label":"Перевыпустить ссылку",children:u==="rot"?e.jsx("span",{className:"spinner"}):e.jsx(Mt,{size:15,strokeWidth:1.9})}),e.jsxs("button",{className:"btn btn--ghost btn--xs mu__act",onClick:Nt,title:"Скопировать ссылку",children:[P?e.jsx(_n,{size:15,strokeWidth:2}):e.jsx(Ts,{size:15,strokeWidth:1.9}),P?"готово":"копировать"]})]}):e.jsx("span",{className:"dim",style:{whiteSpace:"nowrap"},children:"нет — подписка не выдана"})]}),e.jsxs("div",{className:"mu__row",children:[e.jsx(Ge,{icon:$s,children:"Сменить адрес"}),e.jsx("input",{className:"mu__input",type:"email",placeholder:"новый@адрес.ру",value:v,onChange:j=>_(j.target.value)}),e.jsx("button",{className:"btn btn--ghost btn--xs mu__act",disabled:!v.trim()||u==="email",onClick:et,children:u==="email"?"меняю…":"сменить"})]}),e.jsxs("div",{className:"mu__row",children:[e.jsx(Ge,{icon:_s,children:"Ещё адрес"}),e.jsx("input",{className:"mu__input",type:"email",placeholder:"второй@адрес.ру",value:L,onChange:j=>{se(j.target.value),ne(null)}}),!U||U.email!==L.trim().toLowerCase()||U.blockers.length?e.jsx("button",{className:"btn btn--ghost btn--xs mu__act",disabled:!L.trim()||u==="alias",onClick:at,children:u==="al
49ias"?"…":"проверить"}):U.mode==="free"&&!U.blockers.length?e.jsx("button",{className:"btn btn--ghost btn--xs mu__act",disabled:u==="alias",onClick:()=>Ye(),children:u==="alias"?"…":"добавить"}):null]}),!!((dt=i.emails)!=null&&dt.length)&&e.jsxs("div",{className:"mu__row",children:[e.jsx(Ge,{icon:$s,children:"Входит также"}),e.jsx("span",{style:{flex:"1 1 auto",display:"flex",flexWrap:"wrap",gap:6},children:i.emails.map(j=>e.jsxs("span",{className:"dim",style:{display:"inline-flex",alignItems:"center",gap:4,background:"var(--surface-2)",borderRadius:8,padding:"2px 4px 2px 8px",fontSize:12.5},children:[j.email,e.jsx("button",{className:"btn btn--ghost btn--xs",style:{padding:"0 4px"},disabled:u==="alias",title:"Убрать адрес — входить по нему станет нельзя",onClick:()=>lt(j.email),children:e.jsx(Rs,{size:13,strokeWidth:2})})]},j.email))})]}),(U==null?void 0:U.mode)==="merge"&&!U.blockers.length&&U.email===L.trim().toLowerCase()&&e.jsxs("div",{className:"mu__row",style:{flexWrap:"wrap",background:"color-mix(in srgb, var(--danger, #E5484D) 8%, var(--surface-2))",border:"1px solid var(--danger, #E5484D)",borderRadius:10,padding:"8px 10px"},children:[e.jsxs("span",{className:"dim",style:{flex:"1 1 100%",fontSize:12.5},children:["Адрес ",U.email," уже за другим аккаунтом — понадобится объединить."," ","Останется ",e.jsx("b",{children:((st=U.winnerId===U.targetId?U.target:U.owner)==null?void 0:st.email)||"—"}),U.reason?` — ${xi[U.reason]}`:"","."," ","Закроется ",e.jsx("b",{children:((ct=U.loserId===U.targetId?U.target:U.owner)==null?void 0:ct.email)||"—"}),";"," ","входить обоими адресами человек сможет по-прежнему."]}),U.needsChoice?e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"dim",style:{flex:"1 1 100%",fontSize:12.5},children:["Действуют обе подписки. У закрываемой остаётся ",U.loserDaysLeft," дн. — что с ними?"]}),e.jsx("button",{className:"btn btn--ghost btn--xs",disabled:u==="alias",onClick:()=>Ye("add_days"),title:"Остаток срока закрываемой подписки прибавится к выжившей",children:u==="alias"?"…":`Добавить ${U.loserDaysLeft} дн.`}),e.jsx("button",{className:"btn btn--danger btn--xs",disabled:u==="alias",onClick:()=>Ye("close_now"),title:"Закрыть вторую подписку сразу — её остаток сгорит",children:u==="alias"?"…":"Закрыть сразу"})]}):e.jsx("button",{className:"btn btn--danger btn--xs",disabled:u==="alias",onClick:()=>Ye(),children:u==="alias"?"…":"Объединить"})]}),e.jsxs("div",{className:"mu__row",children:[e.jsx(Ge,{icon:Ft,children:"Telegram"}),e.jsx("input",{className:"mu__input mu__input--sm",inputMode:"numeric",placeholder:"id (числом)",value:B,onChange:j=>Q(j.target.value.replace(/[^0-9]/g,""))}),e.jsx("input",{className:"mu__input mu__input--sm",placeholder:"@username",value:A,onChange:j=>$(j.target.value)}),e.jsx("button",{className:"btn btn--ghost btn--xs mu__act",disabled:u==="tg",onClick:Re,children:u==="tg"?"…":i.user.tgId?"изменить":"привязать"})]}),(y==null?void 0:y.otherId)&&e.jsxs("div",{className:"mu__row",style:{background:"color-mix(in srgb, var(--danger, #E5484D) 8%, var(--surface-2))",border:"1px solid var(--danger, #E5484D)",borderRadius:10,padding:"8px 10px"},children:[e.jsxs("span",{className:"dim",style:{flex:"1 1 auto",fontSize:12.5},children:["Адрес ",y.otherEmail||""," уже на другом аккаунте — это слияние, а не смена адреса. Выберите, какой аккаунт остаётся."]}),e.jsx("button",{className:"btn btn--danger btn--xs",disabled:u==="email",onClick:()=>Ie("in"),title:"Тот аккаунт вливается в этот: его подписка и платежи переедут сюда, адрес освободится и встанет на текущий",children:u==="email"?"…":"Тот → сюда"}),e.jsx("button",{className:"btn btn--danger btn--xs",disabled:u==="email",onClick:()=>Ie("out"),title:"Этот аккаунт вливается в тот: текущая карточка станет надгробием, выживет владелец адреса",children:u==="email"?"…":"Этот → туда"})]}),V&&e.jsxs("div",{className:"mu__row",style:{background:"color-mix(in srgb, var(--danger, #E5484D) 8%, var(--surface-2))",border:"1px solid var(--danger, #E5484D)",borderRadius:10,padding:"8px 10px"},children:[e.jsxs("span",{className:"dim",style:{flex:"1 1 auto",fontSize:12.5},children:["Этот Telegram уже привязан к аккаунту ",V.otherEmail||"без почты"," — это слияние."]}),e.jsx("button",{className:"btn btn--danger btn--xs",disabled:u==="tg",onClick:rt,children:u==="tg"?"…":"Слить сюда"})]}),e.jsxs("div",{className:"mu__row mu__row--col",children:[e.jsx(Ge,{icon:Tn,children:"Написать пользователю"}
49),i.tickets&&(i.tickets.total>0?e.jsxs("div",{className:"dim",style:{fontSize:12.5,display:"flex",gap:10,flexWrap:"wrap",alignItems:"center"},children:[e.jsxs("span",{children:["Обращений: ",e.jsx("b",{style:{color:"var(--text)"},children:i.tickets.total})]}),i.tickets.open>0&&e.jsxs("span",{style:{color:"var(--amber, #f5a623)",fontWeight:600},children:["● в работе: ",i.tickets.open]}),i.tickets.closed>0&&e.jsxs("span",{children:["закрытых: ",i.tickets.closed]}),i.tickets.lastId&&e.jsx("button",{className:"linkbtn",style:{fontWeight:600},onClick:()=>{const j=i.tickets.lastId;n(),location.hash=`tickets/${j}`},children:"открыть →"})]}):e.jsx("div",{className:"dim",style:{fontSize:12.5},children:"Обращений ещё не было."})),e.jsx("input",{className:"mu__input",placeholder:"тема (необязательно)",value:le,onChange:j=>ee(j.target.value)}),e.jsx("textarea",{className:"mu__input mu__area",rows:3,placeholder:"текст обращения",value:be,onChange:j=>w(j.target.value)}),e.jsx("button",{className:"btn btn--ghost btn--xs",disabled:!be.trim()||u==="tk",onClick:p,children:u==="tk"?"создаю…":"Создать обращение"})]}),me!=null&&me.plan?e.jsxs("div",{className:"mu__grant",children:[e.jsxs("div",{className:"mu__mode",children:[e.jsx("button",{type:"button",className:"mu__mode-b"+(ce==="add"?" is-on":""),onClick:()=>Be("add"),children:"+ Добавить"}),e.jsx("button",{type:"button",className:"mu__mode-b mu__mode-b--sub"+(ce==="sub"?" is-on":""),onClick:()=>Be("sub"),children:"− Убрать"})]}),e.jsxs("div",{className:"mu__grant-ctl",children:[e.jsx("span",{className:"mu__ico",title:(ce==="add"?"Продлить":"Сократить")+" · "+me.plan,children:e.jsx($n,{size:17,strokeWidth:1.9})}),e.jsx("input",{className:"mu__num",type:"number",min:"1",inputMode:"numeric",placeholder:"срок",value:d,onChange:j=>b(j.target.value),onKeyDown:j=>{j.key==="Enter"&&(ce==="add"?c:te)()}}),e.jsxs("select",{value:F,onChange:j=>Z(j.target.value),children:[e.jsx("option",{value:"days",children:"дней"}),e.jsx("option",{value:"months",children:"месяцев"})]}),e.jsx("span",{className:"mu__grant-sep"}),e.jsx("span",{className:"mu__ico",title:"Устройства",children:e.jsx(rs,{size:17,strokeWidth:1.9})}),e.jsx("input",{className:"mu__num",type:"number",min:"1",inputMode:"numeric",placeholder:(ce==="add"?"+":"−")+" устр.",value:Ae,onChange:j=>Ce(j.target.value),onKeyDown:j=>{j.key==="Enter"&&(ce==="add"?c:te)()}}),e.jsx("button",{className:"btn mu__grant-apply"+(ce==="sub"?" mu__grant-apply--sub":""),disabled:!!u||!d.trim()&&!Ae.trim(),onClick:ce==="add"?c:te,children:u==="grant"?ce==="add"?"добавляю…":"убираю…":ce==="add"?"Добавить":"Убрать"})]}),e.jsxs("p",{className:"mu__grant-hint dim",children:[`Устройств сейчас: ${je()}`,me.extraDevices?` (докуплено ${me.extraDevices})`:" — все по тарифу, докупленных нет",ce==="add"?". Срок прибавится к текущему.":". Срок режется с конца, активный период не гасим."]}),e.jsxs("div",{className:"mu__grant-ctl",style:{marginTop:8},children:[e.jsx("span",{className:"mu__ico",title:"Тариф",children:e.jsx(Ws,{size:17,strokeWidth:1.9})}),e.jsxs("select",{value:R||me.plan||"start",onChange:j=>ze(j.target.value),children:[e.jsx("option",{value:"start",children:"start"}),e.jsx("option",{value:"pro",children:"pro"}),e.jsx("option",{value:"plus",children:"plus"})]}),e.jsx("button",{className:"btn btn--ghost mu__grant-apply",disabled:!!u||!R||R===me.plan,onClick:ve,children:u==="plan"?"меняю…":"Сменить тариф"})]}),e.jsxs("label",{className:"mu__grant-hint dim",style:{display:"flex",alignItems:"flex-start",gap:7,cursor:"pointer"},children:[e.jsx("input",{type:"checkbox",checked:ke,onChange:j=>Le(j.target.checked),style:{marginTop:2}}),e.jsxs("span",{children:["Пересчитать остаток периода ",e.jsx("b",{children:"сроком"}),": неизрасходованная стоимость делится на дневную цену нового тарифа. Дешевле тариф — дней станет больше, дороже — меньше. Деньги не возвращаются и не списываются в любом случае."]})]}),e.jsx("p",{className:"mu__grant-hint dim",children:"Без галочки срок остаётся прежним. Всегда пересчитываются устройства и сумма следующего автосписания; докупленные слоты переносятся, если влезают в новый тариф."}),ce==="sub"&&e.jsx("button",{className:"btn btn--danger btn--xs mu__cancel",disabled:!!u,onClick:Se,children:u==="revoke"?"отменяю…":"Отменить подписку целиком"})]}):e.jsx("div",{className:"mu__grant",children:e.jsxs("div",{className:"mu__grant-row",children:[e.jsx(Ge,{icon:Ws,children:"Выдать подписку"}),e.jsxs("div",{className:"mu__grant-ctl",children:[e.jsxs("select",{value:xe,onChange:j=>ue(j.target.value),children:[e.jsx("option",{value:"start",children:"start"}),e.jsx("option",{value:"pro",children:"pro"}),e.jsx("option",{value:"plus",children:"plus"})]}),e.jsx("input",{className:"mu__num",type:"number",min:"1",inputMode:"numeric",placeholder:"срок",value:d,onChange:j=>b(j.target.value)}),e.jsxs("select",{value:F,onChange:j=>Z(j.target.value),children:[e.jsx("option",{value:"days",children:"дней"}),e.jsx("option",{value:"months",children:"месяцев"})]}),e.jsx("input",{className:"mu__num",type:"number",min:"1",inputMode:"numeric",placeholder:"устройств",value:Ne,onChange:j=>fe(j.target.value)}),e.jsx("button",{className:"btn btn--ghost btn--xs mu__grant-go",disabled:!!u||!d.trim(),onClick:re,children:u==="grant"?"выдаю…":"Выдать"})]})]})}),Ee&&e.jsxs("div",{className:"mu__pw",children:[e.jsx("span",{className:"dim",children:"Новый пароль"}),e.jsx("code",{className:"mu__code",children:Ee}),e.jsxs("button",{className:"btn btn--ghost btn--xs",onClick:()=>{var j;(j=navigator.clipboard)==null||j.writeText(Ee).catch(()=>{})},children:[e.jsx(Ts,{size:14,strokeWidth:1.9})," копировать"]})]}),e.jsxs("div",{className:"mu__actions",children:[!!i.user.genPass&&e.jsxs("button",{className:"btn btn--ghost btn--xs",disabled:!!u,onClick:tt,title:"Пароль, выданный нами (показ пишется в журнал)",children:[e.jsx(Wt,{size:14,strokeWidth:1.9})," ",u==="revpw"?"показываю…":"Показать пароль"]}),i.user.hasPassword?e.jsxs("button",{className:"btn btn--ghost btn--xs",disabled:!!u,onClick:Gt,children:[e.jsx(Wt,{size:14,strokeWidth:1.9})," ",u==="pw"?"убираю…":"Убрать пароль"]}):e.jsxs("button",{className:"btn btn--ghost btn--xs",disabled:!!u,onClick:St,children:[e.jsx(Wt,{size:14,strokeWidth:1.9})," ",u==="setpw"?"генерирую…":"Сгенерировать пароль"]}),!!i.user.twoFactor&&e.jsxs("button",{className:"btn btn--danger btn--xs",disabled:!!u,onClick:Je,title:"Клиент потерял приложение-аутентификатор и резервные коды (пишется в журнал)",children:[e.jsx(Rn,{size:14,strokeWidth:1.9})," ",u==="2fa"?"снимаю…":"Снять 2FA"]}),e.jsxs("button",{className:"btn btn--ghost btn--xs",disabled:!!u||!i.devices.length,onClick:Ve,title:"Отвязать все устройства",children:[e.jsx(Wn,{size:14,strokeWidth:1.9})," ",u==="dev"?"отвязываю…":`Отвязать${i.devices.length?` (${i.devices.length})`:""}`]}),e.jsxs("button",{className:"btn btn--ghost btn--xs",disabled:!!u,onClick:Xt,title:"Войти под пользователем",children:[e.jsx(rn,{size:14,strokeWidth:1.9})," ",u==="imp"?"открываю…":"Войти"]})]}),!!i.devices.length&&e.jsx("div",{className:"mu__devs",children:i.devices.map((j,J)=>e.jsxs("div",{className:"mu__dev",children:[e.jsx(ti,{os:(j.platform||"").toLowerCase(),size:15}),e.jsx("b",{children:si(j.deviceModel)||j.platform||"устройство"}),e.jsxs("span",{className:"dim",children:[j.platform||"",j.updatedAt?` · ${new Date(j.updatedAt).toLocaleDateString("ru-RU")}`:""]}),j.hwid&&e.jsx("button",{className:"btn btn--ghost btn--xs mu__dev-x",title:"Отвязать это устройство",disabled:!!u,onClick:()=>wt(j.hwid),children:u==="dev1:"+j.hwid?"…":e.jsx(Rs,{size:14,strokeWidth:2})})]},j.hwid||J))}),!!l&&!!l.list.length&&e.jsxs(e.Fragment,{children:[e.jsxs(Ge,{icon:an,children:["Платежи · ",l.list.length," · всего ",Es(l.ltvCents,l.currency)]}),e.jsx("div",{className:"mu__pays",children:(M?l.list:l.list.slice(0,6)).map(j=>e.jsxs("div",{className:"mu__pay"+(j.status==="succeeded"?"":" mu__pay--off"),children:[e.jsx("span",{className:"dim mu__pay-d",children:new Date(j.createdAt).toLocaleDateString("ru-RU")}),e.jsx("b",{className:"mu__pay-s",children:Es(j.amountCents||0,j.currency||l.currency)}),e.jsx("span",{className:"mu__pay-k",children:gi[String(j.kind)]||j.kind||"—"}),e.jsx("span",{className:"dim mu__pay-p",children:j.provider||""}),j.status!=="succeeded"&&e.jsx("span",{className:"mu__pay-st",children:fi[String(j.status)]||j.status})]},j.id))}),l.list.length>6&&e.jsx("button",{className:"btn btn--ghost btn--xs",onClick:()=>f(!M),children:M?"свернуть":`показать все (${l.list.length})`})]})]})]})})}const Fs=5,Us=5*1024*1024;function yi(s,t){const m=[];for(const i of Array.from(s||[]))if(i.kind==="file"&&i.type.startsWith("image/")){const C=i.getAsFile();C&&m.push(C)}m.length||m.push(...Array.from(t||[]).filter(i=>i.type.startsWith("image/")));const n=new Date().toISOString().slice(0,19).replace(/[:T]/g,"-"),x=Math.random().toString(36).slice(2,6);return m.map((i,C)=>i.name&&i.name!=="image.png"?i:new File([i],`screenshot-${n}-${x}${C?"-"+(C+1):""}.${(i.type.split("/")[1]||"png").replace("jpeg","jpg")}`,{type:i.type}))}function Jt(s,t,m,n){if(!s.length)return!1;const x=s.filter(g=>g.size>Us),i=s.filter(g=>g.size<=Us),C=Math.max(0,Fs-t.length),u=i.length-Math.min(C,i.length),k=[u?`больше ${Fs} картинок в одно письмо не влезет — не взято ${u}`:"",x.length?`не взято ${x.length}
49: тяжелее 5 МБ`:""].filter(Boolean);return m([...t,...i.slice(0,C)]),k.length&&n(k.join("; ")),!0}const Qt=s=>s.replace(/</g,"&lt;").replace(/>/g,"&gt;"),bi=s=>s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;"),vi=s=>{const t=s.trim();return/^(https?:\/\/|tg:\/\/|mailto:)/i.test(t)&&!/[\s"'<>`]/.test(t)?t:null},Si={b:null,strong:null,i:null,em:null,u:null,ins:null,s:null,strike:null,del:null,code:(s,t)=>s==="class"&&/^language-[\w+-]{1,32}$/.test(t)?t:null,pre:null,blockquote:s=>s==="expandable"?"":null,"tg-spoiler":null,span:(s,t)=>s==="class"&&t==="tg-spoiler"?t:null,a:(s,t)=>s==="href"?vi(t):null,"tg-emoji":(s,t)=>s==="emoji-id"&&/^\d{1,32}$/.test(t)?t:null},wi=new Set(["br","hr","img"]),Ni={a:"href",img:"src"},ki=/<(\/?)([a-zA-Z][\w-]*)((?:\s+[^\s=>\/]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?)*)\s*(\/?)>/g,Ci=/([^\s=>\/]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;function zi(s,t){const m=String(s??""),n=[];let x=0;for(const i of m.matchAll(ki)){n.push(Qt(m.slice(x,i.index))),x=(i.index??0)+i[0].length;const C=i[1]==="/",u=i[2].toLowerCase();if(!(u in t)){n.push(Qt(i[0]));continue}if(C){wi.has(u)||n.push(`</${u}>`);continue}const k=t[u],g=[];if(!k&&i[3].trim(),k)for(const E of i[3].matchAll(Ci)){const X=E[1].toLowerCase(),D=E[2]??E[3]??E[4]??"",W=k(X,D);W!==null&&g.push(W===""&&X==="expandable"?" expandable":` ${X}="${bi(W)}"`)}const I=Ni[u];I&&!g.some(E=>E.startsWith(` ${I}=`))||n.push(`<${u}${g.join("")}>`)}return n.push(Qt(m.slice(x))),n.join("")}const _i=s=>zi(s,Si),Ps=ui({id:Xe.id,name:Xe.wordmark.join(" ").trim(),registrable:Xe.registrable}),yn=s=>"$"+((s||0)/100).toFixed(2),De=(s,t)=>{const m=s||0;return(t||"USD").toUpperCase()==="RUB"?(m/100).toLocaleString("ru-RU",{maximumFractionDigits:0})+" ₽":(t||"USD").toUpperCase()==="USD"?yn(m):(m/100).toFixed(2)+" "+(t||"").toUpperCase()},as=s=>["nx",s.code,s.email,s.contact,s.n!=null?String(s.n):null].filter(Boolean).join(" · "),Me=(s,t=!1)=>new Date(s).toLocaleString("ru-RU",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit",...t?{timeZone:"UTC"}:{}});function Ke({label:s,value:t,tone:m}){return e.jsxs("div",{className:"card pad",style:{padding:"16px 18px",minWidth:0},children:[e.jsx("div",{style:{fontFamily:"Space Grotesk",fontSize:30,lineHeight:1.15,color:m||"var(--text)",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:t}),e.jsx("div",{className:"dim",style:{fontSize:13,lineHeight:1.35,marginTop:6},children:s})]})}const We={fontFamily:"inherit",fontSize:14,color:"var(--text)",background:"var(--surface-2)",border:"1px solid var(--border-strong)",borderRadius:10,padding:"9px 12px",outline:"none"},Ti={initial:"первая оплата",renewal:"продление",refund:"возврат",trial:"пробный период",extra_devices:"устройства",upgrade:"апгрейд"},$i={succeeded:"оплачен",pending:"в обработке",failed:"не прошёл",refunded:"возвращён",canceled:"отменён",created:"создан",expired:"истёк"},Ri=new Set(["10602","20602"]),Wi=s=>{try{return Ri.has(String(JSON.parse(s||"{}").code||""))}catch{return!1}},ls=s=>Ti[String(s||"")]||s||"—",Ai={active:"активна",expired:"истекла",cancelled:"отменена",canceled:"отменена",refunded:"возврат",pending:"ожидает",suspended:"заблокирована"},Ii={open:"открыт",pending:"в работе",closed:"закрыт",answered:"отвечен"},Di={active:"активен",pending:"на модерации",suspended:"заблокирован"},Zt=s=>Ai[String(s||"")]||s||"—",Bi=s=>Ii[String(s||"")]||s||"—",Ms=s=>s==="reg"?"регистрация":s==="dep"?"первая оплата":s==="sale"?"ревшара":s,es=s=>Di[String(s||"")]||s||"—",Li={held:"холд",approved:"к выплате",paid:"выплачено",void:"аннулирована"},Ei={sale:"первая оплата",rebill:"продление",upsell:"устройства",legacy:"перенос долга"},Os=s=>Ei[String(s||"")]||s||"—",qs=s=>Li[String(s||"")]||s||"—",Ze=s=>$i[String(s||"")]||s||"—",Fi=[{re:/nonce already exist/i,text:"Коллизия nonce у процессинга — техническая ошибка на нашей стороне, деньги клиента ни при чём",ours:!0},{re:/no_token/i,text:"Нет сохранённого платёжного токена — списывать нечем",ours:!0},{re:/timeout|timed out/i,text:"Таймаут процессинга",ours:!0},{re:/insufficient|not enough|недостаточно/i,text:"Недостаточно средств у клиента",ours:!1},{re:/rejected|declined|отклон/i,text:"Процессинг отклонил списание (банк или клиент)",ours:!1},{re:/^KG-[A-Z]+-\d+$/i,text:"Процессинг вернул свой код ошибки — расшифровка на стороне FreeKassa",ours:!1},{re:/\b(20201|10204|10206)\b/,text:"Банк ограничил операции по счёту клиента — повторы бессмысленны, нужен другой способ оплаты",ours:!1},{re:/\b10110\b/,text:"Клиент отменил автоплатёж в своём банке — привязка мертва, нужна новая оплата",ours:!1},{re:/\b(10105|20105|10112)\b/,text:"Недостаточно средств у клиента — повторим по расписанию",ours:!1},{re:/\b10113\b/,text:"Недостаточно средств, эмитент запретил повторы",ours:!1},{re:/\b(10602|20602)\b/,text:"Счёт истёк неоплаченным (СБП живёт час) — человек не отсканировал QR",ours:!1},{re:/\b(10201|10203|10812|10106|3021|3029)\b/,text:"Карта негодна (просрочена, скомпрометирована или заблокирована) — нужна другая",ours:!1},{re:/\b(10402|10403|10404|10405|10807|20402)\b/,text:"Отказ риск-системы/антифрода — повторять нельзя",ours:!1},{re:/\b(3184|3301)\b/,text:"Серия автоплатежей отменена или истекла на стороне процессинга",ours:!1},{re:/\b(3027|10102|10103|10502|20502|1070\d)\b/,text:"Ошибка в НАШЕМ запросе к процессингу — клиент ни при чём, чинить нам",ours:!0},{re:/\b(10202|10505|20202|20601|10601)\b/,text:"Банк или платёжная система временно недоступны — повторим",ours:!1},{re:/\b(20000|10000|10100|20100)\b/,text:"Общий отказ без расшифровки: процессинг присылает такой код по умолчанию, точную причину знает только их поддержка",ours:!1}];function os(s){let t="",m="";try{const i=JSON.parse(s||"{}");t=String(i.reason||""),m=String(i.bucket||"")}catch{t=String(s||"")}const n=Fi.find(i=>i.re.test(t)),x=m==="ours"||(n?n.ours:!1);return{text:n?n.text:t||"причина не сохранена",ours:x,raw:t}}function Bt(s){const t=String(s||"").split(`
50`).filter(Boolean).pop();if(!t)return"";try{return JSON.parse(t.slice(t.indexOf("~")+1)||"{}").reason||""}catch{return""}}function ts(s){return s?String(s).split(`
51`).filter(Boolean).map(t=>{const m=t.indexOf("~");return{at:Number(t.slice(0,m))||0,reason:os(t.slice(m+1))}}).sort((t,m)=>m.at-t.at):[]}function vt({onClick:s,label:t}){const[m,n]=r.useState(!1),x=async()=>{if(!m){n(!0);try{await s()}finally{n(!1)}}};return e.jsx("button",{className:"btn btn--ghost btn--xs btn--pill",onClick:x,disabled:m,title:"Обновить данные",children:m?"обновляю…":t||"↻ обновить"})}function Ui(s){if(!s)return{color:"var(--dim)",hint:""};const t=s-Date.now(),m=864e5;return t<=0?{color:"var(--dim)",hint:"истекла"}:t<m?{color:"var(--coral)",hint:"меньше суток"}:t<7*m?{color:"var(--amber)",hint:`осталось ${Math.ceil(t/m)} дн`}:{color:"var(--green)",hint:`осталось ${Math.ceil(t/m)} дн`}}function ds(s){return s.status==="closed"?{text:"закрыт",mine:!1,done:!0}:s.lastActor==="user"?{text:"наш ход",mine:!0,done:!1}:s.lastActor==="ai"?{text:"ответил бот",mine:!1,done:!1}:{text:"ждём клиента",mine:!1,done:!1}}const Hs=s=>{const t=ds(s),m=t.done?"var(--muted)":t.mine?"var(--coral)":"var(--green)",n=t.done?"rgba(148,163,184,0.12)":t.mine?"rgba(255,122,92,0.14)":"rgba(52,211,153,0.10)";return e.jsx("span",{title:`статус: ${Bi(s.status)}`,style:{fontSize:12,fontWeight:700,padding:"3px 10px",borderRadius:999,color:m,background:n,whiteSpace:"nowrap"},children:t.text})},Pi={active:{icon:qt,color:"var(--green)"},expired:{icon:Ut,color:"var(--coral)"},cancelled:{icon:Pt,color:"var(--coral)"},canceled:{icon:Pt,color:"var(--coral)"},refunded:{icon:ps,color:"var(--amber)"},pending:{icon:mn,color:"var(--amber)"},suspended:{icon:Ot,color:"var(--coral)"}},Mi=s=>{const t=String(s||""),m=Pi[t];if(!m)return e.jsx("span",{className:"dim",title:Zt(t),children:"—"});const n=m.icon;return e.jsx("span",{title:Zt(t),"aria-label":Zt(t),style:{color:m.color,display:"inline-flex"},children:e.jsx(n,{size:16,strokeWidth:2.2})})},bn={start:{icon:Jn,color:"var(--cyan)",bg:"rgba(34,211,238,0.12)"},plus:{icon:Yn,color:"var(--violet-2)",bg:"rgba(124,92,255,0.14)"},pro:{icon:nn,color:"var(--amber)",bg:"rgba(245,183,74,0.14)"}},Oi=s=>{const t=bn[String(s||"")];if(!t)return null;const m=t.icon;return e.jsx("span",{style:{color:t.color,display:"inline-flex"},children:e.jsx(m,{size:15,strokeWidth:2.2})})},vn=s=>{const t=String(s||"");if(!t)return e.jsx("span",{className:"dim",children:"—"});const m=bn[t],n=m==null?void 0:m.icon;return e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:5,fontSize:12,fontWeight:700,padding:"3px 9px",borderRadius:999,whiteSpace:"nowrap",color:(m==null?void 0:m.color)||"var(--text-2)",background:(m==null?void 0:m.bg)||"rgba(255,255,255,0.06)"},children:[n?e.jsx(n,{size:13,strokeWidth:2.2}):null,t]})},qi={initial:{icon:ln,short:"первая"},renewal:{icon:Mt,short:"продление"},refund:{icon:ps,short:"возврат"},extra_devices:{icon:rs,short:"устройства"},upgrade:{icon:Zn,short:"апгрейд"},trial:{icon:Qn,short:"триал"}},Hi=s=>{const t=String(s||""),m=qi[t];if(!m)return e.jsx("span",{className:"dim",children:ls(t)});const n=m.icon;return e.jsxs("span",{title:ls(t),style:{display:"inline-flex",alignItems:"center",gap:5,whiteSpace:"nowrap"},children:[e.jsx(n,{size:13,strokeWidth:2,style:{flex:"none",opacity:.8}}),m.short]})},Vi={active:{icon:qt,color:"var(--green)"},pending:{icon:Ut,color:"var(--amber)"},suspended:{icon:Ot,color:"var(--coral)"}},Gi=s=>{const t=String(s||""),m=Vi[t];if(!m)return e.jsx("span",{className:"dim",title:es(t),children:"—"});const n=m.icon;return e.jsx("span",{title:es(t),"aria-label":es(t),style:{color:m.color,display:"inline-flex"},children:e.jsx(n,{size:16,strokeWidth:2.2})})},Vs=(s,t,m)=>e.jsx("span",{style:{fontSize:12,fontWeight:700,padding:"3px 10px",borderRadius:999,color:s?"var(--green)":"var(--coral)",background:s?"rgba(52,211,153,0.12)":"rgba(255,122,92,0.12)"},children:s?t:m}),Ki=[["overview","Обзор","analytics"],["analytics","Аналитика","analytics"],["users","Пользователи","users.read"],["payments","Платежи","payments.read"],["tickets","Тикеты","tickets"],["mail","Почта","mail"],["partners","Партнёры","partners"],["servers","Серверы","nodes"],["processors","Процессинги","processors"],["tariff","Тарифы","tariff"],["msgcopy","Сообщения","broadcast"],["staff","Сотрудники","roles"],["help","Справка","help"],["postbacks","Постбеки","analytics"]],ss=s=>{const t=(typeof location<"u"?location.hash:"").replace(/^#/,"").split("/")[0];return s.includes(t)?t:s[0]||"tickets"},Xi=[["auto","Как в системе"],["light","Светлая"],["dark","Тёмная"]],Yi=[["steel","Сталь"],["graphite","Графит"],["indigo","Индиго"],["teal","Хвоя"]],Gs="adm_theme",Ks="adm_skin";function Xs(){const[s,t]=r.useState(()=>{try{return localStorage.getItem(Gs)||"auto"}catch{return"auto"}}),[m,n]=r.useState(()=>{try{return localStorage.getItem(Ks)||"steel"}catch{return"steel"}});return r.useEffect(()=>{const x=document.documentElement,i=window.matchMedia("(prefers-color-scheme: light)"),C=()=>{s==="light"||s==="auto"&&i.matches?x.setAttribute("data-adm-theme","light"):x.removeAttribute("data-adm-theme")};C(),s==="auto"&&i.addEventListener("change",C);try{localStorage.setItem(Gs,s)}catch{}return()=>{i.removeEventListener("change",C),x.removeAttribute("data-adm-theme")}},[s]),r.useEffect(()=>{const x=document.documentElement;m==="steel"?x.removeAttribute("data-adm-skin"):x.setAttribute("data-adm-skin",m);try{localStorage.setItem(Ks,m)}catch{}return()=>{x.removeAttribute("data-adm-skin")}},[m]),e.jsx("select",{className:"atabs__skin",value:`${s}|${m}
51`,"aria-label":"Внешний вид админки",onChange:x=>{const[i,C]=x.target.value.split("|");t(i),n(C)},children:Xi.map(([x,i])=>e.jsx("optgroup",{label:i,children:Yi.map(([C,u])=>e.jsxs("option",{value:`${x}|${C}`,children:[i," · ",u]},C))},x))})}function Ji(){return r.useEffect(()=>{var n;const s=navigator.userAgent;return/Chrome\/\d/.test(s)&&!/Edge\//.test(s)&&((n=CSS==null?void 0:CSS.supports)==null?void 0:n.call(CSS,"backdrop-filter","url(#adm-lens) blur(1px)"))&&document.documentElement.setAttribute("data-glass","lens"),()=>{document.documentElement.removeAttribute("data-glass")}},[]),e.jsx("svg",{width:"0",height:"0",style:{position:"absolute"},"aria-hidden":!0,children:e.jsxs("filter",{id:"adm-lens",x:"0",y:"0",width:"100%",height:"100%",children:[e.jsx("feTurbulence",{type:"fractalNoise",baseFrequency:"0.008 0.014",numOctaves:2,seed:7,result:"noise"}),e.jsx("feGaussianBlur",{in:"noise",stdDeviation:"3",result:"soft"}),e.jsx("feDisplacementMap",{in:"SourceGraphic",in2:"soft",scale:14,xChannelSelector:"R",yChannelSelector:"G"})]})})}function Qi(){const s=r.useRef(null);return r.useEffect(()=>{const t=s.current;if(!t||typeof MutationObserver>"u")return;let m=!1;const n=()=>{m=!1;for(const i of Array.from(t.querySelectorAll("table"))){const C=Array.from(i.querySelectorAll("thead th")).map(u=>(u.textContent||"").replace(/[▼▲↓↑⌃⌄]/g,"").trim());if(C.length)for(const u of Array.from(i.querySelectorAll("tbody tr"))){let k=0;for(const g of Array.from(u.cells)){const I=Math.max(1,g.colSpan||1),E=I>1?"":C[k]??"";g.getAttribute("data-l")!==E&&g.setAttribute("data-l",E),k+=I}}}},x=new MutationObserver(i=>{i.some(u=>{const k=u.target instanceof Element?u.target:null;return k!=null&&k.closest("table")?!0:Array.from(u.addedNodes).some(g=>{var I;return g instanceof Element&&(g.matches("table, tbody, tr, td")||!!((I=g.querySelector)!=null&&I.call(g,"table")))})})&&!m&&(m=!0,requestAnimationFrame(n))});return n(),x.observe(t,{childList:!0,subtree:!0,attributeFilter:["colspan"]}),()=>x.disconnect()},[]),s}function Dr(){const s=Qi(),[t,m]=r.useState(()=>{try{return localStorage.getItem("adm_tbl")==="wide"?"wide":"cards"}catch{return"cards"}}),n=()=>m(D=>{const W=D==="cards"?"wide":"cards";try{localStorage.setItem("adm_tbl",W)}catch{}return W}),{user:x,loading:i,api:C}=ni(),u=(x==null?void 0:x.adminCaps)||[],k=r.useMemo(()=>Ki.filter(([,,D])=>u.includes(D)),[u.join(",")]),g=r.useMemo(()=>k.map(([D])=>D),[k]),[I,E]=r.useState(()=>ss(g)),X=D=>{E(D);try{history.replaceState(null,"","#"+D)}catch{}};return r.useEffect(()=>{const D=()=>E(ss(g));return window.addEventListener("hashchange",D),()=>window.removeEventListener("hashchange",D)},[g.join(",")]),r.useEffect(()=>{g.length&&E(ss(g))},[g.join(",")]),i?e.jsx("div",{className:"dim",style:{padding:40},children:"Загрузка…"}):x!=null&&x.isAdmin?e.jsxs("div",{className:"adm",ref:s,"data-tbl":t,style:{maxWidth:1240,margin:"0 auto",padding:"24px 20px 60px"},children:[e.jsx(Ji,{}),(()=>{const D=Math.ceil(k.length/2),W=k.length>6?[k.slice(0,D),k.slice(D)]:[k],o=Ds[I];return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"atabs atabs--wide",children:W.map((N,v)=>e.jsxs("div",{className:"atabs__row",children:[N.map(([_,B])=>{const Q=Ds[_],A=I===_;return e.jsxs("button",{onClick:()=>X(_),title:B,className:"atabs__b"+(A?" atabs__b--on":""),children:[e.jsx("span",{className:"atabs__i",children:Q?e.jsx(Q,{size:15,strokeWidth:2}):null}),B]},_)}),v===W.length-1&&e.jsxs(e.Fragment,{children:[e.jsx(Xs,{}),e.jsx("a",{href:"/",className:"dim atabs__back",children:"← в кабинет"})]})]},v))}),e.jsxs("div",{className:"atabs atabs--narrow",children:[e.jsx("span",{className:"atabs__cur",children:o?e.jsx(o,{size:17,strokeWidth:2}):null}),e.jsx("select",{className:"atabs__sel",value:I,onChange:N=>X(N.target.value),"aria-label":"Раздел админки",children:k.map(([N,v])=>e.jsx("option",{value:N,children:v},N))}),e.jsx(Xs,{}),e.jsx("button",{className:`toggle-pill tblswitch${t==="wide"?" is-on":""}`,onClick:n,title:t==="cards"?"Показать таблицей — колонки как на компьютере":"Показать карточками — по строке на экран",children:t==="cards"?"Карточки":"Таблица"}),e.jsx("a",{href:"/",className:"dim atabs__back",children:"← в кабинет"})]})]})})(),I==="overview"&&e.jsx(sr,{api:C}),I==="analytics"&&e.jsx(nr,{api:C}),I==="users"&&e.jsx(ir,{api:C}),I==="payments"&&e.jsx(or,{api:C}),I==="tickets"&&e.jsx(dr,{api:C}),I==="mail"&&e.jsx(Wr,{api:C}),I==="partners"&&e.jsx(mr,{api:C}),I==="postbacks"&&e.jsx(gr,{api:C}),I==="servers"&&e.jsx(fr,{api:C}),I==="processors"&&e.jsx(Sr,{api:C}),I==="tariff"&&e.jsx(br,{api:C}),I==="msgcopy"&&e.jsx(kr,{api:C}),I==="staff"&&e.jsx(tr,{api:C,me:(x==null?void 0:x.email)||""}),I==="help"&&e.jsx(er,{caps:u,role:(x==null?void 0:x.adminRole)||"owner"})]}):e.jsx("div",{className:"card pad",style:{margin:40},children:e.jsx("p",{className:"lead",children:"Нет доступа."})})}const qe={owner:{label:"Владелец",hint:"всё, включая возвраты, ключи процессингов, серверы и раздачу ролей",tone:"var(--amber)"},manager:{label:"Управляющий",hint:"аналитика, клиенты, партнёры, рассылки. Без возвратов, ключей и серверов",tone
51:"var(--violet)"},support:{label:"Поддержка",hint:"тикеты, почта, карточка клиента и его оплаты — только чтение",tone:"var(--cyan)"}},Zi=[{cap:"help",title:"С чего начать",body:["Админка — рабочее место, а не витрина: почти всё, что тут показано, влияет на живого человека с оплаченной подпиской. Правило одно: сначала понять, что видишь, потом нажимать.","Доступ выдаётся ролью, и вкладки собраны под неё. Если вкладки нет — значит это право не выдано, а не «сломалось». Нужна другая — пиши владельцу, а не обходи.","Данные почти везде за период. Прежде чем удивляться цифре, посмотри на выбранный период и часовой пояс: переключатель UTC меняет ВСЁ время на странице."]},{cap:"analytics",title:"Обзор и Аналитика",body:["Обзор — восемь чисел за 30 дней и три графика по дням. Наведи курсор на график: покажет дату и значение.","Выручка считается ТОЛЬКО по оплаченным платежам. Выставленные счета, отказы и возвраты в неё не попадают — если цифра кажется низкой, это не потеря данных.","Аналитика — воронка от захода на сайт до оплаты, разрезы по источникам и лендингам, LTV и удержание. Там же видно, какие продления доживают до второго и третьего цикла."]},{cap:"users.read",title:"Пользователи",body:["Поиск живой: набирай почту или часть — список сузится сам, кнопку жать не надо.","В строке видно тариф, срок, автопродление и привязку Telegram. Если человек пришёл от партнёра, рядом с ним стоит код партнёра.","Клик по человеку раскрывает карточку: устройства, история платежей, события подписки. С этого и Ð
51½Ð°Ñ‡Ð¸Ð½Ð°Ð¹ любой разбор — почти на все вопросы саппорта ответ там."]},{cap:"users.write",title:"Выдать и отозвать доступ",body:["Выдать доступ — это подарок за наш счёт, а не оплата: деньги в отчётах не появятся. Пользуйся, когда мы виноваты (простой сервиса, наша ошибка), и пиши в комментарии, за что.","Отзыв доступа гасит подписку немедленно. Если человек заплатил, а доступ надо снять — это возврат, а не отзыв: возврат вернёт деньги и снимет доступ одним действием."]},{cap:"payments.read",title:"Платежи: как читать ленту",body:["Одна строка — одно движение денег или один поход за покупкой. Статусы:",["создан — счёт выставлен, денег ещё нет. Нигде не считается. Клик покажет, какими способами человек пробовал заплатить.","оплачен — деньги пришли. Если рядом число, человек дошёл не с первого захода: клик покажет весь путь.","не прошёл — процессинг отказал. Клик покажет причину человеческим языком и чья это вина: банка, клиента или наша.","истёк — счёт умер неоплаченным. Денег не списывали, чаще всего человек вернулся и заплатил другим способом.","возвращён — деньги вернули полностью."],"Значок ↻N на оплате означает, что списание удалось не с первого раза: биллер повторял. Клик — причины прошлых попыток. #N у продления — какое это продление по счёту, по нему видно глубину удержания.","Технические повторы и созданные счета, за которыми пришла оплата, из ленты убраны намеренно: событие одно, и показывать его тремя строками значит врать про количество платежей."]},{cap:"payments.refund",title:"Возврат денег",body:["Возврат необратим и стоит нам больше суммы платежа: комиссия эквайринга при возврате НЕ возвращается. В окне возврата эта цена показана — читай её перед нажатием.","Возврат снимает доступ и гасит автопродление. Частичный возврат оставляет подписку живой — им пересчитывают, а не расстаются.","Если деньги не приходили (счёт висел неоплаченным, а доступ выдан по ошибке) — это не возврат, а откат: он снимает выданное время, не трогая деньги."]},{cap:"tickets",title:"Тикеты",body:["Отвечай из карточки клиента, а не по памяти: там видно, что у него за тариф, когда платил и что с подпиской. Половина вопросов снимается одним взглядом на срок.","Ответ уходит человеку письмом и в Telegram, если он привязан. Пиши так, как готов увидеть это в скриншоте: переписка часто уезжает в чаты и отзывы.","Никогда не пиши слово «VPN» — ни в тикетах, ни в письмах, ни в переписке с подрядчиками. Мы описываем продукт нейтрально: веб-сервис с подпиской."]},{cap:"mail",title:"Почта",body:["Сюда падают входящие письма на support-адрес. Отвечать можно прямо отсюда — ответ уйдёт с нашего домена и попадёт в ту же цепочку.","Помечай разобранное: непомеченные письма считаются очередью, по ним смотрят нагрузку."]},{cap:"partners",title:"Партнёры",body:["Заявка — это человек, который хочет лить трафик. Прежде чем включать, посмотри контакт и договорись о ставке: ставка меняется только вперёд, уже начисленные комиссии не пересчитываются.","Комиссия висит в холде 14 дней — это окно возврата. Возврат покупателя гасит начисление за ТОТ платёж, остальные остаются.","Оборот партнёра не может быть больше нашей выручки. Если видишь такое — это баг, зови владельца, а не выплачивай."]},{cap:"processors",title:"Процессинги",body:["Тумблер выключает процессинг мгновенно, без выкатки. Выключай, когда он отказывает подряд: каскад уведёт людей на следующий, и продажи не встанут.","Конверсия в таблице — из выставленных счетов в оплаченные. Вторым уровнем разбиты способы оплаты: с какого метода уходят, видно раньше, чем просядет выручка.","Ключи и секреты не трогай без владельца. Без ключей процессинг включить нельзя — это защита, а не ошибка."]},{cap:"nodes",title:"Серверы",body:["Здесь состояние нод и панели. Красная нода — это люди без доступа прямо сейчас, приоритет выше любой другой задачи.","Ноду не выключай в одиночку: на неё смотрят живые подписки, и уход трафика надо готовить."]},{cap:"broadcast",title:"Сообщения и рассылки",body:["Тексты уведомлений редактируются здесь, на двух языках. Перед сохранением жми предпросмотр: плейсхолдеры вроде {plan} и {amount} подставляются реальными значениями, и опечатка в них ломает письмо целиком.","Рассылка уходит живым людям и отменить её нельзя. Отправляй только с согласия владельца и всегда сначала себе.","Квота писем общая на весь аккаунт: большая рассылка может съесть лимит и оставить без писем регистрацию другого бренда. Это уже случалось."]},{cap:"roles",title:"Сотрудники",body:["Роль выдаётся по почте — человек должен сначала завести обычный аккаунт. Владелец может всё, управляющий ведёт продукт и деньги в отчётах, поддержка отвечает клиентам.","Себя разжаловать нельзя: иначе последний владелец закрыл бы админку для всех.","Снимай доступ сразу, как человек перестал работать. Это одно нажатие и оно дешевле любого разбора потом."]}
51,{cap:"help",title:"Если что-то пошло не так",body:["Деньги списались дважды, доступ пропал у оплатившего, партнёру начислено больше выручки — не чини сам, зови владельца. Такие вещи почти всегда означают баг, и второе действие поверх первого делает разбор невозможным.","Прежде чем звать, собери факты: почта клиента, время, что видно в ленте платежей и в карточке. С этим разбор занимает минуты, без этого — часы."]}];function er({caps:s,role:t}){var x,i;const m=Zi.filter(C=>s.includes(C.cap)),n=((x=qe[t])==null?void 0:x.label)||"Сотрудник";return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:6,fontSize:15},children:["Справка — ",n.toLowerCase()]}),e.jsxs("p",{className:"dim",style:{fontSize:13,margin:0,lineHeight:1.6},children:["Здесь описано только то, что доступно твоей роли: ",((i=qe[t])==null?void 0:i.hint)||"—",". Разделы идут в том же порядке, что вкладки сверху."]})]}),m.map(C=>e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:10,fontSize:14},children:C.title}),C.body.map((u,k)=>Array.isArray(u)?e.jsx("ul",{style:{margin:"0 0 10px",paddingLeft:18,fontSize:13.5,lineHeight:1.7,color:"var(--muted)"},children:u.map((g,I)=>e.jsx("li",{children:g},I))},k):e.jsx("p",{style:{margin:"0 0 10px",fontSize:13.5,lineHeight:1.65,color:"var(--muted)"},children:u},k))]},C.title))]})}function tr({api:s,me:t}){var W;const[m,n]=r.useState([]),[x,i]=r.useState(""),[C,u]=r.useState("support"),[k,g]=r.useState(""),[I,E]=r.useState(!1),X=()=>s("/admin/staff").then(o=>n(o.rows||[])).catch(()=>{});r.useEffect(()=>{X()},[]);const D=async(o,N)=>{E(!0),g("");try{await s("/admin/staff/set",{email:o,role:N}),i(""),await X(),g(N==="none"?"Доступ снят":"Роль назначена")}catch(v){const _=String((v==null?void 0:v.message)||"");g(_==="no_user"?"Пользователя с таким адресом нет — сначала он должен завести аккаунт":_==="self_demote"?"Себя разжаловать нельзя — иначе некому будет вернуть права":"Не получилось: "+_)}finally{E(!1)}};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"card pad",style:{marginBottom:16},children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:12,fontSize:15},children:"Выдать доступ"}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[e.jsx("input",{style:{...We,minWidth:260,flex:"1 1 260px"},placeholder:"почта сотрудника",value:x,onChange:o=>i(o.target.value)}),e.jsx("select",{style:{...We,cursor:"pointer"},value:C,onChange:o=>u(o.target.value),children:Object.entries(qe).map(([o,N])=>e.jsx("option",{value:o,children:N.label},o))}),e.jsx("button",{className:"btn",disabled:I||!x.trim(),onClick:()=>D(x.trim(),C),children:"Назначить"}),k&&e.jsx("span",{className:"dim",style:{fontSize:13},children:k})]}),e.jsx("div",{className:"dim",style:{fontSize:12.5,marginTop:10,lineHeight:1.5},children:(W=qe[C])==null?void 0:W.hint})]}),e.jsxs("div",{className:"card pad",children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:12,fontSize:15},children:["Доступ в админку — ",m.length]}),e.jsx("div",{style:{overflowX:"auto"},children:e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx("th",{style:{padding:"6px 8px"},children:"почта"}),e.jsx("th",{style:{padding:"6px 8px"},children:"роль"}),e.jsx("th",{style:{padding:"6px 8px"},children:"что может"}),e.jsx("th",{style:{padding:"6px 8px"},children:"действия"})]})}),e.jsx("tbody",{children:m.map(o=>{const N=qe[o.adminRole]||qe.owner,v=String(o.email||"").toLowerCase()===t.toLowerCase();return e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsxs("td",{style:{padding:"8px"},children:[o.email,v&&e.jsx("span",{className:"dim",style:{marginLeft:6,fontSize:12},children:"(вы)"})]}),e.jsx("td",{style:{padding:"8px"},children:e.jsx("select",{style:{...We,padding:"5px 9px",fontSize:13,cursor:v?"not-allowed":"pointer",color:N.tone},value:o.adminRole,disabled:v||I,onChange:_=>{var Q,A;const B=_.target.value;if(!confirm(`Сменить роль ${o.email} на «${((Q=qe[B])==null?void 0:Q.label)||B}»?
52
53${((A=qe[B])==null?void 0:A.hint)||""}`)){_.target.value=o.adminRole;return}D(o.email,B)},children:Object.entries(qe).map(([_,B])=>e.jsx("option",{value:_,children:B.label},_))})}),e.jsx("td",{style:{padding:"8px",whiteSpace:"normal"},className:"dim",children:N.hint}),e.jsx("td",{style:{padding:"8px",textAlign:"right"},children:!v&&e.jsx("button",{className:"iconbtn iconbtn--danger",disabled:I,title:"Снять доступ в админку","aria-label":"Снять доступ в админку",onClick:()=>{confirm(`Снять доступ в админку у ${o.email}?
54
55Человек потеряет вход в панель немедленно. Вернуть можно только выдав роль заново.`)&&D(o.email,"none")},children:e.jsx(Xn,{size:15,strokeWidth:2})})})]},o.id)})})]})})]})]})}function Vt(s,t=[],m=6e4){const n=r.useRef(s);n.current=s,r.useEffect(()=>{const i=setInterval(()=>{document.hidden||n.current()},m),C=()=>{document.hidden||n.current()};return document.addEventListener("visibilitychange",C),()=>{clearInterval(i),document.removeEventListener("visibilitychange",C)}},t)}function sr({api:s}){var u,k,g,I,E,X,D,W,o,N;const[t,m]=r.useState(null),[n,x]=r.useState(null),i=-new Date().getTimezoneOffset(),C=()=>{s("/admin/overview").then(m).catch(()=>{}),s(`/admin/charts?tz=${i}`).then(x).catch(()=>{})};return r.useEffect(()=>{C()},[]),Vt(C),e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"kpi-row",children:[e.jsx(Ke,{label:(()=>{const _=((t==null?void 0:t.revenue30ByCurrency)||[]).slice(1).map(Q=>
55`${(Q.cents/100).toLocaleString("ru-RU")} ${gs(Q.currency)}`),B=_.length?`выручка · 30 дней · ещё ${_.join(" · ")}`:"выручка · 30 дней";return t!=null&&t.refunded30Cents?`${B} · за вычетом возвратов`:B})(),value:t?De(((k=(u=t.revenue30ByCurrency)==null?void 0:u[0])==null?void 0:k.cents)??t.revenue30NativeCents??t.revenue30Cents,((I=(g=t.revenue30ByCurrency)==null?void 0:g[0])==null?void 0:I.currency)??t.revenue30Currency):"—",tone:"var(--green)"}),e.jsx(Ke,{label:"платежей · 30 дней",value:(t==null?void 0:t.sales30)??"—"}),e.jsx(Ke,{label:"покупателей · 30 дней",value:(t==null?void 0:t.buyers30)??"—",tone:"var(--cyan)"}),e.jsx(Ke,{label:"регистраций · 30 дней",value:(t==null?void 0:t.users30)??"—"}),e.jsx(Ke,{label:t?`подписок: ${t.subsPaid} платных · ${t.subsTrial} триал · ${t.subsGranted} грант`:"активных подписок",value:(t==null?void 0:t.subsActive)??"—"}),e.jsx(Ke,{label:t?`автопродление: ${t.subsCron} на кроне`:"под демоном",value:t?`${t.subsDaemon} / ${t.subsDaemon+t.subsCron}`:"—",tone:"var(--violet)"}),e.jsx(Ke,{label:t?`тикетов: ${t.ticketsOpen??0} открытых · ${t.ticketsWaiting??0} без ответа`:"тикеты",value:t?`${t.ticketsOpen??0} / ${t.ticketsWaiting??0}`:"—",tone:t!=null&&t.ticketsWaiting?"var(--coral)":void 0}),e.jsx(Ke,{label:"заявок партнёров",value:(t==null?void 0:t.partnersPending)??"—",tone:t!=null&&t.partnersPending?"var(--coral)":void 0})]}),!!(t!=null&&t.refunded30Cents)&&e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginTop:-8,marginBottom:16},children:["Возвращено за 30 дней: ",De(t.refunded30Cents,((X=(E=t.revenue30ByCurrency)==null?void 0:E[0])==null?void 0:X.currency)??t.revenue30Currency)," — эта сумма из выручки уже вычтена."]}),!!((D=t==null?void 0:t.revenue30Skipped)!=null&&D.length)&&e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginTop:-8,marginBottom:16},children:["Не учтено в выручке: ",t.revenue30Skipped.join(", ")," — нет курса к USD. Песочница Apple исключена."]}),!!((o=(W=t==null?void 0:t.cronFailed)==null?void 0:W.failed)!=null&&o.length)&&e.jsxs("div",{className:"card pad",style:{borderColor:"var(--coral)",marginBottom:16},children:[e.jsxs("div",{style:{color:"var(--coral)",fontWeight:600,fontSize:14,marginBottom:6},children:["Крон: упало шагов — ",t.cronFailed.failed.length," (",new Date(t.cronFailed.at).toLocaleString("ru-RU"),")"]}),t.cronFailed.failed.map((v,_)=>e.jsx("div",{className:"dim",style:{fontSize:12.5,fontFamily:"ui-monospace, monospace"},children:v},_))]}),!!((N=t==null?void 0:t.dbSlow)!=null&&N.length)&&e.jsxs("div",{className:"card pad",style:{borderColor:"var(--coral)",marginBottom:16},children:[e.jsxs("div",{style:{color:"var(--coral)",fontWeight:600,fontSize:14,marginBottom:6},children:["База отвечает дорого — запрос перестал попадать в индекс (",new Date(t.dbSlow[0].at).toLocaleString("ru-RU"),")"]}),t.dbSlow.map((v,_)=>e.jsxs("div",{className:"dim",style:{fontSize:12.5},children:[v.name," — ",v.rows<0?"запрос не прошёл":`прочитано ${v.rows.toLocaleString("ru-RU")} строк за ${Math.round(v.ms)} мс`]},_))]}),n&&e.jsx("div",{style:{display:"grid",gridTemplateColumns:"1fr",gap:14},children:[["Регистрации / день",n.regs,yt.violet,!1],["Платежи / день",n.sales,yt.cyan,!1],["Выручка / день",n.revenueCents,yt.green,!0]].map(([v,_,B,Q])=>e.jsxs("div",{className:"card pad",children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:10,fontSize:14},children:v}),e.jsx(xn,{data:_,days:n.days,color:B,width:un,height:pn,fmtValue:Q?(A=>De(A,(n==null?void 0:n.revenueCurrency)||(t==null?void 0:t.revenue30Currency))):void 0})]},v))})]})}const cs=[{id:"today",label:"Сегодня",range:s=>{const t=He(s,0);return[t,t]}},{id:"yesterday",label:"Вчера",range:s=>{const t=He(s,-1);return[t,t]}},{id:"week",label:"Неделя",range:s=>[He(s,-6),He(s,0)]},{id:"month",label:"Месяц",range:s=>[He(s,-29),He(s,0)]},{id:"all",label:"Всё",range:s=>["",""]}];function He(s,t){return new Date(Date.now()+s*6e4+t*864e5).toISOString().slice(0,10)}const Ys="2026-03-01";function Sn(s="week",t=!1){const[m,n]=r.useState(!1),x=m?0:-new Date().getTimezoneOffset(),[i,C]=r.useState(s),[u,k]=r.useState(""),[g,I]=r.useState(""),[E,X]=r.useState(""),[D,W]=r.useState("");let[o,N]=i?(cs.find(B=>B.id===i)||cs[2]).range(x):[u,g];i==="all"&&t&&!o&&(o=He(x,-730),N=He(x,0));const v=o&&E?`${o}T${E}`:o,_=N&&D?`${N}T${D}`:N;return{utc:m,setUtc:n,tzMin:x,preset:i,setPreset:C,from:u,setFrom:k,to:g,setTo:I,hFrom:E,setHFrom:X,hTo:D,setHTo:W,rFrom:o,rTo:N,qFrom:v,qTo:_,ready:!!(o&&N)}}function Js({utc:s,setUtc:t,narrow:m}){return e.jsx("button",{type:"button",onClick:()=>t(!s),"aria-pressed":s,className:"pbar__utc"+(s?" pbar__utc--on":"")+(m?" pbar__utc--m":" pbar__utc--d"),title:"Сутки режутся по UTC вместо часового пояса браузера — для сверки с чужими отчётами",children:"UTC"})}function wn({p:s,busy:t,extra:m,allowAll:n}
55){const{preset:x,setPreset:i,from:C,setFrom:u,to:k,setTo:g,utc:I,setUtc:E,hFrom:X,setHFrom:D,hTo:W,setHTo:o}=s,N=He(s.tzMin,0),v=cs;return e.jsxs("div",{className:"card pad pbar",children:[e.jsxs("div",{className:"pbar__presets",style:{"--pcols":(v.length+1)%3===0?3:(v.length+1)%2===0?2:3},children:[v.map(_=>e.jsx("button",{onClick:()=>{i(_.id),u(""),g("")},style:{padding:"7px 16px",fontSize:13,fontWeight:x===_.id?600:400,border:0,borderRadius:8,cursor:"pointer",transition:"background .15s, color .15s",background:x===_.id?"rgba(91,156,255,0.22)":"transparent",color:x===_.id?"var(--text)":"var(--muted)"},children:_.label},_.id)),e.jsx(Js,{utc:I,setUtc:E,narrow:!0})]}),e.jsxs("div",{className:"pbar__dates",children:[e.jsx("input",{type:"date",value:x?s.rFrom||"":C,max:k||N,min:Ys,onChange:_=>{const B=_.target.value;if(!B)return;const Q=k||s.rTo||B;u(B),g(B>Q?B:Q),i("")},style:{...We,padding:"6px 10px",fontSize:12.5,width:"auto"}}),e.jsx("span",{className:"dim",style:{fontSize:13},children:"—"}),e.jsx("input",{type:"date",value:x?s.rTo||"":k,min:C||s.rFrom||Ys,max:N,onChange:_=>{const B=_.target.value;if(!B)return;const Q=C||s.rFrom||B;g(B),u(B<Q?B:Q),i("")},style:{...We,padding:"6px 10px",fontSize:12.5,width:"auto"}}),e.jsx("span",{className:"dim pbar__hsep",style:{fontSize:12},children:"время"}),e.jsx("input",{type:"time",value:X,step:60,"aria-label":"Время начала периода",title:"С какого часа считать. Пусто — с начала суток.",onChange:_=>D(_.target.value),onBlur:_=>{const B=_.target.value;B&&W&&W<B&&s.rFrom===s.rTo&&o(B)},style:{...We,padding:"6px 8px",fontSize:12.5,width:"auto"}}),e.jsx("span",{className:"dim",style:{fontSize:13},children:"—"}),e.jsx("input",{type:"time",value:W,step:60,"aria-label":"Время конца периода",title:"По какой час считать, включительно. Пусто — до конца суток.",onChange:_=>o(_.target.value),onBlur:_=>{const B=_.target.value;B&&X&&B<X&&s.rFrom===s.rTo&&D(B)},style:{...We,padding:"6px 8px",fontSize:12.5,width:"auto"}}),(X||W)&&e.jsx("button",{className:"btn btn--ghost",style:{padding:"6px 9px",fontSize:12.5},title:"Вернуть период к целым суткам",onClick:()=>{D(""),o("")},children:"сутки"}),!x&&e.jsx("button",{className:"btn btn--ghost",style:{padding:"6px 11px",fontSize:12.5},onClick:()=>{i(n?"all":"week"),u(""),g(""),D(""),o("")},children:"сброс"})]}),m,e.jsxs("div",{className:"pbar__right",children:[e.jsx("span",{className:"dim",style:{fontSize:12,opacity:t?1:0,transition:"opacity .2s"},children:"обновляю…"}),e.jsx(Js,{utc:I,setUtc:E})]})]})}function nr({api:s}){var Be,Ae,Ce,R,ze,ke,Le,Ee,Fe;const t=Sn("week",!0),{tzMin:m,utc:n,rFrom:x,rTo:i,qFrom:C,qTo:u,ready:k}=t,[g,I]=r.useState(""),[E,X]=r.useState([]),[D,W]=r.useState([]);r.useEffect(()=>{let l=!0;return s("/partner/admin/list").then(a=>{l&&X(((a==null?void 0:a.partners)||[]).filter(M=>M.code))}).catch(()=>{}),s("/admin/nx-codes").then(a=>{l&&W((a==null?void 0:a.codes)||[])}).catch(()=>{}),()=>{l=!1}},[]);const[o,N]=r.useState(null),[v,_]=r.useState(!0),[B,Q]=r.useState(!0),[A,$]=r.useState(null);r.useEffect(()=>{if(!k)return;let l=!0;const a=new URLSearchParams({from:C,to:u,tz:String(m)});g&&a.set("partner",g);let M=0;const f=(K=!1)=>{K||Q(!0);const re=++M,je=new URLSearchParams(a);je.set("section","core");const c=new URLSearchParams(a);return c.set("section","health"),K||N(te=>te&&{...te,movement:void 0,stickiness:void 0,churned:void 0,rebills:void 0}),s("/admin/funnel?"+c).then(te=>{l&&re===M&&N(ve=>({...ve,...te}))}).catch(()=>{}),s("/admin/funnel?"+je).then(te=>{l&&re===M&&(N(ve=>({...ve,...te})),$(Date.now()))}).catch(()=>{}).finally(()=>{l&&!K&&(Q(!1),_(!1))})};f();const H=setInterval(()=>{document.hidden||f(!0)},6e4),p=()=>{document.hidden||f(!0)};return document.addEventListener("visibilitychange",p),()=>{l=!1,clearInterval(H),document.removeEventListener("visibilitychange",p)}},[C,u,m,g]);const[V,T]=r.useState({k:"revCents",dir:-1}),[y,q]=r.useState(null),[P,oe]=r.useState(null),[L,se]=r.useState({k:"uniques",dir:-1}
55),[U,ne]=r.useState({k:"crPaid",dir:-1}),[le,ee]=r.useState({k:"day",dir:-1}),be=(l,a,M)=>[...l||[]].sort((f,S)=>{const H=a.k==="cr"?f.uniques||f.regs?f.paid/(f.uniques||f.regs):0:f[a.k],p=a.k==="cr"?S.uniques||S.regs?S.paid/(S.uniques||S.regs):0:S[a.k];return a.k===M?String(H).localeCompare(String(p))*a.dir:(H>p?1:H<p?-1:0)*a.dir}),w=({k:l,cur:a,set:M,children:f,right:S})=>e.jsxs("th",{className:S?"r":"",style:{padding:6,cursor:"pointer",userSelect:"none",whiteSpace:"nowrap"},onClick:()=>M(a.k===l?{k:l,dir:-a.dir}:{k:l,dir:-1}),children:[f,a.k===l?a.dir===-1?" ▾":" ▴":""]}),d=(l,a,M)=>{var H,p;const f=[a.join(";"),...M.map(K=>K.join(";"))].join(`
56`),S=document.createElement("a");S.href=URL.createObjectURL(new Blob(["\uFEFF"+f],{type:"text/csv;charset=utf-8"})),S.download=`${l}-${((H=o==null?void 0:o.range)==null?void 0:H.from)||""}_${((p=o==null?void 0:o.range)==null?void 0:p.to)||""}.csv`,document.body.appendChild(S),S.click(),document.body.removeChild(S),setTimeout(()=>URL.revokeObjectURL(S.href),1e3)},b=l=>De(l,o==null?void 0:o.currency),F=(l,a)=>a?(l/a*100).toFixed(1)+"%":"—",Z=o==null?void 0:o.totals,xe=(l,a,M,f="var(--text)",S)=>e.jsxs("div",{className:"card pad",style:{flex:1,minWidth:130},children:[e.jsx("div",{className:"dim",style:{fontSize:12.5,marginBottom:4},children:l}),e.jsx("div",{style:{fontSize:24,fontWeight:800,color:f},children:a}),M&&e.jsx("div",{className:"dim",style:{fontSize:12,marginTop:2},title:S,children:M})]}),ue=g.startsWith("nx:"),Ne="клики nexora считаются в nexora",fe=(l,a)=>ue?"—":F(l,a),ce=({n:l,cents:a,money:M,tone:f,dim:S,label:H})=>e.jsx("td",{className:"r"+(S?" dim":""),style:{whiteSpace:"nowrap",color:f,fontWeight:f?600:400},title:l?`${H}: ${l} на ${M(a||0)}`:H,children:l?e.jsxs(e.Fragment,{children:[l,e.jsxs("span",{className:"dim cellsum",style:{fontSize:11.5,fontWeight:400},children:[" · ",M(a||0)]})]}):""});return Math.max(1,...((o==null?void 0:o.revByDay)||[]).map(l=>l.revCents)),e.jsxs(e.Fragment,{children:[e.jsx(wn,{p:t,busy:B&&!v,extra:(E.length>0||D.length>0)&&e.jsxs("select",{value:g,onChange:l=>I(l.target.value),title:"Показать воронку только по одному партнёру (по его реф-коду)",style:{...We,padding:"6px 10px",fontSize:12.5,width:"auto",maxWidth:220},children:[e.jsx("option",{value:"",children:"Все партнёры"}),E.map(l=>e.jsxs("option",{value:l.code,children:[l.code,l.email||l.contact?` · ${l.email||l.contact}`:""]},l.code)),D.length>0&&e.jsx("optgroup",{label:"nexora",children:D.map(l=>e.jsx("option",{value:"nx:"+l.code,children:as(l)},"nx:"+l.code))})]})}),g&&e.jsxs("div",{className:"dim",style:{fontSize:12,marginTop:-6,marginBottom:8},children:[g.startsWith("nx:")?e.jsxs(e.Fragment,{children:["Разрез по партнёру nexora ",e.jsx("b",{style:{color:"var(--violet-2)"},children:as(D.find(l=>"nx:"+l.code===g)||{code:g.slice(3)})})," — регистрации и оплаты его людей (nx_code); визиты — браузеры, из которых его люди потом вошли."]}):e.jsxs(e.Fragment,{children:["Разрез по партнёру ",e.jsx("b",{style:{color:"var(--violet-2)"},children:g})," — визиты по ref, регистрации и оплаты его юзеров."]})," ",e.jsx("button",{className:"linkbtn",onClick:()=>I(""),children:"сбросить"})]}),(o==null?void 0:o.range)&&e.jsxs("div",{className:"dim",style:{fontSize:12,marginTop:-6,marginBottom:12},children:[o.range.from===o.range.to?o.range.from:`${o.range.from} — ${o.range.to}`," · сутки по ",n?"UTC":`времени браузера (UTC${m>=0?"+":""}${m/60})`,A&&e.jsxs("span",{title:"Данные обновляются сами, пока страница открыта",children:[" · обновлено ",new Date(A).toLocaleTimeString("ru-RU",{hour:"2-digit",minute:"2-digit"})]})]}),v||!Z?e.jsxs("div",{className:"card pad",style:{display:"flex",alignItems:"center",gap:10},children:[e.jsx("span",{className:"spinner"}),e.jsx("span",{className:"dim",style:{fontSize:13},children:"считаю…"})]}):e.jsxs("div",{style:{opacity:B?.45:1,transition:"opacity .18s ease",pointerEvents:B?"none":"auto"},children:[((Be=o.visits)==null?void 0:Be.source)&&e.jsxs("div",{className:"dim",style:{fontSize:12,marginBottom:6},children:["Заходы: ",o.visits.source==="ae"?"Analytics Engine (точки за последние 3 месяца)":o.visits.source==="daily"?"суточные агрегаты":"таблица visits (D1)",o.visits.sampled&&" · оценка (сэмплинг)",o.visits.note&&` · ${o.visits.note}`]}),e.jsxs("div",{style:{display:"flex",gap:10,flexWrap:"wrap",marginBottom:12},children:[xe("Показы",Z.raws,"все просмотры страниц"),xe("Визиты",Z.visits,"сессии"),xe("Уники",Z.uniques,"посетители","var(--cyan)"),xe("Регистрации",Z.regs,fe(Z.regs,Z.uniques)+" от уников","#A855F7",ue?Ne:void 0),xe("Покупатели",Z.paid,F(Z.paid,Z.regs)+" от реги","var(--green)"),xe("Выручка",b(Z.revCents),(Z.paid?"ср. чек "+b(Math.round(Z.revCents/Z.paid)):"ср. чек —")+(Z.refundCents?` · возвраты ${b(Z.refundCents)} уже вычтены`:""),"var(--green)"),o.cohort&&xe("LTV",o.cohort.paid?b(o.cohort.ltvCents):"—",`на покупателя · когорта: ${o.cohort.paid} из ${o.cohort.regs} пришедших`,"var(--green)"),o.cohort&&xe("ARPU",o.cohort.regs?b(o.cohort.arpuCents):"—",`на регистрацию · выручка когорты ${b(o.cohort.rev
56Cents)}`,"var(--cyan)")]}),(((Ae=o.currencies)==null?void 0:Ae.length)||0)>1&&e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginTop:-6,marginBottom:12,color:"var(--coral)"},children:["В периоде оплаты в разных валютах (",o.currencies.map(l=>`${(l.cents/100).toLocaleString("ru-RU")} ${gs(l.currency)}`).join(" · "),") — суммы в таблицах ниже сложены как есть, без пересчёта по курсу."]}),((Ce=o.revByDay)==null?void 0:Ce.length)>0&&e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10,display:"flex",alignItems:"center",gap:8},children:[e.jsx("span",{children:"Выручка по дням"}),e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("revenue-by-day",["date","registrations","payments","full","full_rub","trial","trial_rub","from_trial","from_trial_rub","renewals","renewals_rub","devices","devices_rub","revenue","avg"],be(o.revByDay,le,"day").map(l=>[new Date(l.day).toISOString().slice(0,10),l.regs||0,l.n,l.fullN||0,((l.fullCents||0)/100).toFixed(2),l.trialN||0,((l.trialCents||0)/100).toFixed(2),l.trialConvN||0,((l.trialConvCents||0)/100).toFixed(2),l.renewN||0,((l.renewCents||0)/100).toFixed(2),l.devicesN||0,((l.devicesCents||0)/100).toFixed(2),(l.revCents/100).toFixed(2),(l.n?l.revCents/l.n/100:0).toFixed(2)])),children:"CSV"})]}),o.revByDay.length>1&&e.jsx(xn,{data:o.revByDay.map(l=>l.revCents),days:o.revByDay.map(l=>{const a=new Date(l.day);return`${a.getFullYear()}-${String(a.getMonth()+1).padStart(2,"0")}-${String(a.getDate()).padStart(2,"0")}`}),color:yt.green,fmtValue:l=>De(l,o.currency),width:un,height:pn}),e.jsx("div",{style:{maxHeight:260,overflowY:"auto",overflowX:"auto"},children:e.jsxs("table",{className:"revday",style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)",position:"sticky",top:0,background:"var(--surface)"},children:[e.jsx(w,{k:"day",cur:le,set:ee,children:e.jsx("span",{title:"Дата",children:e.jsx(An,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"regs",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Регистраций за день",children:e.jsx(In,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"n",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Платежей за день — всего",children:e.jsx(Dn,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"fullN",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Полный тариф — первая покупка по цене",children:e.jsx(an,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"trialN",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Пробный — первая покупка по льготной цене (1 ₽ или 19 ₽, зависит от оффера партнёра); разбивка по фактическим ценам — в блоке «Пробный период»",children:e.jsx(ln,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"trialConvN",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Из пробного — доплатил полную цену после рубля",children:e.jsx(Bn,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"renewN",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Продления — очередное списание по подписке",children:e.jsx(Mt,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"devicesN",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Устройства — докупка слотов сверх тарифа",children:e.jsx(Ln,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"revCents",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Выручка за день",children:e.jsx(En,{size:15,strokeWidth:2})})}),e.jsx(w,{k:"avg",cur:le,set:ee,right:!0,children:e.jsx("span",{title:"Средний чек — выручка ÷ платежей",children:e.jsx(Fn,{size:15,strokeWidth:2})})})]})}),e.jsxs("tbody",{children:[be(o.revByDay.map(l=>({...l,avg:l.n?l.revCents/l.n:0})),le,"day").map(l=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6},children:new Date(l.day).toLocaleDateString("ru-RU")}),e.jsx("td",{className:"r",style:{color:"var(--violet)"},title:`Регистраций за день: ${l.regs||0}`,children:l.regs||0}),e.jsx("td",{className:"r",title:`Платежей за день: ${l.n}`,children:l.n}),e.jsx(ce,{n:l.fullN,cents:l.fullCents,money:b,label:"Полный тариф"}),e.jsx(ce,{n:l.trialN,cents:l.trialCents,money:b,label:"Пробный (вход по льготной цене)"}),e.jsx(ce,{n:l.trialConvN,cents:l.trialConvCents,money:b,tone:"var(--violet)",label:"Из пробного в полную оплату"}),e.jsx(ce,{n:l.renewN,cents:l.renewCents,money:b,label:"Продления"}
56),e.jsx(ce,{n:l.devicesN,cents:l.devicesCents,money:b,dim:!0,label:"Докупка устройств"}),e.jsx("td",{className:"r",style:{fontWeight:700,color:"var(--green)"},title:`Выручка: ${b(l.revCents)}`,children:b(l.revCents)}),e.jsx("td",{className:"r dim",title:`Средний чек: ${b(Math.round(l.avg))}`,children:b(Math.round(l.avg))})]},l.day)),(()=>{const l=o.revByDay.reduce((a,M)=>({n:a.n+(M.n||0),regs:a.regs+(M.regs||0),fullN:a.fullN+(M.fullN||0),fullCents:a.fullCents+(M.fullCents||0),trialN:a.trialN+(M.trialN||0),trialCents:a.trialCents+(M.trialCents||0),trialConvN:a.trialConvN+(M.trialConvN||0),trialConvCents:a.trialConvCents+(M.trialConvCents||0),renewN:a.renewN+(M.renewN||0),renewCents:a.renewCents+(M.renewCents||0),devicesN:a.devicesN+(M.devicesN||0),devicesCents:a.devicesCents+(M.devicesCents||0),revCents:a.revCents+(M.revCents||0)}),{n:0,regs:0,fullN:0,fullCents:0,trialN:0,trialCents:0,trialConvN:0,trialConvCents:0,renewN:0,renewCents:0,devicesN:0,devicesCents:0,revCents:0});return e.jsxs("tr",{style:{borderTop:"2px solid var(--border)",position:"sticky",bottom:0,background:"var(--surface)",fontWeight:600},children:[e.jsxs("td",{style:{padding:6},title:"Дней, в которые была хотя бы одна оплата или регистрация. Пустые дни в таблицу не попадают.",children:["Итого · ",o.revByDay.length," дн."]}),e.jsx("td",{className:"r",style:{color:"var(--violet)"},title:`Регистраций за период: ${l.regs}`,children:l.regs}),e.jsx("td",{className:"r",title:`Платежей за период: ${l.n}`,children:l.n}),e.jsx(ce,{n:l.fullN,cents:l.fullCents,money:b,label:"Полный тариф — за период"}),e.jsx(ce,{n:l.trialN,cents:l.trialCents,money:b,label:"Пробный — за период"}),e.jsx(ce,{n:l.trialConvN,cents:l.trialConvCents,money:b,tone:"var(--violet)",label:"Из пробного в полную оплату — за период"}),e.jsx(ce,{n:l.renewN,cents:l.renewCents,money:b,label:"Продления — за период"}),e.jsx(ce,{n:l.devicesN,cents:l.devicesCents,money:b,dim:!0,label:"Докупка устройств — за период"}),e.jsx("td",{className:"r",style:{fontWeight:700,color:"var(--green)"},title:`Выручка за период: ${b(l.revCents)}`,children:b(l.revCents)}),e.jsx("td",{className:"r dim",title:"Средний чек за период — выручка ÷ платежи",children:b(l.n?Math.round(l.revCents/l.n):0)})]})})()]})]})})]}),!!o.movement&&e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10},children:["Движение базы ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(растём или затыкаем отток)"})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(140px,1fr))",gap:10},children:[e.jsx($e,{label:"новые",value:o.movement.fresh,tone:"var(--green)"}),e.jsx($e,{label:"вернулись",value:o.movement.back,tone:"var(--cyan)"}),e.jsx($e,{label:"ушли",value:o.movement.gone,tone:o.movement.gone?"var(--coral)":void 0}),e.jsx($e,{label:"quick ratio",value:o.movement.quickRatio==null?"—":o.movement.quickRatio,tone:o.movement.quickRatio==null?void 0:o.movement.quickRatio>=1?"var(--green)":"var(--coral)"}),((R=o.stickiness)==null?void 0:R.pct)!=null&&e.jsx($e,{label:`липкость DAU/MAU (${o.stickiness.dau}/${o.stickiness.mau})`,value:o.stickiness.pct+"%"})]}),e.jsx("p",{className:"dim",style:{fontSize:12.5,margin:"10px 0 0",lineHeight:1.5,fontWeight:500,color:"var(--muted)"},children:o.movement.quickRatio==null?"Никто не ушёл за период — quick ratio не определён.":o.movement.quickRatio>=1?`На каждого ушедшего приходит ${o.movement.quickRatio} пришедших — база растёт.`:`На каждого ушедшего приходит лишь ${o.movement.quickRatio} — база сжимается, привлечение не покрывает отток.`}),e.jsxs("div",{className:"dim",style:{fontSize:12,margin:"10px 0 0",lineHeight:1.6},children:[e.jsx("b",{style:{color:"var(--muted)"},children:"Как считаем (по фактам оплат/подключений, не по полю статуса — reconcile ставит статус с задержкой):"}),e.jsxs("div",{style:{marginTop:4},children:["• ",e.jsx("b",{children:"новые"})," — юзеры, чья ",e.jsx("b",{children:"первая в истории"})," успешная оплата попала в период (MIN даты оплаты на юзера)."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"вернулись"})," — платили ",e.jsx("b",{children:"до"})," периода, потом перерыв ",e.jsx("b",{children:">35 дней"})," без оплат, и снова оплатили внутри периода (реактивация)."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"ушли"})," — подписка ",e.jsx("b",{children:"истекла"})," внутри периода И человек ",e.jsx("b",{children:"не вернулся"})," (нет ни одной успешной оплаты после даты истечения)."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"quick ratio"})," = (новые + вернулись) ÷ ушли. ",e.jsx("b",{children:">1"})," — растём, ",e.jsx("b",{children:"<1"})," — сжимаемся. Метрика из SaaS: показывает здоровье роста, а не голое число."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"липкость DAU/MAU"}
56)," = заходивших (реально подключались к сервису) за сутки ÷ за 30 дней. Высокая = пользуются часто; падает ",e.jsx("b",{children:"раньше"}),", чем отвал виден в деньгах — ранний сигнал оттока."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"Выручка везде НЕТТО"})," — возвраты вычтены той же суммой, какой пришли (и частичные тоже). Сколько вернули за период, написано под карточкой «Выручка». Песочница Apple не считается нигде: годовая подписка продлевается там каждые ~12 часов и была бы чистой выдумкой."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"Удалённые аккаунты в потоках периода считаются."})," Человек пришёл и заплатил — это произошло, и позднее удаление аккаунта историю не переписывает (деньги мы не вернули). Из «живых» счётчиков обзора (всего юзеров, активные подписки) удалённые, наоборот, исключены — там вопрос «сколько есть сейчас», а не «что случилось за период»."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"Почему выручка в таблицах разная."})," «По источнику» и «По лендингу» — ",e.jsx("b",{children:"когорта"}),": люди, которые ПРИШЛИ в этом периоде, и деньги, которые принесли они. «Кто привёл покупателя» — ",e.jsx("b",{children:"касса"}),": все успешные оплаты периода, включая продления давних клиентов. Поэтому касса обычно больше когорты, и сумма по кодам не обязана сходиться с итогом «по источнику». Первое отвечает «какой трафик окупается», второе — «откуда пришли деньги в кассу»."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"LTV"})," = выручка когорты ÷ покупатели — сколько в среднем принёс платящий. ",e.jsx("b",{children:"ARPU"})," = выручка когорты ÷ регистрации — сколько принёс каждый пришедший, включая тех, кто не заплатил. Когорта — пришедшие в периоде, деньги — их оплаты в том же периоде (с продлениями, за вычетом возвратов). На коротком периоде оба занижены: продления ещё не наступили — сравнивайте источники между собой, а для «сколько стоит клиент» берите длинный период."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"LTV/CAC"})," (в «По источнику») = средняя выручка с покупателя источника ÷ средняя комиссия партнёру за покупателя. Источник — партнёрский код, его комиссия — наша цена привлечения; свой трафик без расхода в системе показывает «—», а не ноль — «бесплатных» источников не рисуем. ",e.jsx("b",{children:"≥3"})," — здоровая экономика, ",e.jsx("b",{children:"1–3"})," — на грани, ",e.jsx("b",{children:"<1"})," — источник убыточен первым же платежом. Наведение на число раскрывает LTV и CAC по отдельности."]})]})]}),!!o.trials&&(o.trials.taken>0||o.trials.converted>0)&&e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10},children:["Пробный период ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(взяли пробный → доплатили полный тариф)"})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(150px,1fr))",gap:10},children:[e.jsx($e,{label:"взяли пробный",value:o.trials.taken}),e.jsx($e,{label:`в пробный от реги (${o.totals.regs})`,value:F(o.trials.taken,o.totals.regs)}),e.jsx($e,{label:"сконвертились",value:o.trials.converted,tone:"var(--green)"}),e.jsx($e,{label:"из пробного в платный",value:F(o.trials.converted,o.trials.taken),tone:"var(--green)"}),e.jsx($e,{label:"выручка с конверсий",value:b(o.trials.convRevCents)})]}),((ze=o.trialPrices)==null?void 0:ze.length)>1&&e.jsx("div",{style:{overflowX:"auto",marginTop:12},children:e.jsxs("table",{className:"tbl",style:{width:"100%",fontSize:12.5},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{textAlign:"left",padding:5},children:"цена входа"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"взяли"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"доплатили"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"конверсия"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"выручка с них"})]})}),e.jsx("tbody",{children:o.trialPrices.map(l=>e.jsxs("tr",{children:[e.jsx("td",{style:{padding:5,fontWeight:600},children:b(l.amountCents)}),e.jsx("td",{style:{padding:5,textAlign:"right",fontVariantNumeric:"tabular-nums"},children:l.n}),e.jsx("td",{style:{padding:5,textAlign:"right",fontVariantNumeric:"tabular-nums"},children:l.converted}),e.jsx("td",{style:{padding:5,textAlign:"right",color:"var(--green)"},children:F(l.converted,l.n)}),e.jsx("td",{style:{padding:5,textAlign:"right",fontVariantNumeric:"tabular-nums"},children:b(l.convRevCents)})]},l.amountCents))})]})}),o.trials.taken>o.trials.converted&&e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"10px 0 0"},children:["Не доплатили: ",o.trials.taken-o.trials.converted," — списание идёт за 4 часа до конца пробного дня, часть ещё в процессе."]})]}),!!o.rebills&&(((ke=o.rebills.cohort)==null?void 0:ke.length)>0||o.rebills.newCount>0)&&(()=>{const l=o.rebills,a=l.cohort||[],M=Math.max(1,...a.map(S=>S.count)),f=S=>S==null?"var(--muted)":S>=60?"var(--green)":S>=40?"var(--amber)":"var(--coral)";return e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10},children:["Ребилы ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(удержание за всё время · % = дошли из подошедших к сроку)"})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(150px,1fr))",gap:10,marginBottom:a.length?16:0},children:[e.jsx($e,{label:"новые покупки",value:l.newCount,tone:"var(--cyan)"}),e.jsx($e,{label:"выручка новых",value:b(l.newRevCents)}),e.jsx($e,{label:"продления",value:l.rebillCount,tone:"var(--green)"}),e.jsx($e,{label:"выручка продлений",value:b(l.rebillRevCents),tone:"var(--green)"}),e.jsx($e,{label:"дошли до 1-го продления",value:l.firstRebillPct==null?"—":l.firstRebillPct+"%",tone:f(l.firstRebillPct)})]}),a.length>0&&e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:6},children:a.map(S=>e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,fontSize:13},children:[e.jsxs("span",{className:"dim",style:{width:92,flex:"none"},children:[S.cycle,"-е продление"]}),e.jsx("div",{style:{flex:1,background:"var(--surface-2)",borderRadius:6,overflow:"hidden",height:22},children:e.jsx("div",{style:{width:`${Math.max(4,S.count/M*100)}%`,height:"100%",background:"linear-gradient(90deg,var(--green),var(--cyan))",borderRadius:6}})}),e.jsx("span",{title:"доля дошедших из тех, у кого срок уже кончился",style:{width:48,flex:"none",textAlign:"right",fontWeight:700,color:f(S.ratePct)},children:S.ratePct==null?"—":S.ratePct+"%"}),e.jsx("span",{style:{width:46,flex:"none",textAlign:"right",fontWeight:700},children:S.count})]},S.cycle))}),e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginTop:14,lineHeight:1.5},children:[e.jsx("b",{children:"Как это считается (важно):"}),e.jsxs("div",{children:["• Весь блок — ",e.jsx("b",{children:"ЗА ВСЁ ВРЕМЯ"}),", независимо от выбранного периода вверху. Удержание — свойство когорты, а не окна: «сколько из купивших дожили до N-го продления» имеет смысл только целиком."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"Полоса и число справа"})," — сколько уникальных клиентов ",e.jsx("b",{children:"дошли до N-го продления"})," (сделали хотя бы N продлений). Падение от полосы к полосе = где отваливаются."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"% (цветной)"})," — честная ставка удержания на шаге: ",e.jsx("b",{children:"дошедшие до N ÷ те, кому пора было продлевать"}),". В знаменателе ",e.jsx("u",{children:"только подошедшие к сроку"})," — у кого с последнего платежа прошло ≥ 35 дней (месяц + запас). Кто ещё внутри оплаченного периода (купил недавно, продлеваться рано) — в знаменатель НЕ попадает: иначе процент занижался бы теми, кто физически не мог продлиться."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"дошли до 1-го продления"})," в шапке — та же ставка для первого шаÐ
56³Ð° (купил → первое продление): главный индикатор, доходят ли новые до второго платежа."]}),e.jsxs("div",{children:["• ",e.jsx("b",{children:"новые покупки / продления"})," и их выручка — суммарные счётчики за всё время (рост vs удержание)."]}),e.jsx("div",{children:"• Считается тяжело (все оплаты), поэтому кэшируется на час — цифры могут отставать до 60 мин; период вверху на этот блок не влияет."}),e.jsx("div",{style:{marginTop:4},children:"Пример чтения: «2-е продление 60%» = из тех, у кого 2-й срок уже кончился, продлили 60% — здоровье; «40%» — на этом шаге теряем больше половины, повод смотреть цену/оффер."})]})]})})(),!g&&e.jsxs("div",{className:"card pad",style:{marginBottom:12,overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{children:["По источнику ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(first-touch · когорта: кто ПРИШЁЛ за период)"})]}),e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("sources",["source","uniques","regs","paid","CR%","revenue","LTV","ARPU"],be(o.sources,V,"source").map(l=>[l.source,l.uniques,l.regs,l.paid,l.uniques||l.regs?(l.paid/(l.uniques||l.regs)*100).toFixed(1):"0",(l.revCents/100).toFixed(2),((l.ltvCents||0)/100).toFixed(2),((l.arpuCents||0)/100).toFixed(2)])),children:"CSV"})]}),e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx(w,{k:"source",cur:V,set:T,children:"источник"}),e.jsx(w,{k:"uniques",cur:V,set:T,right:!0,children:"уники"}),e.jsx(w,{k:"regs",cur:V,set:T,right:!0,children:"реги"}),e.jsx(w,{k:"paid",cur:V,set:T,right:!0,children:"покупателей"}),e.jsx(w,{k:"cr",cur:V,set:T,right:!0,children:"CR"}),e.jsx(w,{k:"revCents",cur:V,set:T,right:!0,children:"выручка когорты"}),e.jsx(w,{k:"ltvCents",cur:V,set:T,right:!0,children:"LTV"}),e.jsx(w,{k:"arpuCents",cur:V,set:T,right:!0,children:"ARPU"}),e.jsx(w,{k:"ltvCac",cur:V,set:T,right:!0,children:"LTV/CAC"})]})}),e.jsxs("tbody",{children:[be(o.sources,V,"source").map(l=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6},children:l.source}),e.jsx("td",{className:"r",children:l.uniques}),e.jsx("td",{className:"r",children:l.regs}),e.jsx("td",{className:"r",style:{color:"var(--green)"},children:l.paid}),e.jsx("td",{className:"r dim",title:ue?Ne:void 0,children:fe(l.paid,l.uniques||l.regs)}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(l.revCents)}),e.jsx("td",{className:"r",children:l.paid?b(l.ltvCents):e.jsx("span",{className:"dim",children:"—"})}),e.jsx("td",{className:"r dim",children:l.regs?b(l.arpuCents):"—"}),e.jsx("td",{className:"r",title:l.cacCents?`LTV ${b(l.ltvCents)} · CAC ${b(l.cacCents)}`:"расход по источнику неизвестен",children:l.ltvCac==null?e.jsx("span",{className:"dim",children:"—"}):e.jsx("span",{style:{color:l.ltvCac>=3?"var(--green)":l.ltvCac>=1?"var(--amber)":"var(--coral)",fontWeight:600},children:l.ltvCac})})]},l.source)),(!o.sources||!o.sources.length)&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:9,style:{padding:10},children:"нет данных — заходы ещё не накопились"})})]})]})]}),(()=>{const l=o.acquisition||[],a=new Map;for(const f of l){const S=f.code||"",H=a.get(S)||{code:f.code,buyers:0,payments:0,revCents:0,subs:[]};H.buyers+=f.buyers,H.payments+=f.payments,H.revCents+=f.revCents,H.subs.push(f),a.set(S,H)}const M=[...a.values()].sort((f,S)=>S.revCents-f.revCents);return e.jsxs("div",{className:"card pad",style:{marginBottom:12,overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{children:["Кто привёл покупателя ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(касса: ВСЕ оплаты периода, включая старых клиентов)"})]}),e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("acquisition",["code","subid","buyers","payments","revenue"],l.map(f=>[f.code||"органика",f.subid||"",f.buyers,f.payments,(f.revCents/100).toFixed(2)])),children:"CSV"})]}),e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx("th",{style:{padding:6},children:"кто привёл"}),e.jsx("th",{className:"r",style:{padding:6},children:"покупателей"}),e.jsx("th",{className:"r",style:{padding:6},children:"оплат"}),e.jsx("th",{className:"r",style:{padding:6},children:"выручка за период"}),e.jsx("th",{className:"r",style:{padding:6},children:"средний чек"}),e.jsx("th",{className:"r",style:{padding:6},children:"меток"})]})}),e.jsxs("tbody",{children:[M.map(f=>e.jsxs(r.Fragment,{children:[e.jsxs("tr",{style:{borderTop:"1px solid var(--border)",cursor:f.subs.length>1?"pointer":"default"},onClick:()=>f.subs.length>1&&q(y===(f.code||"organic")?null:f.code||"organic"),children:[e.jsxs("td",{style:{padding:6,fontWeight:600},children:[f.code||e.jsx("span",{style:{color:"var(--green)"},children:"органика"}),f.subs.length>1&&e.jsx("span",{className:"dim",style:{marginLeft:6,fontSize:12},children:y===(f.code||"organic")?"▾":"▸"})]}),e.jsx("td",{className:"r",children:f.buyers}),e.jsx("td",{className:"r",children:f.payments}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(f.revCents)}),e.jsx("td",{className:"r dim",children:f.payments?b(Math.round(f.revCents/f.payments)):"—"}),e.jsx("td",{className:"r dim",children:f.subs.filter(S=>S.subid).length||"—"})]}),y===(f.code||"organic")&&f.subs.slice().sort((S,H)=>H.revCents-S.revCents).slice(0,30).map((S,H)=>e.jsxs("tr",{style:{color:"var(--muted)",fontSize:12.5},children:[e.jsx("td",{style:{padding:"4px 6px 4px 22px"},className:S.subid?"":"dim",children:S.subid||"без метки"}),e.jsx("td",{className:"r",children:S.buyers}),e.jsx("td",{className:"r",children:S.payments}),e.jsx("td",{className:"r",children:b(S.revCents)}),e.jsx("td",{}),e.jsx("td",{})]},H))]},f.code||"organic")),!M.length&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:6,style:{padding:10},children:"за период оплат не было"})})]})]}),M.some(f=>f.subs.length>20)&&e.jsx("p",{className:"dim",style:{fontSize:12.5,margin:"10px 0 0",lineHeight:1.5},children:"У кода с десятками меток вебмастер кладёт в subid идентификатор клика — тогда метка перестаёт быть срезом кампании. Для клика есть отдельное поле clickid: оно уходит в постбэк и не ломает отчёт."})]})})(),e.jsxs("div",{className:"card pad",style:{marginBottom:12,overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{children:["По лендингу ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(первый вход · когорта: кто ПРИШЁЛ за период; не-ленды = «без ленда»)"})]}),e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("landings",["landing","uniques","regs","paid","CR%","revenue","LTV","ARPU"],be(o.landings,L,"landing").map(l=>[l.landing,l.uniques,l.regs,l.paid,l.uniques||l.regs?(l.paid/(l.uniques||l.regs)*100).toFixed(1):"0",(l.revCents/100).toFixed(2),((l.ltvCents||0)/100).toFixed(2),((l.arpuCents||0)/100).toFixed(2)])),children:"CSV"})]}),e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx(w,{k:"landing",cur:L,set:se,children:"лендинг"}),e.jsx(w,{k:"uniques",cur:L,set:se,right:!0,children:"уники"}),e.jsx(w,{k:"regs",cur:L,set:se,right:!0,children:"реги"}),e.jsx(w,{k:"paid",cur:L,set:se,right:!0,children:"покупателей"}),e.jsx(w,{k:"cr",cur:L,set:se,right:!0,children:"CR"}),e.jsx(w,{k:"revCents",cur:L,set:se,right:!0,children:"выручка"}),e.jsx(w,{k:"ltvCents",cur:L,set:se,right:!0,children:"LTV"}),e.jsx(w,{k:"arpuCents",cur:L,set:se,right:!0,children:"ARPU"})]})}),e.jsxs("tbody",{children:[pr(be(o.landings,L,"landing")).map(l
56=>e.jsxs(r.Fragment,{children:[e.jsx("tr",{children:e.jsxs("td",{colSpan:8,style:{padding:"12px 6px 4px",fontSize:12,color:"var(--muted)",textTransform:"uppercase",letterSpacing:".06em"},children:[l.title," ",e.jsxs("span",{style:{opacity:.6},children:["· ",l.rows.length]})]})}),l.rows.map(a=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6,fontFamily:"ui-monospace, monospace"},children:Zs(a.landing)?e.jsx("a",{href:Zs(a.landing),target:"_blank",rel:"noopener noreferrer",children:a.landing}):a.landing}),e.jsx("td",{className:"r",children:a.uniques}),e.jsx("td",{className:"r",children:a.regs}),e.jsx("td",{className:"r",style:{color:"var(--green)"},children:a.paid}),e.jsx("td",{className:"r dim",title:ue?Ne:void 0,children:fe(a.paid,a.uniques||a.regs)}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(a.revCents)}),e.jsx("td",{className:"r",children:a.paid?b(a.ltvCents):e.jsx("span",{className:"dim",children:"—"})}),e.jsx("td",{className:"r dim",children:a.regs?b(a.arpuCents):"—"})]},a.landing)),l.rows.length>1&&(()=>{const a=en(l.rows);return e.jsxs("tr",{className:"totrow",style:{borderTop:"1px solid var(--border-strong)"},children:[e.jsxs("td",{style:{padding:6,color:"var(--muted)"},children:["итого · ",l.title.toLowerCase()]}),e.jsx("td",{className:"r",style:{fontWeight:700},children:a.uniques}),e.jsx("td",{className:"r",style:{fontWeight:700},children:a.regs}),e.jsx("td",{className:"r",style:{fontWeight:700,color:"var(--green)"},children:a.paid}),e.jsx("td",{className:"r dim",title:ue?Ne:void 0,children:fe(a.paid,a.base)}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(a.revCents)}),e.jsx("td",{className:"r",children:a.paid?b(Math.round(a.revCents/a.paid)):"—"}),e.jsx("td",{className:"r dim",children:a.regs?b(Math.round(a.revCents/a.regs)):"—"})]})})()]},l.title)),!!(o.landings&&o.landings.length)&&(()=>{const l=en(o.landings);return e.jsxs("tr",{className:"totrow",style:{borderTop:"2px solid var(--border-strong)"},children:[e.jsx("td",{style:{padding:"10px 6px",fontWeight:700},children:"всего"}),e.jsx("td",{className:"r",style:{fontWeight:700},children:l.uniques}),e.jsx("td",{className:"r",style:{fontWeight:700},children:l.regs}),e.jsx("td",{className:"r",style:{fontWeight:700,color:"var(--green)"},children:l.paid}),e.jsx("td",{className:"r dim",title:ue?Ne:void 0,children:fe(l.paid,l.base)}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(l.revCents)}),e.jsx("td",{className:"r",style:{fontWeight:700},children:l.paid?b(Math.round(l.revCents/l.paid)):"—"}),e.jsx("td",{className:"r dim",children:l.regs?b(Math.round(l.revCents/l.regs)):"—"})]})})(),(!o.landings||!o.landings.length)&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:8,style:{padding:10},children:"нет данных — заходы ещё не накопились"})})]})]})]}),!g&&(()=>{const l=o.variants||[],a=p=>p.replace(/(light|dark)$/,"")||p,M=`https://order.${Xe.registrable}`,f=new Map;for(const p of l){const K=a(String(p.variant||"")),re=f.get(K)||{key:K,uniques:0,starts:0,paid:0,revCents:0,subs:[]};re.uniques+=p.uniques||0,re.starts+=p.starts||0,re.paid+=p.paid||0,re.revCents+=p.revCents||0,re.subs.push(p),f.set(K,re)}const S=[...f.values()].map(p=>({...p,crStart:p.uniques?Math.round(p.starts/p.uniques*1e3)/10:null,crPaid:p.uniques?Math.round(p.paid/p.uniques*1e3)/10:null})),H=p=>`${M}/?v=${encodeURIComponent(p)}&vpreview=1`;return e.jsxs("div",{className:"card pad",style:{marginBottom:12,overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{children:["По шаблону оплаты ",e.jsx("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:"(клик по названию — предпросмотр, по стрелке — тёмная и светлая отдельно)"})]}),e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("variants",["variant","uniques","started","paid","CR_start%","CR_paid%","revenue"],be(l,U,"variant").map(p=>[p.variant,p.uniques,p.starts,p.paid,p.crStart??0,p.crPaid??0,(p.revCents/100).toFixed(2)])),children:"CSV"})]}),e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx(w,{k:"key",cur:U,set:ne,children:"шаблон"}),e.jsx(w,{k:"uniques",cur:U,set:ne,right:!0,children:"видели"}),e.jsx(w,{k:"starts",cur:U,set:ne,right:!0,children:"дошли"}),e.jsx(w,{k:"paid",cur:U,set:ne,right:!0,children:"покупателей"}),e.jsx(w,{k:"crStart",cur:U,set:ne,right:!0,children:"CR→оплату"}),e.jsx(w,{k:"crPaid",cur:U,set:ne,right:!0,children:"CR покупки"}),e.jsx(w,{k:"revCents",cur:U,set:ne,right:!0,children:"выручка"})]})}
56),e.jsxs("tbody",{children:[be(S,U,"key").map(p=>e.jsxs(r.Fragment,{children:[e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsxs("td",{style:{padding:6,fontFamily:"ui-monospace, monospace"},children:[e.jsx("a",{href:H(p.key),target:"_blank",rel:"noopener",style:{color:"var(--violet-2)",textDecoration:"none"},children:p.key}),p.subs.length>1&&e.jsx("button",{onClick:()=>oe(P===p.key?null:p.key),title:"тёмная и светлая по отдельности",style:{marginLeft:6,background:"none",border:"none",color:"var(--muted)",cursor:"pointer",font:"inherit",fontSize:12,padding:0},children:P===p.key?"▾":"▸"})]}),e.jsx("td",{className:"r",children:p.uniques}),e.jsx("td",{className:"r",children:p.starts}),e.jsx("td",{className:"r",style:{color:"var(--green)"},children:p.paid}),e.jsx("td",{className:"r dim",children:p.crStart==null?"—":p.crStart+"%"}),e.jsx("td",{className:"r dim",children:p.crPaid==null?"—":p.crPaid+"%"}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(p.revCents)})]}),P===p.key&&p.subs.slice().sort((K,re)=>re.uniques-K.uniques).map(K=>e.jsxs("tr",{style:{color:"var(--muted)",fontSize:12.5},children:[e.jsxs("td",{style:{padding:"4px 6px 4px 22px",fontFamily:"ui-monospace, monospace"},children:[e.jsx("a",{href:H(K.variant),target:"_blank",rel:"noopener",style:{color:"inherit"},children:K.variant}),e.jsx("span",{className:"dim",style:{marginLeft:6},children:/(light)$/.test(K.variant)?"светлая":"тёмная"})]}),e.jsx("td",{className:"r",children:K.uniques}),e.jsx("td",{className:"r",children:K.starts}),e.jsx("td",{className:"r",children:K.paid}),e.jsx("td",{className:"r",children:K.crStart==null?"—":K.crStart+"%"}),e.jsx("td",{className:"r",children:K.crPaid==null?"—":K.crPaid+"%"}),e.jsx("td",{className:"r",children:b(K.revCents)})]},K.variant))]},p.key)),S.length>1&&(()=>{const p=S.reduce((K,re)=>({uniques:K.uniques+(re.uniques||0),starts:K.starts+(re.starts||0),paid:K.paid+(re.paid||0),revCents:K.revCents+(re.revCents||0)}),{uniques:0,starts:0,paid:0,revCents:0});return e.jsxs("tr",{className:"totrow",style:{borderTop:"2px solid var(--border-strong)"},children:[e.jsx("td",{style:{padding:"10px 6px",fontWeight:700},children:"всего"}),e.jsx("td",{className:"r",style:{fontWeight:700},children:p.uniques}),e.jsx("td",{className:"r",style:{fontWeight:700},children:p.starts}),e.jsx("td",{className:"r",style:{fontWeight:700,color:"var(--green)"},children:p.paid}),e.jsx("td",{className:"r dim",children:F(p.starts,p.uniques)}),e.jsx("td",{className:"r dim",children:F(p.paid,p.uniques)}),e.jsx("td",{className:"r",style:{fontWeight:700},children:b(p.revCents)})]})})(),!S.length&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:7,style:{padding:10},children:"нет данных — заходы по шаблонам ещё не накопились"})})]})]})]})})(),e.jsxs("div",{className:"card pad",style:{overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:4,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{children:["Зарегались, но не купили ",e.jsxs("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:["(",((Le=o.notBought)==null?void 0:Le.length)||0,") — база для ретаргета"]})]}),!!((Ee=o.notBought)!=null&&Ee.length)&&e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("not-bought",["email","registered"],o.notBought.map(l=>[l.email||l.id,new Date(l.createdAt).toISOString().slice(0,10)])),children:"CSV"})]}),e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:e.jsxs("tbody",{children:[(o.notBought||[]).slice(0,100).map(l=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6},children:l.email||l.id.slice(0,8)}),e.jsx("td",{className:"dim r",children:new Date(l.createdAt).toLocaleDateString("ru-RU")}),e.jsx("td",{className:"r",children:l.email&&e.jsx("a",{href:"mailto:"+l.email,style:{color:"var(--violet-2)",fontSize:12.5},children:"написать"})})]},l.id)),(!o.notBought||!o.notBought.length)&&e.jsx("tr",{children:e.jsx("td",{className:"dim",style:{padding:10},children:"все зарегавшиеся купили 🎉"})})]})})]}),!!((Fe=o.churned)!=null&&Fe.length)&&e.jsxs("div",{className:"card pad",style:{marginTop:12,overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:4,display:"flex",alignItems:"center",gap:8},children:[e.jsxs("span",{children:["Ушедшие ",e.jsxs("span",{className:"dim",style:{fontSize:12,fontWeight:400},children:["(",o.churned.length,") — подписка кончилась и не продлена, база для winback"]})]}),e.jsx("button",{className:"btn btn--ghost",style:{marginLeft:"auto",padding:"5px 10px",fontSize:12},onClick:()=>d("churned",["email","plan","expired","last_online"],o.churned.map(l=>[l.email,l.plan||"",new Date(l.expiresAt).toISOString().slice(0,10),l.lastOnlineAt?new Date(l.lastOnlineAt).toISOString().slice(0,10):""])),children:"CSV"})]}),e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"}
56,children:e.jsx("tbody",{children:o.churned.slice(0,100).map(l=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6},children:l.email}),e.jsx("td",{className:"dim",children:l.plan||"—"}),e.jsx("td",{className:"dim r",title:"когда кончилась подписка",children:new Date(l.expiresAt).toLocaleDateString("ru-RU")}),e.jsx("td",{className:"dim r",title:"последний онлайн",children:l.lastOnlineAt?new Date(l.lastOnlineAt).toLocaleDateString("ru-RU"):"—"}),e.jsx("td",{className:"r",children:e.jsx("a",{href:"mailto:"+l.email,style:{color:"var(--violet-2)",fontSize:12.5},children:"написать"})})]},l.id))})})]})]})]})}function ir({api:s}){var l;const[t,m]=r.useState([]),[n,x]=r.useState(""),[i,C]=r.useState(!1),[u,k]=r.useState(!1),[g,I]=r.useState({email:"",plan:"pro",period:"1y"}),[E,X]=r.useState(""),[D,W]=r.useState({key:"created",dir:"desc"}),[o,N]=r.useState(!1),[v,_]=r.useState(""),[B,Q]=r.useState([]),[A,$]=r.useState([]),[V,T]=r.useState(""),[y,q]=r.useState([]);r.useEffect(()=>{let a=!0;return s("/partner/admin/list").then(M=>{a&&Q(((M==null?void 0:M.partners)||[]).filter(f=>f.code))}).catch(()=>{}),s("/admin/nx-codes").then(M=>{a&&$((M==null?void 0:M.codes)||[])}).catch(()=>{}),()=>{a=!1}},[]),r.useEffect(()=>{let a=!0;return s("/admin/user-subids"+(v&&v!=="-"?"?partner="+encodeURIComponent(v):"")).then(M=>{a&&q((M==null?void 0:M.subids)||[])}).catch(()=>{a&&q([])}),()=>{a=!1}},[v]);const[P,oe]=r.useState([]),L=r.useRef(0),se=(a=0,M=!1,f,S,H,p,K)=>{k(!0);const re=++L.current,je=f||D,c=S===void 0?o:S,te=H===void 0?v:H,ve=(K===void 0?n:K).trim(),Se=new URLSearchParams;ve&&Se.set("q",ve),a&&Se.set("offset",String(a)),(je.key!=="created"||je.dir!=="desc")&&(Se.set("sort",je.key),Se.set("dir",je.dir)),c&&Se.set("paid","1"),te&&Se.set("partner",te);const pe=p===void 0?V:p;return pe&&Se.set("subid",pe),s("/admin/users"+(Se.toString()?"?"+Se:"")).then(Re=>{re===L.current&&(m(Ie=>M?[...Ie,...Re.users||[]]:Re.users||[]),C(!!Re.hasMore))}).catch(()=>{}).finally(()=>{re===L.current&&k(!1)})};r.useEffect(()=>{se(0)},[]);const U=r.useRef(null),ne=a=>{clearTimeout(U.current),U.current=setTimeout(()=>{if(se(0,!1,void 0,void 0,void 0,void 0,a),a.includes("@")&&a.trim().length>5){const M=L.current;s("/admin/users/similar?email="+encodeURIComponent(a.trim())).then(f=>{M===L.current&&oe(f.similar||[])}).catch(()=>{M===L.current&&oe([])})}else oe([])},350)};r.useEffect(()=>()=>clearTimeout(U.current),[]);const le=t,ee=()=>{const M=[["email","telegram","plan","status","autoRenew","expiresAt","createdAt"].join(";"),...t.map(S=>{var H,p,K;return[S.email||S.deletedEmail||S.id,S.telegramUsername?"@"+S.telegramUsername:S.hasTelegram?"id:"+S.telegramId:"",((H=S.subscription)==null?void 0:H.plan)||"",((p=S.subscription)==null?void 0:p.status)||"",S.autoRenew?"1":"0",(K=S.subscription)!=null&&K.expiresAt?new Date(S.subscription.expiresAt).toISOString().slice(0,10):"",new Date(S.createdAt).toISOString()].join(";")})].join(`
57`),f=document.createElement("a");f.href=URL.createObjectURL(new Blob(["\uFEFF"+M],{type:"text/csv;charset=utf-8"})),f.download="users.csv",document.body.appendChild(f),f.click(),document.body.removeChild(f),setTimeout(()=>URL.revokeObjectURL(f.href),1e3)},[be,w]=r.useState(!1),d=r.useRef(null),b=a=>{d.current=null,I(a)},F=async()=>{var a;if(!be){w(!0),X(""),d.current||(d.current=((a=crypto.randomUUID)==null?void 0:a.call(crypto))||Date.now().toString(36)+Math.random().toString(36).slice(2,12));try{await s("/admin/grant",{...g,requestId:d.current}),d.current=null,X("Выдано ✓"),se()}catch(M){X("Ошибка: "+M.message)}finally{w(!1)}}},Z=async a=>{if(confirm(`Отозвать подписку ${a}?`))try{await s("/admin/revoke",{email:a}),se()}catch(M){alert("Ошибка: "+M.message)}},xe=async a=>{try{const M=await s("/admin/impersonate",{userId:a.id});M!=null&&M.url?window.open(M.url,"_blank","noopener"):(M==null?void 0:M.error)==="two_factor"?alert("У пользователя включена двухфакторная аутентификация — вход под ним недоступен. Данные аккаунта смотрите через «Управление»."):alert("Нет email — magic-логин недоступен")}catch(M){alert("Ошибка: "+M.message)}},[ue,Ne]=r.useState(null),[fe,ce]=r.useState(null),[Be,Ae]=r.useState(""),[Ce,R]=r.useState(!1),[ze,ke]=r.useState(""),Le=fe?String(fe.email||fe.id||"").trim().toLowerCase():"",Ee=!!fe&&!!Le&&Be.trim().toLowerCase()===Le,Fe=async()=>
57{if(!(!Ee||!fe)){R(!0),ke("");try{await s("/admin/delete-user",{userId:fe.id,confirm:Be.trim()}),ce(null),Ae(""),se()}catch(a){ke((a==null?void 0:a.message)||"Ошибка")}finally{R(!1)}}};return e.jsxs(e.Fragment,{children:[ue&&e.jsx(Ht,{api:s,userId:ue.id,email:ue.email,onClose:()=>Ne(null),onChanged:se}),e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:10},children:"Выдать подписку"}),e.jsxs("div",{style:{display:"flex",gap:10,flexWrap:"wrap",alignItems:"center"},children:[e.jsx("input",{style:{...We,flex:2,minWidth:200},placeholder:"email",value:g.email,onChange:a=>b({...g,email:a.target.value})}),e.jsxs("select",{style:{...We,flex:1},value:g.plan,onChange:a=>b({...g,plan:a.target.value}),children:[e.jsx("option",{value:"start",children:"start"}),e.jsx("option",{value:"pro",children:"pro"}),e.jsx("option",{value:"plus",children:"plus"})]}),e.jsxs("select",{style:{...We,flex:1},value:g.period,onChange:a=>b({...g,period:a.target.value}),children:[e.jsx("option",{value:"mo",children:"1 мес"}),e.jsx("option",{value:"1y",children:"1 год"}),e.jsx("option",{value:"2y",children:"2 года"})]}),e.jsx("button",{className:"btn",disabled:be,onClick:F,children:be?"Выдаю…":"Выдать"}),E&&e.jsx("span",{className:"dim",style:{fontSize:13},children:E})]})]}),e.jsxs("div",{className:"atools",children:[e.jsx("input",{className:"atools__q",style:We,placeholder:"Поиск по email…",value:n,onChange:a=>{x(a.target.value),ne(a.target.value)},onKeyDown:a=>{a.key==="Enter"&&se(0)}}),e.jsxs("div",{className:"atools__btns",children:[e.jsx("button",{className:"btn",style:{padding:9},onClick:()=>se(0),title:"Найти","aria-label":"Найти",children:e.jsx(us,{size:16,strokeWidth:2.2})}),e.jsx(vt,{onClick:()=>se(0)}),e.jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:7,fontSize:12.5,color:"var(--muted)",cursor:"pointer",whiteSpace:"nowrap"},title:"Показывать только тех, у кого есть успешный платёж",children:[e.jsx("input",{type:"checkbox",checked:o,onChange:a=>{N(a.target.checked),se(0,!1,void 0,a.target.checked)}}),"только покупавшие"]}),e.jsxs("select",{style:{...We,padding:"8px 10px",fontSize:12.5,maxWidth:190},value:v,onChange:a=>{_(a.target.value),T(""),se(0,!1,void 0,void 0,a.target.value,"")},title:"Чей трафик",children:[e.jsx("option",{value:"",children:"все партнёры"}),e.jsx("option",{value:"-",children:"без партнёра (свои)"}),B.map(a=>e.jsxs("option",{value:a.code,children:[a.code,a.contact?" · "+String(a.contact).slice(0,18):""]},a.code)),!!A.length&&e.jsx("optgroup",{label:"nexora",children:A.map(a=>e.jsx("option",{value:"nx:"+a.code,children:as(a)},"nx:"+a.code))})]}),e.jsxs("select",{style:{...We,padding:"8px 10px",fontSize:12.5,maxWidth:190},value:V,onChange:a=>{T(a.target.value),se(0,!1,void 0,void 0,void 0,a.target.value)},title:"Метка кампании (subid)",children:[e.jsx("option",{value:"",children:"все метки"}),e.jsx("option",{value:"-",children:"без метки"}),y.map(a=>e.jsxs("option",{value:a.subid,children:[String(a.subid).slice(0,22)," · ",a.n]},a.subid))]}),!!t.length&&e.jsx("button",{className:"btn btn--ghost",style:{padding:"9px 14px",fontSize:12.5},onClick:ee,children:"CSV"})]})]}),!!P.length&&e.jsxs("div",{className:"card pad",style:{marginBottom:10,borderColor:"var(--amber)"},children:[e.jsxs("div",{style:{fontWeight:700,fontSize:13.5,marginBottom:6},children:["Похожие адреса ",e.jsx("span",{className:"dim",style:{fontWeight:400,fontSize:12},children:"— возможно, тот же человек платил отсюда"})]}),P.map(a=>{var M;return e.jsxs("div",{style:{display:"flex",gap:10,alignItems:"center",flexWrap:"wrap",padding:"4px 0",fontSize:13},children:[e.jsx("button",{onClick:()=>{x(a.email),ne(a.email)},title:"искать этот адрес",style:{background:"none",border:"none",padding:0,font:"inherit",color:"var(--violet-2)",cursor:"pointer",fontWeight:600},children:a.email}),e.jsx("span",{className:"dim",children:((M=a.subscription)==null?void 0:M.status)==="active"?`${a.subscription.plan} · до ${new Date(a.subscription.expiresAt).toLocaleDateString("ru-RU")}`:"без подписки"}),e.jsx("span",{className:"dim",children:a.paidCount?`оплат: ${a.paidCount}
57 · ${(a.paidCents/100).toLocaleString("ru-RU",{maximumFractionDigits:0})} ₽`:"не платил"}),e.jsxs("span",{className:"dim",style:{marginLeft:"auto",fontSize:12},children:["рег ",new Date(a.createdAt).toLocaleDateString("ru-RU")]})]},a.id)})]}),e.jsxs("div",{className:"card pad",style:{overflowX:"auto"},children:[e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[[["email","email"],["tg","tg"],["plan","тариф"],["status","статус"],["auto","авто"],["until","до"],["created","рега"]].map(([a,M],f)=>{const S=D.key===a,H={key:a,dir:S&&D.dir==="desc"?"asc":"desc"};return e.jsxs("th",{style:{padding:f===0?6:void 0,cursor:"pointer",userSelect:"none",color:S?"var(--text)":void 0},title:`Сортировать по «${M}»`,onClick:()=>{W(H),se(0,!1,H)},children:[M,e.jsx("span",{style:{marginLeft:4,opacity:S?.9:.25,fontSize:10},children:S&&D.dir==="asc"?"▲":"▼"})]},a)}),e.jsx("th",{})]})}),e.jsx("tbody",{children:le.map(a=>{var M,f;return e.jsxs("tr",{style:{borderTop:"1px solid var(--border)",opacity:a.deletedAt?.5:1},children:[e.jsxs("td",{style:{padding:6},children:[a.email||(a.deletedEmail?e.jsx("span",{className:"dim",title:"Адрес на момент удаления (войти по нему нельзя)",children:e.jsx("s",{children:a.deletedEmail})}):e.jsx("span",{className:"dim",title:a.deletedAt?a.deletedBy==="self"?"Удалил себя сам":a.deletedBy?"Удалил оператор: "+String(a.deletedBy).replace(/^admin:/,""):"Кто удалил — неизвестно (до появления пометки)":"Почты у аккаунта нет",children:a.hasTelegram?"телеграм ✓":a.deletedAt?"№ "+a.id.slice(0,8):"без почты"})),a.isAdmin?" 👑":"",a.deletedAt?e.jsx("span",{title:"Удалён "+new Date(a.deletedAt).toLocaleString(),style:{marginLeft:6,fontSize:11,color:"var(--coral)",border:"1px solid var(--coral)",borderRadius:4,padding:"0 5px"},children:"удалён"}):"",a.partner&&e.jsxs("span",{title:a.partner.nx?`nexora · партнёр ${a.partner.code||"—"}${a.partner.email?" ("+a.partner.email+(a.partner.contact?", "+a.partner.contact:"")+")":""}${a.partner.flow?" · поток "+a.partner.flow+(a.partner.flowTitle?" «"+a.partner.flowTitle+"»":""):""}${a.partner.subid?" · метка "+a.partner.subid:""}`:`Партнёр ${a.partner.code}${a.partner.subid?" · метка "+a.partner.subid:""}`,className:"pbadge",children:[a.partner.nx?"nx · "+(a.partner.flowTitle||a.partner.flow||a.partner.code)+(a.partner.email?" · "+String(a.partner.email).split("@")[0]:""):a.partner.code,a.partner.subid?" · "+String(a.partner.subid):""]})]}),e.jsx("td",{style:{whiteSpace:"nowrap"},children:a.telegramUsername?e.jsxs("a",{href:`https://t.me/${a.telegramUsername}`,target:"_blank",rel:"noopener",style:{color:"var(--violet-2)",fontSize:12.5},children:["@",a.telegramUsername]}):a.hasTelegram?e.jsx("a",{href:`tg://user?id=${a.telegramId}`,title:`Telegram привязан, без @username · id ${a.telegramId} · писать можно, бот шлёт по chat_id`,style:{color:"var(--green)",fontSize:12.5},children:a.name||"привязан"}):e.jsx("span",{className:"dim",children:"—"})}),e.jsx("td",{children:vn((M=a.subscription)==null?void 0:M.plan)}),e.jsx("td",{children:a.subscription?Mi(a.subscription.status):e.jsx("span",{className:"dim",children:"—"})}),e.jsx("td",{children:a.autoRenew?a.hasToken?e.jsx("span",{style:{color:"var(--green)",display:"inline-flex"},title:"Автопродление включено, токен/карта есть","aria-label":"Автопродление включено",children:e.jsx(Mt,{size:15,strokeWidth:2.2})}):e.jsx("span",{style:{color:"var(--amber)",display:"inline-flex"},title:"Включено, но токена/карты нет — списать нечем","aria-label":"Автопродление без токена",children:e.jsx(Un,{size:15,strokeWidth:2.2})}):e.jsx("span",{className:"dim",children:"—"})}),e.jsx("td",{style:{whiteSpace:"nowrap"},children:(()=>{var H;if(!((H=a.subscription)!=null&&H.expiresAt))return e.jsx("span",{className:"dim",children:"—"});const S=Ui(a.subscription.expiresAt);return e.jsx("span",{style:{color:S.color,fontWeight:600},title:S.hint,children:new Date(a.subscription.expiresAt).toLocaleDateString("ru-RU")})})()}),e.jsx("td",{className:"dim",title:new Date(a.createdAt).toLocaleStr
57ing("ru-RU"),children:Me(a.createdAt)}),e.jsxs("td",{style:{whiteSpace:"nowrap"},children:[e.jsx("button",{className:"iconbtn",title:"Управление пользователем","aria-label":"Управление пользователем",onClick:()=>Ne({id:a.id,email:a.email}),children:e.jsx(on,{size:15,strokeWidth:2})}),a.email&&e.jsx("button",{className:"iconbtn",title:"Войти под пользователем","aria-label":"Войти под пользователем",onClick:()=>xe(a),children:e.jsx(rn,{size:15,strokeWidth:2})}),((f=a.subscription)==null?void 0:f.status)==="active"&&a.email&&e.jsx("button",{className:"iconbtn iconbtn--danger",title:"Отозвать подписку","aria-label":"Отозвать подписку",onClick:()=>Z(a.email),children:e.jsx(Ot,{size:15,strokeWidth:2})}),!a.isAdmin&&!a.deletedAt&&e.jsx("button",{className:"iconbtn iconbtn--muted",title:"Полностью удалить юзера (нода + БД)","aria-label":"Удалить пользователя",onClick:()=>{ke(""),Ae(""),ce(a)},children:e.jsx(dn,{size:15,strokeWidth:2})})]})]},a.id)})})]}),e.jsxs("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",gap:12,marginTop:12},children:[e.jsxs("span",{className:"dim",style:{fontSize:12.5},children:["показано ",t.length]}),i&&e.jsx("button",{className:"btn btn--ghost",disabled:u,onClick:()=>se(t.length,!0),children:u?"…":"Показать ещё"})]})]}),fe&&e.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.6)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:16},onClick:()=>!Ce&&ce(null),children:e.jsxs("div",{className:"modal pad",style:{maxWidth:460,width:"100%"},onClick:a=>a.stopPropagation(),children:[e.jsx("h3",{style:{margin:"0 0 10px",fontSize:17,color:"var(--coral)"},children:"Удалить юзера полностью?"}),e.jsxs("div",{style:{background:"rgba(255,122,92,0.10)",border:"1px solid rgba(255,122,92,0.3)",borderRadius:8,padding:"10px 12px",fontSize:13,lineHeight:1.5,marginBottom:14},children:[e.jsx("b",{children:fe.email||((l=fe.id)==null?void 0:l.slice(0,8))})," будет ",e.jsx("b",{children:"удалён навсегда"}),": доступ на ноде (Remnawave), подписка, платежи, профиль, сессии, реферал/партнёрские данные. Это ",e.jsx("b",{children:"необратимо"})," и НЕ возвращает деньги."]}),e.jsxs("label",{className:"dim",style:{display:"block",marginBottom:6,fontSize:13},children:[fe.email?"Введите email юзера для подтверждения":"Почты нет — введите id аккаунта для подтверждения",": ",e.jsx("b",{style:{color:"var(--text)"},children:fe.email||fe.id})]}),e.jsx("input",{value:Be,onChange:a=>Ae(a.target.value),autoFocus:!0,placeholder:fe.email||fe.id,style:{...We,width:"100%",marginBottom:12}}),ze&&e.jsxs("p",{style:{color:"var(--coral)",fontSize:13,margin:"0 0 12px"},children:["Ошибка: ",ze]}),e.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end"},children:[e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:Ce,onClick:()=>ce(null),children:"Отмена"}),e.jsx("button",{className:"btn btn--danger btn--sm",disabled:!Ee||Ce,onClick:Fe,children:Ce?"Удаляю…":"Удалить навсегда"})]})]})})]})}function gt(s){return Math.max(0,Number(s.amountCents||0)-Math.max(0,Number(s.refundedCents||0)))}function Lt(s){return s.currency?s.currency==="USD"?yn(s.amountCents):`${(s.amountCents/100).toFixed(2)} ${s.currency}`:`${(s.amountCents/100).toFixed(2)} ?`}function jt(s){try{const t=JSON.parse((s==null?void 0:s.meta)||"{}");return[t.method,t.test?"тест":""].filter(Boolean).join(" · ")||null}catch{return null}}function Qs(s){try{const t=String(JSON.parse((s==null?void 0:s.meta)||"{}").country||"").toUpperCase();return/^[A-Z]{2}$/.test(t)?t:null}catch{return null}}const rr=s=>s.replace(/[A-Z]/g,t=>String.fromCodePoint(127397+t.charCodeAt(0))),bt={etoplatezhi:{label:"ЭтоПлатежи",color:"var(--cyan)"},freekassa:{label:"FreeKassa",color:"var(--amber)"},unitpay:{label:"UnitPay",color:"var(--violet-2)"}
57,stripe:{label:"Stripe",color:"#635BFF"},coingate:{label:"CoinGate",color:"var(--green)"},apple:{label:"App Store",color:"var(--muted)"},lava:{label:"Lava",color:"var(--coral)"}};function ar({id:s}){const t=String(s||"").toLowerCase(),m=bt[t];return t?m?e.jsxs("span",{title:t,style:{display:"inline-flex",alignItems:"center",gap:6,whiteSpace:"nowrap",fontSize:12.5,fontWeight:600,color:"var(--text)"},children:[e.jsx("span",{style:{width:7,height:7,borderRadius:2,background:m.color,flex:"none"}}),m.label]}):e.jsx("span",{className:"dim",children:s}):e.jsx("span",{className:"dim",children:"—"})}const it=(()=>{const s={size:13,style:{flex:"none",opacity:.85}},t={icon:e.jsx(Is,{...s}),label:"СБП"},m={icon:e.jsx(It,{...s}),label:"Карта"},n={icon:e.jsx(Dt,{...s}),label:"SberPay"},x={icon:e.jsx(At,{...s}),label:"ЮMoney"};return{"sbp-qr":t,sbp:t,sbp2:t,us_kgt:{icon:e.jsx(Is,{...s}),label:"СБП·токен"},card:m,"card-partner":m,"card partner":m,cc:m,sberpay:n,"yoomoney-wallet":x,yoomoney:x,crypto:{icon:e.jsx(ei,{...s}),label:"Крипта"},bank_card:m,sberbank:n,yoo_money:x,tinkoff_bank:{icon:e.jsx(Dt,{...s}),label:"Т-Банк"},b2b_sberbank:{icon:e.jsx(Dt,{...s}),label:"Сбер · B2B"},sber_loan:{icon:e.jsx(Dt,{...s}),label:"Кредит Сбера"},installments:{icon:e.jsx(It,{...s}),label:"Рассрочка"},apple_pay:{icon:e.jsx(It,{...s}),label:"Apple Pay"},google_pay:{icon:e.jsx(It,{...s}),label:"Google Pay"},mobile_balance:{icon:e.jsx(At,{...s}),label:"Со счёта телефона"},electronic_certificate:{icon:e.jsx(At,{...s}),label:"Сертификат"},cash:{icon:e.jsx(At,{...s}),label:"Наличные"}}})();function lr(s){const t=jt(s);if(!t)return"—";const[m,...n]=t.split(" · "),x=it[m]||it[m.toLowerCase()];return x?e.jsxs("span",{title:t,style:{display:"inline-flex",alignItems:"center",gap:5,whiteSpace:"nowrap"},children:[x.icon,x.label,n.length?` · ${n.join(" · ")}`:""]}):t}function ns(s){try{return!!JSON.parse((s==null?void 0:s.meta)||"{}").trial}catch{return!1}}function or({api:s}){var S,H,p,K,re,je;const[t,m]=r.useState([]),[n,x]=r.useState(""),[i,C]=r.useState(null),[u,k]=r.useState(!1),[g,I]=r.useState(""),[E,X]=r.useState(!1),[D,W]=r.useState(null),[o,N]=r.useState(null),[v,_]=r.useState(null),[B,Q]=r.useState(null),A=async c=>{W({loading:!0});try{W(await s("/admin/user/"+c+"/payments"))}catch{W({error:!0})}},[$,V]=r.useState(!0),[T,y]=r.useState(null),q=r.useRef(!1),P=r.useRef(null),oe=async c=>{I(""),X(!1),V(!1),y(null),q.current=!1,P.current=c,C(c);try{const te=await s("/admin/user/"+c.userId+"/payments");if(P.current!==c)return;const ve=((te==null?void 0:te.payments)||[]).filter(Se=>Se.status==="succeeded"&&Se.kind!=="refund").length;y(ve),q.current||V(!c.userDeletedAt&&ve<=1)}catch{}},L=c=>{q.current=!0,V(c)},[se,U]=r.useState(!1),[ne,le]=r.useState(!1),[ee,be]=r.useState(""),[w,d]=r.useState(""),[b,F]=r.useState(""),[Z,xe]=r.useState(!1),[ue,Ne]=r.useState(null),fe=!1,ce=Sn(""),{tzMin:Be,rFrom:Ae,rTo:Ce,qFrom:R,qTo:ze,utc:ke}=ce,Le=r.useRef(0),[Ee,Fe]=r.useState(null),l=(c=n,te=null,ve=!1,Se=!1)=>{Se||le(!0);const pe=++Le.current,Re=new URLSearchParams;return c&&Re.set("q",c),ve&&te&&(Re.set("before",String(te.b)),Re.set("beforeId",te.id)),R&&ze&&(Re.set("from",R),Re.set("to",ze),Re.set("tz",String(Be))),ee&&Re.set("status",ee),w&&Re.set("kind",w),b&&Re.set("method",b),s("/admin/payments"+(Re.toString()?"?"+Re:"")).then(Ie=>{pe===Le.current&&(m(rt=>ve?[...rt,...Ie.payments||[]]:Ie.payments||[]),U(!!Ie.hasMore),Fe(Ie.nextBefore!=null&&Ie.nextBeforeId?{b:Number(Ie.nextBefore),id:String(Ie.nextBeforeId)}:null))}).catch(()=>{}).finally(()=>{Se||le(!1)})};r.useEffect(()=>{l(n)},[R,ze,Be,fe,ee,w,b]),Vt(()=>{t.length<=100&&l(n,null,!1,!0)},[n,R,ze,Be,fe,ee,w,b,t.length,se]);const a=r.useRef(null),M=c=>{clearTimeout(a.current),a.current=setTimeout(()=>l(c.trim(),null,!1),350)};r.useEffect(()=>()=>clearTimeout(a.current),[]);const f=async()=>{var te;const c=i;k(!0),I(""),X(!1);try{await s("/admin/refund",{userId:c.userId,provider:c.provider,txnId:c.txnId,amountCents:gt(c),currency:c.currency,disableSub:$}),C(null),l()}catch(ve){(te=ve==null?void 0:ve.data)!=null&&te.pending?X(!0):I((ve==null?void 0:ve.message)||"Ошибка")}finally{k(!1)}};return e.jsxs(e.Fragment,{children:[ue&&e.jsx(Ht,{api:s,userId:ue.i
57d,email:ue.email,onClose:()=>Ne(null),onChanged:()=>l(n)}),e.jsx(wn,{p:ce,busy:ne,allowAll:!0}),(()=>{const c=[ee,w,b].filter(Boolean).length;return e.jsxs("button",{type:"button",className:"pfilters-toggle","aria-expanded":Z,onClick:()=>xe(te=>!te),children:[e.jsx(on,{size:15,strokeWidth:2}),"Фильтры",c?` · ${c}`:"",e.jsx(Pn,{size:15,strokeWidth:2,style:{marginLeft:"auto",transform:Z?"rotate(180deg)":"none",transition:"transform .15s"}})]})})(),e.jsxs("div",{className:"pfilters"+(Z?"":" pfilters--collapsed"),children:[e.jsx(Oe,{options:[["","Все"],["ok","Успешные"],["fail","Отказ"],["expired","Истёк счёт"],["wait","В работе"]],value:ee,onChange:be}),e.jsx("span",{"aria-hidden":!0,className:"pfilters__sep"}),e.jsx(Oe,{options:[["","Любой тип"],["initial","Первые"],["renewal","Продления"],["trial","Пробные"],["extra_devices","Устройства"],["refund","Возвраты"]],value:w,onChange:d}),e.jsx("span",{"aria-hidden":!0,className:"pfilters__sep"}),e.jsx(Oe,{options:[["","Любой способ"],["sbp","СБП"],["card","Карта"],["sberpay","SberPay"],["yoomoney","ЮMoney"]],value:b,onChange:F})]}),e.jsxs("div",{className:"atools",children:[e.jsx("input",{className:"atools__q",style:We,title:"Ищет по всей истории платежей: email, id платежа/операции (external_id / payment_id / txn), id пользователя",placeholder:"Поиск: email / id платежа / txn / сумма…",value:n,onChange:c=>{x(c.target.value),M(c.target.value)},onKeyDown:c=>{c.key==="Enter"&&l()}}),e.jsxs("div",{className:"atools__btns",children:[e.jsx("button",{className:"btn",style:{padding:9},onClick:()=>l(),title:"Найти","aria-label":"Найти",children:e.jsx(us,{size:16,strokeWidth:2.2})}),e.jsx(vt,{onClick:()=>l()}),!!t.length&&e.jsx("button",{className:"btn btn--ghost",style:{padding:"9px 14px",fontSize:12.5},onClick:()=>{const c=Xe.id==="aurora",ve=[["date","email","provider",...c?["country"]:[],"kind","amount","currency","status","plan","period","txn"].join(";"),...t.map(pe=>[new Date(pe.createdAt).toISOString(),pe.email||pe.userDeletedEmail||pe.userId,pe.provider,...c?[Qs(pe)||""]:[],pe.kind||"initial",((pe.amountCents||0)/100).toFixed(2),pe.currency||"",pe.status,pe.plan||"",pe.period||"",pe.txnId||""].join(";"))].join(`
58`),Se=document.createElement("a");Se.href=URL.createObjectURL(new Blob(["\uFEFF"+ve],{type:"text/csv;charset=utf-8"})),Se.download=`payments-${Ae||"all"}_${Ce||""}.csv`,document.body.appendChild(Se),Se.click(),document.body.removeChild(Se),setTimeout(()=>URL.revokeObjectURL(Se.href),1e3)},children:"CSV"})]})]}),e.jsxs("div",{className:"card pad",style:{overflowX:"auto"},children:[e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx("th",{style:{padding:6},children:"когда"}),e.jsx("th",{children:"email"}),e.jsx("th",{children:"сумма"}),e.jsx("th",{children:"тариф"}),e.jsx("th",{children:"провайдер"}),Xe.id==="aurora"&&e.jsx("th",{children:"страна"}),e.jsx("th",{children:"метод"}),e.jsx("th",{children:"тип"}),e.jsx("th",{children:"статус"}),e.jsx("th",{children:"действия"})]})}),e.jsx("tbody",{children:t.map(c=>{var te,ve;return e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsxs("td",{style:{padding:6},className:"dim",title:new Date(c.createdAt).toLocaleString("ru-RU")+(ke?" (в поясе браузера)":""),children:[Me(c.createdAt,ke),ke?" UTC":""]}),e.jsx("td",{children:c.email?e.jsx("button",{type:"button",className:"linkbtn",title:"Управление аккаунтом",onClick:()=>Ne({id:c.userId,email:c.email}),children:c.email}):c.userDeletedEmail?e.jsx("span",{title:"Аккаунт удалён — адрес на момент удаления",children:e.jsx("s",{className:"dim",children:c.userDeletedEmail})}):e.jsx("button",{type:"button",className:"linkbtn",title:"Управление аккаунтом",onClick:()=>Ne({id:c.userId}),children:(te=c.userId)==null?void 0:te.slice(0,8)})}),e.jsx("td",{style:{fontWeight:700},title:c.currency?void 0:"Валюта не записана — сумма показана как есть",children:Lt(c)}),e.jsx("td",{children:c.plan?e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:5},children:[vn(c.plan),c.period?e.jsx("span",{className:"dim",style:{fontSize:12},children:c.period}):null]}):e.jsx("span",{className:"dim",children:"—"})}),e.jsx("td",{children:e.jsx(ar,{id:c.provider})}),Xe.id==="aurora"&&e.jsx("td",{className:"dim",children:(()=>{const Se=Qs(c);return Se?e.jsxs("span",{title:`Страна: ${Se}`,style:{whiteSpace:"nowrap"},children:[rr(Se)," ",e.jsx("span",{style:{fontSize:12},children:Se})]}):e.jsx("span",{className:"dim",children:"—"})})()}),e.jsx("td",{className:"dim",children:lr(c)}),e.jsxs("td",{className:"dim",children:[Hi(ns(c)?"trial":c.kind),ns(c)&&e.jsx("span",{className:"dim",style:{fontSize:11,marginLeft:6,padding:"1px 5px",borderRadius:4,border:"1px solid rgba(168,85,247,0.4)",color:"#C084FC"},children:"1 день"}),c.kind==="renewal"&&c.status==="succeeded"&&c.renewalNo>0&&e.jsxs("button",{title:"Какое это продление по счёту. Открыть всю историю платежей и LTV",onClick:()=>c.userId&&A(c.userId),className:"chipb chipb--xs chipb--info",style:{marginLeft:6},children:["#",c.renewalNo]}),c.kind==="renewal"&&c.status==="failed"&&e.jsxs("button",{title:"Какое продление сорвалось. Открыть причину отказа",onClick:()=>N({email:c.email||c.userDeletedEmail||c.userId,list:[{at:c.createdAt,reason:os(c.meta)}],single:!0,flow:c.flow,method:jt(c),provider:c.provider}),className:"chipb chipb--xs chipb--danger",style:{marginLeft:6},children:["#",(c.renewalNo||0)+1]}),c.status==="failed"&&c.priorFails>0&&e.jsxs("button",{title:"Повторные попытки этого же списания — показать причины",onClick:()=>N({email:c.email||c.userDeletedEmail||c.userId,list:ts(c.priorFailsInfo)}),className:"chipb chipb--xs chipb--warn",style:{marginLeft:6},children:["↻",c.priorFails+1]}),c.status==="succeeded"&&c.priorFails>0&&e.jsxs("button",{title:"Показать, почему не прошли предыдущие попытки",onClick:()=>N({email:c.email||c.userDeletedEmail||c.userId,list:ts(c.priorFailsInfo)}),className:"chipb chipb--xs chipb--warn",style:{marginLeft:6},children:["↻",c.priorFails+1]}),c.refundedAt>0&&e.jsxs("button",{title:`Возврат ${De(c.refundedCents,c.currency)} · ${Me(c.refundedAt,ke)}${Bt(c.refundInfo)?" · "+Bt(c.refundInfo):""}. Открыть историю платежей.`,onClick:()=>c.userId&&A(c.userId),className:"chipb chipb--xs chipb--danger",style:{marginLeft:6},children:["↩",c.refundedCents<c.amountCents?" "+De(c.refundedCents,c.currency):""]})]}),e.jsx("td",{children:c.refundedAt>0&&c.refundedCents>=c.amountCents?Vs(!1,Ze("refunded"),Ze("refunded")):c.status==="created"||c.status==="expired"?e.jsxs("button",{title:"Показать заходы на оплату: метод, процессинг, чем закончился",onClick:()=>Q({email:c.email||c.userDeletedEmail||c.userId,list:c.flow||[],paidAt:null,currency:c.currency}),className:"chipb",children:[c.status==="created"?e.jsx(Mn,{size:12,strokeWidth:2.2}):e.jsx(Ut,{size:12,strokeWidth:2.2}),c.status==="created"?"создан":"истёк"]}):c.status==="failed"&&Wi(c.meta)?e.jsxs("button",{title:"Показать детали счёта",onClick:()=>_({email:c.email||c.userDeletedEmail||c.userId,invoicedAt:c.invoicedAt,at:c.createdAt,amountCents:c.amountCents,currency:c.currency,method:jt(c),provider:c.provider,plan:c.plan,peri
58od:c.period}),className:"chipb",children:[e.jsx(Ut,{size:12,strokeWidth:2.2}),"истёк"]}):c.status==="failed"?e.jsxs("button",{title:"Показать причину отказа",onClick:()=>N({email:c.email||c.userDeletedEmail||c.userId,list:[{at:c.createdAt,reason:os(c.meta)}],single:!0,flow:c.flow,method:jt(c),provider:c.provider}),className:"chipb chipb--danger",children:[e.jsx(Pt,{size:12,strokeWidth:2.2}),Ze("failed")]}):c.status==="succeeded"?e.jsxs("button",{title:"Показать всю историю платежа: счета, попытки, оплата, возврат",onClick:()=>Q({email:c.email||c.userDeletedEmail||c.userId,list:c.flow||[],paidAt:c.createdAt,currency:c.currency,amountCents:c.amountCents,method:jt(c),provider:c.provider,fails:ts(c.priorFailsInfo),refundedAt:c.refundedAt,refundedCents:c.refundedCents,refundInfo:c.refundInfo}),style:{display:"inline-flex",alignItems:"center",gap:4,padding:"2px 8px",borderRadius:6,fontSize:12,fontWeight:600,cursor:"pointer",color:"var(--green)",background:"rgba(52,211,153,0.12)",border:"1px solid rgba(52,211,153,0.35)"},children:[e.jsx(qt,{size:12,strokeWidth:2.2}),Ze("succeeded"),(((ve=c.flow)==null?void 0:ve.length)||0)>1?" · "+c.flow.length:""]}):Vs(c.status==="succeeded",Ze("succeeded"),Ze(c.status))}),e.jsx("td",{children:c.status==="succeeded"&&(c.kind==="initial"||c.kind==="renewal")&&(c.refundedAt>0&&c.refundedCents>=c.amountCents?e.jsx("span",{className:"dim",style:{display:"inline-flex"},"aria-label":"Возвращено",title:`Возвращено ${De(c.refundedCents,c.currency)} · ${Me(c.refundedAt,ke)}`,children:e.jsx(cn,{size:15,strokeWidth:2})}):c.provider==="apple"?e.jsx("span",{className:"dim",style:{fontSize:12},title:"Возврат по покупке в App Store делает Apple. Мы получаем уведомление и отключаем подписку автоматически.",children:"через Apple"}):e.jsx("button",{className:"iconbtn iconbtn--danger","aria-label":"Возврат",title:c.userDeletedAt?"Возврат. Аккаунт удалён: вернём деньги и запишем в ledger, отключать нечего":"Возврат",onClick:()=>{oe(c)},children:e.jsx(ps,{size:15,strokeWidth:2})}))})]},c.id)})})]}),e.jsxs("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",gap:12,marginTop:12},children:[e.jsxs("span",{className:"dim",style:{fontSize:12.5},children:["показано ",t.length]}),se&&e.jsx("button",{className:"btn btn--ghost",disabled:ne,onClick:()=>l(n,Ee,!0),children:ne?"…":"Показать ещё"})]})]}),v&&e.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.55)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:16},onClick:()=>_(null),children:e.jsxs("div",{className:"modal pad",style:{maxWidth:520,width:"100%"},onClick:c=>c.stopPropagation(),children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:4},children:"Счёт истёк неоплаченным"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 14px"},children:[v.email," · денег не списывали"]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"8px 14px",fontSize:14},children:[e.jsx("span",{className:"dim",children:"Счёт выставлен"}),e.jsx("span",{children:v.invoicedAt?new Date(v.invoicedAt).toLocaleString("ru-RU"):"—"}),e.jsx("span",{className:"dim",children:"Истёк"}),e.jsx("span",{children:new Date(v.at).toLocaleString("ru-RU")}),e.jsx("span",{className:"dim",children:"Сумма"}),e.jsx("span",{children:De(v.amountCents,v.currency)}),e.jsx("span",{className:"dim",children:"Тариф"}),e.jsxs("span",{children:[v.plan||"—",v.period?"/"+v.period:""]}),e.jsx("span",{className:"dim",children:"Метод"}),e.jsx("span",{children:v.method||"—"})]}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,marginTop:14,marginBottom:0},children:[Ls(v.provider,v.method)," Человек не оплатил — часто потому, что вернулся и заплатил другим способом."]}),e.jsx("button",{className:"btn btn--ghost",style:{marginTop:12},onClick:()=>_(null),children:"Закрыть"})]})}),B&&e.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.55)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:16},onClick:()=>Q(null),children:e.jsxs("div",{className:"modal pad",style:{maxWidth:640,width:"100%",maxHeight:"82vh",overflowY:"auto"},onClick:c=>c.stopPropagation(),children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:4},children:B.paidAt?"История платежа":"Заходы на оплату"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 14px"},children:[B.email," · заходов: ",B.list.length,B.paidAt?", деньги пришли":", денег пока нет"]}),e.jsx("div",{style:{display:"grid",gap:2,marginBottom:14},children:[...B.list.flatMap(c=>{var te,ve;return[{at:c.createdAt,t:`счёт выставлен · ${((te=it[c.method])==null?void 0:te.label)||c.method||"метод не указан"} · ${((ve=bt[c.provider])==null?void 0:ve.label)||c.provider||"—"}`,c:"var(--muted)",url:c.payUrl},...c.stageAt?[{at:c.stageAt,t:c.stage==="shown"?"человек открыл платёжную страницу, код показан":"банк начал проводить",c:"var(--muted)"}]:[]]}),...(B.fails||[]).map(c=>({at:c.at,t:`попытка не прошла · ${c.reason.text}`,c:"var(--coral)"})),...B.paidAt?[{at:B.paidAt,t:`деньги пришли${B.amountCents?" · "+De(B.amountCents,B.currency):""}`,c:"var(--green)",bold:!0}]:[],...B.refundedAt?[{at:B.refundedAt,t:`возврат${B.refundedCents?" · "+De(B.refundedCents,B.currency):""}${Bt(B.refundInfo)?" · "+Bt(B.refundInfo):""}`,c:"var(--coral)"}]:[]].sort((c,te)=>(c.at||0)-(te.at||0)).map((c,te)=>e.jsxs("div",{style:{display:"flex",gap:10,fontSize:13.5,padding:"4px 0",color:c.c,fontWeight:c.bold?700:400},children:[e.jsx("span",{className:"dim",style:{minWidth:112,flex:"none"},children:c.at?Me(c.at,ke):"—"}),e.jsxs("span",{style:{whiteSpace:"normal"},children:[c.t,c.url&&e.jsx("a",{href:c.url,target:"_blank",rel:"noopener",style:{marginLeft:8,color:"var(--violet-2)"},children:"открыть счёт ↗"})]})]},te))}),e.jsx("p",{className:"dim",style:{fontSize:12.5,marginTop:0,marginBottom:0,lineHeight:1.5},children:B.paidAt?"Заходы без денег — не потеря: человек чаще всего возвращался и платил другим способом. Смотреть стоит на метод, с которого уходят.":Ls((S=B.list[B.list.length-1])==null?void 0:S.provider,(H=B.list[B.list.length-1])==null?void 0:H.method)}),e.jsx("button",{className:"btn btn--ghost",style:{marginTop:12},onClick:()=>Q(null),children:"Закрыть"})]})}),o&&e.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.55)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:16},onClick:()=>N(null),children:e.jsxs("div",{className:"modal pad",style:{maxWidth:620,width:"100%",maxHeight:"82vh",overflowY:"auto"},onClick:c=>c.stopPropagation(),children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:4},children:o.single?"Почему не прошло списание":"Неудачные попытки списания"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 14px"},children:[o.email,o.single?" · деньги НЕ списаны":" · списание в итоге прошло"]}),!!((p=o.flow)!=null&&p.length)&&e.jsxs("div",{style:{marginBottom:14,padding:"10px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border)"},children:[e.jsx("div",{className:"dim",style:{fontSize:11.5,fontWeight:700,letterSpacing:"0.05em",textTransform:"uppercase",marginBottom:8},children:"Что было со счётом"}),[...o.flow].sort((c,te)=>c.createdAt-te.createdAt).map((c,te)=>{var ve,Se;return e.jsxs(r.Fragment,{children:[e.jsxs("div",{style:{display:"flex",gap:10,fontSize:13,padding:"3px 0"},children:[e.jsx("span",{className:"dim",style:{minWidth:108},children:Me(c.createdAt,ke)}),e.jsxs("span",{children:["счёт выставлен · ",((ve=it[c.method])==null?void 0:ve.label)||c.method||"метод не указан"," · ",((Se=bt[c.provider])==null?void 0:Se.label)||c.provider]})]}),c.stageAt&&e.jsxs("div",{style:{display:"flex",gap:10,fontSize:13,padding:"3px 0"},children:[e.jsx("span",{className:"dim",style:{minWidth:108},children:Me(c.stageAt,ke)}),e.jsx("span",{children:c.stage==="shown"?"человек открыл платёжную страницу, код показан":"банк начал проводить"})]})]},te)}),((K=o.list[0])==null?void 0:K.at)&&e.jsxs("div",{style:{display:"flex",gap:10,fontSize:13,padding:"3px 0",color:"var(--coral)"},children:[e.jsx("span",{style:{minWidth:108,opacity:.8},children:Me(o.list[0].at,ke)}),e.jsxs("span",{children:["отказ",(re=o.flow[0])!=null&&re.createdAt?` · через ${Math.max(1,Math.round((o.list[0].at-Math.min(...o.flow.map(c=>c.createdAt)))/6e4))} мин после выставления`:""]})]})]}),!o.list.length&&e.jsx("p",{className:"dim",style:{margin:0},children:"Причины не сохранились."}),o.list.map((c,te)=>e.jsxs("div",{style:{borderTop:te?"1px solid var(--border)":"none",padding:"12px 0"},children:[e.jsxs("div",{style:{display:"flex",gap:10,alignItems:"baseline",flexWrap:"wrap"},children:[e.jsx("span",{className:"dim",style:{fontSize:12.5}
58,children:c.at?new Date(c.at).toLocaleString("ru-RU"):"—"}),e.jsx("span",{style:{fontSize:11,fontWeight:700,padding:"1px 7px",borderRadius:4,color:c.reason.ours?"var(--coral)":"var(--amber)",background:c.reason.ours?"rgba(255,122,92,0.10)":"rgba(245,166,35,0.10)",border:"1px solid "+(c.reason.ours?"rgba(255,122,92,0.35)":"rgba(245,166,35,0.35)")},children:c.reason.ours?"наша ошибка":"отказ процессинга"})]}),e.jsx("div",{style:{fontSize:14,marginTop:6},children:c.reason.text}),c.reason.raw&&c.reason.raw!==c.reason.text&&e.jsx("div",{className:"dim",style:{fontSize:12,marginTop:4,fontFamily:"ui-monospace, monospace",wordBreak:"break-word"},children:c.reason.raw})]},te)),e.jsx("button",{className:"btn btn--ghost",style:{marginTop:12},onClick:()=>N(null),children:"Закрыть"})]})}),D&&e.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.55)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:16},onClick:()=>W(null),children:e.jsxs("div",{className:"modal pad",style:{maxWidth:720,width:"100%",maxHeight:"82vh",overflowY:"auto"},onClick:c=>c.stopPropagation(),children:[D.loading&&e.jsx("p",{className:"dim",style:{margin:0},children:"Загружаю…"}),D.error&&e.jsx("p",{className:"err",style:{margin:0},children:"Не удалось загрузить историю."}),D.payments&&e.jsxs(e.Fragment,{children:[e.jsxs("h3",{style:{margin:"0 0 4px",fontSize:17,overflowWrap:"anywhere"},children:[D.user.email||D.user.id.slice(0,8),D.user.deleted&&e.jsx("span",{style:{marginLeft:8,fontSize:11,color:"var(--coral)",border:"1px solid var(--coral)",borderRadius:4,padding:"0 5px"},children:"удалён"})]}),e.jsxs("p",{className:"dim",style:{margin:"0 0 12px",fontSize:12.5},children:["с ",D.user.createdAt?new Date(D.user.createdAt).toLocaleDateString("ru-RU"):"—"," · платежей ",D.payments.length]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(130px,1fr))",gap:10,marginBottom:14},children:[e.jsx($e,{label:"LTV (за вычетом возвратов)",value:De(D.ltvCents,D.currency),tone:"var(--green)"}),e.jsx($e,{label:"продлений",value:D.renewals})]}),e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12.5},children:e.jsxs("tbody",{children:[D.payments.map(c=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)",opacity:c.status==="succeeded"?1:.55},children:[e.jsx("td",{className:"dim",style:{padding:5,whiteSpace:"nowrap"},children:new Date(c.createdAt).toLocaleString("ru-RU")}),e.jsx("td",{className:"dim",children:ns(c)?"триал":ls(c.kind)}),e.jsxs("td",{className:"dim",children:[c.plan||"—",c.period?"/"+c.period:""]}),e.jsx("td",{className:"dim",children:c.provider||"—"}),e.jsx("td",{className:"r",style:{fontWeight:700,whiteSpace:"nowrap"},children:De(c.amountCents,c.currency||D.currency)}),e.jsx("td",{className:"r dim",children:Ze(c.status)})]},c.id)),D.payments.length===0&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:6,style:{padding:8},children:"Платежей нет."})})]})}),e.jsx("div",{style:{marginTop:14},children:e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>W(null),children:"Закрыть"})})]})]})}),i&&e.jsx("div",{style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.6)",display:"flex",alignItems:"center",justifyContent:"center",zIndex:100,padding:16},onClick:()=>!u&&C(null),children:e.jsxs("div",{className:"modal pad",style:{maxWidth:460,width:"100%"},onClick:c=>c.stopPropagation(),children:[e.jsx("h3",{style:{margin:"0 0 10px",fontSize:17,color:"var(--coral)"},children:"Вернуть платёж?"}),e.jsxs("p",{style:{margin:"0 0 12px",fontSize:14},children:[e.jsx("b",{children:Lt({...i,amountCents:gt(i)})}),gt(i)<i.amountCents&&e.jsxs("span",{className:"dim",children:[" (остаток из ",Lt(i),")"]})," ","· ",i.email||((je=i.userId)==null?void 0:je.slice(0,8))," · ",i.provider]}),i.feePct>0&&e.jsxs("p",{style:{margin:"0 0 12px",padding:"8px 10px",borderRadius:8,background:"rgba(255,122,92,0.10)",border:"1px solid rgba(255,122,92,0.30
58)",fontSize:12.5,lineHeight:1.5},children:["Комиссия процессинга ",e.jsxs("b",{children:[i.feePct,"%"]})," при возврате не возвращается. Клиент получит ",e.jsx("b",{children:Lt({...i,amountCents:gt(i)})}),", наша потеря — ",e.jsx("b",{children:De(Math.round(gt(i)+i.amountCents*i.feePct/100),i.currency)}),"."]}),e.jsxs("div",{style:{fontSize:12.5,color:"var(--muted)",marginBottom:6},children:["У юзера успешных платежей: ",e.jsx("b",{style:{color:"var(--text)"},children:T??"…"})]}),e.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:8,marginBottom:12},children:[e.jsxs("label",{style:{display:"flex",gap:8,alignItems:"flex-start",cursor:"pointer",fontSize:13,lineHeight:1.45},children:[e.jsx("input",{type:"radio",name:"refmode",checked:!$,onChange:()=>L(!1),style:{marginTop:3}}),e.jsxs("span",{children:[e.jsx("b",{children:"Вернуть только этот платёж"})," — подписка остаётся активной. Гасим комиссию этого платежа. Для случайного дубля."]})]}),e.jsxs("label",{style:{display:"flex",gap:8,alignItems:"flex-start",cursor:"pointer",fontSize:13,lineHeight:1.45},children:[e.jsx("input",{type:"radio",name:"refmode",checked:$,onChange:()=>L(!0),style:{marginTop:3}}),e.jsxs("span",{children:[e.jsx("b",{children:"Полный возврат + отключить подписку"})," — доступ к сервису, автопродление, реферал/партнёрская комиссия. Отключается ",e.jsx("b",{children:"вся"})," подписка."]})]})]}),e.jsx("div",{style:{background:"rgba(255,122,92,0.10)",border:"1px solid rgba(255,122,92,0.3)",borderRadius:8,padding:"10px 12px",fontSize:13,lineHeight:1.5,marginBottom:14},children:$?e.jsxs(e.Fragment,{children:["Деньги вернутся через ",e.jsx("b",{children:i.provider}),", подписка ",e.jsx("b",{children:"полностью отключится"}),", не только этот платёж. Действие ",e.jsx("b",{children:"необратимо"}),"."]}):e.jsxs(e.Fragment,{children:["Деньги за ",e.jsx("b",{children:"этот платёж"})," вернутся через ",e.jsx("b",{children:i.provider}),". Подписка и доступ ",e.jsx("b",{children:"сохранятся"}),". Действие ",e.jsx("b",{children:"необратимо"}),"."]})}),g&&e.jsxs("p",{style:{color:"var(--coral)",fontSize:13,margin:"0 0 12px"},children:["Ошибка: ",g]}),E&&e.jsxs("p",{className:"dim",style:{fontSize:13,lineHeight:1.5,margin:"0 0 12px"},children:["Возврат в обработке у процессинга — ",e.jsx("b",{children:"не повторяйте"}),". Деньги уйдут клиенту, как только процессинг подтвердит; строка возврата появится в ленте сама."]}),e.jsxs("div",{style:{display:"flex",gap:10,justifyContent:"flex-end"},children:[e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:u,onClick:()=>{C(null),E&&l()},children:E?"Закрыть":"Отмена"}),!E&&e.jsx("button",{className:"btn btn--danger btn--sm",disabled:u,onClick:f,children:u?"Возврат…":$?"Вернуть деньги и отключить":"Вернуть платёж"})]})]})})]})}function dr({api:s}){var Ce,R,ze,ke,Le,Ee,Fe,l,a,M;const[t,m]=r.useState([]),[n,x]=r.useState(null),[i,C]=r.useState(""),[u,k]=r.useState([]),[g,I]=r.useState(!1),E=r.useRef(!1),[X,D]=r.useState(""),[W,o]=r.useState({}),[N,v]=r.useState({}),[_,B]=r.useState(!1),[Q,A]=r.useState(null),[$,V]=r.useState(null),[T,y]=r.useState(!1),[q,P]=r.useState(""),oe=()=>s("/admin/tickets").then(f=>m(f.tickets||[])).catch(()=>{}),L=()=>s("/admin/supportcfg").then(A).catch(()=>{}),se=async f=>{const S=await s("/admin/supportcfg",f).catch(()=>null);S!=null&&S.cfg&&A(H=>({...H,cfg:S.cfg})),L()},U=r.useMemo(()=>q?t.filter(f=>{const S=ds(f);return q==="closed"?S.done:S.done?!1:q==="mine"?S.mine:q==="bot"?f.lastActor==="ai":!S.mine&&f.lastActor!=="ai"}):t,[t,q]),ne=()=>location.hash.replace(/^#/,"").split("/")[1]||"",le=f=>{try{history.replaceState(null,"","#tickets/"+f)}catch{}return s(`/admin/tickets/${f}`).then(x).catch(()=>{})},ee=()=>{x(null);try{history.replaceState(null,"","#tickets")}catch{}oe()},[be,w]=r.useState(!1),d=async(f,S)=>{if(!be){w(!0);try{await b(f,S)}finally{w(!1)}}},b=async(f,S)=>{var je,c;const H=await s(`/admin/users/merge-plan?from=${encodeURIComponent(f.id)}&to=${encodeURIComponent(S.userId)}`).catch(()=>null);if(!H)return alert("Не удалось получить план слияния");if((je=H.blockers)!=null&&je.length)return alert(`Слить нельзя:
59· `+H.blockers.join(`
60· `));const p=Object.entries(H.counts||{}).filter(([,te])=>te>0).map(([te,ve])=>`${te}: ${ve}`).join(", ")||"ничего, кроме самого аккаунта",K=prompt(`Перенести с ${H.fromEmail} на ${H.toEmail}.
61Переедет: ${p}.
62Аккаунт ${H.fromEmail} станет закрытым.
63
64Для подтверждения напечатайте адрес, с которого переносим:`);if(!K)return;const re=await s("/admin/users/merge",{from:f.id,to:S.userId,confirmEmail:K}
64).catch(te=>({error:String((te==null?void 0:te.message)||te),...(te==null?void 0:te.data)||{}}));if(re!=null&&re.error){const te=re.expected?`адрес не совпал, ожидался ${re.expected}`:(c=re.blockers)!=null&&c.length?re.blockers.join("; "):re.error;return alert("Не слилось: "+te)}alert("Готово. Платежи и подписка теперь на "+H.toEmail),le(n.ticket.id)};r.useEffect(()=>{oe(),L();const f=ne();f&&le(f)},[]);const F=r.useRef(null),Z=r.useRef({id:"",n:0}),xe=r.useRef(!0);r.useEffect(()=>{const f=()=>{xe.current=window.innerHeight+window.scrollY>=document.documentElement.scrollHeight-160};return window.addEventListener("scroll",f,{passive:!0}),()=>window.removeEventListener("scroll",f)},[]),r.useEffect(()=>{var p;const f=((p=n==null?void 0:n.ticket)==null?void 0:p.id)||"",S=((n==null?void 0:n.messages)||[]).length;if(!f){Z.current={id:"",n:0};return}if(!S)return;const H=Z.current.id!==f;!H&&S===Z.current.n||(Z.current={id:f,n:S},(H||xe.current)&&requestAnimationFrame(()=>{var K;return(K=F.current)==null?void 0:K.scrollIntoView({block:"end",behavior:H?"auto":"smooth"})}))},[(Ce=n==null?void 0:n.ticket)==null?void 0:Ce.id,((n==null?void 0:n.messages)||[]).length]),r.useEffect(()=>{if(!n)return;const f=n.ticket.id,S=setInterval(()=>s(`/admin/tickets/${f}`).then(x).catch(()=>{}),7e3);return()=>clearInterval(S)},[n==null?void 0:n.ticket.id]);const ue=async(f,S,H)=>{const p=await s("/admin/translate",{text:f,to:S,messageId:H}).catch(()=>null);return{text:String((p==null?void 0:p.text)||""),from:p!=null&&p.from?String(p.from):null}},Ne=f=>{const S=(f.match(new RegExp("\\p{L}","gu"))||[]).length;return S<8?!0:(f.match(/[а-яёА-ЯЁ]/g)||[]).length/S>.3};r.useEffect(()=>{const f=((n==null?void 0:n.messages)||[]).filter(S=>S.bodyRu&&S.bodyRu!=="—");f.length&&(o(S=>{const H={...S};for(const p of f)H[p.id]=p.bodyRu;return H}),v(S=>{const H={...S};for(const p of f)p.bodyLang&&(H[p.id]=p.bodyLang);return H}))},[(R=n==null?void 0:n.ticket)==null?void 0:R.id,((n==null?void 0:n.messages)||[]).length]),r.useEffect(()=>{const f=((n==null?void 0:n.messages)||[]).filter(H=>H.body&&!H.bodyRu&&!Ne(H.body)&&!W[H.id]);if(!f.length)return;let S=!1;return(async()=>{for(const H of f.slice(0,12)){const p=await ue(H.body,"ru",H.id);if(S)return;p.text&&o(K=>({...K,[H.id]:p.text})),p.from&&v(K=>({...K,[H.id]:p.from}))}})(),()=>{S=!0}},[(ze=n==null?void 0:n.ticket)==null?void 0:ze.id,((n==null?void 0:n.messages)||[]).length]);const fe=async()=>{var f;if(n){if(u.length){await ce(u);return}if(i.trim()&&!E.current){E.current=!0,I(!0),D("");try{await s(`/admin/tickets/${n.ticket.id}/reply`,{body:i.trim()}),C(""),le(n.ticket.id),oe()}catch(S){D((f=S==null?void 0:S.data)!=null&&f.error?`Не отправилось: ${S.data.error}`:"Не удалось отправить — попробуйте ещё раз")}finally{E.current=!1,I(!1)}}}},ce=async f=>{if(!(!n||!f.length)){I(!0),D("");try{const S=new FormData;for(const K of f.slice(0,5))S.append("file",K);i.trim()&&S.append("body",i.trim());const H=await fetch(`/api/admin/tickets/${n.ticket.id}/files`,{method:"POST",body:S,credentials:"include"}),p=await H.json().catch(()=>({}));if(!H.ok){D((p==null?void 0:p.error)==="too_big"?`Файл больше ${p.maxMb||5} МБ`:(p==null?void 0:p.error)==="not_an_image"?"Не принимаем такой файл — можно png, jpg, gif, webp и pdf":(p==null?void 0:p.error)==="too_many"?`За раз не больше ${p.max||5} картинок`:(p==null?void 0:p.error)==="ticket_full"?"В тикете уже слишком много вложений":(p==null?void 0:p.error)==="storage_off"?"Хранилище файлов не настроено":"Не удалось отправить");return}C(""),k([]),await le(n.ticket.id),oe()}catch{D("Сеть не отвечает — попробуйте ещё раз")}finally{I(!1)}}};r.useEffect(()=>{if(!n)return;const f=S=>{if(S.key!=="Enter"||!(S.metaKey||S.ctrlKey))return;const H=S.target;H&&(H.tagName==="INPUT"||H.tagName==="TEXTAREA")||(S.preventDefault(),g||fe())};return window.addEventListener("keydown",f),()=>window.removeEventListener("keydown",f)});const Be=async(f=!1)=>{n&&(await s(`/admin/tickets/${n.ticket.id}/status`,{status:"closed",review:f}),ee())},Ae=async f=>{try{const S=await s("/admin/impersonate",{userId:f});S!=null&&S.url?window.open(S.url,"_blank","noopener"):(S==null?void 0:S.error)==="two_factor"?alert("У пользователя включена двухфакторная аутентификация — вход под ним недоступен. Данные аккаунта смотрите через «Управление»."):alert("Нет email — magic-логин недоступен")}
64catch(S){alert("Ошибка: "+((S==null?void 0:S.message)||S))}};if(n){const f=n.author,S=p=>p==="agent"?"вы (агент)":p==="ai"?"🤖 бот":(f==null?void 0:f.email)||"клиент",H=p=>p==="agent"||p==="ai";return e.jsxs("div",{className:"card pad",children:[$&&e.jsx(Ht,{api:s,userId:$.id,email:$.email,onClose:()=>V(null),onChanged:()=>le(n.ticket.id)}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,flexWrap:"wrap",marginBottom:12},children:[e.jsx("button",{className:"btn btn--ghost btn--xs btn--pill",onClick:ee,children:"← к списку"}),e.jsx("span",{className:"sec-row__t",style:{fontWeight:700},children:n.ticket.subject}),n.ticket.subjectRu&&e.jsxs("span",{className:"dim",style:{fontWeight:400,fontSize:13},children:["· ",n.ticket.subjectRu]}),Hs(n.ticket),e.jsx("span",{title:"нажми — скопировать ID тикета",onClick:()=>{var p;(p=navigator.clipboard)==null||p.writeText(n.ticket.id),y(!0),setTimeout(()=>y(!1),1500)},style:{marginLeft:"auto",cursor:"pointer",fontSize:12,fontFamily:"monospace",padding:"4px 10px",borderRadius:8,background:T?"rgba(52,211,153,0.18)":"var(--surface-2)",border:"1px solid var(--border-strong)",color:T?"var(--green)":void 0},children:T?"✓ скопировано":`#${n.ticket.id.slice(0,8)} 📋`})]}),f&&e.jsxs("div",{style:{marginTop:12,padding:14,borderRadius:14,background:"var(--surface)",border:"1px solid var(--border)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,flexWrap:"wrap"},children:[e.jsx("div",{style:{fontWeight:700,fontSize:15,overflowWrap:"anywhere"},children:f.email||f.userId.slice(0,8)}),e.jsx("button",{className:"btn btn--ghost btn--sm",style:{marginLeft:"auto"},onClick:()=>V({id:f.userId,email:f.email}),children:"Управление"}),e.jsx("button",{className:"btn",style:{padding:"8px 18px",fontSize:13},onClick:()=>Ae(f.userId),children:"Войти под юзером"})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(120px,1fr))",gap:8,marginTop:12},children:[e.jsx(ft,{label:"подписка",children:f.plan?e.jsxs(e.Fragment,{children:[e.jsx("b",{children:f.plan})," · ",e.jsx("span",{style:{color:f.subStatus==="active"?"var(--green)":"var(--amber)"},children:f.subStatus==="active"?"активна":f.subStatus||"—"})]}):e.jsx("span",{className:"dim",children:"нет"})}),e.jsx(ft,{label:"действует до",children:f.expiresAt?new Date(f.expiresAt).toLocaleDateString("ru-RU"):e.jsx("span",{className:"dim",children:"—"})}),e.jsx(ft,{label:"автопродление",children:f.autoRenew?e.jsx("span",{style:{color:f.hasToken?"var(--green)":"var(--amber)"},children:f.hasToken?"вкл":"вкл (без токена)"}):e.jsx("span",{className:"dim",children:"выкл"})}),e.jsx(ft,{label:"пользование",children:f.used?e.jsxs("span",{style:{color:"var(--green)"},title:f.lastOnline?"посл. онлайн "+new Date(f.lastOnline).toLocaleString("ru-RU"):"",children:["✓ заходил",f.trafficBytes>0?` · ${xs(f.trafficBytes)}`:""]}):e.jsx("span",{style:{color:"var(--amber)"},children:"ни разу"})}),e.jsxs(ft,{label:"заплатил",children:[(f.paidByCurrency||[]).length?f.paidByCurrency.map(p=>De(p.cents,p.currency)).join(" + "):De(0,(Le=(ke=f.paidByCurrency)==null?void 0:ke[0])==null?void 0:Le.currency)," ",e.jsxs("span",{className:"dim",children:["· ",f.paidCount]})]})]}),!!(n.similar||[]).length&&e.jsxs("div",{style:{marginTop:12,paddingTop:12,borderTop:"1px solid var(--border)"},children:[e.jsx("div",{className:"dim",style:{fontSize:12,marginBottom:8},children:"Похожие адреса"}),e.jsx("div",{style:{display:"flex",flexDirection:"column",gap:8},children:(n.similar||[]).map(p=>{var K;return e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,flexWrap:"wrap",fontSize:13},children:[e.jsx("span",{style:{overflowWrap:"anywhere",fontWeight:p.paidCount?700:400},children:p.email}),e.jsxs("span",{className:"dim",children:[p.paidCount?`${De(p.paidCents,"RUB")} · ${p.paidCount} опл.`:"без оплат",((K=p.subscription)==null?void 0:K.status)==="active"?" · подписка активна":""]}),e.jsx("button",{className:"btn btn--ghost btn--xs",style:{marginLeft:"auto"},disabled:be,onClick:()=>d(p,f),children:be?"Сливаю…":"Слить сюда →"})]}
64,p.id)})})]})]}),e.jsx("div",{style:{margin:"14px 0",display:"flex",flexDirection:"column",gap:10},children:(n.messages||[]).map(p=>e.jsxs("div",{style:{alignSelf:H(p.author)?"flex-end":"flex-start",maxWidth:"80%",minWidth:0,overflowWrap:"anywhere",padding:"10px 14px",borderRadius:12,fontSize:14,background:p.author==="ai"?"rgba(52,211,153,0.13)":p.author==="agent"?"rgba(91,156,255,0.15)":"var(--surface-2)"},children:[e.jsxs("div",{className:"dim",style:{fontSize:11,marginBottom:3},children:[S(p.author)," · ",Me(p.createdAt)]}),e.jsx(Bs,{text:p.body}),W[p.id]&&e.jsxs("div",{style:{marginTop:8,paddingTop:8,borderTop:"1px dashed var(--border)",fontSize:13,opacity:.85},children:[e.jsx("span",{className:"dim",style:{fontSize:11},children:"перевод"}),e.jsx("div",{style:{marginTop:2},children:e.jsx(Bs,{text:W[p.id]})})]}),e.jsx(hn,{files:p.files||[],size:200})]},p.id))}),e.jsx("textarea",{style:{...We,width:"100%",minHeight:80},value:i,onChange:p=>C(p.target.value),onKeyDown:p=>{p.key==="Enter"&&(p.metaKey||p.ctrlKey)&&(p.preventDefault(),g||fe())},onPaste:p=>{var re;const K=[...((re=p.clipboardData)==null?void 0:re.items)||[]].filter(je=>je.kind==="file"&&je.type.startsWith("image/")).map(je=>je.getAsFile()).filter(je=>!!je);K.length&&(p.preventDefault(),k(je=>[...je,...K].slice(0,5)))},placeholder:"ответ… (картинку можно вставить из буфера · ⌘/Ctrl+Enter — отправить)"}),!!u.length&&e.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:8},children:u.map((p,K)=>e.jsxs("span",{className:"linkbtn",style:{display:"inline-flex",alignItems:"center",gap:6,fontSize:12.5},children:["📎 ",p.name||"скриншот"," · ",Math.round(p.size/1024)," КБ",e.jsx("button",{type:"button",className:"linkbtn linkbtn--danger",title:"Убрать",onClick:()=>k(re=>re.filter((je,c)=>c!==K)),children:"×"})]},K))}),e.jsxs("div",{style:{display:"flex",gap:10,marginTop:10,alignItems:"center",flexWrap:"wrap"},children:[e.jsx("button",{className:"btn btn--sm",onClick:fe,disabled:g||!i.trim()&&!u.length,title:"⌘/Ctrl + Enter",children:g?"Отправляю…":u.length?`Ответить · ${u.length} 📎`:"Ответить"}),n.subNote&&e.jsx("button",{className:"btn btn--sm",style:{background:"var(--green)",color:"#07130E",fontWeight:700},title:`Вставить в ответ ссылку подписки и объяснение${n.subUrl?` · ${n.subUrl}`:""}`,onClick:()=>C(p=>(p.trim()?p.replace(/\s+$/,"")+`
65
66`:"")+n.subNote),children:"🔗 Вставить ссылку-подписку"}),Ps&&e.jsx("button",{className:"btn btn--ghost btn--sm",title:"Вставить в ответ порядок возврата по оферте (пункты, форма заявления, сроки)",onClick:()=>C(p=>(p.trim()?p.replace(/\s+$/,"")+`
67
68`:"")+Ps),children:"📄 Порядок возврата"}),(()=>{var re,je;const p=[...n.messages||[]].reverse().find(c=>c.author==="user");if(!p)return null;const K=N[p.id]||String(((re=n.ticket)==null?void 0:re.lang)||"")||(Ne(p.body)||W[p.id]?"":String(((je=n.author)==null?void 0:je.lang)||""));return!K||K.slice(0,2)==="ru"?null:e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:_||i.trim().length<2,title:`Перевести ответ на язык последнего сообщения клиента (${K})`,onClick:async()=>{B(!0);try{const c=await ue(i.trim(),K);c.text&&C(c.text)}finally{B(!1)}},children:_?"Перевожу…":`🌐 На язык клиента (${K})`})})(),e.jsxs("label",{className:"btn btn--ghost btn--sm",style:{cursor:g?"default":"pointer"},children:[g?"Отправляю…":"📎 Картинка",e.jsx("input",{type:"file",accept:"image/*,application/pdf",multiple:!0,hidden:!0,disabled:g,onChange:p=>{const K=Array.from(p.target.files||[]);p.target.value="",k(re=>[...re,...K].slice(0,5))}})]}),e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>Be(!1),children:"Закрыть тикет"}),e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>Be(!0),title:"Отправит клиенту ссылки на магазины и закроет тикет",children:"⭐ Закрыть и попросить отзыв"}),X&&e.jsx("span",{style:{color:"var(--coral)",fontSize:13},children:X})]}),e.jsx("div",{ref:F})]})}return e.jsxs("div",{className:"card pad",children:[Q&&e.jsxs("div",{style:{marginBottom:12,padding:"10px 12px",borderRadius:12,background:"var(--surface)",border:"1px solid var(--border)",fontSize:13},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10},children:[e.jsx("b",{style:{flex:1,minWidth:0}
68,children:"🤖 Саппорт-агент"}),e.jsx("button",{className:"toggle-pill"+((Ee=Q.cfg)!=null&&Ee.enabled?" is-on":""),onClick:()=>{var f,S,H;confirm(`${(f=Q.cfg)!=null&&f.enabled?"Выключить":"Включить"} агента поддержки?
69
70${(S=Q.cfg)!=null&&S.enabled?"Обращения будут ждать человека — никто не ответит автоматически.":"Агент снова начнёт отвечать сам, тяжёлое эскалировать."}`)&&se({enabled:!((H=Q.cfg)!=null&&H.enabled)})},children:(Fe=Q.cfg)!=null&&Fe.enabled?"включён":"выключен"})]}),e.jsxs("div",{style:{fontSize:12,marginTop:6,lineHeight:1.5},children:[!Q.llmConfigured&&e.jsx("div",{style:{color:"var(--amber)"},children:"⚠ нет LLM-прокси (LLM_API_URL/KEY) — молчит"}),Q.llmConfigured&&((l=Q.cfg)==null?void 0:l.enabled)&&e.jsx("div",{style:{color:"var(--green)"},children:"✓ отвечает сам, тяжёлое эскалирует"}),e.jsxs("div",{className:"dim",style:{overflowWrap:"anywhere"},children:[(a=Q.cfg)==null?void 0:a.model," · порог ",Math.round((((M=Q.cfg)==null?void 0:M.minConfidence)??.6)*100),"%"]})]})]}),e.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap",marginBottom:10},children:[(()=>{const f=p=>{const K=ds(p);return K.done?"closed":K.mine?"mine":p.lastActor==="ai"?"bot":"wait"},S=p=>t.filter(K=>f(K)===p).length;return[["","все","var(--violet)"],["mine","наш ход","var(--red)"],["bot","ответил бот","var(--green)"],["wait","ждём клиента","var(--muted)"],["closed","закрыт","var(--muted)"]].map(([p,K,re])=>{const je=p===""?t.length:S(p);return e.jsxs("button",{className:"toggle-pill"+(q===p?" is-on":""),style:{borderColor:q===p?re:"var(--line)",fontWeight:q===p?700:400},onClick:()=>P(p),children:[K,je?` · ${je}`:""]},p||"all")})})(),e.jsx("div",{style:{marginLeft:"auto"},children:e.jsx(vt,{onClick:oe})})]}),t.length===0&&e.jsx("p",{className:"dim",children:"Тикетов нет."}),t.length>0&&U.length===0&&e.jsx("p",{className:"dim",children:"В этом срезе пусто."}),U.map(f=>{var S;return e.jsxs("div",{onClick:()=>le(f.id),style:{display:"flex",gap:10,alignItems:"center",padding:"10px 6px",borderTop:"1px solid var(--border)",cursor:"pointer"},children:[e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsx("div",{style:{fontWeight:600,fontSize:14,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:f.subject}),f.subjectRu&&e.jsx("div",{className:"dim",style:{fontSize:12.5,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:f.subjectRu}),e.jsxs("div",{className:"dim",style:{fontSize:12,marginTop:2,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},children:[f.email||((S=f.userId)==null?void 0:S.slice(0,8))," · ",Me(f.updatedAt)]})]}),e.jsx("span",{style:{flexShrink:0},children:Hs(f)})]},f.id)})]})}function cr({api:s}){const[t,m]=r.useState([]),[n,x]=r.useState("requested"),i=()=>s(`/partner/admin/payouts${n?"?status="+n:""}`).then(g=>m(g.rows||[])).catch(()=>{});r.useEffect(()=>{i()},[n]);const C=(g,I)=>De(g||0,I),u=async(g,I)=>{const E=prompt(I==="paid"?`Хеш перевода или номер платёжки для ${C(g.amount_cents,g.currency)} → ${g.dest}:`:"Причина отказа (её увидит партнёр):","");if(E!==null){if(I==="paid"&&!E.trim()){alert("Без хеша перевода закрывать нельзя");return}try{await s("/partner/admin/payout/set-status",I==="paid"?{id:g.id,status:I,txn:E.trim()}:{id:g.id,status:I,note:E.trim()})}catch(X){alert(String(X==null?void 0:X.message)==="not_open"?"Заявка уже закрыта — обновите список.":"Не удалось: "+((X==null?void 0:X.message)||"сеть"))}i()}},k={requested:["в работе","var(--amber)"],paid:["выплачено","var(--green)"],rejected:["отклонено","var(--coral)"]};return e.jsxs("div",{className:"card pad",style:{marginBottom:12,overflowX:"auto"},children:[e.jsxs("div",{className:"sec-row__t",style:{marginBottom:10,display:"flex",alignItems:"center",gap:10},children:[e.jsx("span",{children:"Заявки на выплату"}),t.length>0&&n==="requested"&&e.jsx("span",{className:"chipb",title:"Ждут обработки",children:t.length}),e.jsxs("select",{value:n,onChange:g=>x(g.target.value),style:{...We,marginLeft:"auto",padding:"5px 9px",fontSize:12.5,width:"auto"},children:[e.jsx("option",{value:"requested",children:"в работе"}),e.jsx("option",{value:"paid",children:"выплаченные"}),e.jsx("option",{value:"rejected",children:"отклонённые"}),e.jsx("option",{value:"",children:"все"})]}),e.jsx(vt,{onClick:i})]}),t.length?e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:e.jsx("tbody",{children:t.map(g=>{const[I,E]=k[g.status]||[g.status,"var(--muted)"];return e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{className:"dim",style:{padding:8}
70,children:new Date(g.created_at).toLocaleString("ru-RU")}),e.jsx("td",{children:g.email||g.code||"—"}),e.jsx("td",{style:{fontWeight:700},children:C(g.amount_cents,g.currency)}),e.jsx("td",{className:"dim",children:g.method==="usdt"?"USDT":"Карта"}),e.jsx("td",{style:{fontFamily:"ui-monospace, monospace",fontSize:12.5,maxWidth:260,overflow:"hidden",textOverflow:"ellipsis"},title:g.dest,children:g.dest}),e.jsx("td",{style:{color:E},children:I}),e.jsx("td",{className:"dim",style:{maxWidth:220,overflow:"hidden",textOverflow:"ellipsis"},title:g.txn||g.note||"",children:g.txn||g.note||""}),e.jsx("td",{style:{textAlign:"right"},children:g.status==="requested"&&e.jsxs(e.Fragment,{children:[e.jsx("button",{className:"linkbtn",onClick:()=>u(g,"paid"),children:"выплачено"}),e.jsx("button",{className:"linkbtn linkbtn--danger",style:{marginLeft:10},onClick:()=>u(g,"rejected"),children:"отклонить"})]})})]},g.id)})})}):e.jsx("p",{className:"dim",style:{margin:0},children:"Заявок нет."})]})}function mr({api:s}){const[t,m]=r.useState([]),[n,x]=r.useState(null),[i,C]=r.useState(null),[u,k]=r.useState(30),g=()=>s("/partner/admin/list").then(o=>m(o.partners||[])).catch(()=>{});r.useEffect(()=>{g()},[]);const I=async(o,N)=>{await s("/partner/admin/set-status",{id:o,status:N}),g()},E=async(o,N)=>{const v=prompt("Ставка партнёра, % от платежа (уже начисленные комиссии не пересчитываются):",String(N));if(v==null)return;const _=Number(String(v).replace(",","."));!isFinite(_)||_<0||_>100||(await s("/partner/admin/set-rate",{id:o,ratePercent:_}),g(),n===o&&D(o,!0))},X=async(o,N)=>{const v=prompt("Холд в днях. Пусто — общий срок, 0 — без холда:",N==null?"":String(N));if(v===null)return;const _=v.trim()===""?null:Number(v.trim());if(_!==null&&(!isFinite(_)||_<0||_>365)){alert("От 0 до 365 или пусто");return}await s("/partner/admin/set-hold",{id:o,days:_}),g()},D=async(o,N=!1)=>{if(n===o&&!N){x(null),C(null);return}x(o),C(null);try{C(await s(`/partner/admin/${o}/detail?days=${u}`))}catch{C({error:!0})}};r.useEffect(()=>{n&&D(n,!0)},[u]);const W=o=>De(o||0,i==null?void 0:i.currency);return e.jsxs(e.Fragment,{children:[e.jsx(cr,{api:s}),e.jsxs("div",{className:"card pad",style:{overflowX:"auto"},children:[e.jsx("div",{style:{display:"flex",justifyContent:"flex-end",marginBottom:10},children:e.jsx(vt,{onClick:g})}),t.length===0&&e.jsx("p",{className:"dim",children:"Заявок нет."}),e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:e.jsx("tbody",{children:t.map(o=>{var N;return e.jsxs(r.Fragment,{children:[e.jsxs("tr",{style:{borderTop:"1px solid var(--border)",cursor:"pointer",background:n===o.id?"var(--surface)":void 0},onClick:()=>D(o.id),children:[e.jsxs("td",{style:{padding:8},children:[e.jsx("span",{className:"dim",style:{marginRight:6},children:n===o.id?"▾":"▸"}),o.email||"—"]}),e.jsx("td",{className:"dim",style:{maxWidth:260,overflow:"hidden",textOverflow:"ellipsis"},title:o.contact||void 0,children:o.contact||"—"}),e.jsxs("td",{children:[o.rateBps/100,"%"]}),e.jsx("td",{children:Gi(o.status)}),e.jsx("td",{className:"dim",children:new Date(o.createdAt).toLocaleDateString("ru-RU")}),e.jsxs("td",{style:{textAlign:"right"},onClick:v=>v.stopPropagation(),children:[e.jsx("button",{className:"iconbtn",title:"Изменить ставку","aria-label":"Изменить ставку",onClick:()=>E(o.id,o.rateBps/100),children:e.jsx(qn,{size:15,strokeWidth:2})}),e.jsxs("button",{className:"chipb",style:{marginLeft:6,marginRight:6},title:"Срок холда комиссии",onClick:()=>X(o.id,o.holdDays),children:[e.jsx(mn,{size:12,strokeWidth:2.2}),o.holdDays===0?"нет":o.holdDays?`${o.holdDays} дн`:"холд"]}),o.status!=="active"&&e.jsx("button",{className:"iconbtn",title:"Активировать партнёра","aria-label":"Активировать партнёра",onClick:()=>{confirm(`Активировать партнёра ${o.email||o.id}?
71
72Его ссылки начнут считать конверсии, а комиссия — начисляться.`)&&I(o.id,"active")},children:e.jsx(cn,{size:15,strokeWidth:2})}),o.status==="active"&&e.jsx("button",{className:"iconbtn iconbtn--danger",title:"Заблокировать партнёра","aria-label":"Заблокировать партнёра",onClick:()=>{confirm(`Заблокировать партнёра ${o.email||o.id}?
73
74Начисления по его трафику остановятся.`)&&I(o.id,"suspended")},children:e.jsx(Ot,{size:15,strokeWidth:2})})]})]}),n===o.id&&e.jsx("tr",{children:e.jsxs("td",{colSpan:6,style:{padding:"4px 8px 18px"},children:[!i&&e.jsx("p",{className:"dim",style:{margin:0},children:"Загружаю…"}),(i==null?void 0:i.error)&&e.jsx("p",{className:"err",style:{margin:0},children:"Не удалось загрузить статистику."}),i&&!i.error&&e.jsxs("div",{style:{whiteSpace:"normal"},children:[e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginBottom:10},children:["код ",e.jsx("b",{style:{color:"var(--violet-2)"},children:i.user.code||"—"}),i.user.telegram&&e.jsxs(e.Fragment,{children:[" · @",i.user.telegram]}),i.partner.postbackUrl&&e.jsx(e.Fragment,{children:" · постбэк настроен"})]}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(120px,1fr))",gap:10,marginBottom:14},children:[e.jsx($e,{label:"кликов",value:i.clicks}),e.jsx($e,{label:"привёл юзеров",value:i.referrals}),e.jsx($e,{label:"конверсий",value:i.byKind.reduce((v,_)=>v+_.n,0)}),e.jsx($e,{label:"оборот",value:W(i.byKind.reduce((v,_)=>v+_.amt,0))}),e.jsx($e,{label:"комиссия",value:W(i.byKind.reduce((v,_)=>v+_.com,0)),tone:"var(--green)"})]}),e.jsx(is,{title:"По типу",rows:i.byKind.map(v=>[Os(v.kind),v.n,W(v.com)])}),e.jsx(is,{title:"По статусу выплаты",rows:[...i.b
74yStatus.map(v=>[qs(v.status),v.n,W(v.com)]),...i.payouts?[["выплачено",i.payouts.paidCount,W(i.payouts.paidCents)],...i.payouts.openCount?[["в заявке",i.payouts.openCount,W(i.payouts.openCents)]]:[],["остаток к выплате","—",W(i.payouts.leftCents)]]:[]]}),i.bySubid.length>0&&e.jsx(is,{title:"По subid",rows:i.bySubid.map(v=>[v.subid,v.n,W(v.com)])}),e.jsx(xr,{funnel:i.funnel,days:u,setDays:k,cur:W}),e.jsx("div",{className:"sec-row__t",style:{fontSize:13,margin:"14px 0 6px"},children:"Последние конверсии"}),e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12.5},children:e.jsxs("tbody",{children:[i.recent.map((v,_)=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{className:"dim",style:{padding:5},children:new Date(v.created_at).toLocaleString("ru-RU")}),e.jsx("td",{style:{overflowWrap:"anywhere"},children:v.email||"—"}),e.jsx("td",{className:"dim",children:Os(v.kind)}),e.jsx("td",{className:"dim",children:v.subid||"—"}),e.jsx("td",{className:"r",children:W(v.amount_cents)}),e.jsx("td",{className:"r",style:{fontWeight:700,color:"var(--green)"},children:W(v.commission_cents)}),e.jsx("td",{className:"r dim",children:qs(v.status)})]},_)),i.recent.length===0&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:7,style:{padding:8},children:"Конверсий пока нет."})})]})}),e.jsxs("div",{className:"sec-row__t",style:{fontSize:13,margin:"14px 0 6px"},children:["Постбэки",((N=i.postbackStats)==null?void 0:N.length)>0&&e.jsx("span",{className:"dim",style:{fontWeight:400,marginLeft:8},children:i.postbackStats.map(v=>`${Ms(v.slot)}: ${v.ok_n}/${v.n}`).join(" · ")})]}),e.jsx("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12.5},children:e.jsxs("tbody",{children:[(i.postbacks||[]).map((v,_)=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{className:"dim",style:{padding:5},children:new Date(v.created_at).toLocaleString("ru-RU")}),e.jsx("td",{className:"dim",children:Ms(v.slot)}),e.jsx("td",{style:{overflowWrap:"anywhere",maxWidth:420},title:v.url,children:String(v.url||"").slice(0,90)}),e.jsx("td",{className:"r dim",children:v.ms!=null?`${v.ms} мс`:"—"}),e.jsx("td",{className:"r",style:{fontWeight:700,color:v.ok?"var(--green)":"var(--red)"},children:v.ok?"принят":v.status?`код ${v.status}`:v.error||"не ответил"})]},_)),(i.postbacks||[]).length===0&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:5,style:{padding:8},children:"Постбэков не было — проверьте адреса и галочки событий."})})]})})]})]})})]},o.id)})})})]})]})}function $e({label:s,value:t,tone:m,hint:n}){const[x,i]=r.useState(!1);return e.jsxs("div",{onClick:()=>n&&i(C=>!C),style:{padding:"8px 10px",borderRadius:10,background:"var(--surface)",border:"1px solid "+(x?"rgba(91,156,255,0.5)":"var(--border)"),cursor:n?"pointer":void 0},children:[e.jsx("div",{style:{fontSize:17,fontWeight:700,color:m||"var(--text)"},children:t}),e.jsxs("div",{className:"dim",style:{fontSize:11.5,display:"flex",alignItems:"center",gap:4},children:[s,n&&e.jsx("span",{style:{opacity:x?1:.5,color:x?"var(--violet-2)":void 0},children:"ⓘ"})]}),x&&n&&e.jsx("div",{style:{fontSize:12,lineHeight:1.45,color:"var(--muted)",marginTop:8,paddingTop:8,borderTop:"1px solid var(--border)"},children:n})]})}const hr=`https://lp.${Xe.registrable}`;function Zs(s){const t=String(s||"");return t.startsWith("lp/")?hr+t.slice(2):null}const ur=["vps","vps2"];function en(s){return s.reduce((t,m)=>({uniques:t.uniques+(m.uniques||0),regs:t.regs+(m.regs||0),paid:t.paid+(m.paid||0),revCents:t.revCents+(m.revCents||0),base:t.base+(m.uniques||0||m.regs||0)}),{uniques:0,regs:0,paid:0,revCents:0,base:0})}function pr(s){const t=x=>{const i=String(x||"");if(!i.startsWith("lp/"))return"Прямые заходы";if(i.startsWith("lp/r/"))return"Рейтинги";if(i.startsWith("lp/q/"))return"Квизы";if(i.startsWith("lp/s/")){const C=i.slice(5).replace(/\/+$/,"");return ur.includes(C)?"Лендинги":"Города"}return"Прочее"},m=["Рейтинги","Лендинги","Квизы","Города","Прочее","Прямые заходы"],n=new Map;for(const x of s||[]){const i=t(x.landing);n.has(i)||n.set(i,[]),n.get(i).push(x)}return m.filter(x=>n.has(x)).map(x=>({title:x,rows:n.get(x)}
74))}function ft({label:s,children:t}){return e.jsxs("div",{style:{padding:"7px 10px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border)"},children:[e.jsx("div",{className:"dim",style:{fontSize:10.5,textTransform:"uppercase",letterSpacing:.3,marginBottom:3},children:s}),e.jsx("div",{style:{fontSize:13.5,fontWeight:600,overflowWrap:"anywhere"},children:t})]})}function xr({funnel:s,days:t,setDays:m,cur:n}){if(!s)return null;const x=s.total||{},i=u=>u>=60?"var(--green)":u>=35?"var(--amber)":"var(--coral)",C=({label:u,r:k,sub:g})=>e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:g?"5px 5px 5px 18px":5,fontWeight:g?400:700,color:g?"var(--muted)":void 0},children:u}),e.jsx("td",{className:"r dim",children:k.attempts||0}),e.jsx("td",{className:"r",children:k.people||0}),e.jsx("td",{className:"r",children:k.paid||0}),e.jsx("td",{className:"r",style:{fontWeight:700,color:k.people?i(k.conversion):"var(--muted)"},children:k.people?`${k.conversion}%`:"—"}),e.jsx("td",{className:"r dim",title:"конверсия по попыткам — занижена перебором способов",children:k.attempts?`${k.conversionAttempts}%`:"—"}),e.jsx("td",{className:"r",style:{fontWeight:g?400:700},children:n(k.revenueCents||0)})]});return e.jsxs("div",{style:{marginBottom:12},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,margin:"14px 0 6px",flexWrap:"wrap"},children:[e.jsx("span",{className:"sec-row__t",style:{fontSize:13},children:"Путь до оплаты"}),e.jsx("span",{style:{marginLeft:"auto",display:"flex",gap:6},children:[7,30,90].map(u=>e.jsxs("button",{className:"toggle-pill"+(t===u?" is-on":""),onClick:()=>m(u),children:[u," дней"]},u))})]}),e.jsx("p",{className:"dim",style:{fontSize:12,margin:"0 0 6px"},children:"Попыток — все начатые оплаты; один человек, перебирающий способы, делает несколько. Людей — сколько это уникальных покупателей. Конверсия считается по людям — по попыткам она занижена."}),e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:12.5},children:[e.jsx("thead",{children:e.jsxs("tr",{className:"dim",style:{textAlign:"left",fontSize:11.5},children:[e.jsx("th",{style:{padding:5},children:"способ"}),e.jsx("th",{className:"r",children:"попыток"}),e.jsx("th",{className:"r",children:"людей"}),e.jsx("th",{className:"r",children:"оплат"}),e.jsx("th",{className:"r",children:"конверсия"}),e.jsx("th",{className:"r",children:"по попыткам"}),e.jsx("th",{className:"r",children:"выручка"})]})}),e.jsxs("tbody",{children:[e.jsx(C,{label:"всего",r:x}),(s.methods||[]).map((u,k)=>{var g;return e.jsx(C,{sub:!0,label:((g=it[u.method])==null?void 0:g.label)||u.method||"—",r:u},k)}),!x.attempts&&e.jsx("tr",{children:e.jsx("td",{className:"dim",colSpan:7,style:{padding:8},children:"За этот период попыток оплаты не было."})})]})]})]})}function is({title:s,rows:t}){return t.length?e.jsxs("div",{style:{marginBottom:10},children:[e.jsx("div",{className:"dim",style:{fontSize:11.5,textTransform:"uppercase",letterSpacing:.4,marginBottom:4},children:s}),t.map((m,n)=>e.jsxs("div",{style:{display:"flex",gap:10,fontSize:12.5,padding:"2px 0"},children:[e.jsx("span",{style:{minWidth:150,overflowWrap:"anywhere"},children:m[0]}),e.jsxs("span",{className:"dim",children:[m[1]," шт"]}),e.jsx("span",{style:{marginLeft:"auto",fontWeight:700},children:m[2]})]},n))]}):null}function gr({api:s}){var be,w;const[t,m]=r.useState("metrika"),[n,x]=r.useState(null),[i,C]=r.useState(""),[u,k]=r.useState(""),[g,I]=r.useState(""),[E,X]=r.useState(null),[D,W]=r.useState(""),[o,N]=r.useState("");r.useEffect(()=>{const d=setTimeout(()=>{N(D.trim()),V(0)},350);return()=>clearTimeout(d)},[D]);const[v,_]=r.useState(null),[B,Q]=r.useState(!1);r.useEffect(()=>{t==="metrika"&&s("/admin/analytics-cfg").then(_).catch(()=>_(null))},[t]);const A=d=>{B||(v==null?void 0:v.transport)===d||(Q(!0),s("/admin/analytics-cfg",{transport:d}).then(b=>_(F=>({...F,transport:b.transport,stored:d}))).catch(b=>alert(String((b==null?void 0:b.message)||b).includes("no_measurement_key")?"Нет ключа измерений — протокол работать не будет. Ключ включается в настройках счётчика, раздел «Безопасность и использование данных».":"Не удалось переключить")).finally(()=>Q(!1)))},[$,V]=r.useState(0),[T,y]=r.useState(!0),[q,P]=r.useState(null);r.useEffect(()=>{V(0),P(null),C(""),k(""),I(""),X(null),W(""),N("")},[t]),r.useEffect(()=>{let d=!0;y(!0);let b;if(t==="metrika"){const F=new URLSearchParams({offset:String($)});i&&F.set("outcome",i),u&&F.set("target",u),g&&F.set("cid",g),E&&F.set("user_id",E.id),o&&F.set("q",o),b=s("/admin/analytics-log?"+F)}else{const F=new URLSearchParams({limit:"200"});i==="failed"&&F.set("failed","1"),b=s("/partner/admin/postbacks?"+F).then(
74Z=>({rows:((Z==null?void 0:Z.postbacks)||[]).map(xe=>({...xe,ts:xe.created_at})),hasMore:!1}))}return b.then(F=>{d&&x(F)}).catch(()=>{d&&x(null)}).finally(()=>{d&&y(!1)}),()=>{d=!1}},[t,i,$,u,g,E,o]);const oe=d=>new Date(d).toLocaleString("ru-RU",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}),L=d=>d==="sent"?"var(--green)":d==="error"?"var(--red)":String(d).startsWith("skip")?"var(--muted)":"var(--text)",se=(n==null?void 0:n.rows)||[],U=t!=="metrika"?se:(()=>{const d=[],b=new Map;for(const F of se){const Z=F.conv_id;if(!Z){d.push({...F,kids:[],worst:null});continue}const xe=b.get(Z);if(xe==null){b.set(Z,d.length),d.push({...F,kids:[],worst:null});continue}const ue=d[xe];ue.kids.push(F);const Ne=fe=>fe==="error"?3:String(fe).startsWith("skip")?2:1;Ne(F.outcome)>Ne(ue.worst??ue.outcome)&&(ue.worst=F.outcome,ue.worstErr=F.err,ue.worstHttp=F.http)}return d})(),ne=(n==null?void 0:n.summary)||{},le=[["","все"],["sent","отправлено"],["error","ошибки"],["skip_nocid","без cid"],["skip_noconf","нет настройки"]],ee=({k:d,v:b})=>b==null||b===""?null:e.jsxs("div",{style:{display:"flex",gap:8,padding:"2px 0"},children:[e.jsx("span",{className:"dim",style:{width:90,flex:"none"},children:d}),e.jsx("span",{style:{wordBreak:"break-all",fontFamily:"ui-monospace,monospace",fontSize:12},children:String(b)})]});return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[e.jsx("button",{className:"toggle-pill"+(t==="metrika"?" is-on":""),style:{borderColor:t==="metrika"?"var(--violet)":"var(--line)",fontWeight:t==="metrika"?700:400},onClick:()=>m("metrika"),children:"Метрика / GA"}),e.jsx("button",{className:"toggle-pill"+(t==="trackers"?" is-on":""),style:{borderColor:t==="trackers"?"var(--violet)":"var(--line)",fontWeight:t==="trackers"?700:400},onClick:()=>m("trackers"),children:"Трекеры партнёров"}),e.jsx("span",{className:"dim",style:{fontSize:12},children:t==="metrika"?"серверные конверсии в Яндекс.Метрику / GA4":"S2S-постбеки в сети вебмастеров"}),e.jsx("span",{className:"dim",style:{fontSize:12,marginLeft:"auto",opacity:T?1:0},children:"обновляю…"})]}),e.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:10},children:t==="metrika"?le.map(([d,b])=>e.jsxs("button",{className:"toggle-pill",style:{borderColor:i===d?L(d||"sent"):"var(--line)",fontWeight:i===d?700:400},onClick:()=>{C(d),V(0)},children:[b,d&&ne[d]!=null?` · ${ne[d]}`:""]},d)):[["","все"],["failed","только ошибки"]].map(([d,b])=>e.jsx("button",{className:"toggle-pill",style:{borderColor:i===d?"var(--red)":"var(--line)",fontWeight:i===d?700:400},onClick:()=>{C(d),P(null)},children:b},d))}),e.jsx("div",{className:"dim",style:{fontSize:12,marginTop:8},children:"Клик по строке — полный лог: URL, HTTP, тело ответа, ошибка, время, повторы."}),t==="metrika"&&v&&e.jsxs("div",{style:{marginTop:10,paddingTop:10,borderTop:"1px solid var(--line)",display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[e.jsx("span",{style:{fontSize:12.5,fontWeight:600},children:"Путь доставки:"}),[["mp","протокол измерений"],["offline","офлайн-загрузка"]].map(([d,b])=>e.jsx("button",{className:"toggle-pill"+(v.transport===d?" is-on":""),disabled:B||d==="mp"&&!v.hasKey||d==="offline"&&!v.hasToken,style:{borderColor:v.transport===d?"var(--violet)":"var(--line)",fontWeight:v.transport===d?700:400,opacity:d==="mp"&&!v.hasKey||d==="offline"&&!v.hasToken?.45:1},onClick:()=>A(d),children:b},d)),e.jsxs("span",{className:"dim",style:{fontSize:12},children:[v.transport==="mp"?"наполняет отчёты электронной торговли; путь по клику отключён, чтобы выручка не двоилась":"умеет привязку к рекламному клику, но в электронную торговлю не попадает",!v.hasKey&&" · ключа измерений нет",v.stored==null&&" · значение из конфига"]})]}),t==="metrika"&&(v==null?void 0:v.visitSink)&&e.jsxs("div",{style:{marginTop:10,paddingTop:10,borderTop:"1px solid var(--line)",display:"grid",gap:8},children:[e.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap"},children:[e.jsx("span",{style:{fontSize:12.5,fontWeight:600},children:"Запись заходов:"}),e.jsxs("span",{className:"dim",style:{fontSize:12},children:[v.aeBound?"Analytics Engine подключён":"биндинга AE нет — ae/both пишут только в D1",v.aeBound&&(v.aeSql?" · SQL API настроен":" · SQL API не настроен (отчёты из AE недоступны)")]})]}),["lp","order","web","app"].map(d=>e.jsxs("div",{style:{display:"flex",gap:6,alignItems:"center",flexWrap:"wrap"},children:[e.jsx("code",{style:{width:48,fontSize:12},children:d}),(v.visitSinks||["d1","both","ae","converters","off"]).map(b=>e.jsx("button",{className:"toggle-pill"+(v.visitSink[d]===b?" is-on":""),disabled:B,style:{borderColor:v.visitSink[d]===b?"var(--violet)":"var(--line)",fontWeight:v.visitSink[d]===b?700:400,fontSize:12},onClick:()=>{B||v.visitSink[d]===b||(Q(!0),s("/admin/analytics-sink",{visitSink:{[d]:b}}).then(F=>_(Z=>({...Z,visitSink:F.visitSink}))).catch(()=>alert("Не удалось переключить")).finally(()=>Q(!1)))},children:b},b))]},d)),e.jsx("span",{className:"dim",style:{fontSize:12},children:"both — D1 + AE (переход), ae — только AE (цель), d1 — как раньше, converters — в D1 только вошедшие и заходы с номером Ð
74ºÐ»Ð¸ÐºÐ°, off — не писать."}),e.jsxs("div",{style:{display:"flex",gap:6,alignItems:"center",flexWrap:"wrap",marginTop:4},children:[e.jsx("span",{style:{fontSize:12.5,fontWeight:600},children:"Отчёты читают:"}),[["d1","таблицу visits (D1)"],["auto","AE (≤ 3 мес) / суточные агрегаты"]].map(([d,b])=>e.jsx("button",{className:"toggle-pill"+(v.visitReaders===d?" is-on":""),disabled:B,style:{borderColor:v.visitReaders===d?"var(--violet)":"var(--line)",fontWeight:v.visitReaders===d?700:400,fontSize:12},onClick:()=>{B||v.visitReaders===d||(Q(!0),s("/admin/analytics-sink",{visitReaders:d}).then(F=>_(Z=>({...Z,visitReaders:F.visitReaders}))).catch(()=>alert("Не удалось переключить")).finally(()=>Q(!1)))},children:b},d)),e.jsx("span",{className:"dim",style:{fontSize:12},children:"d1 — пока идёт сверка D1↔AE (две недели); auto — после неё. Когда хоть одна поверхность пишет только в AE, отчёты читают AE независимо от этого."})]})]}),t==="metrika"&&e.jsxs("details",{style:{marginTop:10},children:[e.jsx("summary",{style:{cursor:"pointer",fontSize:13,fontWeight:600},children:"Что мы шлём и зачем"}),e.jsxs("div",{style:{fontSize:12.5,lineHeight:1.65,marginTop:8,display:"grid",gap:10},children:[e.jsxs("div",{children:[e.jsx("b",{children:"Цели."})," Каждая строка — одно событие, отправленное в Яндекс.Метрику (и в GA4, где он настроен). Цель в счётчике ловит его по ИМЕНИ: заводится как «JavaScript-событие», и в поле идентификатора стоит ровно то слово, что видно в колонке «цель». Деньги несут ",e.jsx("code",{children:"purchase"})," — первая покупка, ",e.jsx("code",{children:"renewal"})," — продление,",e.jsx("code",{children:" trial_convert"})," — переход с пробного на платный, ",e.jsx("code",{children:"extra_devices"})," — докупка устройств, ",e.jsx("code",{children:"trial"})," — включение пробного. Без денег: ",e.jsx("code",{children:"registration"})," — заведён аккаунт, ",e.jsx("code",{children:"checkout"})," — человек дошёл до платёжной формы, ",e.jsx("code",{children:"connected"})," — первое подключение. Каждая денежная цель дублируется в общую ",e.jsx("code",{children:"ct_payment"})," — «оплата всех видов»: рекламная стратегия оптимизируется по ОДНОЙ цели с доходом, и по любой отдельной она видела бы лишь часть выручки."]}),e.jsxs("div",{children:[e.jsxs("b",{children:["Почему ",e.jsx("code",{children:"checkout"})," считается."]})," Покупок в день столько, что стратегия учится неделями. Доведённых до формы в разы больше, и это уже не случайный посетитель — по такому событию она набирает статистику быстрее. Денег он не несёт и выручку не задваивает. Одна отправка на поход: счёт заводится заново на каждую попытку оплаты, а их у одного человека бывает и девять подряд."]}),e.jsxs("div",{children:[e.jsx("b",{children:"Чем связано с человеком."})," Метрика склеивает наши серверные события с посетителем только по ClientID — куке ",e.jsx("code",{children:"_ym_uid"}),", которую мы сохраняем на аккаунт. Есть живой идентификатор — она сама находит в истории рекламн
74ый клик и приписывает деньги ему. Нет (человек пришёл из бота, где наших кук не бывает) — строка получает исход «без cid», и событие никуда не уходит: привязать его не к кому."]}),e.jsxs("div",{children:[e.jsx("b",{children:"Что уходит вместе с событием."})," Время самой оплаты — чтобы она легла в свой час, а не в час отправки. Разрезы: тариф, срок, способ оплаты, процессинг, поток закупки, витрина, вёрстка страницы — по ним стратегия видит, ЧТО окупается, а не только «было/не было». Профиль человека: сколько заплатил за всё время, сколько раз, давно ли с нами, жив ли сейчас — из этого собираются сегменты и ретаргет. Отправленное видно в раскрытии строки."]}),e.jsxs("div",{children:[e.jsx("b",{children:"Как читать исходы."})," «ушло» — Метрика приняла. «ошибки» — отвергла, и причина написана её словами в раскрытии (например, занятое ею имя параметра: такой хит отвергается целиком). «без cid» — человека не к чему привязать, см. выше. «нет настройки» — у бренда не заведён счётчик, это не поломка."]}),e.jsxs("div",{children:[e.jsx("b",{children:"Электронная торговля — отдельным хитом."})," Обычная цель считается по ИМЕНИ события (",e.jsx("code",{children:"purchase"}),", ",e.jsx("code",{children:"renewal"}),"…), а отчёт по электронной торговле — нет: он наполняется только специальным ecommerce-хитом, и цель под него в счётчике имеет тип",e.jsx("code",{children:"e_purchase"})," (на matrix это «Ecommerce: покупка»). Поэтому на покупке, продлении и переходе с пробного мы шлём ВТОРОЙ хит: действие ",e.jsx("code",{children:"ecom"}),", тип операции",e.jsx("code",{children:"purchase"}),", номер заказа, сумма и валюта, а товаром — реальный тариф со сроком (",e.jsx("code",{children:"start_mo"}),"), названием, категорией по виду списания и брендом. Действие названо",e.jsx("code",{children:"ecom"}),", а не ",e.jsx("code",{children:"purchase"}),", специально: совпади оно с именем обычной цели — та срабатывала бы второй раз, и продления считались бы покупками.",e.jsx("br",{}),"Отсюда же берётся правило про пути доставки: офлайн-загрузка ecommerce НЕ наполняет — у неё есть конверсия, но нет покупки. Поэтому на бренде с протоколом измерений вся выручка идёт им одним, без запасного пути по рекламному клику."]}),e.jsxs("div",{children:[e.jsx("b",{children:"Одна конверсия — одна строка."})," Оплата уходит веером: своя цель, общая, наследный алиас, ecommerce — и по каждому каналу. Строки схлопнуты, в заголовке ХУДШИЙ исход веера, иначе частичная доставка выглядела бы полным успехом. Раскрытие перечисляет каналы."]})]})]})]}),t==="metrika"&&e.jsxs("div",{className:"card pad",style:{marginBottom:12,display:"flex",gap:10,alignItems:"center",flexWrap:"wrap"},children:[e.jsx(us,{size:15,strokeWidth:1.9,className:"dim"}),e.jsx("input",{value:D,onChange:d=>W(d.target.value),placeholder:"почта, ClientID, id аккаунта или @ник",style:{flex:"1 1 260px",padding:"9px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),!!o&&e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"dim",style:{fontSize:12.5},children:["найдено событий: ",Object.values((n==null?void 0:n.summary)||{}).reduce((d,b)=>d+Number(b||0),0)]}),e.jsx("button",{className:"btn btn--ghost btn--xs",onClick:()=>W(""),children:"сбросить"})]})]}),t==="metrika"&&(((be=n==null?void 0:n.byTarget)==null?void 0:be.length)>0||((w=n==null?void 0:n.byCid)==null?void 0:w.length)>0)&&e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit,minmax(320px,1fr))",gap:18},children:[e.jsxs("div",{children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["По целям ",e.jsx("span",{className:"dim",style:{fontWeight:400,fontSize:12}
74,children:o?"по найденному":"за сутки"})]}),e.jsxs("table",{className:"tbl",style:{width:"100%",fontSize:12.5},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{textAlign:"left",padding:5},children:"цель"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"ушло"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"ошибки"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"пропуск"})]})}),e.jsx("tbody",{children:((n==null?void 0:n.byTarget)||[]).map(d=>{const b=d.target!=="загрузка пачкой",F=u===d.target;return e.jsxs("tr",{style:{cursor:b?"pointer":"default",background:F?"var(--surface-2)":void 0},onClick:()=>{b&&(k(F?"":d.target),I(""),V(0))},children:[e.jsx("td",{style:{padding:5,fontWeight:F?700:400},children:d.target}),e.jsx("td",{style:{padding:5,textAlign:"right",color:"var(--green)"},children:d.sent||""}),e.jsx("td",{style:{padding:5,textAlign:"right",color:d.error?"var(--red)":"var(--muted)",fontWeight:d.error?700:400},children:d.error||""}),e.jsx("td",{style:{padding:5,textAlign:"right"},className:"dim",children:d.skipped||""})]},d.target)})})]})]}),e.jsxs("div",{children:[e.jsxs("div",{style:{fontWeight:600,marginBottom:6},children:["По посетителям ",e.jsxs("span",{className:"dim",style:{fontWeight:400,fontSize:12},children:["ClientID Метрики, ",o?"по найденному":"за сутки"]})]}),e.jsxs("table",{className:"tbl",style:{width:"100%",fontSize:12.5},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{textAlign:"left",padding:5},children:"кто"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"событий"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"целей"}),e.jsx("th",{style:{textAlign:"right",padding:5},children:"ошибки"})]})}),e.jsxs("tbody",{children:[((n==null?void 0:n.byCid)||[]).map(d=>{const b=g===d.cid;return e.jsxs("tr",{style:{cursor:"pointer",background:b?"var(--surface-2)":void 0},onClick:()=>{I(b?"":d.cid),k(""),X(null),V(0)},children:[e.jsx("td",{style:{padding:5,fontWeight:b?700:400,maxWidth:220,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},title:d.cid,children:d.people>1?e.jsxs(e.Fragment,{children:[d.email||d.cid," ",e.jsxs("span",{className:"dim",style:{fontSize:11},children:["и ещё ",d.people-1]})]}):d.email||e.jsx("span",{style:{fontFamily:"ui-monospace,monospace"},className:"dim",children:d.cid})}),e.jsx("td",{style:{padding:5,textAlign:"right"},children:d.n}),e.jsx("td",{style:{padding:5,textAlign:"right"},className:"dim",children:d.targets}),e.jsx("td",{style:{padding:5,textAlign:"right",color:d.error?"var(--red)":"var(--muted)",fontWeight:d.error?700:400},children:d.error||""})]},d.cid)}),((n==null?void 0:n.byCid)||[]).length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:4,className:"dim",style:{padding:6},children:"За сутки ни одного события с ClientID."})})]})]})]})]}),(u||g||E)&&e.jsxs("div",{style:{marginTop:10,display:"flex",gap:8,alignItems:"center",flexWrap:"wrap"},children:[e.jsx("span",{className:"dim",style:{fontSize:12},children:"лента отфильтрована:"}),u&&e.jsxs("span",{className:"toggle-pill is-on",children:["цель ",u]}),g&&e.jsx("span",{className:"toggle-pill is-on",style:{fontFamily:"ui-monospace,monospace"},children:g}),E&&e.jsx("span",{className:"toggle-pill is-on",children:E.email||E.id}),e.jsx("button",{className:"btn btn--ghost",onClick:()=>{k(""),I(""),X(null),V(0)},children:"сбросить"})]})]}),e.jsxs("div",{className:"card pad",children:[e.jsx("div",{style:{overflowX:"auto"},children:e.jsxs("table",{className:"tbl",style:{width:"100%",fontSize:12.5},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{textAlign:"left",padding:6},children:"когда"}),t==="metrika"?e.jsxs(e.Fragment,{children:[e.jsx("th",{style:{textAlign:"left",padding:6},children:"цель"}),e.jsx("th",{style:{textAlign:"left",padding:6},children:"человек"}),e.jsx("th",{style:{textAlign:"left",padding:6},children:"исход"})]}):e.jsxs(e.Fragment,{children:[e.jsx("th",{style:{textAlign:"left",padding:6},children:"партнёр"}),e.jsx("th",{style:{textAlign:"left",padding:6},children:"событие"}),e.jsx("th",{style:{textAlign:"left",padding:6},children:"приём"})]}),e.jsx("th",{style:{textAlign:"left",padding:6},children:"http"}),e.jsx("th",{style:{textAlign:"left",padding:6}})]})}),e.jsxs("tbody",{children:[U.map(d=>{var Z,xe;const b=d.id,F=q===b;return e.jsxs(r.Fragment,{children:[e.jsxs("tr",{style:{cursor:"pointer"},onClick:()=>P(F?null:b),children:[e.jsx("td",{className:"dim",style:{padding:6,whiteSpace:"nowrap"},children:oe(d.ts)}),t==="metrika"?e.jsxs(e.Fragment,{children:[e.jsx("td",{style:{padding:6},children:d.target||"—"}),e.jsx("td",{style:{padding:6,maxWidth:220,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:d.email||d.tg||(d.user_id?e.jsx("span",{className:"dim",children:"без почты"}):e.jsx("span",{className:"dim",children:"—"}))}),e.jsx("td",{style:{padding:6,color:L(d.worst||d.outcome),fontWeight:600},children:d.worst||d.outcome})]}):e.jsxs(e.Fragment,{children:[e.jsx("td",{style:{padding:6},children:d.code||"—"}),e.jsx("td",{className:"dim",style:{padding:6},children:{reg:"регистрация",dep:"первая оплата",sale:"продажа"}[d.slot]||d.slot}),e.jsxs("td",{style:{padding:6,color:d.ok?"var(--green)":"var(--red)",fontWeight:600},children:[d.ok?"принят":"ошибка",d.retries?` · ${d.retries}×`:""]})]}),e.jsx("td",{className:"dim",style:{padding:6},children:(d.worst?d.worstHttp:d.http)||d.status||""}),e.jsxs("td",{className:"dim",style:{padding:6,whiteSpace:"nowrap"},children:[(Z=d.kids)!=null&&Z.length?`${d.kids.length+1} канала`:"",F?" ▾":" ▸"]})]}),F&&e.jsx("tr",{children:e.jsx("td",{colSpan:6,style:{padding:"4px 10px 12px",background:"var(--surface-2)"},children:t==="metrika"?e.jsxs(e.Fragment,{children:[e.jsx(ee,{k:"провайдер",v:d.provider}),e.jsx(ee,{k:"цель",v:d.target}),e.jsx(ee,{k:"исход",v:d.outcome}),e.jsx(ee,{k:"http",v:d.http}),e.jsx(ee,{k:"ms",v:d.ms}),e.jsx(ee,{k:"ошибка",v:d.err}),e.jsx(ee,{k:"сумма",v:d.amount_cents!=null?`${(d.amount_cents/100).toLocaleStr
74ing("ru-RU")} ${d.currency||""}`.trim():null}),e.jsx(ee,{k:"разрезы",v:d.params}),d.cid&&e.jsxs("div",{style:{display:"flex",gap:8,padding:"2px 0",alignItems:"center"},children:[e.jsx("span",{className:"dim",style:{width:90,flex:"none"},children:"cid"}),e.jsx("button",{className:"linkbtn",style:{fontFamily:"ui-monospace,monospace",fontSize:12},onClick:ue=>{ue.stopPropagation(),I(d.cid),k(""),X(null),V(0)},children:d.cid}),e.jsx("span",{className:"dim",style:{fontSize:11},children:"— показать всё по этому посетителю"})]}),d.user_id&&e.jsxs("div",{style:{display:"flex",gap:8,padding:"2px 0",alignItems:"center"},children:[e.jsx("span",{className:"dim",style:{width:90,flex:"none"},children:"человек"}),e.jsx("button",{className:"linkbtn",style:{fontSize:12},onClick:ue=>{ue.stopPropagation(),X({id:d.user_id,email:d.email}),I(""),k(""),V(0)},children:d.email||d.tg||d.user_id}),e.jsx("span",{className:"dim",style:{fontSize:11},children:"— все его события"})]}),((xe=d.kids)==null?void 0:xe.length)>0&&e.jsxs("div",{style:{marginTop:8},children:[e.jsx("div",{className:"dim",style:{fontSize:12,marginBottom:4},children:"каналы этой конверсии:"}),e.jsx("table",{className:"tbl",style:{width:"100%",fontSize:12},children:e.jsx("tbody",{children:[d,...d.kids].map(ue=>e.jsxs("tr",{children:[e.jsx("td",{style:{padding:4},children:ue.provider}),e.jsx("td",{style:{padding:4,color:L(ue.outcome),fontWeight:600},children:ue.outcome}),e.jsx("td",{className:"dim",style:{padding:4},children:ue.http||""}),e.jsx("td",{className:"dim",style:{padding:4,fontFamily:"ui-monospace,monospace"},children:ue.cid||""}),e.jsx("td",{className:"dim",style:{padding:4,wordBreak:"break-all"},children:ue.err||""})]},ue.id))})})]})]}):e.jsxs(e.Fragment,{children:[e.jsx(ee,{k:"партнёр",v:(d.code||"")+(d.partner_email?" · "+d.partner_email:"")}),e.jsx(ee,{k:"событие",v:d.slot}),e.jsx(ee,{k:"приём",v:d.ok?"принят":"ошибка"}),e.jsx(ee,{k:"http",v:d.status}),e.jsx(ee,{k:"повторы",v:d.retries}),e.jsx(ee,{k:"ms",v:d.ms}),e.jsx(ee,{k:"ошибка",v:d.error}),e.jsx(ee,{k:"conversion",v:d.conversion_id}),e.jsx(ee,{k:"URL",v:d.url}),e.jsxs("div",{style:{marginTop:6},children:[e.jsx("span",{className:"dim",style:{fontSize:12},children:"тело ответа трекера:"}),e.jsx("pre",{style:{margin:"4px 0 0",padding:8,background:"var(--surface)",borderRadius:6,fontSize:11.5,whiteSpace:"pre-wrap",wordBreak:"break-all",maxHeight:240,overflow:"auto"},children:d.body||"(пусто)"})]})]})})})]},b)}),!T&&!U.length&&e.jsx("tr",{children:e.jsx("td",{colSpan:6,className:"dim",style:{padding:12,textAlign:"center"},children:"пусто"})})]})]})}),t==="metrika"&&($>0||(n==null?void 0:n.hasMore))&&e.jsxs("div",{style:{display:"flex",gap:8,marginTop:10},children:[e.jsx("button",{className:"btn btn--ghost btn--xs",disabled:$<=0,onClick:()=>{V(Math.max(0,$-100)),P(null)},children:"← назад"}),e.jsx("button",{className:"btn btn--ghost btn--xs",disabled:!(n!=null&&n.hasMore),onClick:()=>{V((n==null?void 0:n.nextOffset)??$+100),P(null)},children:"вперёд →"})]})]})]})}function fr({api:s}){const[t,m]=r.useState(null),n=()=>s("/admin/nodes").then(x=>m(x.nodes||[])).catch(()=>m(x=>x??[]));return r.useEffect(()=>{n()},[]),Vt(n,[],3e4),t?e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(220px, 1fr))",gap:12},children:[t.map((x,i)=>e.jsxs("div",{className:"card pad",style:{display:"flex",justifyContent:"space-between",alignItems:"flex-start",gap:10,padding:16},children:[e.jsxs("div",{style:{minWidth:0,flex:1},children:[e.jsx("div",{style:{fontWeight:700,fontSize:14,wordBreak:"break-word",lineHeight:1.3},children:x.name||"—"}),e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginTop:4,display:"flex",gap:12,flexWrap:"wrap",alignItems:"center"},children:[e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:4},title:"страна",children:[e.jsx(Hn,{size:13,strokeWidth:2}),x.country||"??"]}),typeof x.usersOnline=="number"&&e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:4},title:"юзеров онлайн",children:[e.jsx(Vn,{size:13,strokeWidth:2}),x.usersOnline]}),typeof x.trafficUsedBytes=="number"&&x.trafficUsedBytes>0&&e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:4},title:"трафик",children:[e.jsx(Gn,{size:13,strokeWidth:2}),xs(x.trafficUsedBytes)]})]})]}),e.jsx("span",{style:{flex:"none",display:"inline-flex",color:x.disabled?"var(--muted)":x.online?"var(--green)":"var(--coral)"},title:x.disabled?"Выключена":x.online?"Онлайн":"Не отвечает","aria-label":x.disabled?"Выключена":x.online?"Онлайн":"Не отвечает",children:x.disabled?e.jsx(Kn,{size:17,strokeWidth:2.2}):x.online?e.jsx(qt,{size:17,strokeWidth:2.2}):e.jsx(Pt,{size:17,strokeWidth:2.2})})]},i)),t.length===0&&e.jsx("p",{className:"dim",children:"Панель недоступна или нод нет."})]}):e.jsx("p",{className:"dim",children:"Загрузка…"})}const jr=s=>s==="sbp2"?"sbp":s;function yr(s){const t=[];for(const m of s){const n=jr(m),x=t.find(i=>i.label===n);x?x.codes.push(m):t.push({label:n,codes:[m]})}return t}const ms=[{id:"aurora",label:"Aurora VPN · мир"},{id:"babka",label:"Бабка · Россия"}];function hs({shop:s,setShop:t}){return ms.length?e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginBottom:12,flexWrap:"wrap"},children:[e.jsx("span",{className:"dim",style:{fontSize:12.5},children:"Витрина"}),ms.map(m=>e.jsx("button",{onClick:()=>t(m.id),className:"chipb"+(s===m.id?" chipb--info":""),style:{fontSize:12.5,padding:"5px 11px"},children:m.label},m.id))]}):null}function br({api:s}){const[t,m]=r.useState("aurora");return e.jsx(vr,{api:s,shop:t,setShop:m},t)}function vr({api:s,shop:t,setShop:m}){var be;const[n,x]=r.useState(null),[i,C]=r.useState({}),[u,k]=r.useState({}),[g,I]=r.useState([]),[E,X]=r.useState([]),[D,W]=r.useState(!1),[o,N]=r.useState(""),[v,_]=r.useState("default");if(r.useEffect(()=>{let w=!0;return s("/admin/tariff?shop="+t).then(d=>{var b,F,Z,xe,ue;w&&(x(d.doc),k(d),X(d.warnings||[]),_(((F=(((b=d.doc)==null?void 0:b.grids)||[]).find(Ne=>Ne.isDefault))==null?void 0:F.id)||((ue=(xe=(Z=d.doc)==null?void 0:Z.grids)==null?void 0:xe[0])==null?void 0:ue.id)||"default"))}).catch(()=>{}),()=>{w=!1}},[t]),!n)return e.jsxs(e.Fragment,{children:[e.jsx(hs,{shop:t,setShop:m}),e.jsx("div",{className:"card pad dim",children:"Загрузка…"})]});const B=n.currency==="rub"?"₽":"$",Q=w=>
74(w/100).toLocaleString("ru-RU",{maximumFractionDigits:2}),A=w=>Math.round((parseFloat(String(w).replace(",","."))||0)*100),$=(n.grids||[]).find(w=>w.id===v)||((be=n.grids)==null?void 0:be[0]),V=w=>{x(w),N("")},T=(w,d)=>V({...n,grids:n.grids.map(b=>b.id===w?d({...b}):b)}),y=(w,d)=>T(v,b=>({...b,offers:b.offers.map(F=>F.id===w?d({...F}):F)})),q=()=>T(v,w=>{var Z;const d=((Z=n.plans[0])==null?void 0:Z.id)||"start";let b=`${d}-30`,F=1;for(;w.offers.some(xe=>xe.id===b);)b=`${d}-30-${++F}`;return{...w,offers:[...w.offers,{id:b,planId:d,days:30,priceCents:0,order:w.offers.length+1}]}}),P=w=>{confirm(`Удалить предложение из сетки?
75
76Вместе с ним уйдут его цена и пробный период. Правка применится после публикации.`)&&T(v,d=>({...d,offers:d.offers.filter(b=>b.id!==w)}))},oe=()=>{const w=$||{offers:[]};let d="grid2",b=1;for(;n.grids.some(Z=>Z.id===d);)d=`grid${++b+1}`;const F={id:d,name:"Копия "+(w.name||""),offers:JSON.parse(JSON.stringify(w.offers||[])),forPartners:!1};V({...n,grids:[...n.grids,F]}),_(d)},L=w=>{var b,F;if(n.grids.length<2)return;const d=n.grids.find(Z=>Z.id===w);d!=null&&d.isDefault||confirm(`Удалить сетку «${(d==null?void 0:d.name)||w}»?
77
78Вместе с ней уйдут все её предложения (${((b=d==null?void 0:d.offers)==null?void 0:b.length)||0}). Ссылки с ?g=${w} после публикации откроют основной прайс.`)&&(V({...n,grids:n.grids.filter(Z=>Z.id!==w)}),_(((F=n.grids.find(Z=>Z.isDefault))==null?void 0:F.id)||n.grids[0].id))},se=w=>V({...n,grids:n.grids.map(d=>({...d,isDefault:d.id===w}))}),U=async()=>{var w,d;if(ms.length&&u.brand&&u.brand!==t){I([`Загружен прайс витрины «${u.brand}», выбрана «${t}». Перезагрузите страницу.`]);return}W(!0),I([]),N("");try{const b=await s("/admin/tariff?shop="+t,{doc:n});X(b.warnings||[]),N(`Опубликовано, версия ${b.version}`),x({...n,version:b.version})}catch(b){const F=(w=b==null?void 0:b.data)==null?void 0:w.errors;I(Array.isArray(F)&&F.length?F:[((d=b==null?void 0:b.data)==null?void 0:d.error)||"не удалось опубликовать"])}finally{W(!1)}},ne=w=>{var d,b;return((b=(d=n.plans.find(F=>F.id===w))==null?void 0:d.names)==null?void 0:b.ru)||w},le=w=>w.days>0?w.priceCents/w.days:0,ee=`${u.previewBase||""}${$!=null&&$.isDefault?"":`?g=${$==null?void 0:$.id}`}`;return e.jsxs(e.Fragment,{children:[e.jsx(hs,{shop:t,setShop:m}),e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:6,fontSize:14},children:"Сетки тарифов"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 12px"},children:["Сетка — набор предложений, который видит покупатель. Дефолтная показывается при прямом заходе, остальные открываются ссылкой ",e.jsx("code",{children:"?g=<id>"}),". Отмеченные «партнёрам» появятся у вебмастеров в конструкторе ссылок. Правки применяются только после публикации."]}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center",marginBottom:12},children:[n.grids.map(w=>e.jsxs("button",{className:"toggle-pill"+(w.id===v?" is-on":""),onClick:()=>_(w.id),children:[w.name||w.id,w.isDefault?" ·":""]},w.id)),e.jsx("button",{className:"linkbtn linkbtn--sm",onClick:oe,children:"+ сетка (копией)"})]}),$&&e.jsxs("div",{style:{borderTop:"1px solid var(--border)",paddingTop:12},children:[e.jsxs("div",{style:{display:"flex",gap:10,flexWrap:"wrap",alignItems:"center",marginBottom:10},children:[e.jsx("input",{value:$.name||"",onChange:w=>T($.id,d=>({...d,name:w.target.value})),placeholder:"Название",style:{width:190}}),e.jsx("input",{value:$.id,onChange:w=>{const d=w.target.value.replace(/[^a-z0-9-]/gi,"").toLowerCase(),b=$.id;V({...n,grids:n.grids.map(F=>F.id===b?{...F,id:d}:F)}),_(d)},placeholder:"id в ссылке",style:{width:130}}),e.jsxs("label",{className:"dim",style:{fontSize:12.5},title:$.isDefault?"Основная сетка доступна партнёрам всегда":"",children:[e.jsx("input",{type:"checkbox",checked:!!$.forPartners||!!$.isDefault,disabled:!!$.isDefault,onCh
78ange:w=>T($.id,d=>({...d,forPartners:w.target.checked}))})," партнёрам"]}),!$.isDefault&&e.jsx("button",{className:"linkbtn linkbtn--sm",onClick:()=>se($.id),children:"сделать основной"}),!$.isDefault&&e.jsx("button",{className:"linkbtn linkbtn--sm linkbtn--danger",onClick:()=>L($.id),children:"удалить сетку"})]}),e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:12,flexWrap:"wrap",marginBottom:10},children:[e.jsx("span",{className:"dim",style:{fontSize:12.5},children:"Планы на витрине"}),n.plans.map(w=>e.jsxs("label",{className:"dim",style:{fontSize:12.5,display:"inline-flex",alignItems:"center",gap:6,cursor:"pointer"},title:w.visible===!1?"Скрыт: покупатель его не увидит, у действующих подписчиков продление работает":"Показывается покупателю",children:[e.jsx("input",{type:"checkbox",checked:w.visible!==!1,onChange:d=>x(b=>({...b,plans:b.plans.map(F=>F.id===w.id?{...F,visible:d.target.checked}:F)}))}),ne(w.id)]},w.id))]}),e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginBottom:12},children:["Ссылка: ",e.jsx("a",{href:ee,target:"_blank",rel:"noreferrer",children:ee}),(()=>{const w=($.offers||[]).filter(d=>d.intro).length;return w?e.jsxs("span",{style:{marginLeft:10,color:"var(--green)"},children:["· пробный период есть у ",w," предложени",w===1?"я":"й"]}):e.jsx("span",{style:{marginLeft:10},children:"· пробного периода нет"})})()]}),e.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginBottom:10},children:(()=>{const w=[...new Set(($.offers||[]).map(d=>d.planId))];return w.length<2?null:w.map(d=>{const b=(i[$.id]??null)===d,F=($.offers||[]).filter(Z=>Z.planId===d).length;return e.jsxs("button",{className:"chipb"+(b?" chipb--info":""),style:{fontSize:12.5,padding:"5px 11px"},onClick:()=>C(Z=>({...Z,[$.id]:b?null:d})),children:[ne(d)," · ",F]},d)}).concat(e.jsx("button",{className:"chipb"+((i[$.id]??null)===null?" chipb--info":""),style:{fontSize:12.5,padding:"5px 11px"},onClick:()=>C(d=>({...d,[$.id]:null})),children:"все"},"__all"))})()}),e.jsx("div",{style:{overflowX:"auto"},children:e.jsxs("table",{className:"tbl",style:{minWidth:720},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"План"}),e.jsx("th",{children:"Срок, дней"}),e.jsxs("th",{children:["Цена, ",B]}),e.jsxs("th",{title:"Цена первого периода: это и есть пробный. Пусто — платит полную сразу",children:["Пробная цена, ",B]}),e.jsx("th",{title:"Сколько длится пробный период",children:"Пробных дней"}),e.jsx("th",{children:"В день"}),e.jsx("th",{children:"действия"})]})}),e.jsx("tbody",{children:[...$.offers].filter(w=>!i[$.id]||w.planId===i[$.id]).sort((w,d)=>(w.order||0)-(d.order||0)).map(w=>{var d;return e.jsxs("tr",{children:[e.jsx("td",{children:e.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:6},children:[Oi(w.planId),e.jsx("select",{value:w.planId,onChange:b=>y(w.id,F=>({...F,planId:b.target.value})),children:n.plans.map(b=>e.jsx("option",{value:b.id,children:ne(b.id)},b.id))})]})}),e.jsx("td",{children:e.jsx("input",{value:w.days,onChange:b=>y(w.id,F=>({...F,days:parseInt(b.target.value,10)||0})),style:{width:64}})}),e.jsx("td",{children:e.jsx("input",{value:Q(w.priceCents),onChange:b=>y(w.id,F=>({...F,priceCents:A(b.target.value)})),style:{width:92}})}),e.jsx("td",{children:e.jsx("input",{value:w.intro?Q(w.intro.priceCents):"",placeholder:"—",onChange:b=>{const F=b.target.value.trim();y(w.id,Z=>{var xe;return F?{...Z,intro:{priceCents:A(F),days:((xe=Z.intro)==null?void 0:xe.days)||7,once:!0}}:{...Z,intro:void 0}})},style:{width:92}})}),e.jsx("td",{children:e.jsx("input",{value:((d=w.intro)==null?void 0:d.days)??"",placeholder:"—",disabled:!w.intro,onChange:b=>y(w.id,F=>({...F,intro:{...F.intro,days:parseInt(b.target.value,10)||0}})),style:{width:60}})}),e.jsxs("td",{className:"dim",style:{fontSize:12.5},children:[Q(le(w))," ",B]}),e.jsx("td",{children:e.jsx("button",{className:"iconbtn iconbtn--danger",title:"Удалить предложение","aria-label":"Удалить предложение",onClick:()=>P(w.id),children:e.jsx(dn,{size:15,strokeWidth:2})})})]},w.id)})})]})}),e.jsx("button",{className:"linkbtn linkbtn--sm",style:{marginTop:10},onClick:q,children:"+ предложение"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"10px 0 0"},children:[e.jsx("b",{children:"Пробный период — это две колонки справа от цены."})," Ставишь «1 ₽» и «1 день» — человек платит рубль за первые сутки, дальше списывается обычная цена этого же предложения. «99 ₽» и «7 дней» — первая неделя за 99 ₽, дальше как в колонке «Цена». Пусто — пробного нет."," Пробный не раздаётся всем подряд: он срабатывает только по ссылке с ",e.jsx("code",{children:"?trial=1"})," — партнёр включает его сам в своём конструкторе ссылок."]})]})]}),e.jsxs("div",{className:"card pad",children:[g.length>0&&e.jsx("div",{style:{marginBottom:10,color:"var(--coral)",fontSize:13},children:g.map((w,d)=>e.jsxs("div",{children:["• ",w]},d))}),E.length>0&&e.jsx("div",{style:{marginBottom:10,color:"var(--amber)",fontSize:13},children:E.map((w,d)=>e.jsxs("div",{children:["• ",w]},d))}),e.jsxs("div",{style:{display:"flex",gap:12,alignItems:"center",flexWrap:"wrap"},children:[e.jsx("button",{className:"btn",disabled:D,onClick:U,children:D?"Публикую…":"Опубликовать"}),e.jsxs("span",{className:"dim",style:{fontSize:12.5},children:["Версия ",n.version,n.updatedBy?` · ${n.updatedBy}`:"",u.isDefaultDoc?" · пока действует тариф из кода":""]}),o&&e.jsx("span",{style:{color:"var(--green)",fontSize:13},children:o})]}),e.jsx("p",{className:"dim",style:{fontSize:12.5,margin:"10px 0 0"},children:"Действующим подписчикам цена не меняется: она зафиксирована в момент покупки. Повышение для них — отдельная операция с предупреждением за 7 дней, как написано в оферте."})]})]})}const gs=s=>({RUB:"₽",USD:"$",EUR:"€"})[String(s||"").toUpperCase()]||s||"";function Sr({api:s}){const[t,m]=r.useState("aurora"),[n,x]=r.useState(""),[i,C]=r.useState([]),[u,k]=r.useState("30"),[g,I]=r.useState(!1),[E,X]=r.useState([]),[D,W]=r.useState([]),[o,N]=r.useState([]),[v,_]=r.useState(null);r.useEffect(()=>{s("/admin/appcfg").then(y=>_(y)).catch(()=>{})},[]);const B=y=>s("/admin/appcfg",y).then(q=>_(q)).catch(()=>{});r.useEffect(()=>{I(!1),s("/admin/processors?days="+u).then(y=>{x(gs(y.currency)),C(y.processors||[]),W(y.methods||[]),N(y.renewals||[]),I(!0)}).catch(()=>I(!0))},[u]),r.useEffect(()=>{s("/admin/paycfg?shop="+t).then(y=>X(y.providers||[])).catch(()=>{})},[t]);const Q=(y,q)=>confirm(`${q?"Включить":"Выключить"} процессинг «${y}»?
79
80Изменение применяется сразу и меняет то, чем платят люди прямо сейчас.`)?s("/admin/paycfg?shop="+t,{id:y,on:q}).then(P=>X(P.providers||[])).catch(()=>{}):Promise.resolve(),A=async(y,q,P,oe)=>
80{if(!confirm(`${P?"Включить":"Выключить"} способ «${q.join(", ")}» у «${y}» (${oe==="recurring"?"подписки":"разовые"})?
81
82Применяется сразу.`))return;let L=null;for(const se of q)try{L=await s("/admin/paycfg?shop="+t,{id:y,method:se,on:P,mode:oe})}catch{}L!=null&&L.providers&&X(L.providers)},$=y=>y>=60?"var(--green)":y>=30?"var(--amber)":"var(--coral)",V=i.reduce((y,q)=>y+q.attempts,0),T=i.reduce((y,q)=>y+q.paid,0);return e.jsxs(e.Fragment,{children:[e.jsx(hs,{shop:t,setShop:m}),v&&e.jsx("div",{className:"card pad",style:{marginBottom:14},children:e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",gap:12,flexWrap:"wrap"},children:[e.jsxs("div",{children:[e.jsx("div",{className:"sec-row__t",style:{fontSize:14},children:"Установка кабинета на телефон"}),e.jsx("p",{className:"dim",style:{fontSize:12.5,margin:"4px 0 0",maxWidth:620},children:"Кабинет ставится на экран «Домой» как приложение. Левый тумблер — показывать ли предложение в кабинете; выключенное предложение не мешает поставить его руками. Правый выключает установку целиком: манифест перестаёт отдаваться, а уже поставленный обработчик снимается у людей при следующем заходе. Иконку с экрана телефона человек удаляет сам — этого не может никто, кроме него."})]}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[e.jsx("button",{className:"toggle-pill"+(v.installHint?" is-on":""),onClick:()=>B({installHint:!v.installHint}),children:v.installHint?"предлагаем":"не предлагаем"}),e.jsx("button",{className:"toggle-pill"+(v.pwa?" is-on":""),onClick:()=>{confirm(`${v.pwa?"Выключить":"Включить"} установку кабинета на телефон?
83
84${v.pwa?"У тех, кто уже поставил, обработчик снимется при следующем заходе.":"Кабинет снова можно будет поставить как приложение."}`)&&B({pwa:!v.pwa})},children:v.pwa?"установка включена":"установка выключена"})]})]})}),e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsx("div",{className:"sec-row__t",style:{marginBottom:6,fontSize:14},children:"Включение процессингов"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 12px"},children:["Тумблер мгновенно (KV, без деплоя). ",e.jsx("b",{children:"Без секретов включить нельзя"})," — сначала добавь ключи. У каждого процессинга две строки: ",e.jsx("b",{children:"подписки"})," — чем продлеваем автоматически, ",e.jsx("b",{children:"разовые"})," — чем платят единожды (докупка устройств, короткие периоды)."]}),E.map(y=>{const q=y.enabled&&y.configured&&y.registered,P=new Set(y.offRecurring||[]),oe=new Set(y.offOneoff||[]);return e.jsxs("div",{style:{padding:"12px 0",borderTop:"1px solid var(--border)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between"},children:[e.jsxs("div",{children:[e.jsx("span",{style:{fontWeight:700},children:y.id}),e.jsx("span",{style:{width:8,height:8,borderRadius:999,background:q?"var(--green)":"var(--dim)",display:"inline-block",margin:"0 8px 0 10px"}}),!y.configured&&e.jsx("span",{style:{fontSize:11,color:"var(--coral)"},children:"нет секретов"}),!y.registered&&e.jsx("span",{style:{marginLeft:8,fontSize:11,color:"var(--coral)"},children:"не зарегистрирован"})]}),e.jsx("button",{className:"toggle-pill"+(y.enabled?" is-on":""),onClick:()=>y.configured&&Q(y.id,!y.enabled),disabled:!y.configured,title:y.configured?"":"нет секретов — включить нельзя",children:y.enabled?"ВКЛ":"выкл"})]}),[["подписки",y.recurringMethods||[],"чем продлеваем автоматически","recurring"],["разовые",y.methods||[],"докупка устройств, короткие периоды","oneoff"]].map(([L,se,U,ne])=>e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:8,marginTop:8,opacity:y.enabled?1:.45,flexWrap:"wrap"},children:[e.jsx("span",{className:"dim",style:{fontSize:12,minWidth:66},title:U,children:L}),se.length?yr(se).map(le=>{const ee=ne==="recurring"?P:oe,be=le.codes.some(d=>!ee.has(d)),w=le.codes.map(d=>{var b;return(b=y.fees)==null?void 0:b[d]}).find(d=>d!=null);return e.jsxs("button",{className:"toggle-pill toggle-pill--xs"+(be?" is-on":""),onClick:()=>A(y.id,le.codes,!be,ne),disabled:!y.enabled,title:le.codes.length>1?`${U} · коды: ${le.codes.join(", ")}`:U,children:[le.label,w!=null?` · ${w}%`:""]},L+le.label)}):e.jsx("span",{className:"dim",style:{fontSize:12},children:"—"})]},L))]},y.id)}),!E.length&&e.jsx("p",{className:"dim",style:{margin:0,fontSize:13},children:"Загрузка…"})]}),e.jsx("div",{style:{marginBottom:14},children:e.jsx(Oe,{options:[["7","7 дней"],["30","30 дней"],["90","90 дней"]],value:u,onChange:k})}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 10px",lineHeight:1.55},children:[e.jsx("b",{children:"Попыток"})," — все начатые оплаты. Один человек, перебирающий способы (СБП не открылся → карта → SberPay), делает несколько попыток. ",e.jsx("b",{children:"Людей"})," — сколько это уникальных покупателей. Конверсия считается по людям: по попыткам она занижена, потому что перебор способов выглядит как несколько отказов."]}),e.jsxs("div",{className:"card pad",style:{overflowX:"auto"},children:[e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx("th",{style:{padding:6},children:"процессинг"}),e.jsx("th",{title:"Все попытки оплаты. Один человек, пробующий разные способы (СБП, карта, SberPay), делает несколько попыток — здесь они посчитаны все",children:"попыток"}),e.jsx("th",{title:"Уникальные люди. Тот же человек с тремя попытками — это один человек, а не три",children:"людей"}),e.jsx("th",{children:"платежей"}),e.jsx("th",{title:"Оплатившие люди ÷ все люди. По корзинам конверсия занижена: перебор способов оплаты выглядит как три отказа",children:"конверсия"}
84),e.jsx("th",{title:"Оплаченные корзины ÷ все корзины",children:"по корзинам"}),e.jsx("th",{children:"выручка"})]})}),e.jsxs("tbody",{children:[i.map(y=>{var q;return e.jsxs(r.Fragment,{children:[e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6,fontWeight:600},children:((q=bt[y.provider])==null?void 0:q.label)||y.provider}),e.jsx("td",{children:y.attempts}),e.jsx("td",{title:`Оплатили ${y.paidPeople} из ${y.people}`,children:y.people}),e.jsx("td",{children:y.paid}),e.jsxs("td",{style:{fontWeight:700,color:$(y.conversionPeople)},children:[y.conversionPeople,"%"]}),e.jsxs("td",{className:"dim",children:[y.conversion,"%"]}),e.jsxs("td",{children:[(y.revenueCents/100).toLocaleString("ru-RU"),n?" "+n:""]})]}),D.filter(P=>P.provider===y.provider).map(P=>{var oe;return e.jsxs("tr",{style:{color:"var(--muted)",fontSize:12.5},children:[e.jsx("td",{style:{padding:"4px 6px 4px 22px"},children:P.method?((oe=it[P.method])==null?void 0:oe.label)||P.method:"метод не записан"}),e.jsx("td",{children:P.attempts}),e.jsx("td",{title:`Оплатили ${P.paidPeople} из ${P.people}`,children:P.people}),e.jsx("td",{children:P.paid}),e.jsxs("td",{style:{color:$(P.conversionPeople)},children:[P.conversionPeople,"%"]}),e.jsxs("td",{style:{opacity:.6},children:[P.conversion,"%"]}),e.jsxs("td",{children:[(P.revenueCents/100).toLocaleString("ru-RU"),n?" "+n:""]})]},y.provider+":"+P.method)})]},y.provider)}),i.length>1&&e.jsxs("tr",{style:{borderTop:"1px solid rgba(255,255,255,0.14)",color:"var(--muted)"},children:[e.jsx("td",{style:{padding:6},children:"итого"}),e.jsx("td",{children:V}),e.jsx("td",{children:T}),e.jsxs("td",{children:[V?Math.round(T/V*1e3)/10:0,"%"]}),e.jsx("td",{children:"—"})]})]})]}),g&&!i.length&&e.jsx("p",{className:"dim",style:{margin:"10px 0 0",fontSize:14},children:"Пока нет попыток оплаты за период."}),!g&&e.jsx("p",{className:"dim",style:{margin:"10px 0 0"},children:"Загрузка…"})]}),e.jsx("p",{className:"dim",style:{fontSize:12,marginTop:10,lineHeight:1.6},children:"Конверсия = оплачено ÷ попыток (данные из checkout_attempts). Это основа каскада: по конверсии/здоровью процессинга выбирается приоритет. Отказы/ротация на возврате появятся, когда включим диспетчер."}),e.jsx("h3",{style:{margin:"22px 0 6px",fontSize:15.5},children:"Автосписания (продления)"}),e.jsxs("p",{className:"dim",style:{fontSize:12.5,margin:"0 0 10px",lineHeight:1.55},children:["Здесь нет корзины: списание инициируем мы по сохранённому согласию. ",e.jsx("b",{children:"Попыток"})," — все списания за период, ",e.jsx("b",{children:"прошло"})," — удачные, ",e.jsx("b",{children:"не прошло"})," — остальные. Низкий процент значит не «плохо продаём», а что привязки умирают: отозванное согласие, истёкшая карта, отказ банка.",e.jsx("b",{children:" Конверсия считается по людям"}),", а «не прошло» — по попыткам: по одному подписчику биллер повторяет списание несколько раз, поэтому отказов в ленте платежей всегда больше, чем людей."]}),e.jsxs("div",{className:"card pad",style:{overflowX:"auto"},children:[e.jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:13.5,whiteSpace:"nowrap"},children:[e.jsx("thead",{children:e.jsxs("tr",{style:{textAlign:"left",color:"var(--muted)"},children:[e.jsx("th",{style:{padding:6},children:"процессинг"}),e.jsx("th",{title:"Все списания по сохранённому согласию за период, включая повторы после отказа",children:"попыток"}),e.jsx("th",{title:"Уникальные подписчики, по которым шли списания",children:"людей"}),e.jsx("th",{children:"прошло"}),e.jsx("th",{title:"Списания, которые не прошли. По одному человеку попыток бывает несколько: биллер повторяет их по лестнице",children:"не прошло"}),e.jsx("th",{title:"Люди с удачным списанием ÷ все, по кому пытались",children:"конверсия"}),e.jsx("th",{title:"Удачные списания ÷ все попытки",children:"по попыткам"}),e.jsx("th",{children:"выручка"})]})}),e.jsx("tbody",{children:o.map(y=>{var q;return e.jsxs("tr",{style:{borderTop:"1px solid var(--border)"},children:[e.jsx("td",{style:{padding:6,fontWeight:600},children:((q=bt[y.provider])==null?void 0:q.label)||y.provider}),e.jsx("td",{children:y.attempts}),e.jsx("td",{title:`Списалось у ${y.paidPeople} из ${y.people}`,children:y.people}),e.jsx("td",{children:y.paid}),e.jsx("td",{title:`Людей без единого успешного списания: ${Math.max(0,y.people-y.paidPeople)}`,children:Math.max(0,y.attempts-y.paid)}),e.jsxs("td",{style:{fontWeight:700,color:$(y.conversionPeople)},children:[y.conversionPeople,"%"]}),e.jsxs("td",{className:"dim",children:[y.conversion,"%"]}
84),e.jsxs("td",{children:[(y.revenueCents/100).toLocaleString("ru-RU"),n?" "+n:""]})]},"ren:"+y.provider)})})]}),g&&!o.length&&e.jsx("p",{className:"dim",style:{margin:"10px 0 0",fontSize:14},children:"За период автосписаний не было."}),!g&&e.jsx("p",{className:"dim",style:{margin:"10px 0 0"},children:"Загрузка…"})]})]})}const wr={trial_push:"Пробный день за 1 ₽",abandoned_cart:"Брошенная корзина",renew_due:"Пора продлить (ручная оплата)",payment_failed:"Не прошла оплата",expired:"Подписка истекла",not_connected:"Не подключился",winback:"Возврат (win-back)",reactivation:"Реактивация (давно не было)",upsell:"Апсейл на долгий срок",invite:"Пригласи друга",renewed:"Подписка продлена",granted:"Подписка выдана (приветствие)",refunded:"Возврат средств",loyalty:"Лояльность / спасибо",referral_reward:"Бонус за друга (реферал)"},Et=[{key:"tg",label:"Текст сообщения",chan:"telegram",min:130},{key:"email_subject",label:"Тема письма",chan:"email",single:!0},{key:"email_body",label:"Текст письма (необязательно)",chan:"email",min:130,hint:"По умолчанию письмо свёрстано красивым шаблоном (нажми «Предпросмотр»). Заполни это поле, только если хочешь заменить шаблон простым текстом в брендовой рамке."}],Nn={ru:"Русский",en:"English",es:"Español",de:"Deutsch",fr:"Français",uk:"Українська",ar:"العربية",zh:"中文",tr:"Türkçe"};function Nr({item:s,api:t,lang:m,chan:n}){var V;const x=Object.keys(s.defaults||{}),i=(T,y)=>{var q,P;return((P=(q=s.defaults)==null?void 0:q[T])==null?void 0:P[y])??""},C=()=>{var y,q;const T={};for(const P of x){T[P]={};for(const oe of Et)T[P][oe.key]=((q=(y=s.override)==null?void 0:y[P])==null?void 0:q[oe.key])??i(P,oe.key)}return T},[u,k]=r.useState(C),[g,I]=r.useState(u),[E,X]=r.useState(!1),[D,W]=r.useState(""),[o,N]=r.useState(null),v=JSON.stringify(u)!==JSON.stringify(g),_=(T,y,q)=>k(P=>({...P,[T]:{...P[T],[y]:q}})),B=async()=>{X(!0),W("");const T={kind:s.kind};for(const y of x){T[y]={};for(const q of Et)T[y][q.key]=u[y][q.key]===i(y,q.key)?"":u[y][q.key]}try{await t("/admin/msgcopy",T),I(u),W("Сохранено ✓")}catch(y){W("Ошибка: "+y.message)}finally{X(!1)}},Q=async()=>{var y,q;W("");const T={};for(const P of Et)T[P.key]=((y=u[m])==null?void 0:y[P.key])===i(m,P.key)?"":(q=u[m])==null?void 0:q[P.key];try{N(await t("/admin/msgcopy/preview",{kind:s.kind,lang:m,chan:n,draft:T}))}catch(P){W("Ошибка превью: "+P.message)}},A=Et.filter(T=>T.chan===n),$=x.filter(T=>T!==m).some(T=>A.some(y=>{var q;return((q=u[T])==null?void 0:q[y.key])!==i(T,y.key)}));return e.jsxs("div",{className:"card pad",style:{marginBottom:12},children:[e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"baseline",gap:10,flexWrap:"wrap",marginBottom:10},children:[e.jsx("div",{style:{fontWeight:700,fontSize:15},children:wr[s.kind]||s.kind}),e.jsx("span",{className:"dim",style:{fontSize:12},children:s.kind})]}),A.map(T=>{var q,P,oe,L,se;const y=((q=u[m])==null?void 0:q[T.key])!==i(m,T.key);return e.jsxs("div",{style:{marginBottom:10},children:[e.jsxs("div",{style:{fontSize:11.5,marginBottom:3,color:y?"var(--violet-2)":"var(--dim)"},children:[T.label,y?" • изменено":""]}),T.single?e.jsx("input",{style:{...We,width:"100%"},value:((P=u[m])==null?void 0:P[T.key])??"",onChange:U=>_(m,T.key,U.target.value)}):e.jsx("textarea",{style:{...We,width:"100%",minHeight:T.min,resize:"vertical",lineHeight:1.5},placeholder:T.key==="email_body"&&!i(m,T.key)?"Пусто — письмо уйдёт стандартным свёрстанным шаблоном. «Предпросмотр» покажет его целиком.":void 0,value:((oe=u[m])==null?void 0:oe[T.key])??"",onChange:U=>_(m,T.key,U.target.value)}),T.hint&&e.jsx("div",{className:"dim",style:{fontSize:11,marginTop:4,lineHeight:1.5},children:T.hint}),T.key==="tg"&&((se=(L=s.defaults)==null?void 0:L[m])==null?void 0:se.tg_rich)&&e.jsxs("div",{className:"dim",style:{fontSize:11,marginTop:4,lineHeight:1.5},children:["У этого сообщения сложная вёрстка (таблица/список), она задана в коде — нажми «Предпросмотр». Если впишешь свой текст, уйдёт он ",e.jsx("b",{children:"вместо"})," вёрстки."]})]},T.key)}),e.jsxs("div",{style:{display:"flex",gap:10,alignItems:"center",marginTop:6,flexWrap:"wrap"},children:[e.jsx("button",{className:"btn btn--sm",disabled:E||!v,onClick:B,children:E?"Сохранение…":"Сохранить"}),e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:E,onClick:Q,title:"Показать, как сообщение будет выглядеть при отправке",children:"Предпросмотр"}),e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:E,onClick:()=>{for(const T of A)_(m,T.key,i(m,T.key))},title:"Вернуть текст этого языка/канала к заводскому (Ð
84¿Ð¾ÑÐ»Ðµ «Сохранить» оверрайд очистится)",children:"Сбросить к умолчанию"}),$&&e.jsx("span",{className:"dim",style:{fontSize:12},children:"есть несохранённые правки на других языках"}),D&&e.jsx("span",{className:"dim",style:{fontSize:13},children:D})]}),o&&e.jsx("div",{onClick:()=>N(null),style:{position:"fixed",inset:0,background:"rgba(0,0,0,0.65)",zIndex:60,display:"flex",alignItems:"center",justifyContent:"center",padding:20},children:e.jsxs("div",{onClick:T=>T.stopPropagation(),style:{background:"var(--surface-2)",border:"1px solid var(--border)",borderRadius:16,maxWidth:620,width:"100%",maxHeight:"90vh",overflow:"auto",padding:18},children:[e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:12},children:[e.jsxs("div",{style:{fontWeight:700,fontSize:14},children:["Предпросмотр · ",n==="email"?"Почта":"Telegram"," · ",Nn[m]||m.toUpperCase()]}),e.jsx("button",{className:"btn btn--ghost btn--xs",onClick:()=>N(null),children:"✕"})]}),o.stripped&&e.jsx("div",{style:{fontSize:12.5,lineHeight:1.45,marginBottom:10,color:"var(--coral)"},children:"Часть оформления вырезана из соображений безопасности — в сообщении её не будет."}),n==="email"?e.jsxs(e.Fragment,{children:[o.subject!==void 0&&e.jsxs("div",{className:"dim",style:{fontSize:13,marginBottom:8},children:["Тема: ",e.jsx("span",{style:{color:"var(--text)"},children:o.subject})]}),e.jsx("iframe",{title:"email-preview",sandbox:"allow-same-origin",srcDoc:o.html||"",style:{width:"100%",height:320,maxHeight:"72vh",border:"1px solid var(--border)",borderRadius:10,background:"#fff",display:"block"},onLoad:T=>{var y,q,P;try{const oe=T.currentTarget,L=(P=(q=(y=oe.contentWindow)==null?void 0:y.document)==null?void 0:q.documentElement)==null?void 0:P.scrollHeight;L&&(oe.style.height=Math.min(L+4,Math.round(window.innerHeight*.72))+"px")}catch{}}})]}):e.jsxs(e.Fragment,{children:[e.jsx("div",{style:{background:"var(--bg)",border:"1px solid var(--border)",borderRadius:12,padding:"14px 16px",fontSize:14.5,lineHeight:1.5,wordBreak:"break-word",...o.blocksHtml?{}:{whiteSpace:"pre-wrap"}},dangerouslySetInnerHTML:{__html:o.blocksHtml||_i(o.text||"")}}),((V=o.button)==null?void 0:V.text)&&e.jsx("div",{style:{marginTop:10},children:e.jsx("a",{href:o.button.url,target:"_blank",rel:"noreferrer",style:{display:"block",textAlign:"center",padding:"11px 16px",borderRadius:10,background:"rgba(91,156,255,0.16)",border:"1px solid rgba(91,156,255,0.4)",color:"var(--violet-2)",fontWeight:700,fontSize:14,textDecoration:"none"},children:o.button.text})}),e.jsxs("div",{className:"dim",style:{fontSize:11.5,marginTop:8},children:[o.rich?"Это сообщение со сложной вёрсткой (таблица/список) — показана раскладка. ":"","Анимированные эмодзи и фото-баннер отображаются только в самом Telegram."]})]})]})})]})}function Oe({options:s,value:t,onChange:m}){return e.jsx("div",{style:{display:"flex",gap:6,flexWrap:"wrap"},children:s.map(([n,x])=>e.jsx("button",{onClick:()=>m(n),style:{fontFamily:"inherit",fontWeight:700,fontSize:12.5,padding:"6px 13px",borderRadius:999,cursor:"pointer",border:"1px solid "+(t===n?"rgba(91,156,255,0.55)":"rgba(255,255,255,0.1)"),background:t===n?"rgba(91,156,255,0.22)":"var(--surface)",color:"var(--text)"},children:x},n))})}function kr({api:s}){const[t,m]=r.useState([]),[n,x]=r.useState([]),[i,C]=r.useState(!1),[u,k]=r.useState("telegram"),[g,I]=r.useState("ru"),E=()=>s("/admin/msgcopy").then(W=>{m(W.kinds||[]),x(W.placeholders||[]),C(!0)}).catch(()=>C(!0));r.useEffect(()=>{E()},[]);const X=t.length?Object.keys(t[0].defaults||{}):["ru","en"],D=u==="telegram"?t.filter(W=>X.some(o=>{var N,v;return(v=(N=W.defaults)==null?void 0:N[o])==null?void 0:v.tg})):t;return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"card pad",style:{marginBottom:14,fontSize:13.5,lineHeight:1.6},children:[e.jsx("div",{style:{fontWeight:700,marginBottom:6},children:"Как это работает"}),e.jsxs("p",{className:"dim",style:{margin:0},children:["Тексты уведомлений (маркетинг + жизненный цикл подписки). Выберите канал (Telegram или Почта) и язык — ниже покажутся сообщения только для этой пары. «Предпросмотр» показывает, как сообщение уйдёт на самом деле. Правки применяются сразу, без деплоя. Правки на разных языках копятся — сохраняются по кнопке.",e.jsx("br",{}),e.jsx("b",{children:"Telegram:"})," поле «Текст сообщения» — это и есть само сообщение (HTML ",e.jsx("code",{children:"<b>"}),", ",e.jsx("code",{children:"<i>"})," сохраняется).",e.jsx("br",{}),e.jsx("b",{children:"Почта:"})," письмо свёрстано красивым шаблоном в коде — редактируется «Тема», а «Текст письма» заполняется, только если нужно заменить шаблон простым текстом."]}),!!n.length&&e.jsxs("p",{className:"dim",style:{margin:"8px 0 0"},children:["Плейсхолдеры (одна фигурная скобка): ",n.map(W=>`{${W}}`).join(" ")," — подставляются автоматически при отправке; если значения нет, подставится пустая строка (ошибки не будет)."]})]}),e.jsxs("div",{style:{display:"flex",gap:20,flexWrap:"wrap",alignItems:"flex-end",marginBottom:16},children:[e.jsxs("div",{children:[e.jsx("div",{className:"dim",style:{fontSize:11.5,marginBottom:5,textTransform:"uppercase",letterSpacing:"0.04em"},children:"Канал"}),e.jsx(Oe,{options:[["telegram","Telegram"],["email","Почта"]],value:u,onChange:W=>k(W)})]}),e.jsxs("div",{children:[e.jsx("div",{className:"dim",style:{fontSize:11.5,marginBottom:5,textTransform:"uppercase",letterSpacing:"0.04em"},children:"Язык"}),e.jsx(Oe,{options:X.map(W=>[W,Nn[W]||W.toUpperCase()]),value:g,onChange:I})]})]}),D.map(W=>e.jsx(Nr,{item:W,api:s,lang:g,chan:u},W.kind)),i&&!t.length&&e.jsx("p",{className:"dim",children:"Не удалось загрузить."}),i&&!!t.length&&!D.length&&e.jsx("p",{className:"dim",children:"Дл
84я этого канала нет сообщений."}),!i&&e.jsx("p",{className:"dim",children:"Загрузка…"})]})}function Cr(s){return s.replace(/(^|[\s([])_([^_\n]+)_(?=$|[\s).,!?:;])/g,"$1$2").replace(/(^|[\s([])\*([^*\n]+)\*(?=$|[\s).,!?:;])/g,"$1$2")}function tn({body:s}){const t=/(https?:\/\/[^\s<>()"'\][]+)/g,m=(s||"(пустое тело)").split(`
85`);return e.jsx("div",{style:{marginTop:8,padding:"14px 16px",borderRadius:12,border:"1px solid var(--border)",background:"#fff",color:"#1a1d23",wordBreak:"break-word",fontSize:14,lineHeight:1.6},children:m.map((n,x)=>{if(/^\s*[-–—_=]{3,}\s*$/.test(n))return e.jsx("hr",{style:{border:"none",borderTop:"1px solid var(--border)",margin:"14px 0"}},x);const i=n.split(t);return e.jsx("div",{style:{minHeight:n.trim()?void 0:"0.9em",whiteSpace:"pre-wrap"},children:i.map((C,u)=>/^https?:\/\//.test(C)?e.jsx("a",{href:C,target:"_blank",rel:"noopener noreferrer",style:{color:"#5b3df5",wordBreak:"break-all"},children:C},u):e.jsx("span",{children:Cr(C)},u))},x)})})}function sn({files:s,onDrop:t}){const[m,n]=r.useState([]);return r.useEffect(()=>{const x=s.map(i=>URL.createObjectURL(i));return n(x),()=>{for(const i of x)URL.revokeObjectURL(i)}},[s]),s.length?e.jsx("div",{style:{display:"flex",gap:10,flexWrap:"wrap",marginTop:8},children:s.map((x,i)=>e.jsxs("div",{style:{position:"relative",width:132},children:[e.jsx("a",{href:m[i]||"#",target:"_blank",rel:"noopener noreferrer",title:"открыть в полный размер",style:{display:"block",border:"1px solid var(--border)",borderRadius:10,overflow:"hidden",background:"#fff"},children:m[i]&&e.jsx("img",{src:m[i],alt:x.name,style:{display:"block",width:"100%",height:96,objectFit:"cover",objectPosition:"top"}})}),e.jsx("button",{className:"btn btn--ghost btn--xs",title:"убрать",onClick:()=>t(i),style:{position:"absolute",top:4,right:4,padding:"0 6px",background:"var(--surface)",borderRadius:6},children:"×"}),e.jsxs("div",{className:"dim",style:{fontSize:11.5,marginTop:4,lineHeight:1.3,overflowWrap:"anywhere"},children:[x.name," · ",Math.max(1,Math.round(x.size/1024))," КБ"]})]},x.name+i))}):null}function zr({body:s,html:t}){const m=(t||"").trim()||(/^\s*(?:<!doctype|<html|<table|<div|<body|<p\b|<a\b|<img)/i.test(s||"")?s:""),[n,x]=r.useState(!1);return m?e.jsxs("div",{children:[e.jsxs("div",{style:{display:"flex",gap:6,margin:"8px 0 6px"},children:[e.jsx("button",{className:"toggle-pill toggle-pill--xs"+(n?"":" is-on"),onClick:()=>x(!1),children:"письмо"}),e.jsx("button",{className:"toggle-pill toggle-pill--xs"+(n?" is-on":""),onClick:()=>x(!0),children:"текст"})]}),n?e.jsx(tn,{body:s||"(текстовой части нет)"}):e.jsx("iframe",{title:"email",sandbox:"",srcDoc:`<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>html,body{margin:0;padding:0}body{-webkit-text-size-adjust:100%;overflow-wrap:break-word;overflow-x:auto;font:15px/1.5 -apple-system,Segoe UI,Roboto,sans-serif}img{max-width:min(100%,420px)!important;max-height:480px!important;height:auto!important;object-fit:contain}table{max-width:100%}th,td{word-break:normal;overflow-wrap:normal}a{overflow-wrap:anywhere;word-break:break-word}pre{white-space:pre-wrap}*{box-sizing:border-box}</style></head><body>${m}</body></html>`,style:{width:"100%",height:560,maxHeight:"70vh",border:"1px solid var(--border)",borderRadius:12,background:"#fff"}})]}):e.jsxs("div",{children:[e.jsxs("div",{style:{display:"flex",gap:6,margin:"8px 0 6px"},children:[e.jsx("button",{className:"toggle-pill toggle-pill--xs",disabled:!0,title:"У этого письма нет html-части — отправитель прислал только текст",children:"письмо"}),e.jsx("button",{className:"toggle-pill toggle-pill--xs is-on",children:"текст"})]}),e.jsx(tn,{body:s||"(пустое тело)"})]})}function _r({api:s}){const[t,m]=r.useState(null),[n,x]=r.useState(!1);if(r.useEffect(()=>{s("/admin/mail/deliverability").then(m).catch(()=>x(!0))},[]),n||!t)return null;const i=g=>{const I=((g==null?void 0:g.delivered)||0)+((g==null?void 0:g.failed)||0);return I?Math.round(100*g.delivered/I):null},C=i(t.week),u=i(t.today),k=g=>g==null?"var(--muted)":g>=95?"var(--green)":g>=85?"var(--amber)":"var(--coral)";return e.jsx("div",{className:"card pad",style:{marginBottom:14},children:e.jsxs("div",{className:"row",style:{alignItems:"baseline"},children:[e.jsxs("div",{style:{display:"flex",gap:22,flexWrap:"wrap",alignItems:"baseline"},children:[e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:24,fontWeight:700,color:k(u)},children:u==null?"—":u+"%"}),e.jsxs("div",{className:"dim",style:{fontSize:12},children:["дошло сегодня · ",t.today.delivered+t.today.failed," писем"]})]}),e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:24,fontWeight:700,color:k(C)},children:C==null?"—":C+"%"}),e.jsxs("div",{className:"dim",style:{fontSize:12},children:["за неделю · ",t.week.delivered+t.week.failed," писем"]})]}),e.jsxs("div",{children:[e.jsx("div",{style:{fontSize:24,fontWeight:700},children:t.rep.green}),e.jsx("div",{className:"dim",style:{fontSize:12},children:"адресов с подтверждённой доставкой"})]})]}),e.jsx("div",{style:{display:"flex",gap:6,flexWrap:"wrap",maxWidth:460,justifyContent:"flex-end"},children:t.causes.slice(0,5).map(g=>e.jsxs("span",{className:"chipb",title:"причина отказа за неделю",children:[g.cause," · ",g.n]},g.cause))})]})})}function Tr({api:s}){const[t,m]=r.useState(null),[n,x]=r.useState(""),[i,C]=r.useState(""),[u,k]=r.useState(""),[g,I]=r.useState(null),[E,X]=r.useState(null),[D,W]=r.useState(null),[o,N]=r.useState(null),[v,_]=r.useState(!1),B=r.useRef(0),Q=r.useRef(0),A=()=>{const L=++B.current,se=new URLSearchParams;n.trim()&&se.set("q",n.trim()),i&&se.set("state",i),u&&se.set("kind",u),s(`/admin/mail/sent?${se}`).then(U=>{L===B.current&&m(U.sent||[])}).catch(()=>{L===B.current&&_(!0)})};if(r.useEffect(()=>{const L=setTimeout(A,n?350:0);return()=>clearTimeout(L)},[n,i,u]),v)return null;const $=async L=>{if(g===L){Q.current++,I(null),X(null),W(null),N(null);return}const se=++Q.current;I(L),X(null),W(null),N(null);try{const U=await s(`/admin/mail/sent/${encodeURIComponent(L)}/log`);se===Q.current&&X(U)}catch{se===Q.current&&X({steps:[]})}},V=async(L,se)=>{const U=Q.current;W(se?null:"загружаю…"),se&&N("загружаю…");try{const ne=await s(`/admin/mail/sent/${encodeURIComponent(L)}/raw${se?`?part=${se}`:""}`);if(U!==Q.current)return;se?N(ne.raw||""):W(ne.raw||"(пусто)")}catch{if(U!==Q.current)return;const ne="Недоступно: письмо ушло другим каналом либо старше двух недель.";se?N(ne):W(ne)}},T=L=>
85new Date(L).toLocaleString("ru-RU",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"}),y={delivered:["дошло","var(--green)"],bounced:["отбито","var(--coral)"],failed:["не ушло","var(--coral)"],deferred:["отложено","var(--amber)"],complained:["жалоба","var(--coral)"],suppressed:["не слали","var(--muted)"],rejected:["спам-фильтр","var(--coral)"],soft:["не принял ящик","var(--amber)"],unknown:["нет данных","var(--muted)"]},q=L=>String(L||"").split(":")[0].trim(),P=L=>String(L||"").split(":").slice(1).join(":").trim(),oe={delivered:"принято почтовиком",bounced:"отказ",deferred:"придержано, попробует ещё",failed:"не доставлено",complained:"пожаловались на спам",suppressed:"не отправляли",accepted:"принято почтовиком",rejected:"отклонено (спам-фильтр или политика почтовика)"};return e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsxs("div",{className:"row",style:{alignItems:"baseline",marginBottom:10,gap:12,flexWrap:"wrap"},children:[e.jsx("div",{style:{fontWeight:700},children:"Письма · что ушло каждому"}),e.jsx(Oe,{options:[["","все"],["bounced","отбитые"],["pending","в пути"],["delivered","дошли"]],value:i,onChange:L=>C(String(L))}),e.jsx(Oe,{options:[["","любые"],["transactional","важное"],["marketing","рассылка"]],value:u,onChange:L=>k(String(L))}),e.jsx("input",{className:"field",value:n,onChange:L=>x(L.target.value),placeholder:"адрес получателя",style:{maxWidth:240,marginLeft:"auto"}})]}),!t&&e.jsx("div",{className:"dim",children:"считаю…"}),t&&!t.length&&e.jsx("div",{className:"dim",children:"писем нет"}),t&&!!t.length&&e.jsx("div",{className:"ms-rows",children:t.map(L=>{var le;const[se,U]=y[q(L.state)]||["в пути","var(--muted)"],ne=g===L.msg_id;return e.jsxs("div",{className:"ms-row"+(ne?" is-open":""),children:[e.jsxs("button",{className:"ms-main",onClick:()=>$(L.msg_id),children:[e.jsxs("span",{className:"ms-text",children:[e.jsx("span",{className:"ms-subj",children:L.subject||(L.kind==="marketing"?"Рассылка":"Письмо без темы")}),e.jsx("span",{className:"ms-to",children:L.email})]}),e.jsxs("span",{className:"ms-meta",children:[e.jsx("span",{style:{color:U,fontWeight:700},children:se}),e.jsx("span",{className:"dim",children:T(L.sent_at)}),e.jsxs("span",{className:"dim",children:[L.kind==="marketing"?"рассылка":"важное"," · ",L.channel]})]})]}),ne&&e.jsxs("div",{className:"ms-trail",children:[!E&&e.jsx("div",{className:"dim",children:"смотрю путь…"}),E&&!(E.steps||[]).length&&(L.state?e.jsxs("div",{children:[((le=y[q(L.state)])==null?void 0:le[0])||L.state,P(L.state)&&e.jsx("div",{className:"mv-why",children:P(L.state)})]}):e.jsx("div",{className:"dim",children:"Отчёт почтовика ещё не пришёл: журнал Cloudflare отстаёт от отправки на минуты–часы, разбор идёт каждые 15 минут."})),E&&!!(E.steps||[]).length&&e.jsx("ol",{className:"ms-steps",children:E.steps.map((ee,be)=>e.jsxs("li",{children:[e.jsx("span",{className:"dim",children:T(ee.created_at)})," · ",oe[ee.event]||ee.event,!!ee.smtp_code&&e.jsxs(e.Fragment,{children:[" "," · ","код ",ee.smtp_code]}),!!ee.enhanced&&e.jsxs(e.Fragment,{children:[" (",ee.enhanced,")"]}),!!ee.message&&e.jsx("div",{className:"mv-why",children:ee.message}),ee.dead&&e.jsx("div",{className:"dim",style:{fontSize:12},children:"адрес погашен по этому ответу"})]},be))}),L.channel==="cf"?e.jsx("div",{className:"dim",style:{marginTop:8,fontSize:13},children:"Письмо ушло через Cloudflare — его текст не хранится."}):e.jsxs("div",{style:{marginTop:8,display:"flex",gap:8,flexWrap:"wrap"},children:[e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>V(L.msg_id,"html"),children:"Показать письмо"}),e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>V(L.msg_id),children:"Исходник"})]}),o!==null&&(o==="загружаю…"?e.jsx("div",{className:"dim",style:{marginTop:10},children:o}):e.jsx("iframe",{className:"ms-letter",sandbox:"",title:"письмо",srcDoc:o})),D&&e.jsx("pre",{className:"ms-raw",children:D})]})]},L.msg_id)})})]})}function $r({api:s}){const[t,m]=r.useState(null),[n,x]=r.useState(1),[i,C]=r.useState(!1),[u,k]=r.useState(!1);if(r.useEffect(()=>{m(null),s(`/admin/mail/verdicts?days=${n}`).then(m).catch(()=>k(!0))},[n]),u)return null;const g=(t==null?void 0:t.domains)||[],I=g.reduce((A,$)=>A+($.delivered||0)+($.bounced||0),0);if(t&&!I)return null;const E=A=>{const $=(A.delivered||0)+(A.bounced||0);return $?Math.round(100*(A.delivered||0)/$):0},X=A=>A>=95?"var(--green)":A>=60?"var(--amber)":"var(--coral)",D=A=>(A.delivered||0)+(A.bounced||0)+(A.deferred||0),W=g.filter(A=>
85D(A)>=3),o=g.filter(A=>D(A)<3),N=A=>{const $=A%100,V=A%10;return`${A} ${$>=11&&$<=14||V===1?"человек":V>=2&&V<=4?"человека":"человек"}`},v=A=>String(A||"").replace(/^\(?host\s+\S+\s+said:\s*/i,"").replace(/\s*\(in reply to [^)]*\)\)?\s*$/i,"").trim(),_=(A,$,V,T)=>{const y=Math.abs(A)%100,q=y%10;return y>10&&y<20?T:q>1&&q<5?V:q===1?$:T},B=A=>`${A} ${_(A,"письмо","письма","писем")}`,Q=(()=>{const A=new Map;for(const $ of(t==null?void 0:t.reasons)||[]){const V=v($.reason),T=`${$.domain}|${$.smtp_code}|${V}`,y=A.get(T);y?(y.n+=$.n||0,y.dead+=$.dead||0):A.set(T,{...$,reason:V,n:$.n||0,dead:$.dead||0})}return[...A.values()].sort(($,V)=>V.n-$.n)})();return e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsxs("div",{className:"row",style:{alignItems:"baseline",marginBottom:10},children:[e.jsx("div",{style:{fontWeight:700},children:"Доставка · кто принимает"}),e.jsx(Oe,{options:[[1,"сутки"],[7,"неделя"],[30,"месяц"]],value:n,onChange:A=>x(Number(A))})]}),!t&&e.jsx("div",{className:"dim",children:"считаю…"}),t&&e.jsxs(e.Fragment,{children:[!!(t.channels||[]).length&&e.jsx("div",{className:"mv-rows mv-stats",style:{marginBottom:12},children:(t.channels||[]).map(A=>{const $=A.sent?Math.round((A.known||0)/A.sent*100):0,V={own:"свой сервер",cf:"Cloudflare",smtpbz:"SMTP.bz",resend:"Resend",relay:"Relay"};return e.jsxs("div",{className:"mv-row",children:[e.jsxs("div",{className:"mv-head",children:[e.jsx("span",{className:"mv-dom",children:V[A.channel]||A.channel||"—"}),e.jsxs("span",{style:{color:X($),fontWeight:700},children:[$,"%"]})]}),e.jsxs("div",{className:"mv-nums",children:[e.jsxs("span",{children:[e.jsx("b",{children:A.sent||0})," отправлено"]}),e.jsxs("span",{children:[e.jsx("b",{children:A.known||0})," знаем судьбу"]}),e.jsxs("span",{className:"dim",children:[e.jsx("b",{children:A.bounced||0})," отбито"]})]})]},A.channel||"?")})}),e.jsx("div",{className:"mv-rows mv-stats",children:(i?g:W).map(A=>e.jsxs("div",{className:"mv-row",children:[e.jsxs("div",{className:"mv-head",children:[e.jsx("span",{className:"mv-dom",children:A.domain||"—"}),e.jsxs("span",{style:{color:X(E(A)),fontWeight:700},children:[E(A),"%"]})]}),e.jsxs("div",{className:"mv-nums",children:[e.jsxs("span",{children:[e.jsx("b",{children:A.delivered||0})," дошло"]}),e.jsxs("span",{children:[e.jsx("b",{children:A.bounced||0})," отбито"]}),e.jsxs("span",{className:"dim",children:[e.jsx("b",{children:A.deferred||0})," отложено"]}),e.jsx("span",{className:"dim",children:N(A.people||0)})]})]},A.domain))}),!!o.length&&e.jsx("button",{className:"linkbtn linkbtn--muted linkbtn--sm",style:{alignSelf:"flex-start",marginTop:2},onClick:()=>C(!i),children:i?"свернуть редкие домены":`ещё ${o.length} ${o.length===1?"домен":o.length<5?"домена":"доменов"} по одному-двум письмам`}),!!Q.length&&e.jsxs(e.Fragment,{children:[e.jsx("div",{style:{fontWeight:700,margin:"14px 0 6px"},children:"Чем отказывают"}),e.jsx("div",{className:"mv-rows",children:Object.entries(Q.reduce((A,$)=>{var V;return(A[V=$.domain||"—"]||(A[V]=[])).push($),A},{})).sort((A,$)=>$[1].reduce((V,T)=>V+T.n,0)-A[1].reduce((V,T)=>V+T.n,0)).map(([A,$])=>{const V=$.reduce((y,q)=>y+q.n,0),T=$.reduce((y,q)=>y+(q.dead||0),0);return e.jsxs("div",{className:"mv-row",children:[e.jsxs("div",{className:"mv-head",children:[e.jsx("span",{className:"mv-dom",children:A}),e.jsxs("span",{className:"dim",children:[B(V),T?` · ${T} ${_(T,"адрес погашен","адреса погашено","адресов погашено")}`:""]})]}),$.sort((y,q)=>q.n-y.n).map((y,q)=>e.jsxs("details",{className:"mv-why-wrap",children:[e.jsxs("summary",{className:"mv-why",children:[y.family||y.reason,e.jsxs("span",{className:"dim",children:[" · ",B(y.n)," · код ",y.smtp_code||"—"]})]}),e.jsx("div",{className:"mv-why mv-why--full",children:y.reason}),e.jsx("div",{className:"dim",style:{fontSize:12,marginTop:4},children:y.dead?`${y.dead} — адрес погашен`:"адрес не тронут: дело Ð
85½Ðµ в нём"})]},q))]},A)})})]})]})]})}function Rr({api:s}){var u;const[t,m]=r.useState(null);if(r.useEffect(()=>{s("/admin/dmarc?days=30").then(m).catch(()=>m(null))},[]),!((u=t==null?void 0:t.byOrg)!=null&&u.length))return null;const n=t.byOrg.reduce((k,g)=>k+g.messages,0),x=(k,g)=>g?Math.round(100*k/g):0,i=k=>k>=95?"var(--green)":k>=85?"var(--amber)":"var(--coral)",C={"google.com":"Gmail","mail.ru":"Mail.ru","corp.mail.ru":"Mail.ru","yandex.ru":"Яндекс","zohomail.com":"Zoho","yahoo.com":"Yahoo","protection.outlook.com":"Outlook"};return e.jsxs("div",{className:"card pad",style:{marginBottom:14},children:[e.jsxs("div",{style:{display:"flex",alignItems:"baseline",gap:10,flexWrap:"wrap",marginBottom:10},children:[e.jsx("div",{style:{fontSize:16,fontWeight:700},children:"Что видят почтовики"}),e.jsxs("span",{className:"dim",style:{fontSize:12.5},children:["отчёты DMARC за 30 дней · ",n.toLocaleString("ru-RU")," писем"]})]}),e.jsx("div",{style:{overflowX:"auto"},children:e.jsxs("table",{className:"tbl",style:{width:"100%",fontSize:13},children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{style:{textAlign:"left",padding:6},children:"почтовик"}),e.jsx("th",{style:{textAlign:"right",padding:6},children:"писем"}),e.jsx("th",{style:{textAlign:"right",padding:6},title:"прошли проверку DMARC — выровнен DKIM или SPF",children:"принято"}),e.jsx("th",{style:{textAlign:"right",padding:6},title:"подпись DKIM сошлась",children:"DKIM"}),e.jsx("th",{style:{textAlign:"right",padding:6},title:"SPF сошёлся",children:"SPF"}),e.jsx("th",{style:{textAlign:"right",padding:6},title:"положили в спам или отбили",children:"в спам · отбито"})]})}),e.jsx("tbody",{children:t.byOrg.map(k=>{const g=x(k.pass,k.messages);return e.jsxs("tr",{children:[e.jsx("td",{style:{padding:6},children:C[k.org]||k.org}),e.jsx("td",{style:{padding:6,textAlign:"right",fontVariantNumeric:"tabular-nums"},children:k.messages.toLocaleString("ru-RU")}),e.jsxs("td",{style:{padding:6,textAlign:"right",color:i(g),fontWeight:600},children:[g,"%"]}),e.jsxs("td",{className:"dim",style:{padding:6,textAlign:"right"},children:[x(k.dkim,k.messages),"%"]}),e.jsxs("td",{className:"dim",style:{padding:6,textAlign:"right"},children:[x(k.spf,k.messages),"%"]}),e.jsx("td",{style:{padding:6,textAlign:"right",color:k.quarantined+k.rejected?"var(--coral)":void 0},children:k.quarantined+k.rejected||"—"})]},k.org)})})]})})]})}function Wr({api:s}){var Tt,ht,ut,pt,nt,$t,fs,js,ys,bs,vs,Ss,ws,Ns,ks,Cs,zs;const[t,m]=r.useState([]),[n,x]=r.useState([]),[i,C]=r.useState(!1),[u,k]=r.useState(!1),[g,I]=r.useState(!1),[E,X]=r.useState(""),[D,W]=r.useState(""),o=r.useRef(!1),[N,v]=r.useState(null),[_,B]=r.useState(""),[Q,A]=r.useState(""),[$,V]=r.useState([]),[T,y]=r.useState(!1),[q,P]=r.useState(""),[oe,L]=r.useState(null),[se,U]=r.useState(null),ne=r.useRef(0),[le,ee]=r.useState(!1),[be,w]=r.useState(""),[d,b]=r.useState(""),[F,Z]=r.useState(""),[xe,ue]=r.useState(""),[Ne,fe]=r.useState([]),[ce,Be]=r.useState("inbox"),[Ae,Ce]=r.useState(""),[R,ze]=r.useState(null),[ke,Le]=r.useState(!1),[Ee,Fe]=r.useState(!1),[l,a]=r.useState(""),[M,f]=r.useState(""),[S,H]=r.useState(""),[p,K]=r.useState(""),re=r.useRef(0),je=(h,z)=>{const O=new URLSearchParams;z&&O.set("disputes","1"),D&&O.set("q",D),h&&(O.set("before",String(h.ts)),h.id&&O.set("before_id",String(h.id)));const Y=O.toString();return Y?"?"+Y:""},c=h=>h.length?{ts:h[h.length-1].ts,id:h[h.length-1].id}:null,te=(h=re.current)=>s("/admin/mail"+je()).then(z=>{if(h!==re.current)return;const O=z.mail||[];o.current||k(!!z.more),m(Y=>{if(!O.length)return O;const G=O[O.length-1],ae=new Set(O.map(he=>he.id)),de=Y.filter(he=>!ae.has(he.id)&&(he.ts<G.ts||he.ts===G.ts&&String(he.id)<String(G.id)));return[...O,...de]})}).catch(()=>{}),ve=async()=>{const h=ce==="disputes",z=c(h?n:t);if(!z)return;const O=re.current;I(!0);try{const Y=await s("/admin/mail"+je(z,h));if(O!==re.current)return;const G=Y.mail||[];h||(o.current=!0);const ae=new Set((h?n:t).map(we=>we.id)),de=G.filter(we=>!ae.has(we.id));(h?x:m)(we=>{const _e=new Set(we.map(Te=>Te.id));return[...we,...de.filter(Te=>!_e.has(Te.id))]}),(h?C:k)(!!Y.more&&de.length>0)}catch{}finally{I(!1)}},Se=(h=re.current)=>s("/admin/mail"+je(void 0,!0)).then(z=>{h===re.current&&(x(z.mail||[]),C(!!z.more))}).catch(()=>{});r.useEffect(()=>{re.current+=1,o.current=!1,m([]),x([]),k(!1),C(!1),te(re.current),Se(re.current)},[D]),Vt(()=>{te(),ce==="disputes"&&Se()},[D]);const pe=h=>(h||"").replace(/[\u00a0\u202f\u2007]/g," ").replace(/^(?:\s*(?:re|rе|fwd?|fw|отв|ответ|перес(?:л|ылка))\s*(?:\[\d+\]|\(\d+\))?\s*:\s*)+/i,"").trim().toLowerCase().replace(/^\((?:no subject|без темы|нет темы)\)$/,""),Re=h=>typeof h.peer=="string"?h.peer:Yt(h.direction==="out"?h.toAddr:h.fromAddr),Ie=h=>ri.includes(Yt(h.peer||h.from)),rt=h=>Re(h)+"|"+pe(h.subject),et=()=>{ne.current++,v(null)},at=ai,Ye=h=>[(h==null?void 0:h.subject)||"",...((h==null?void 0:h.msgs)||[]).map(z=>`${z.subject||""} ${z.body||""} ${z.html||""}`)].join(" "),lt=h=>{var z;return((z=Ye(h).match(at))==null?void 0:z[0])||""},Gt=h=>[...new Set(Ye(h).match(at)||[])],Je=r.useRef(0),St=r.useRef(null);r.useEffect(()=>{var O,Y;const h=((O=N==null?void 0:N.thread)==null?void 0:O.key)??null;if(h===St.current||(St.current=h,Je.current++,Ce(""),ze(null),a(""),Le(!1),!N||!Ie(N.thread||N)))return;const z=lt({subject:(Y=N.thread)==null?void 0:Y.subject,msgs:N.msgs});z&&Ce(z)},[N]);const tt=h=>{var ae;if(!N)return"";const z=[((ae=N.thread)==null?void 0:ae.subject)||"",...(N.msgs||[]).map(de=>
85de.subject||"")].filter(Boolean).join(" | "),O=(N.msgs||[]).map(de=>de.body||"").join(`
86`),Y=h?O.toLowerCase().indexOf(h.toLowerCase()):-1,G=O.length<=900?O:Y>=0?O.slice(Math.max(0,Y-300),Y+300):O.slice(0,900);return`${z}
87${G}`.slice(0,1200)},Ve=async(h,z,O)=>{var _e;const Y=String(h||"").trim();if(!Y){a("Укажите номер операции из письма процессинга");return}const G=String(tt(Y)).trim(),ae=String(O??"").trim();f(ae);const de=S.trim()?`&traffic_gb=${encodeURIComponent(S.trim())}`:"",he=p.trim()?`&online_ms=${Date.parse(p.trim())||""}`:"",we=++Je.current;Le(!0),a(""),ze(null);try{const Te=await s(`/admin/dispute/report?txn=${encodeURIComponent(Y)}${G?`&reason=${encodeURIComponent(G)}`:""}${ae?`&reason_code=${encodeURIComponent(ae)}`:""}${de}${he}`);if(we!==Je.current)return;ze(Te),Te!=null&&Te.found||a(((_e=Te==null?void 0:Te.verdict)==null?void 0:_e.note)||"Операция не найдена")}catch(Te){we===Je.current&&a((Te==null?void 0:Te.message)||"Не удалось собрать досье")}finally{we===Je.current&&Le(!1)}},Kt=async h=>{const z=String(h||"").trim();if(z){Fe(!0),a("");try{const O=tt(z),Y=await fetch("/api/admin/dispute/build-reply",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({txn:z,reason:O||void 0,reason_code:M||void 0,traffic_gb:S.trim()||void 0,online_ms:p.trim()?String(Date.parse(p.trim())||""):void 0})}),G=await Y.json().catch(()=>({}));if(!Y.ok)throw new Error(G.error||"request_failed");const ae=(we,_e,Te)=>{const Pe=atob(we),Qe=new Uint8Array(Pe.length);for(let Rt=0;Rt<Pe.length;Rt++)Qe[Rt]=Pe.charCodeAt(Rt);return new File([Qe],_e,{type:Te})},de=(G.shots||[]).map(we=>ae(we.b64,we.name,we.mime||"image/png"));B(String(G.text||"")),V(de);const he=Array.isArray(G.missing)?G.missing:[];a(G.failed?`Собрано ${de.length} из ${G.planned||3}. Не снялось: ${he.join(", ")||"часть кадров"} — доснимите вручную кнопкой «Открыть под скриншоты».`:""),setTimeout(()=>{var we;return(we=document.querySelector(".mail-reply-box"))==null?void 0:we.scrollIntoView({behavior:"smooth",block:"center"})},60)}catch(O){a("Не удалось собрать письмо: "+((O==null?void 0:O.message)||O))}finally{Fe(!1)}}},[wt,Nt]=r.useState(!1),Xt=async(h,z)=>{var Y,G;const O=((Y=R==null?void 0:R.user)==null?void 0:Y.email)||"клиента";if(!(!z&&!confirm(`Заблокировать ${O}?
88
89Закроется вход в кабинет, доступ к сервису погаснет, автопродление выключится. Снять блокировку можно этой же кнопкой.`))){Nt(!0),a("");try{const ae=await fetch("/api/admin/dispute/block",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({txn:h,undo:z,userId:((G=R==null?void 0:R.user)==null?void 0:G.id)||void 0,reason:tt(h)?"спор по операции "+h:"чарджбек"})}),de=await ae.json().catch(()=>({}));if(de.error==="user_mismatch")throw new Error("по этому номеру сервер нашёл другого клиента, никого не заблокировали — пересоберите досье");if(!ae.ok)throw new Error(de.error||"request_failed");ze(he=>he&&{...he,user:{...he.user||{},blockedAt:z?null:Date.now()}}),P(z?"Блокировка снята":`Клиент заблокирован${de.panelOff?", доступ в панели погашен":" (панель не ответила — добьёт крон)"}`)}catch(ae){a("Не удалось: "+((ae==null?void 0:ae.message)||ae))}finally{Nt(!1)}}},me=(h,z)=>O=>{var de,he,we,_e,Te;const Y=yi((de=O.clipboardData)==null?void 0:de.items,(he=O.clipboardData)==null?void 0:he.files),ae=!((((_e=(we=O.clipboardData)==null?void 0:we.files)==null?void 0:_e.length)??0)>0)&&!!((Te=O.clipboardData)!=null&&Te.getData("text/plain"));Jt(Y,h,Pe=>z(Pe),P)&&!ae&&O.preventDefault()},kt=async()=>{const h=be.trim(),z=F.trim(),O=xe.trim();if(!h||!z||!O){P("Заполните адрес, тему и текст");return}y(!0),P("");try{if(Ne.length){const Y=new FormData;Y.set("to",h),Y.set("subject",z),Y.set("text",O),d.trim()&&Y.set("cc",d.trim());for(const de of Ne)Y.append("file",de);const G=await fetch("/api/admin/mail/send",{method:"POST",credentials:"include",body:Y}),ae=await G.json().catch(()=>({}));if(!G.ok)throw new Error(ae.error||"request_failed")}else await s("/admin/mail/send",{to:h,subject:z,text:O,cc:d.trim()||void 0});P("Отправлено ✓"),w(""),b(""),Z(""),ue(""),fe([]),ee(!1),await te()}catch(Y){P("Ошибка: "+((Y==null?void 0:Y.message)||Y))}finally{y(!1)}},Ct=async h=>{const z=++ne.current;B(""),P(""),L(null),V([]);const O=Yt(h.peer||h.from);O.includes("@")&&s("/admin/users?q="+encodeURIComponent(O)).then(Y=>{if(ne.current!==z)return;const G=((Y==null?void 0:Y.users)||[]).find(ae=>String(ae.email||"").toLowerCase()===O||(ae.aliases||[]).some(de=>String(de).toLowerCase()===O));G&&L({id:G.id,email:G.email??null,sub:G.subscription??null})}).catch(()=>{});try{const Y=h.msgs.slice().sort((he,we)=>he.ts-we.ts),G=await Promise.all(Y.map(he=>s("/admin/mail/"+he.id)));if(ne.current!==z)return;v({thread:h,msgs:G});const de=String(h.to||"").toLowerCase().startsWith("privacy@")?[]:h.msgs.filter(he=>!he.handled).map(he=>he.id);de.length&&(m(he=>he.map(we=>de.includes(we.id)?{...we,handled:!0}:we)),de.forEach(he=>
89s("/admin/mail/"+he+"/handled",{handled:!0}).catch(()=>{})))}catch{ne.current===z&&P("Не удалось открыть")}},ot=async(h,z)=>{y(!0),P("");try{return await s(h,z??{}),await te(),!0}catch(O){return P("Ошибка: "+O.message),!1}finally{y(!1)}},zt=async()=>{if(_.trim().length<2||!N)return;const h=N.msgs.filter(de=>de.direction!=="out"),z=Ie(N.thread||N),O=60*864e5,Y=h[h.length-1],G=Y?h.filter(de=>Y.ts-de.ts<=O):[],ae=(z?G[0]:Y)||N.msgs[N.msgs.length-1];if($.length){y(!0),P("");try{const de=new FormData;de.set("text",_),Q.trim()&&de.set("cc",Q.trim());for(const _e of $)de.append("file",_e);const he=await fetch("/api/admin/mail/"+ae.id+"/reply",{method:"POST",credentials:"include",body:de}),we=await he.json().catch(()=>({}));if(!he.ok)throw new Error(we.error||"request_failed");await te(),P("Отправлено ✓"),A(""),V([]),et()}catch(de){P("Ошибка: "+((de==null?void 0:de.message)||de))}finally{y(!1)}return}await ot("/admin/mail/"+ae.id+"/reply",{text:_,cc:Q.trim()||void 0})&&(P("Отправлено ✓"),A(""),et())},_t=async()=>{if(!(!N||!confirm("Удалить весь тред безвозвратно?"))){y(!0),P("");try{await Promise.all(N.msgs.map(h=>s("/admin/mail/"+h.id+"/delete"))),await te(),et()}catch(h){P("Ошибка: "+h.message)}finally{y(!1)}}},dt=r.useMemo(()=>{const h=new Map(n.map(z=>[z.id,z]));return[...t.map(z=>h.get(z.id)||z),...n.filter(z=>!t.some(O=>O.id===z.id))]},[t,n]),st=r.useMemo(()=>{const h=new Map;for(const z of dt){const O=rt(z);let Y=h.get(O);Y||(Y={key:O,from:Re(z),to:z.direction==="out"?z.fromAddr:z.toAddr,subject:z.subject,msgs:[],latestTs:0,unread:0},h.set(O,Y)),Y.msgs.push(z),z.ts>=Y.latestTs&&(Y.latestTs=z.ts,Y.subject=z.subject,Y.subjectRu=z.subjectRu||null,Y.to=z.direction==="out"?z.fromAddr:z.toAddr,Y.peer=Re(z)),!Y.subjectRu&&z.subjectRu&&(Y.subjectRu=z.subjectRu),z.handled||Y.unread++}for(const[z,O]of[...h.entries()]){if(!z.startsWith("|"))continue;const Y=z.slice(1),G=[...h.values()].filter(ae=>ae!==O&&pe(ae.subject)===Y);G.length===1&&(G[0].msgs.push(...O.msgs),G[0].unread+=O.unread,O.latestTs>G[0].latestTs&&(G[0].latestTs=O.latestTs),h.delete(z))}for(const z of h.values()){z.msgs.sort((Y,G)=>Y.ts-G.ts);const O=z.msgs[z.msgs.length-1];z.replied=O&&O.direction==="out",z.resolved=z.msgs.length>0&&z.msgs.every(Y=>Y.resolved)}return[...h.values()].sort((z,O)=>z.unread>0==O.unread>0?O.latestTs-z.latestTs:z.unread>0?-1:1)},[dt]),ct=st.filter(Ie),j=st.filter(h=>!Ie(h)),J=ce==="disputes"?ct:j,ie=j.filter(h=>h.unread>0).length,ge=ct.filter(h=>h.unread>0).length,ye=N&&Ie(N.thread||N)?Gt({subject:(Tt=N.thread)==null?void 0:Tt.subject,msgs:N.msgs}):[],Ue=(()=>{const h=new Set;if(!N)return h;const z=G=>li(G),O=(N.msgs||[]).filter(G=>G.direction==="out").map(z).join(" "),Y=new Set(O.match(at)||[]);for(const G of ye)Y.has(G)&&h.add(G);return h})(),mt=e.jsxs("div",{style:{marginBottom:16},children:[ye.length>1&&e.jsxs("div",{style:{marginBottom:12,padding:12,borderRadius:10,background:"var(--surface-2)",border:"1px solid var(--border)"},children:[e.jsxs("div",{style:{fontSize:12.5,fontWeight:700,marginBottom:8},children:["Запросов в этом письме: ",ye.length]}),e.jsx("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:ye.map(h=>{const z=Ue.has(h),O=(R==null?void 0:R.txn)===h;return e.jsxs("button",{onClick:()=>{Ce(h),Ve(h)},title:z?"По этому запросу уже отправлен ответ":void 0,style:{fontFamily:"inherit",fontSize:12,fontWeight:600,padding:"5px 11px",borderRadius:8,cursor:"pointer",border:"1px solid "+(z?"rgba(61,220,132,0.6)":O?"rgba(91,156,255,0.55)":"var(--border-strong)"),background:z?"rgba(61,220,132,0.16)":O?"rgba(91,156,255,0.2)":"var(--surface)",color:z?"var(--green, #1c6b34)":"var(--text)"},children:[z?"✓ ":"",h]},h)})}),e.jsxs("div",{className:"dim",style:{fontSize:11.5,marginTop:8},children:["Зелёным — запросы, по которым ответ уже отправлен (",Ue.size," из ",ye.length,")."]})]}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[e.jsx("input",{value:Ae,onChange:h=>Ce(h.target.value),onKeyDown:h=>{h.key==="Enter"&&Ve(Ae)},placeholder:"Номер операции из письма — например ep_bfb64d87_84dc4aa8bb31",style:{flex:"1 1 320px",padding:"9px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),e.jsx("button",{className:"btn btn--sm",disabled:ke,onClick:()=>Ve(Ae),children:ke?"Собираю…":"Сформировать отчёт"}),(R==null?void 0:R.found)&&e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>{const h=tt(R.txn),z=S.trim()?`&traffic_gb=${encodeURIComponent(S.trim())}`:"",O=p.trim()?`&online_ms=${Date.parse(p.trim())||""}`:"";window.open(`/api/admin/dispute/report.html?txn=${encodeURIComponent(R.txn)}${h?`&reason=${encodeURIComponent(h)}`:""}${M?`&reason_code=${encodeURIComponent(M)}`:""}${z}${O}`,"_blank","noopener")},children:"Открыть под скриншоты"}),(R==null?void 0:R.found)&&N&&e.jsx("button",{className:"btn btn--sm",disabled:Ee,onClick:()=>Kt(R.txn),title:"Соберёт текст и 3 скриншота (операции, страница оплаты, переписка) и вставит в поле ответа",children:Ee?"Собираю письмо…":"Сформировать ответ на почту"}
89),(R==null?void 0:R.found)&&((ht=R.user)==null?void 0:ht.id)&&e.jsx("button",{className:"btn btn--sm "+((ut=R.user)!=null&&ut.blockedAt?"btn--ghost":"btn--danger"),disabled:wt,onClick:()=>{var h;return Xt(R.txn,!!((h=R.user)!=null&&h.blockedAt))},title:(pt=R.user)!=null&&pt.blockedAt?"Снять блокировку: вход в кабинет откроется, доступ вернёт обычное продление":"Закрыть вход в кабинет, погасить доступ в панели и выключить автопродление",children:wt?"Секунду…":(nt=R.user)!=null&&nt.blockedAt?"Разблокировать":"Заблокировать клиента"}),R&&e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>{ze(null),a("")},children:"Закрыть"})]}),(R==null?void 0:R.found)&&R.payPage&&e.jsxs("div",{style:{marginTop:10,padding:12,borderRadius:10,background:"var(--surface-2)",border:"1px solid var(--border)"},children:[e.jsx("div",{style:{fontSize:12.5,fontWeight:700,marginBottom:8},children:"Страница оплаты для скриншота"}),e.jsxs("div",{className:"dim",style:{fontSize:12,marginBottom:8,lineHeight:1.45},children:["Приложите кадр ТОЙ страницы, что видел клиент.",R.payPage.promoPrice&&e.jsxs(e.Fragment,{children:[" Первое списание клиента — ",e.jsx("b",{style:{color:R.payPage.isPromo?"var(--amber, #f5a623)":"var(--text)"},children:R.payPage.promoPrice}),R.payPage.isPromo?" — это ПРОМО-подписка: берите промо-страницу, иначе банк увидит не ту цену и спор проигран.":" — обычная цена, промо не было."]})]}),e.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[e.jsxs("a",{className:"btn btn--ghost btn--sm",href:R.payPage.promo,target:"_blank",rel:"noopener",children:["Промо-страница",R.payPage.isPromo&&R.payPage.promoPrice?` (${R.payPage.promoPrice} → далее полная)`:" (1 ₽ → далее полная)"]}),e.jsx("a",{className:"btn btn--ghost btn--sm",href:R.payPage.normal,target:"_blank",rel:"noopener",children:"Обычная страница"})]})]}),l&&e.jsx("div",{style:{marginTop:8,fontSize:13,color:"var(--coral)"},children:l}),(R==null?void 0:R.found)&&e.jsxs("div",{style:{marginTop:14,display:"grid",gap:12},children:[e.jsxs("div",{className:"card pad",style:{background:"var(--surface-2)",borderLeft:`3px solid ${(($t=R.verdict)==null?void 0:$t.level)==="strong"?"var(--green)":((fs=R.verdict)==null?void 0:fs.level)==="mixed"?"var(--amber)":"var(--coral)"}`},children:[R.reasonLabel&&e.jsxs("div",{style:{fontSize:12.5,fontWeight:700,color:"var(--muted)",marginBottom:4},children:["Причина спора: ",R.reasonLabel]}),e.jsxs("div",{style:{display:"flex",gap:6,flexWrap:"wrap",alignItems:"center",margin:"2px 0 8px"},children:[e.jsx("span",{className:"dim",style:{fontSize:11.5},children:"указать причину:"}),[["","авто"],["fraud","Фрод"],["sub_not_made","Подписка/отмена"],["not_provided","Услуга не оказана"],["duplicate","Двойное списание"],["arbitration","Пре-арбитраж"],["not_recognized","Не узнаёт операцию"]].map(([h,z])=>{const O=(M||"")===h;return e.jsx("button",{onClick:()=>Ve(R.txn,void 0,h||void 0),style:{fontFamily:"inherit",fontSize:11.5,fontWeight:600,padding:"3px 10px",borderRadius:999,cursor:"pointer",border:"1px solid "+(O?"rgba(91,156,255,0.55)":"var(--border)"),background:O?"rgba(91,156,255,0.2)":"var(--surface)",color:"var(--text)"},children:z},h||"auto")})]}),e.jsx("div",{style:{fontSize:14,fontWeight:600},children:(js=R.verdict)==null?void 0:js.note}),e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginTop:6},children:[(ys=R.payment)==null?void 0:ys.txnId," · ",(bs=R.payment)==null?void 0:bs.amount," · ",(vs=R.payment)==null?void 0:vs.provider," · ",new Date((Ss=R.payment)==null?void 0:Ss.at).toLocaleString("ru-RU"),(ws=R.user)!=null&&ws.email?` · ${R.user.email}`:""]})]}),e.jsx("div",{style:{display:"grid",gap:12,gridTemplateColumns:"repeat(auto-fit, minmax(300px, 1fr))"},children:(R.exhibits||[]).map((h,z)=>e.jsxs("div",{className:"card pad",style:{background:"var(--surface)"},children:[e.jsxs("div",{style:{display:"flex",gap:8,alignItems:"baseline"},children:[e.jsx("span",{style:{fontSize:12,fontWeight:700,color:"var(--muted)"},children:z+1}),e.jsx("span",{style:{fontSize:14.5,fontWeight:700},children:h.title}),e.jsx("span",{style:{marginLeft:"auto",fontSize:11.5,fontWeight:600,padding:"2px 8px",borderRadius:999,color:h.status==="ok"?"var(--green)":h.status==="weak"?"var(--amber)":"var(--coral)",border:`1px solid ${h.status==="ok"?"var(--green)":h.status==="weak"?"var(--amber)":"var(--coral)"}`},children:h.status==="ok"?"закрыт":h.status==="weak"?"слабый":"нечем закрыть"})]}),e.jsx("div",{className:"dim",style:{fontSize:11.5,marginTop:4,lineHeight:1.4},children:h.ask}),e.jsx("div",{style:{marginTop:10,display:"grid",gap:5},children:(h.rows||[]).map(O=>e.jsxs("div",{style:{display:"flex",gap:10,fontSize:13,borderTop:"1px solid var(--border)",paddingTop:5},children:[e.jsx("span",{className:"dim",style:{flex:"0 0 auto",maxWidth:"45%"},children:O.k}),e.jsx("span",{style:{flex:"1 1 auto",minWidth:0,fontWeight:600,fontVariantNumeric:"tabular-nums",textAlign:"right",overflowWrap:"anywhere"},children:O.v})]},O.k))}),h.gap&&e.jsx("div",{style:{marginTop:10,fontSize:12.5,lineHeight:1.45,color:"var(--amber)"},children:h.gap})]},h.key))}),R.needsUsageInput&&e.jsxs("div",{className:"card pad",style:{background:"var(--surface-2)",border:"1px solid var(--amber, #f5a623)"},children:[(()=>{var Y,G;const h=((Y=R.usageMissing)==null?void 0:Y.traffic)??!0,z=((G=R.usageMissing)==null?void 0:G.online)??!0,O=h&&z?"Трафик и последний онлайн":h?"Трафик":"Последний онлайн";return e.jsxs(e.Fragment,{children:[e.jsx("div",{style:{fontSize:13.5,fontWeight:700,marginBottom:4},children:h&&z?"Метрики пользования пусты":`${O} — панель не отдала`}),e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginBottom:10,lineHeight:1.45},children:[O," у нас нет (мигрированный профиль либо панель не отдала цифру). ПРОВЕРЬ реальные значения и впиши — досье пересоберётся с ними. Пусто оставишь — раздел пойдёт по факту оплат. Живые данные ручной ввод не затирает: подставляется только туда, где у нас ноль."]})]})})(),e.jsxs("div",{style:{display:"flex",gap:10,flexWrap:"wrap",alignItems:"flex-end"},children:[(((Ns=R.usageMissing)==null?void 0:Ns.traffic)??!0)&&e.jsxs("label",{style:{fontSize:12},children:["Трафик, ГБ",e.jsx("br",{}),e.jsx("input",{type:"number",min:"0",step:"0.1",value:S,onChange:h=>H(h.target.value),placeholder:"например 42",style:{marginTop:4,padding:"7px 10px",borderRadius:8,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13,width:120}})]}),(((ks=R.usageMissing)==null?void 0:ks.online)??!0)&&e.jsxs("label",{style:{fontSize:12},children:["Последний онлайн",e.jsx("br",{}),e.jsx("input",{type:"date",value:p,onChange:h=>K(h.target.value),style:{marginTop:4,padding:"7px 10px",borderRadius:8,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13}})]}),e.jsx("button",{className:"btn btn--sm",disabled:ke,onClick:()=>Ve(R.txn,void 0,M||void 0),children:"Пересобрать с данными"})]})]}),R.profile&&e.jsxs("div",{className:"card pad",style:{background:"var(--surface)"},children:[e.jsx("div",{style:{fontSize:14.5,fontWeight:700,marginBottom:8},children:"Портрет клиента · ответ в нашу пользу"}),e.jsx("div",{style:{display:"grid",gap:5},children:[["Стаж аккаунта",R.profile.tenureDays==null?"—":R.profile.tenureDays<45?`${R.profile.tenureDays} дн.`:R.profile.tenureDays<365?`${Math.round(R.profile.tenureDays/30)} мес.`:`${(R.profile.tenureDays/365).toFixed(1)} г.`],["Оплат / сумма",`${R.profile.paymentsOk} · ${R.profile.ltv}`],["Оплат после спорной",String(R.profile.paymentsAfter)],["Пользовался",R.profile.usedAfter?"да, после списания":R.profile.usedEver?"да":"не зафиксировано"],["Обращений в поддержку",`${R.profile.complaints}${R.profile.complaints===0?" — сразу в чарджбек":""}`]].map(([h,z])=>e.jsxs("div",{style:{display:"flex",gap:10,fontSize:13,borderTop:"1px solid var(--border)",paddingTop:5},children:[e.jsx("span",{className:"dim",style:{flex:"1 1 auto"},children:h}),e.jsx("span",{style:{flex:"none",fontWeight:600,textAlign:"right"},children:z})]},h))}),!!(R.profile.pro||[]).length&&e.jsxs("div",{style:{marginTop:10,fontSize:12.5,lineHeight:1.5,color:"var(--green)"},children:[e.jsx("b",{children:"В нашу пользу:"})," ",R.profile.pro.join("; "),"."]}),!!(R.profile.con||[]).length&&e.jsxs("div",{style:{marginTop:8,fontSize:12.5,lineHeight:1.5,color:"var(--amber)"},children:[e.jsx("b",{children:"Слабые места (только вам, не в отправку):"})," ",R.profile.con.join("; "),"."]}),e.jsxs("div",{style:{display:"flex",alignItems:"baseline",gap:10,margin:"14px 0 6px"},children:[e.jsx("div",{style:{fontSize:13.5,fontWeight:700},children:"Комментарий по ситуации"}),e.jsx("button",{className:"btn btn--ghost btn--xs",style:{marginLeft:"auto"},onClick:()=>{var h;(h=navigator.clipboard)==null||h.writeText(R.profile.comment||""),P("Комментарий скопирован")},children:"Скопировать"})]}),e.jsx("textarea",{readOnly:!0,value:R.profile.comment||"",rows:7,style:{width:"100%",padding:12,borderRadius:10,background:"var(--surface-2)",border:"1px solid var(--border)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5,lineHeight:1.5}})]}),e.jsxs("div",{className:"card pad",style:{background:"var(--surface)"},children:[e.jsxs("div",{style:{display:"flex",alignItems:"baseline",gap:10,marginBottom:8},children:[e.jsx("div",{style:{fontSize:14.5,fontWeight:700},children:"Черновик ответа"}),e.jsx("span",{className:"dim",style:{fontSize:12.5},children:"прочитайте и отправьте сами — отсюда ничего не уходит"}),e.jsx("button",{className:"btn btn--ghost btn--xs",style:{marginLeft:"auto"},onClick:()=>{var h;(h=navigator.clipboard)==null||h.writeText(R.draft||""),P("Черновик скопирован")},children:"Скопировать"})]}),e.jsx("textarea",{readOnly:!0,value:R.draft||"",rows:16,style:{width:"100%",padding:12,borderRadius:10,background:"var(--surface-2)",border:"1px solid var(--border)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5,lineHeight:1.5}})]})]})]});if(N){const h=N.thread,z=((Cs=String(h.to||"").match(/<([^>]+)>/))==null?void 0:Cs[1])||h.to||"noreply",O=((zs=String(h.peer||h.from||"").match(/<([^>]+)>/))==null?void 0:zs[1])||h.peer||h.from||"—",Y=String(h.to||"").startsWith("privacy@");return e.jsxs("div",{className:"card pad",children:[e.jsx("button",{className:"btn btn--ghost btn--xs btn--pill",onClick:()=>et(),children:"← к списку"}),e.jsxs("div",{style:{marginTop:14,fontSize:18,fontWeight:700},children:[h.subject||"(без темы)",N.msgs.length>1&&e.jsxs("span",{className:"dim",style:{fontSize:13,fontWeight:500},children:[" · ",N.msgs.length," писем"]})]}),e.jsxs("div",{className:"dim",style:{fontSize:13,marginTop:4,overflowWrap:"anywhere"},children:["Клиент: ",h.peer||h.from," · наш адрес: ",h.to]}),Ie(h)&&e.jsx("div",{style:{marginTop:14},children:mt}),oe&&e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,flexWrap:"wrap",marginTop:10},children:[e.jsxs("button",{className:"btn btn--ghost btn--xs",onClick:()=>U(oe),children:[e.jsx(On,{size:14,strokeWidth:1.9})," Управление профилем"]}),e.jsx("span",{className:"dim",style:{fontSize:12.5},children:oe.sub?`${oe.sub.plan||"тариф"} · ${oe.sub.status||""}${oe.sub.expiresAt?" до "+new Date(oe.sub.expiresAt).toLocaleDateString("ru-RU"):""}`:"аккаунт есть, подписки нет"})]}),se&&e.jsx(Ht,{api:s,userId:se.id,email:se.email,onClose:()=>U(null),onChanged:te}),Y&&e.jsx("div",{style:{marginTop:12,padding:"10px 12px",borderRadius:10,background:"rgba(224,169,59,0.10)",border:"1px solid rgba(224,169,59,0.3)",fontSize:13},children:"Запрос на адрес прав субъекта данных. По GDPR ответить нужно в течение месяца с момента получения."}),N.msgs.map((G,ae)=>{var de;return e.jsxs("div",{style:{marginTop:16,paddingTop:ae?16:0,borderTop:ae?"1px solid var(--border)":"none"},children:[e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginBottom:6,overflowWrap:"anywhere"}
89,children:[new Date(G.ts).toLocaleString()," · ",G.fromAddr,G.direction==="out"&&e.jsx("span",{style:{marginLeft:6,fontSize:11,color:"var(--green)",border:"1px solid rgba(52,211,153,0.45)",borderRadius:4,padding:"0 5px"},children:"наш ответ"})]}),e.jsxs("div",{style:G.direction==="out"?{borderLeft:"2px solid rgba(52,211,153,0.5)",paddingLeft:10}:void 0,children:[e.jsx(zr,{body:G.body||"(пустое тело)",html:G.html}),G.bodyRu&&G.bodyRu!=="—"&&e.jsxs("div",{style:{marginTop:10,paddingTop:10,borderTop:"1px dashed var(--border)",fontSize:13.5,opacity:.9,whiteSpace:"pre-wrap"},children:[e.jsxs("div",{className:"dim",style:{fontSize:11,marginBottom:3},children:["перевод",G.lang?` · ${G.lang}`:""]}),G.bodyRu]}),!!((de=G.files)!=null&&de.length)&&e.jsx(hn,{files:G.files,srcOf:he=>`/api/admin/mail/file/${he.id}`})]})]},G.id)}),e.jsx("textarea",{className:"mail-reply-box",value:_,onChange:G=>B(G.target.value),rows:6,onPaste:me($,V),placeholder:`Ответ уйдёт ${O} — с адреса ${z}. Картинку можно вставить прямо сюда (⌘V)…`,style:{width:"100%",marginTop:18,padding:12,borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:14}}),e.jsx("input",{value:Q,onChange:G=>A(G.target.value),placeholder:"Копия (через запятую) — необязательно",style:{width:"100%",marginTop:8,padding:"9px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),e.jsx("div",{style:{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap",marginTop:8},children:e.jsxs("label",{className:"btn btn--ghost btn--xs",style:{cursor:"pointer"},children:[e.jsx(As,{size:14,strokeWidth:1.9})," Прикрепить",e.jsx("input",{type:"file",accept:"image/*",multiple:!0,hidden:!0,onChange:G=>{const ae=Array.from(G.target.files||[]);Jt(ae,$,V,P),G.target.value=""}})]})}),e.jsx(sn,{files:$,onDrop:G=>V(ae=>ae.filter((de,he)=>he!==G))}),e.jsxs("div",{style:{display:"flex",gap:10,marginTop:12,flexWrap:"wrap"},children:[e.jsx("button",{className:"btn btn--sm",disabled:T||_.trim().length<2,title:"Ответ уйдёт в ту же ветку, с процитированным исходным письмом",onClick:zt,children:"Ответить"}),(()=>{const G=N.msgs.length>0&&N.msgs.every(ae=>ae.resolved);return e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:T,onClick:async()=>{const ae=N.msgs.map(he=>he.id);await ot("/admin/mail/resolve",{ids:ae,resolved:!G})&&v(null)},children:G?"Снять «разобрано»":"✓ Разобрано"})})(),e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:T,onClick:async()=>{await Promise.all(N.msgs.map(G=>s("/admin/mail/"+G.id+"/handled",{handled:!1}))),await te(),v(null)},children:"Вернуть в непрочитанные"}),e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:T,style:{color:"var(--red, #ff6b6b)"},onClick:_t,children:"Удалить тред"})]}),q&&e.jsx("div",{className:"dim",style:{marginTop:10,fontSize:13},children:q})]})}return e.jsxs(e.Fragment,{children:[e.jsx("div",{style:{marginBottom:12},children:e.jsx(Oe,{options:[["inbox",ie>0?`Переписка · ${ie}`:"Переписка"],["disputes",ge>0?`Диспуты · ${ge}`:"Диспуты"],["health","Доставка"]],value:ce,onChange:h=>Be(h)})}),ce==="health"?e.jsxs(e.Fragment,{children:[e.jsx(_r,{api:s}),e.jsx($r,{api:s}),e.jsx(Tr,{api:s}),e.jsx(Rr,{api:s})]}):e.jsxs("div",{className:"card pad",children:[e.jsxs("div",{style:{display:"flex",alignItems:"baseline",gap:12,marginBottom:14},children:[e.jsx("div",{style:{fontSize:16,fontWeight:700},children:ce==="disputes"?"Диспуты":"Входящая почта"}),ce!=="disputes"&&e.jsxs("button",{className:"btn btn--sm",style:{marginLeft:"auto"},onClick:()=>ee(h=>!h),children:[e.jsx(Ft,{size:14,strokeWidth:1.9})," ",le?"Свернуть":"Написать письмо"]}),ce==="disputes"?e.jsxs("span",{className:"dim",style:{marginLeft:"auto",fontSize:13},children:[ge>0?`${ge} непрочитанных · `:"","отвечать пока не отвечаем — текст согласуем"]}):ie>0&&e.jsxs("span",{className:"dim",style:{fontSize:13},children:[ie," непрочитанных"]})]}),e.jsxs("div",{style:{display:"flex",gap:8,alignItems:"center",marginBottom:12},children:[e.jsx("input",{value:E,onChange:h=>X(h.target.value),onKeyDown:h=>{h.key==="Enter"&&W(E.trim())},placeholder:"Поиск по адресу, теме или тексту письма",style:{flex:1,minWidth:0,padding:"8px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),e.jsx("button",{className:"btn btn--sm",onClick:()=>W(E.trim()),children:"Найти"}),!!D&&e.jsx("button",{className:"btn btn--ghost btn--sm",onClick:()=>{X(""),W("")},children:"Сбросить"})]}),!!D&&e.jsxs("div",{className:"dim",style:{fontSize:12.5,marginBottom:10},children:["Поиск: «",D,"» · найдено писем: ",t.length,u?"+":""]}),le&&e.jsx("div",{className:"card pad",style:{marginBottom:14,background:"var(--surface-2)"},children:e.jsxs("div",{style:{display:"grid",gap:8},children:[e.jsx("input",{value:be,onChange:h=>w(h.target.value),placeholder:"Кому — адрес получателя",style:{padding:"9px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),e.jsx("input",{value:d,onChange:h=>b(h.target.value),placeholder:"Копия (через запятую) — необязательно",style:{padding:"9px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),e.jsx("input",{value:F,onChange:h=>Z(h.target.value),placeholder:"Тема",style:{padding:"9px 12px",borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:13.5}}),e.jsx("textarea",{value:xe,onChange:h=>
89ue(h.target.value),rows:7,onPaste:me(Ne,fe),placeholder:"Текст письма — картинку можно вставить прямо сюда (⌘V)",style:{padding:12,borderRadius:10,background:"var(--surface)",border:"1px solid var(--border-strong)",color:"var(--text)",fontFamily:"inherit",fontSize:14}}),e.jsx("div",{style:{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap"},children:e.jsxs("label",{className:"btn btn--ghost btn--xs",style:{cursor:"pointer"},children:[e.jsx(As,{size:14,strokeWidth:1.9})," Прикрепить",e.jsx("input",{type:"file",accept:"image/*",multiple:!0,hidden:!0,onChange:h=>{const z=Array.from(h.target.files||[]);Jt(z,Ne,fe,P),h.target.value=""}})]})}),e.jsx(sn,{files:Ne,onDrop:h=>fe(z=>z.filter((O,Y)=>Y!==h))}),e.jsxs("div",{style:{display:"flex",gap:10,alignItems:"center",flexWrap:"wrap"},children:[e.jsx("button",{className:"btn btn--sm",disabled:T,onClick:kt,children:"Отправить"}),e.jsx("span",{className:"dim",style:{fontSize:12.5},children:"Уйдёт с адреса поддержки этой витрины. Письмо ляжет в переписку — ответ придёт сюда же."})]})]})}),ce==="disputes"&&mt,!st.length&&e.jsx("div",{className:"dim",style:{fontSize:14},children:"Пусто."}),J.map(h=>{const z=h.unread>0,O=Math.floor((Date.now()-h.latestTs)/864e5),Y=String(h.peer||h.from||"").split("@")[0].toLowerCase(),G=/(^|[._-])(no.?reply|donotreply|mailer.?daemon|postmaster|notifications?|bounces?|dmarc|abuse|robot|noreply)([._-]|$)/.test(Y),ae=new Set,de=new Set;if(ce==="disputes")for(const Pe of h.msgs)for(const Qe of Pe.txns||[])(Pe.direction==="out"?de:ae).add(Qe);const he=[...ae].filter(Pe=>de.has(Pe)).length,we=ae.size>1&&he<ae.size,_e=z?"new":h.resolved?"resolved":h.replied&&!we?"replied":G?"info":"waiting",Te=_e==="new"?"var(--green, #3ddc84)":_e==="waiting"?"var(--amber, #f5a623)":"transparent";return e.jsxs("div",{onClick:()=>Ct(h),style:{display:"flex",gap:12,alignItems:"center",padding:"11px 0",borderTop:"1px solid var(--border)",cursor:"pointer",opacity:z?1:.62},children:[e.jsx("span",{style:{width:8,height:8,borderRadius:"50%",flex:"none",background:Te}}),e.jsxs("div",{style:{flex:1,minWidth:0},children:[e.jsxs("div",{style:{fontWeight:z?700:600,fontSize:14,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:[h.subject||"(без темы)",h.msgs.length>1&&e.jsxs("span",{className:"dim",style:{fontWeight:500},children:[" · ",h.msgs.length]})]}),h.subjectRu&&e.jsx("div",{className:"dim",style:{fontSize:12.5,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:h.subjectRu}),e.jsxs("div",{className:"dim",style:{fontSize:12.5,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:[h.from," → ",h.to]})]}),_e!=="info"&&e.jsx("span",{style:{flex:"none",fontSize:11,fontWeight:600,padding:"2px 8px",borderRadius:999,color:_e==="new"?"var(--green, #3ddc84)":_e==="waiting"?"var(--amber, #f5a623)":"var(--dim, #8b97a7)",background:_e==="new"?"rgba(61,220,132,0.13)":_e==="waiting"?"rgba(245,166,35,0.13)":"var(--surface-2, rgba(255,255,255,0.05))"},children:_e==="new"?h.unread>1?`новое · ${h.unread}`:"новое":_e==="resolved"?"✓ разобрано":_e==="replied"?ae.size>1?`✓ отвечено ${he} из ${ae.size}`:"✓ отвечено":ae.size>1&&he>0?`отвечено ${he} из ${ae.size}`:"ждёт"}),ce==="disputes"&&!!lt(h)&&e.jsx("button",{className:"btn btn--ghost btn--xs",style:{flex:"none"},onClick:Pe=>{Pe.stopPropagation();const Qe=lt(h);Ce(Qe),Ve(Qe)},children:"Отчёт"}),e.jsx("div",{className:"dim",style:{fontSize:12.5,flex:"none"},children:O===0?"сегодня":O+" дн."})]},h.key)}),(ce==="disputes"?i:u)&&e.jsxs("div",{style:{display:"flex",alignItems:"center",gap:10,marginTop:14},children:[e.jsx("button",{className:"btn btn--ghost btn--sm",disabled:g,onClick:ve,children:g?"Гружу…":"Показать письма постарше"}),e.jsxs("span",{className:"dim",style:{fontSize:12.5},children:["загружено ",(ce==="disputes"?n:t).length," писем"]})]}),q&&e.jsx("div",{className:"dim",style:{marginTop:12,fontSize:13},children:q})]})]})}export{Dr as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.