PageSourceSearch

https://markopromo.vercel.app/assets/Overview-01844092.js

js markopromo.vercel.app collected 2026-10-03 07:17:34 UTC 27,769 bytes, 1 lines download raw bytes

1import{j as e}from"./vendor-markdown-f11696d1.js";import{K as T}from"./KpiCard-508d76e4.js";import{r as Y}from"./vendor-react-1ba83e0f.js";import{e as se,c as M,d as ne,f as le,g as oe}from"./signal-0c0a1bca.js";import{ah as B,b,ai as h,f as S,E as k,e as N,S as ie}from"./index-2d659560.js";import{B as E}from"./Badge-621de24e.js";import{w as G,d as _,t as I,n as ce,a as de,v as ae,q as K,B as me,x as ue,h as xe,j as pe}from"./format-24b5e5a0.js";import{P as Q}from"./Page-6f5fa55a.js";import{s as A}from"./softFail-52b03694.js";import he from"./SeasonGoNoGo-edaba180.js";import{T as C}from"./Table-a486c05f.js";import{D as z}from"./DateInput-cbf79927.js";import{i as ge}from"./orderStatus-10f272a4.js";import{d as y}from"./dates-3b2ae756.js";import{c as fe}from"./campaignScaleCut-43f48111.js";import{U as je}from"./business-dd6af627.js";import"./vendor-supabase-bd3420f7.js";import"./flame-77e7afeb.js";function p({title:t,subtitle:s,action:r,padding:n="md",className:l="",children:a}){const c=n==="none"?"":"p-6";return e.jsxs("section",{className:`rounded-card bg-panel ${c} ${l}`,children:[(t||r)&&e.jsxs("header",{className:`flex items-center gap-3 ${n==="none"?"px-6 pt-5 pb-3":"mb-4"}`,children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[t&&e.jsx("h2",{className:"text-sm font-semibold text-foreground",children:t}),s&&e.jsx("p",{className:"mt-0.5 text-xs text-subtle",children:s})]}),r]}),a]})}const ve=new Set(["paid_instagram","paid_facebook","paid_meta","google_ads"]);function be(t){return t==="email"?"email":ve.has(t)?"paid":"organic"}const ye={paid:"Платні",organic:"Органіка",email:"Email"},J={paid:0,organic:1,email:2};function ke(t,s){return J[t.group]-J[s.group]||s.revenue-t.revenue}function Ne(t){const s=t.find(a=>a.key==="paid_instagram"),r=t.find(a=>a.key==="paid_facebook");if(!s&&!r)return t;const n={key:"paid_meta",channel:"Meta",sub:"платний",group:"paid",groupLabel:"Платні",revenue:((s==null?void 0:s.revenue)||0)+((r==null?void 0:r.revenue)||0),sales:((s==null?void 0:s.sales)||0)+((r==null?void 0:r.sales)||0),share:((s==null?void 0:s.share)||0)+((r==null?void 0:r.share)||0),provenance:"mix"};return[...t.filter(a=>a.key!=="paid_instagram"&&a.key!=="paid_facebook"),n]}function we(t){const s=t==null?void 0:t.sources;if(!Array.isArray(s))return[];const r=s.filter(n=>((n==null?void 0:n.revenue)??0)>0||((n==null?void 0:n.sales)??0)>0).map(n=>{const l=be(n.key);return{key:n.key,channel:n.label??n.key,sub:n.group||"",group:l,groupLabel:ye[l],revenue:Math.round(n.revenue??0),sales:Math.round(n.sales??0),share:n.share??0,provenance:n.source??null}});return Ne(r).sort(ke)}function _e(t,{metaSpendUah:s=0,googleSpendUah:r=0}={}){return t.map(n=>{let l=0;n.key==="paid_meta"?l=s:n.key==="google_ads"&&(l=r),l=Math.round(l);const a=G(n.revenue,l,{decimals:1});return{...n,spend:l,roas:a}})}function Se(t={},{totalRevenue:s=null,totalOrders:r=null,adSpendUah:n=0}={}){const l=s??t.revenue??null,a=r??t.ticketsSold??null,c=Math.round(n)||t.adSpend||null,d=c?G(l,c,{decimals:1}):t.realRoas??null;return{...t,revenue:l,ticketsSold:a,adSpend:c,realRoas:d}}const L=(...t)=>{for(const s of t)if(s!=null&&s!==""&&!Number.isNaN(Number(s)))return Number(s)},O=(t,s)=>{let r=!1,n=0;for(const l of t){const a=s(l);a!=null&&(r=!0,n+=a)}return r?n:null};function Re(t){const s=O(t,a=>L(a.revenue,a.revenue_uah,a.total_revenue)),r=O(t,a=>L(a.ticketsSold,a.tickets,a.ticket_count,a.tickets_sold,a.sold)),n=O(t,a=>L(a.adSpend,a.spend,a.ad_spend,a.spend_uah)),l=G(s,n,{decimals:1});return{kpi:{revenue:s,ticketsSold:r,realRoas:l,adSpend:n,deltas:{}},channels:[],pace:[]}}const Ae={"7d":7,"30d":30,"90d":90},Te={"7d":"last_7d","30d":"last_30d","90d":"last_90d"},$e={"7d":"LAST_7_DAYS","30d":"LAST_30_DAYS","90d":"LAST_90_DAYS"};async function Me(t="30d"){var j,v,R,x;const s=Ae[t]??30,[r,n,l,a,c]=await Promise.all([h.get("/api/events-dashboard",{period:t}),h.get("/api/channel-sales",{days:s}).catch(A("overview:channel-sales")),h.get("/api/meta-ads",{mode:"metrics",period:Te[t]??"last_30d"}).catch(A("overview:meta-ads")),h.get("/api/google-ads",{mode:"spend",period:$e[t]??"LAST_30_DAYS"}).catch(A("overview:google-ads")),h.get("/api/fx",{base:"USD"}).catch(A("overview:fx"))]),d=Array.isArray(r)?r:r.events??[],o=Re(d),u=(c==null?void 0:c.rate)??null,f=((j=l
1==null?void 0:l.totals)==null?void 0:j.spend)!=null&&u?l.totals.spend*u:0,i=((v=a==null?void 0:a.totals)==null?void 0:v.spend)??0,m=_e(we(n),{metaSpendUah:f,googleSpendUah:i}),g=Se(o.kpi,{totalRevenue:(R=n==null?void 0:n.total)==null?void 0:R.revenue,totalOrders:(x=n==null?void 0:n.total)==null?void 0:x.sales,adSpendUah:f+i});return{...o,kpi:g,channels:m,fx:u?{usdUah:u}:null,generatedAt:new Date().toISOString()}}const Ce={"7d":"7d","30d":"30d","90d":"90d"};function Ee(){const t=B(s=>s.dateRange.preset);return b({queryKey:["overview",t],queryFn:()=>Me(Ce[t]??"30d"),staleTime:20*6e4})}const D=I;function Pe({rows:t}){return e.jsx(p,{title:"Канали — витрати, виручка, ROAS",children:!t||t.length===0?e.jsx(S,{children:"Дані каналів зʼявляться після підключення розбивки."}):e.jsxs(e.Fragment,{children:[t.map(s=>{const r=se(s.roas);return e.jsxs("div",{className:"border-b border-border py-2.5 last:border-0",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("span",{className:"min-w-0 truncate text-[13px] text-foreground",children:[s.channel,e.jsxs("span",{className:"ml-2 text-[11px] text-muted",children:[s.groupLabel,s.provenance==="est"?" · оцінка":""]})]}),s.roas!=null?e.jsx(E,{tone:r.tone,children:r.label}):e.jsx("span",{className:"text-[12px] text-muted",children:"—"})]}),e.jsxs("div",{className:"mt-1 text-[11.5px] text-muted tabular-nums",children:[s.spend>0?`${D(s.spend)} → ${D(s.revenue)}`:D(s.revenue),e.jsxs("span",{className:"ml-2",children:["· ",_(s.share),"%"]})]})]},s.key)}),e.jsx("p",{className:"mt-3.5 text-[11.5px] text-muted",children:`Виручка — реальна (TicketBox + GA4). ROAS = виручка ÷ витрати; Meta у ₴ за курсом НБУ, Google — у ₴. Ціль ≥ ${_(M)}× (зелений), < ${_(ne)}× — збитково. Органіка/Email — без витрат.`})]})})}function Le({points:t}){const s=t.reduce((d,o)=>d+o.tickets,0),r=800,n=64,l=Math.max(1,...t.map(d=>d.tickets)),a=t.length>1?r/(t.length-1):r,c=t.map((d,o)=>`${(o*a).toFixed(0)},${(n-d.tickets/l*(n-8)-4).toFixed(0)}`).join(" ");return e.jsx(p,{title:"Динаміка продажів",action:e.jsxs(E,{tone:"neutral",children:[s," ",ce(s,"квитків")]}),children:t.length===0?e.jsx(S,{children:"Денна динаміка зʼявиться після підключення джерела."}):e.jsxs("svg",{viewBox:`0 0 ${r} ${n}`,preserveAspectRatio:"none",className:"block h-16 w-full text-foreground",children:[e.jsx("defs",{children:e.jsxs("linearGradient",{id:"ov-g",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"0",stopColor:"currentColor",stopOpacity:"0.18"}),e.jsx("stop",{offset:"1",stopColor:"currentColor",stopOpacity:"0"})]})}),e.jsx("polyline",{points:c,fill:"none",stroke:"currentColor",strokeWidth:"2",vectorEffect:"non-scaling-stroke"}),e.jsx("polygon",{points:`${c} ${r},${n} 0,${n}`,fill:"url(#ov-g)"})]})})}const U=(t,s)=>{const r=(t||[]).find(n=>n.event===s);return r&&Number(r.count)||0};function Oe(t){var o;const s=(t==null?void 0:t.events)||[],r=Number((o=t==null?void 0:t.totals)==null?void 0:o.sessions)||0,n=[{k:"Сеанси",v:r},{k:"Клік «Купити квиток»",v:U(s,"buy_ticket_click")},{k:"Початок оплати",v:U(s,"begin_checkout")},{k:"Покупка",v:U(s,"purchase")}],l=Math.max(1,n[0].v);let a=-1,c=2;for(let u=1;u<n.length;u++)n[u].conv=n[u-1].v?n[u].v/n[u-1].v:0,n[u].conv<c&&(c=n[u].conv,a=u);const d=r?n[3].v/r:0;return{steps:n,worst:a,overall:d,max:l}}function V(t,s){return t==null||s==null?"text-foreground":t<s*.6?"text-destructive":t<s*.85?"text-warning":"text-foreground"}function De(t={},s=null){const r=[{k:"Відкрив віджет",v:t.opened,conv:null},{k:"Явно закрив, не купивши",v:t.closed,conv:t.opened?Math.round(t.closed/t.opened*100):null,muted:!0}
1,{k:"Дійшов до оплати",v:t.authed,conv:t.pctOpenToAuth}],n=Math.max(1,t.opened||1);return r.map(l=>({...l,w:Math.max(4,Math.round((l.v||0)/n*100)),bad:l.conv!=null&&!l.muted&&s!=null&&l.conv<s}))}const Ue=ae;function Fe(){const s=B(i=>i.dateRange.preset)||"30d",{data:r,isLoading:n,isError:l,error:a,refetch:c}=b({queryKey:["ga4-overview",s],queryFn:()=>h.get("/api/ga4",{period:s}),staleTime:20*6e4});if(l)return e.jsx(p,{title:"Воронка покупки",children:e.jsx(k,{message:a==null?void 0:a.message,onRetry:c})});if(n||!r)return e.jsx(p,{title:"Воронка покупки",children:e.jsx(N,{rows:4})});const{steps:d,worst:o,overall:u,max:f}=Oe(r);return e.jsxs(p,{title:"Воронка покупки",action:e.jsxs("span",{className:"text-xs text-muted",children:["GA4 · ",s]}),children:[e.jsx("div",{className:"grid gap-3",children:d.map((i,m)=>{const g=Math.max(4,Math.round(i.v/f*100)),j=m===o;return e.jsxs("div",{children:[e.jsxs("div",{className:"mb-1 flex items-baseline justify-between",children:[e.jsx("span",{className:"text-sm text-foreground",children:i.k}),e.jsx("span",{className:"text-sm tabular-nums text-foreground",children:Ue(i.v)})]}),e.jsx("div",{className:"h-2 overflow-hidden rounded-element bg-muted-surface",children:e.jsx("div",{className:`h-full rounded-element ${j?"bg-destructive":"bg-primary"}`,style:{width:`${g}%`}})}),m>0&&e.jsxs("div",{className:`mt-1 text-[11px] ${j?"text-destructive":"text-muted"}`,children:[Math.round(i.conv*100),"% від «",d[m-1].k,"»",j?" · головний злив":""]})]},i.k)})}),e.jsxs("div",{className:"mt-3 border-t border-border pt-2 text-[11px] text-muted",children:["Загальна конверсія сеанс → покупка: ",e.jsxs("span",{className:"text-foreground",children:[de(u*100,1),"%"]})]})]})}const w=ae,$=t=>t==null?"—":`${_(t)}%`;function qe(){const{data:t,isLoading:s,isError:r,error:n,refetch:l}=b({queryKey:["widget-funnel",14],queryFn:()=>h.get("/api/widget-funnel",{days:14}),staleTime:12e5});if(r)return e.jsx(p,{title:"Воронка всередині віджета",children:e.jsx(k,{message:n==null?void 0:n.message,onRetry:l})});if(s||!t)return e.jsx(p,{title:"Воронка всередині віджета",children:e.jsx(N,{rows:5})});const a=t.totals||{},c=t.comparison||{},d=c.nonMetaPct,o=t.sources||[],u=t.pages||[],f=De(a,d);return e.jsxs(p,{title:"Воронка всередині віджета",action:e.jsxs("span",{className:"text-xs text-muted",children:["TicketBox · ",t.days," днів"]}),children:[e.jsx("div",{className:"grid gap-3",children:f.map(i=>{const{w:m,bad:g}=i;return e.jsxs("div",{children:[e.jsxs("div",{className:"mb-1 flex items-baseline justify-between",children:[e.jsx("span",{className:"text-sm text-foreground",children:i.k}),e.jsx("span",{className:"text-sm tabular-nums text-foreground",children:w(i.v)})]}),e.jsx("div",{className:"h-2 overflow-hidden rounded-element bg-muted-surface",children:e.jsx("div",{className:`h-full rounded-element ${g?"bg-destructive":i.muted?"bg-muted-surface":"bg-primary"}`,style:{width:`${m}%`,...i.muted?{outline:"1px solid var(--color-border)"}:null}})}),i.conv!=null&&e.jsxs("div",{className:`mt-1 text-[11px] ${g?"text-destructive":"text-muted"}`,children:[_(i.conv),"% від «Відкрив віджет»"]})]},i.k)})}),c.metaPaidPct!=null&&c.nonMetaPct!=null&&e.jsxs("div",{className:"mt-3 rounded-element border border-border bg-muted-surface p-3 text-[12px] leading-relaxed",children:[e.jsx("div",{className:"mb-1 font-medium text-foreground",children:"Головна різниця — не подія, а канал"}),e.jsxs("div",{className:"text-muted",children:["Платні плейсменти Meta доходять до оплати у"," ",e.jsx("span",{className:"font-semibold text-destructive",children:$(c.metaPaidPct)})," випадків, увесь інший трафік — у ",e.jsx("span",{className:"font-semibold text-foreground",children:$(c.nonMetaPct)}
1),". Віджет працює; різниця в тому, який трафік у нього приходить."]})]}),e.jsxs("div",{className:"mt-4",children:[e.jsx("div",{className:"mb-2 text-[11px] uppercase tracking-wide text-muted",children:"По джерелах — від 30 відкриттів"}),o.length===0?e.jsx(S,{children:"Даних ще немає."}):e.jsx(C,{columns:[{key:"src",label:"Джерело"},{key:"opened",label:"Відкрито",align:"right",width:"90px",render:i=>w(i)},{key:"authed",label:"До оплати",align:"right",width:"90px",render:i=>w(i)},{key:"pctToAuth",label:"%",align:"right",width:"70px",render:i=>e.jsx("span",{className:V(i,d),children:$(i)})}],rows:o,rowKey:i=>i.src})]}),u.length>0&&e.jsxs("div",{className:"mt-4",children:[e.jsx("div",{className:"mb-2 text-[11px] uppercase tracking-wide text-muted",children:"По подіях — щоб побачити сторінку, що ламається сильніше за свій мікс трафіку"}),e.jsx(C,{columns:[{key:"page",label:"Сторінка"},{key:"opened",label:"Відкрито",align:"right",width:"90px",render:i=>w(i)},{key:"authed",label:"До оплати",align:"right",width:"90px",render:i=>w(i)},{key:"pctToAuth",label:"%",align:"right",width:"70px",render:i=>e.jsx("span",{className:V(i,d),children:$(i)})}],rows:u,rowKey:i=>i.page})]}),e.jsxs("div",{className:"mt-3 border-t border-border pt-2 text-[11px] leading-relaxed text-muted",children:["Сесія віджета — за ",e.jsx("code",{children:"wc_timestamp"})," у його URL. «Явно закрив» рахується лише коли віджет надіслав",e.jsx("code",{children:" destroy"}),": хто просто пішов зі сторінки, туди не потрапляє, тож це нижня оцінка.",a.droppedNoSession>0&&e.jsxs(e.Fragment,{children:[" Подій без ідентифікатора сесії пропущено: ",w(a.droppedNoSession),"."]})]})]})}const Ke=t=>t==null?"—":`${Math.round(t)}%`,X=(t,s,r)=>t==null?"text-muted":t>=s?"text-success":t>=r?"text-warning":"text-destructive",Z=(t,s,r)=>t==null?"text-muted":t<=s?"text-success":t<=r?"text-warning":"text-destructive";function Be(){var f,i,m,g,j,v,R;const s=B(x=>x.dateRange.preset)||"30d",r=b({queryKey:["ga4-overview",s],queryFn:()=>h.get("/api/ga4",{period:s}),staleTime:20*6e4}),n=b({queryKey:["tracking-health"],queryFn:()=>h.get("/api/tracking-health"),staleTime:20*6e4});if(r.isError&&n.isError)return e.jsx(p,{title:"Здоровʼя трекінгу",children:e.jsx(k,{message:"Дані недоступні",onRetry:()=>{r.refetch(),n.refetch()}})});if(r.isLoading&&n.isLoading)return e.jsx(p,{title:"Здоровʼя трекінгу",children:e.jsx(N,{rows:4})});let l=null;const a=(f=r.data)==null?void 0:f.sourceMedium;if(Array.isArray(a)){let x=0,H=0;for(const P of a){const W=Number(P.purchases)||0;x+=W,(String(P.source).toLowerCase()==="(not set)"||String(P.medium).toLowerCase()==="(not set)")&&(H+=W)}l=x?H/x*100:null}const c=((m=(i=n.data)==null?void 0:i.coverage)==null?void 0:m.fbpPct)??null,d=((j=(g=n.data)==null?void 0:g.coverage)==null?void 0:j.fbcPct)??null,o=((R=(v=n.data)==null?void 0:v.capi)==null?void 0:R.errorPct)??null,u=[{k:"Unassigned (GA4)",v:l,cls:Z(l,5,12),note:"ціль <5% (cross-domain)"},{k:"fbp покриття",v:c,cls:X(c,85,70),note:"норма 88–92%"},{k:"fbc (ad-click)",v:d,cls:X(d,28,18),note:"стеля ~30–35%"},{k:"CAPI помилки",v:o,cls:Z(o,1,5),note:"має бути ~0"}];return e.jsxs(p,{title:"Здоровʼя трекінгу",children:[e.jsx("div",{className:"grid gap-2",children:u.map(x=>e.jsxs("div",{className:"flex items-center justify-between rounded-element border border-border bg-muted-surface px-3 py-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("div",{className:"text-sm text-foreground",children:x.k}),e.jsx("div",{className:"text-[11px] text-muted",children:x.note})]}),e.jsx("span",{className:`text-base font-medium tabular-nums ${x.cls}`,children:Ke(x.v)})]},x.k))}),e.jsx("div",{className:"mt-2 text-[11px] text-muted",children:"EMQ — у Events Manager (не виводиться в HTTP-API)."})]})}const F=[{id:"7d",label:"7 днів",days:7},{id:"14d",label:"14 днів",days:14},{id:"30d",label:"30 днів",days:30},{id:"90d",label:"90 днів",days:90}];function Ge({range:t,setRange:s}){var n;const r=(n=F.find(l=>t.from===y(l.days)&&t.to===y(0)))==null?void 0:n.id;return e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[e.jsx(ie,{ariaLabel:"Період",options:F.map(l=>({key:l.id,label:l.label})),value:r,onChange:l=>{const a=F.find(c=>c.id===l);s({from:y(a.days),to:y(0)})}}),e.jsx(z,{size:"sm","aria-label":"Від",value:t.from,max:t.to,onChange:l=>l.target.value&&s({...t,from:l.target.value}),className:"ml-2"}),e.jsx("span",{"aria-hidden":"true",className:"text-[11px] text-muted",children:"—"}),e.jsx(z,{size:"sm","aria-label":"До",value:t.to,min:t.from,max:y(0),onChange:l=>l.target.value&&s({...t,to:l.target.value})})]})}const Ie=ue;function He({date:t,onClose:s}){const{data:r,isLoading:n,isError:l,error:a,refetch:c}=b({queryKey:["utm-day-detail",t],queryFn:()=>h.get("/api/utm-daily-stats",{date:t}),staleTime:3e5}),d=(r==null?void 0:r.rows)??[];return e.jsxs("div",{className:"mt-4 rounded-element border border-border bg-muted-surface p-3",children:[e.jsxs("div",{className:"mb-2 flex items-center justify-between",children:[e.jsxs("div",{className:"text-[11px] uppercase tracking-wide text-muted",children:["Заявки за ",K(t)," (",d.length,")"]}),e.jsx("button",{type:"button",onClick:s,className:"text-[11px] text-muted underline",children:"закрити"})]}),l?e.jsx(k,{message:(a==null?void 0:a.message)??"Дані недоступні",onRetry:c}):n?e.jsx(N,{rows:3}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"overflow-x-auto",children:e.jsx(C,{columns:[{key:"time",label:"Час",width:"64px"},{key:"order_id",label:"Замовлення",width:"110px"},{key:"event_name",label:"Подія"},{key:"client",label:"Клієнт"},{key:"tickets_count",label:"Квитків",align:"right",width:"70px"},{key:"amount",label:"Сума",align:"right",width:"90px"},{key:"promocode",label:"Промокод"}
1,{key:"order_status",label:"Статус",width:"110px",render:o=>e.jsx("span",{className:o!=null&&o.ok?"text-success":"text-muted",children:(o==null?void 0:o.label)??"—"})},{key:"utm_source",label:"Джерело"},{key:"utm_medium",label:"Медіум"},{key:"utm_campaign",label:"Кампанія"},{key:"fbc",label:"Meta клік",align:"right",width:"80px"},{key:"referrer",label:"Останній перехід"}],rows:d.map((o,u)=>({key:`${o.order_id||"no-order"}-${u}`,time:me(o.updated_at),order_id:o.order_id||"—",event_name:o.event_name||(o.order_id?"(не знайдено в TicketBox)":"—"),client:o.client_name||o.client_email||"—",tickets_count:o.tickets_count??"—",amount:Ie(o.total_net_amount??o.total_price),promocode:o.promocode_name||"—",order_status:{ok:ge(o.order_status),label:o.order_status||"—"},utm_source:o.utm_source||"—",utm_medium:o.utm_medium||"—",utm_campaign:o.utm_campaign||"—",fbc:o.fbc?"✓":"—",referrer:o.referrer||"—"})),rowKey:o=>o.key,emptyText:"За цей день немає рядків visitor_signals."})}),e.jsx("div",{className:"mt-1.5 text-[10px] text-muted",children:"Подія/квитки/сума/статус — з TicketBox (MySQL) по order_id. «Останній перехід» — це крок перед оформленням (checkout/платіжка), не початкове джерело візиту."})]})]})}function We(){const[t,s]=Y.useState({from:y(14),to:y(0)}),[r,n]=Y.useState(null),{data:l,isLoading:a,isError:c,error:d,refetch:o}=b({queryKey:["utm-daily-stats",t.from,t.to],queryFn:()=>h.get("/api/utm-daily-stats",{from:t.from,to:t.to}),staleTime:20*6e4}),u=(l==null?void 0:l.dailySeries)??[],f=(l==null?void 0:l.topSources)??[],i=Math.max(1,...u.map(m=>m.total));return e.jsxs(p,{title:"UTM за днями",action:e.jsx(Ge,{range:t,setRange:m=>{s(m),n(null)}}),children:[c?e.jsx(k,{message:(d==null?void 0:d.message)??"Дані недоступні",onRetry:o}):a?e.jsx(N,{rows:4}):u.length===0?e.jsx(S,{children:"Поки немає сигналів visitor_signals за цей період."}):e.jsx("div",{className:"flex h-[88px] items-end gap-1.5 overflow-x-auto",children:u.map(m=>{const g=Math.max(2,Math.round(m.total/i*72)),j=m.total>0?Math.round(m.withUtm/m.total*g):0,v=r===m.date;return e.jsxs("button",{type:"button",onClick:()=>n(v?null:m.date),className:"flex min-w-[22px] flex-1 flex-col items-center justify-end gap-1 border-0 bg-transparent p-0",title:`${K(m.date)}: ${m.total} усього, ${m.withUtm} з UTM (${_(m.pctWithUtm??"—")}%) — клік для деталей`,children:[e.jsx("div",{className:`relative w-full rounded-t-[3px] bg-muted-surface ${v?"ring-2 ring-accent-strong":""}`,style:{height:g},children:e.jsx("div",{className:"absolute bottom-0 w-full rounded-t-[3px] bg-accent",style:{height:j}})}),e.jsx("span",{className:`text-[10px] ${v?"font-semibold text-foreground":"text-muted"}`,children:K(m.date)})]},m.date)})}),e.jsxs("div",{className:"mt-2 flex items-center gap-3 text-[11px] text-muted",children:[e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx("span",{className:"inline-block h-2 w-2 rounded-sm bg-accent"})," з UTM"]}),e.jsxs("span",{className:"inline-flex items-center gap-1",children:[e.jsx("span",{className:"inline-block h-2 w-2 rounded-sm bg-muted-surface"})," усього заявок"]}),e.jsx("span",{children:"· клік по дню — деталі"})]}),r&&e.jsx(He,{date:r,onClose:()=>n(null)}),e.jsxs("div",{className:"mt-4",children:[e.jsx("div",{className:"mb-2 text-[11px] uppercase tracking-wide text-muted",children:"Топ джерел за період"}),e.jsx("div",{className:"mb-2 text-[10px] text-muted",children:"Групуємо по «джерело / медіум» — email, viber і telegram_bot усі йдуть з одним utm_source=sendpulse, самого джерела недостатньо, щоб відрізнити канал."}),e.jsx(C,{columns:[{key:"source",label:"Джерело"},{key:"medium",label:"Медіум"},{key:"count",label:"К-сть",align:"right",width:"80px"}],rows:f.map(m=>({...m,medium:m.medium||"—"})),rowKey:m=>`${m.source}|${m.medium}`,emptyText:"За цей період UTM не зафіксовано."})]})]})}const re=I;async function Ye(){const[t,s]=await Promise.all([h.get("/api/meta-campaigns",{period:"last_7d"}),h.get("/api/fx",{base:"USD"}).catch(A("scale-cut:fx"))]),r=(s==null?void 0:s.rate)||je,n=((t==null?void 0:t.campaigns)??[]).map(l
1=>{var a,c;return{id:l.id,name:l.name,spend:(((a=l.insights)==null?void 0:a.spend)??0)*r,roas:((c=l.insights)==null?void 0:c.roas)??0}});return fe(n,{target:M,floor:ne})}function ee({r:t}){const s=se(t.roas);return e.jsxs("div",{className:"flex items-center justify-between gap-2 py-1.5",children:[e.jsx("span",{className:"min-w-0 truncate text-[13px] text-foreground",children:t.name}),e.jsxs("span",{className:"flex shrink-0 items-center gap-3 tabular-nums",children:[e.jsx("span",{className:"text-[12px] text-muted",children:re(t.spend)}),e.jsx(E,{tone:s.tone,children:s.label})]})]})}function Qe(){const{data:t,isLoading:s,isError:r,error:n,refetch:l}=b({queryKey:["campaign-scalecut","last_7d"],queryFn:Ye,staleTime:12e5});if(r)return e.jsx(p,{title:"Кампанії — що підсилити, що скоротити",children:e.jsx(k,{message:(n==null?void 0:n.message)??"Не вдалось завантажити кампанії.",onRetry:l})});const a=t==null?void 0:t.summary,c=a&&a.cutCount+a.watchCount>0,d=t?[...t.cut,...t.watch]:[];return e.jsx(p,{title:"Кампанії — що підсилити, що скоротити",children:s?e.jsx(N,{rows:4}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"mb-3 text-[12px]",children:c?e.jsxs("span",{className:"text-foreground",children:["Потребує уваги: ",e.jsxs("span",{className:"text-destructive",children:[a.cutCount," збиткових"]})," · ",a.watchCount," під ціллю · ",e.jsx("span",{className:"text-warning",children:re(a.atRiskSpend)})," у зоні ризику (7 днів)"]}):e.jsxs("span",{className:"text-success",children:["Усі кампанії ≥ цілі ",M,"× (7 днів)"]})}),e.jsxs("div",{className:"grid grid-cols-1 gap-4 lg:grid-cols-2",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"mb-1 text-[11.5px] text-muted",children:["Підсилити — окупаються (ROAS ≥ ",M,"×)"]}),t.scale.length?t.scale.slice(0,5).map(o=>e.jsx(ee,{r:o},o.id)):e.jsx(S,{children:"Немає кандидатів."})]}),e.jsxs("div",{children:[e.jsx("div",{className:"mb-1 text-[11.5px] text-muted",children:"Скоротити або переглянути"}),d.length?d.slice(0,5).map(o=>e.jsx(ee,{r:o},o.id)):e.jsx(S,{children:"Немає."})]})]}),e.jsx("p",{className:"mt-3 text-[11px] text-muted",children:"Pixel ROAS за 7 днів (відносний сигнал); витрати у ₴ за курсом НБУ. Real ROAS — у per-event діагностиці."})]})})}const te=I,ze=t=>t==null?"—":t.toLocaleString("uk-UA"),q=t=>t==null?null:pe(t/100);function pt(){var d,o,u;const{data:t,isLoading:s,isError:r,error:n,refetch:l}=Ee();if(r)return e.jsx(Q,{children:e.jsx(k,{message:(n==null?void 0:n.message)??"Не вдалось завантажити огляд.",onRetry:l})});const a=t==null?void 0:t.kpi,c=le(a==null?void 0:a.realRoas);return e.jsxs(Q,{children:[e.jsxs("div",{className:"mb-4 flex items-baseline justify-between",children:[e.jsx("h1",{className:"text-xl font-semibold tracking-tight text-foreground",children:"Зведення продажів"}),e.jsx("span",{className:"text-[13px] text-muted",children:"Період — у верхній панелі"})]}),e.jsx("div",{className:"mb-4 grid grid-cols-2 gap-4 lg:grid-cols-4",children:s?e.jsx("div",{className:"col-span-full",children:e.jsx(N,{rows:2})}):e.jsxs(e.Fragment,{children:[e.jsx(T,{label:"Виручка",value:te(a.revenue),delta:q((d=a.deltas)==null?void 0:d.revenue),sub:"TicketBox"}),e.jsx(T,{label:"Замовлення",value:ze(a.ticketsSold),delta:q((o=a.deltas)==null?void 0:o.ticketsSold),sub:"за період · TicketBox"}),e.jsx(T,{label:"Real ROAS",value:a.realRoas!=null?`${xe(a.realRoas,1)}`:"—",sub:e.jsxs("span",{className:"inline-flex items-center gap-2",children:[e.jsx(E,{tone:c.tone,children:oe(a==null?void 0:a.realRoas)}),"MER • дохід/витрати"]})}),e.jsx(T,{label:"Витрати на рекламу",value:te(a.adSpend),delta:q((u=a.deltas)==null?void 0:u.adSpend),sub:"Meta + Google"})]})}),e.jsxs("div",{className:"grid grid-cols-1 items-start gap-4 lg:grid-cols-[1.6fr_1fr]",children:[e.jsx(he,{embedded:!0}),e.jsx(Pe,{rows:(t==null?void 0:t.channels)??[]})]}),e.jsxs("div",{className:"mt-4 grid grid-cols-1 items-start gap-4 lg:grid-cols-[1.6fr_1fr]",children:[e.jsx(Fe,{}),e.jsx(Be,{})]}),e.jsx("div",{className:"mt-4",children:e.jsx(qe,{})}),e.jsx("div",{className:"mt-4",children:e.jsx(We,{})}),e.jsx("div",{className:"mt-4",children:e.jsx(Qe,{})}),e.jsx("div",{className:"mt-4",children:e.jsx(Le,{points:(t==null?void 0:t.pace)??[]})})]})}export{pt as Overview};

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.