1import{r as o,s as k,t as J,j as e,a as Q,e as X,H as Z}from"./index-VBzSRhxd.js";import{g as ee,A as te}from"./AdminHeaderNav-DKd8u_5E.js";import{E as se}from"./EngagementSubnav-DjqF41YX.js";import{B as ae}from"./button-Cx2RyDQO.js";import{I as W}from"./input-BhDMpNE-.js";import{S as ne,a as ie,b as re,c as oe,d as R}from"./select-vgoZ-NYG.js";import{T as D,a as I,b,c as u,d as V,e as x}from"./table-BIrzubEX.js";import{D as le}from"./download-WYTOA8it.js";import"./index-BovEmud_.js";import"./check-CYokwn42.js";import"./file-text-C6iW73QT.js";import"./index-_xEI9D2C.js";const ce={1:1,7:7,30:30,90:90},O=()=>new Date().toISOString().slice(0,10),de=l=>new Date(Date.now()-l*864e5).toISOString().slice(0,10);function me(){const[l,w]=o.useState("7"),[g,S]=o.useState(de(7)),[p,M]=o.useState(O()),[_,T]=o.useState([]),[j,N]=o.useState(new Set),[$,L]=o.useState(!0);o.useEffect(()=>{(async()=>{const{data:t}=await k.from("engagement_excluded_ips").select("ip_address"),s=t??[];N(new Set(s.map(n=>n.ip_address)))})()},[]),o.useEffect(()=>{let t=!1;return(async()=>{L(!0);let s,n=null;if(l==="custom"){if(!g||!p){L(!1);return}s=new Date(`${g}T00:00:00`).toISOString(),n=new Date(`${p}T23:59:59.999`).toISOString()}else s=new Date(Date.now()-ce[l]*864e5).toISOString();let r=k.from("engagement_events").select("*").gte("created_at",s).order("created_at",{ascending:!1}).limit(5e3);n&&(r=r.lte("created_at",n));const{data:i,error:d}=await r;t||(d?(J({title:"Failed to load engagement",description:d.message,variant:"destructive"}),T([])):T(i??[]),L(!1))})(),()=>{t=!0}},[l,g,p]);const c=o.useMemo(()=>_.filter(t=>!t.ip_address||!j.has(t.ip_address)),[_,j]),F=o.useMemo(()=>{const t=new Map;for(const s of c){const n=s.event_target??"â",r=`${s.event_name}::${n}`,i=t.get(r)??{name:s.event_name,target:n,count:0,visitors:new Set};i.count+=1,s.visitor_id&&i.visitors.add(s.visitor_id),t.set(r,i)}return Array.from(t.values()).map(s=>({...s,uniqueVisitors:s.visitors.size})).sort((s,n)=>n.count-s.count)},[c]),A=o.useMemo(()=>{const t=new Set(c.map(i=>i.visitor_id).filter(Boolean)).size,s=c.filter(i=>i.event_name==="page_view").length,n=c.filter(i=>i.event_name==="button_click").length,r=c.filter(i=>i.event_name==="form_submit").length;return{total:c.length,uniqueVisitors:t,pageViews:s,clicks:n,submits:r}},[c]),q=o.useMemo(()=>{const t=new Map;for(const s of c.filter(n=>n.event_name==="page_view"&&n.path))t.set(s.path,(t.get(s.path)??0)+1);return Array.from(t.entries()).sort((s,n)=>n[1]-s[1]).slice(0,15)},[c]),Y=o.useMemo(()=>{const t=a=>{const G=new Set;for(const H of c)a(H)&&H.visitor_id&&G.add(H.visitor_id);return G},s=t(a=>a.event_name==="page_view"&&a.path==="/"),n=t(a=>a.event_name==="button_click"&&a.event_target==="home_primary_cta"),r=t(a=>a.event_name==="page_view"&&a.path==="/apply"),i=t(a=>a.event_name==="form_start"&&a.event_target==="apply_form"),d=t(a=>a.event_name==="form_submit"&&a.event_target==="apply_form"),y=t(a=>a.event_name==="sheet_open"&&a.event_target==="eligibility_sidebar"),C=t(a=>a.event_name==="page_view"&&a.path==="/partner"),P=t(a=>a.event_name==="sheet_open"&&a.event_target==="contact_us_sidebar"),m=t(a=>a.event_name==="form_submit"&&a.event_target==="contact_form"),h=new Set;for(const a of s)C.has(a)&&h.add(a);const z=t(a=>a.event_name==="button_click"&&a.event_target==="home_read_our_story"),E=new Set;for(const a of s)d.has(a)&&E.add(a);return[{label:"Home â clicked Apply CTA",numerator:n.size,denominator:s.size,hint:"Visitors who clicked an Apply CTA on the homepage / all homepage visitors"},{label:"Home â submitted application",numerator:E.size,denominator:s.size,hint:"Homepage visitors who went on to submit the apply form (full funnel)"},{label:"Home â visited Partner page",numerator:h.size,denominator:s.size,hint:"Homepage visitors who also landed on /partner in the same range"},{label:"Home â clicked Read our story",numerator:z.size,denominator:s.size,hint:"Homepage visitors who clicked the Read our story link in the footer"},{label:"Apply page â started form",numerator:i.size,denominator:r.size,hint:"Visitors who began the apply form / all /apply visitors"},{label:"Started apply â submitted",numerator:d.size,denominator:i.size,hint:"Applications submitted / applications started (form completion rate)"},{label:"Apply page â submitted (end-to-end)",numerator:d.size,denominator:r.size,hint:"Full conversion from landing on /apply to submitting"},{label:"Apply page â opened eligibility sidebar",numerator:y.size,denominator:r.size,hint:"How often visitors check eligibility before applying"},{label:"Partner page â opened contact",numerator:P.size,denominator:C.size,hint:"Visitors who opened the contact sidebar on /partner"},{label:"Partner page â submitted contact",numerator:m.size,denominator:C.size,hint:"End-to-end partner contact conversion"}]},[c]),f=o.useMemo(()=>{const t=new Map;for(const m of c)m.event_name!=="page_view"||!m.session_id||t.set(m.session_id,(t.get(m.session_id)??0)+1);const s=t.size,n=Array.from(t.values()).filter(m=>m===1).length,r=s?n/s:0,i=new Map;let d=0,y=0;for(const m of c){if(m.event_name!=="page_leave")continue;const h=m.metadata??{},z=typeof h.duration_ms=="number"?h.duration_ms:0,E=h.path??m.path??"â";if(z<=0)continue;const a=i.get(E)??{total:0,count:0};a.total+=z,a.count+=1,i.set(E,a),d+=z,y+=1}const C=y?d/y:0,P=Array.from(i.entries()).map(([m,h])=>({path:m,avgMs:h.total/h.count,samples:h.count})).sort((m,h)=>h.samples-m.samples).slice(0,15);return{bounceRate:r,totalSessions:s,bouncedSessions:n,avgDwellMs:C,dwellRows:P}},[c]),U=t=>`${(t*100).toFixed(1)}%`,B=t=>{if(!t)return"â";const s=t/1e3;if(s<60)return`${s.toFixed(1)}s`;
1const n=Math.floor(s/60),r=Math.round(s-n*60);return`${n}m ${r}s`},K=()=>{const s=[["created_at","event_name","event_target","path","visitor_id","session_id","metadata"].join(",")].concat(c.map(d=>[d.created_at,d.event_name,d.event_target??"",d.path??"",d.visitor_id??"",d.session_id??"",JSON.stringify(d.metadata??{})].map(y=>`"${String(y).replace(/"/g,'""')}"`).join(","))),n=new Blob([s.join(` 2`)],{type:"text/csv"}),r=URL.createObjectURL(n),i=document.createElement("a");i.href=r,i.download=`engagement_${l}d_${new Date().toISOString().slice(0,10)}.csv`,i.click(),URL.revokeObjectURL(r)};return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex items-end justify-between gap-3 flex-wrap",children:[e.jsxs("div",{className:"flex items-end gap-2 flex-wrap",children:[e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:"Range"}),e.jsxs(ne,{value:l,onValueChange:w,children:[e.jsx(ie,{className:"w-44",children:e.jsx(re,{})}),e.jsxs(oe,{children:[e.jsx(R,{value:"1",children:"Last 24 hours"}),e.jsx(R,{value:"7",children:"Last 7 days"}),e.jsx(R,{value:"30",children:"Last 30 days"}),e.jsx(R,{value:"90",children:"Last 90 days"}),e.jsx(R,{value:"custom",children:"Custom range"})]})]})]}),l==="custom"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("label",{htmlFor:"range-start",className:"text-xs text-muted-foreground",children:"Start"}),e.jsx(W,{id:"range-start",type:"date",value:g,max:p||O(),onChange:t=>S(t.target.value),className:"w-44"})]}),e.jsxs("div",{className:"flex flex-col gap-1",children:[e.jsx("label",{htmlFor:"range-end",className:"text-xs text-muted-foreground",children:"End"}),e.jsx(W,{id:"range-end",type:"date",value:p,min:g,max:O(),onChange:t=>M(t.target.value),className:"w-44"})]})]})]}),e.jsxs(ae,{variant:"outline",onClick:K,disabled:c.length===0,children:[e.jsx(le,{className:"w-4 h-4 mr-2"})," Export CSV"]})]}),$?e.jsx("div",{className:"p-12 flex justify-center",children:e.jsx(Q,{className:"w-6 h-6 animate-spin text-muted-foreground"})}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-5 gap-3",children:[e.jsx(v,{label:"Total events",value:A.total}),e.jsx(v,{label:"Unique visitors",value:A.uniqueVisitors}),e.jsx(v,{label:"Page views",value:A.pageViews}),e.jsx(v,{label:"Button clicks",value:A.clicks}),e.jsx(v,{label:"Form submits",value:A.submits})]}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-3",children:[e.jsx(v,{label:"Bounce rate",display:U(f.bounceRate),hint:`${f.bouncedSessions} of ${f.totalSessions} sessions viewed only one page`}),e.jsx(v,{label:"Avg dwell / page",display:B(f.avgDwellMs),hint:"Average time visitors spend on a page before leaving"}),e.jsx(v,{label:"Sessions",value:f.totalSessions,hint:"Distinct browsing sessions with at least one page view"}),e.jsx(v,{label:"Pages / session",display:f.totalSessions?(A.pageViews/f.totalSessions).toFixed(2):"â",hint:"Average page views per session"})]}),e.jsxs("div",{className:"rounded-lg border bg-card",children:[e.jsxs("div",{className:"px-4 py-3 border-b",children:[e.jsx("h3",{className:"font-semibold text-sm",children:"Conversion funnels"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Percentages compare unique visitors at each step. Use these to spot where people drop off."})]}),e.jsxs(D,{children:[e.jsx(I,{children:e.jsxs(b,{children:[e.jsx(u,{children:"Funnel step"}),e.jsx(u,{className:"text-right",children:"Converted"}),e.jsx(u,{className:"text-right",children:"Total"}),e.jsx(u,{className:"text-right",children:"Rate"})]})}),e.jsx(V,{children:Y.map(t=>{const s=t.denominator?t.numerator/t.denominator:0;return e.jsxs(b,{children:[e.jsxs(x,{children:[e.jsx("div",{className:"text-sm",children:t.label}),e.jsx("div",{className:"text-xs text-muted-foreground",children:t.hint})]}),e.jsx(x,{className:"text-right tabular-nums",children:t.numerator}),e.jsx(x,{className:"text-right tabular-nums",children:t.denominator}),e.jsx(x,{className:"text-right tabular-nums font-semibold",children:t.denominator?U(s):"â"})]},t.label)})})]})]}),e.jsxs("div",{className:"rounded-lg border bg-card",children:[e.jsxs("div",{className:"px-4 py-3 border-b",children:[e.jsx("h3",{className:"font-semibold text-sm",children:"Dwell time by page"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Average time on page before navigating away or closing the tab. Very short dwell on key pages often means the message isn't landing."})]}),f.dwellRows.length===0?e.jsx("div",{className:"p-8 text-center text-sm text-muted-foreground",children:"No dwell data yet â visit a few pages to start collecting."}):e.jsxs(D,{children:[e.jsx(I,{children:e.jsxs(b,{children:[e.jsx(u,{children:"Path"}),e.jsx(u,{className:"text-right",children:"Avg dwell"}),e.jsx(u,{className:"text-right",children:"Samples"})]})}),e.jsx(V,{children:f.dwellRows.map(t=>e.jsxs(b,{children:[e.jsx(x,{className:"font-mono text-xs",children:t.path}),e.jsx(x,{className:"text-right tabular-nums",children:B(t.avgMs)}),e.jsx(x,{className:"text-right tabular-nums",children:t.samples})]},t.path))})]})]}),e.jsxs("div",{className:"rounded-lg border bg-card",children:[e.jsxs("div",{className:"px-4 py-3 border-b",children:[e.jsx("h3",{className:"font-semibold text-sm",children:"Events by target"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Which buttons, sidebars, and forms are getting used."})]}),F.length===0?e.jsx("div",{className:"p-8 text-center text-sm text-muted-foreground",children:"No events in this range yet."}):e.jsxs(D,{children:[e.jsx(I,{children:e.jsxs(b,{children:[e.jsx(u,{children:"Event"}),e.jsx(u,{children:"Target"}),e.jsx(u,{className:"text-right",children:"Count"}),e.jsx(u,{className:"text-right",children:"Unique visitors"})]})}),e.jsx(V,{children:F.map(t=>e.jsxs(b,{children:[e.jsx(x,{className:"font-mono text-xs",children:t.name}),e.jsx(x,{className:"font-mono text-xs",children:t.target}),e.jsx(x,{className:"text-right",children:t.count}),e.jsx(x,{className:"text-right",children:t.uniqueVisitors})]},`${t.name}-${t.target}`))})]})]}),e.jsxs("div",{className:"rounded-lg border bg-card",children:[e.jsxs("div",{className:"px-4 py-3 border-b",children:[e.jsx("h3",{className:"font-semibold text-sm",children:"Top pages"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Page views by URL path."})]}),q.length===0?e.jsx("div",{className:"p-8 text-center text-sm text-muted-foreground",children:"No page views yet."}):e.jsxs(D,{children:[e.jsx(I,{children:e.jsxs(b,{children:[e.jsx(u,{children:"Path"}),e.jsx(u,{className:"text-right",children:"Views"})]})}),e.jsx(V,{children:q.map(([t,s])=>e.jsxs(b,{children:[e.jsx(x,{className:"font-mono text-xs",children:t}),e.jsx(x,{className:"text-right",children:s})]},t))})]})]})]})]})}function v({label:l,value:w,display:g,hint:S}
2){const p=g??(typeof w=="number"?w.toLocaleString():"â");return e.jsxs("div",{className:"rounded-lg border bg-card p-4",children:[e.jsx("p",{className:"text-xs text-muted-foreground",children:l}),e.jsx("p",{className:"text-2xl font-bold text-foreground mt-1",children:p}),S&&e.jsx("p",{className:"text-[11px] text-muted-foreground mt-1 leading-snug",children:S})]})}function ye(){const[l,w]=o.useState(null),[g,S]=o.useState(!0),[p,M]=o.useState(!1),_=X();return o.useEffect(()=>{const{data:{subscription:T}}=k.auth.onAuthStateChange((j,N)=>{j==="SIGNED_OUT"&&_("/admin/login"),w((N==null?void 0:N.user)??null)});return(async()=>{const{data:{session:j}}=await k.auth.getSession();if(!j){_("/admin/login");return}w(j.user);const{data:N,error:$}=await k.from("user_roles").select("role").eq("user_id",j.user.id).eq("role","admin").maybeSingle();if($||!N){J({title:"Access Denied",description:"You don't have admin privileges.",variant:"destructive"}),await k.auth.signOut(),_("/admin/login");return}M(!0),S(!1)})(),()=>T.unsubscribe()},[_]),g?e.jsx("div",{className:"min-h-screen bg-background flex items-center justify-center",children:e.jsx(Q,{className:"w-8 h-8 animate-spin text-primary"})}):p?e.jsxs(e.Fragment,{children:[e.jsxs(Z,{children:[e.jsx("title",{children:"Engagement | Admin"}),e.jsx("meta",{name:"robots",content:"noindex, nofollow"})]}),e.jsxs("div",{className:"min-h-screen bg-background",children:[e.jsx("header",{className:"border-b-2 border-primary/40 bg-white sticky top-0 z-10",children:e.jsxs("div",{className:"container mx-auto px-4 py-4 flex items-center justify-between gap-4 flex-wrap",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(ee,{className:"w-5 h-5 text-primary"}),e.jsxs("div",{children:[e.jsx("h1",{className:"font-semibold text-foreground",children:"Engagement"}),e.jsx("p",{className:"text-xs text-muted-foreground",children:l==null?void 0:l.email})]})]}),e.jsx(te,{userEmail:l==null?void 0:l.email,showBackToApplications:!0})]})}),e.jsx(se,{}),e.jsxs("main",{className:"container mx-auto px-4 py-8 max-w-6xl space-y-4",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-xl font-bold text-foreground",children:"Engagement"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Native tracking of button clicks, form starts/submits, sidebar opens, and page views."})]}),e.jsx(me,{})]})]})]}):null}export{ye 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.