PageSourceSearch

https://teachmo.ai/assets/AdminAnalytics-BihjKSdT.js

js teachmo.ai collected 2026-10-05 17:08:51 UTC 12,858 bytes, 68 lines download raw bytes

1import{r as x,j as e}from"./vendor-react-BDlPBQ8H.js";import{v as w}from"./vendor-ui-helpers-BA9Ou6yF.js";import{n as W,S as U,J as Z,C,a as D,b as R,c as T,B as j}from"./index-C9I8CK0-.js";import{d as G}from"./http-BKZdOg30.js";import{b as X}from"./demoDataSeeder-D9gxPrCr.js";import{I as M}from"./input-DdZnLe1O.js";import{S as B,a as O,b as Q,c as z,d as N}from"./select-BOaPfqF9.js";import{E as K}from"./EnterpriseDataTable--TRdruUJ.js";import{E as Y,a as ee}from"./EnterpriseSurface-CxYSrS8g.js";import"./vendor-nhost-yQ8TKvUP.js";import"./vendor-misc-BcNut0Sq.js";import"./vendor-radix-CoZ84gME.js";import"./vendor-interactions-B-90v6Vt.js";import"./vendor-sentry-B_mZGnMZ.js";import"./EnterpriseStatCard-Wl26IFB0.js";import"./api-BemNGHpK.js";async function te(){const t=await W.auth.getAccessToken();return{"Content-Type":"application/json",...t?{Authorization:`Bearer ${t}`}:{}}}function se(t){return G("/admin/ai/usage-summary",{headers:t})}const ne=te,ae=se;async function re(t){const n=await U(`query Rollups($where: analytics_event_rollups_daily_bool_exp!) {
2      analytics_event_rollups_daily(
3        where: $where,
4        order_by: [{ day: desc }, { event_count: desc }],
5        limit: 500
6      ) {
7        day
8        event_name
9        district_id
10        school_id
11        event_count
12      }
13    }`,{where:t});return(n==null?void 0:n.analytics_event_rollups_daily)??[]}async function le(t){const n=await U(`query Drill($where: analytics_events_bool_exp!) {
14      analytics_events(
15        where: $where,
16        order_by: { event_ts: desc },
17        limit: 200
18      ) {
19        id
20        event_ts
21        event_name
22        actor_user_id
23        entity_type
24        entity_id
25        metadata
26      }
27    }`,{where:t});return(n==null?void 0:n.analytics_events)??[]}function ie(t){const n=new Date;return n.setDate(n.getDate()-t),n.toISOString().slice(0,10)}function de(){const{data:t}=Z(),[n,d]=x.useState(()=>ie(14)),[m,_]=x.useState(()=>new Date().toISOString().slice(0,10)),[o,f]=x.useState("all"),[l,A]=x.useState("all"),[y,S]=x.useState(null),b=w({queryKey:["analytics-ai-token"],queryFn:ne}),$=w({queryKey:["analytics-ai-usage-summary"],enabled:!!b.data,queryFn:async()=>ae(b.data)}),v=x.useMemo(()=>{const i={day:{_gte:n,_lte:m}};return t!=null&&t.districtId&&(i.district_id={_eq:t.districtId}),t!=null&&t.schoolId&&(i.school_id={_eq:t.schoolId}),o!=="all"&&(i.event_name={_eq:o}),i},[n,m,t==null?void 0:t.districtId,t==null?void 0:t.schoolId,o]),g=w({queryKey:["analytics-rollups",v],enabled:!!(t!=null&&t.districtId),queryFn:async()=>re(v)}),h=x.useMemo(()=>{if(!y)return null;const i={event_ts:{_gte:`${n}T00:00:00.000Z`,_lte:`${m}T23:59:59.999Z`},event_name:{_eq:y}};return t!=null&&t.districtId&&(i.district_id={_eq:t.districtId}),t!=null&&t.schoolId&&(i.school_id={_eq:t.schoolId}),l!=="all"&&(i.metadata={_contains:{actor_role:l}}),i},[y,n,m,t==null?void 0:t.districtId,t==null?void 0:t.schoolId,l]),p=w({queryKey:["analytics-drill",h],enabled:!!h,queryFn:async()=>le(h)}),r=x.useMemo(()=>{const i=new Set;for(const I of g.data??[])i.add(String(I.event_name));return Array.from(i).sort()},[g.data]),E=x.useMemo(()=>X(),[]);return{actorRole:l,aiUsageQuery:$,demoData:E,drillEventName:y,drillQuery:p,eventName:o,from:n,rollupsQuery:g,scope:t,setActorRole:A,setDrillEventName:S,setEventName:f,setFrom:d,setTo:_,to:m,uniqueEventNames:r}}function H(t){return t?t.slice(0,10):""}function J(t,n,d="text/plain"){const m=new Blob([n],{type:d}),_=URL.createObjectURL(m),o=document.createElement("a");o.href=_,o.download=t,o.click(),URL.revokeObjectURL(_)}function u(t){const n=String(t??"");return/[\n\r,"]/g.test(n)?`"${n.replaceAll('"','""')}"`:n}function Ae(){var q,L,k,F;const{actorRole:t,aiUsageQuery:n,drillEventName:d,drillQuery:m,eventName:_,from:o,rollupsQuery:f,scope:l,setActorRole:A,setDrillEventName:y,setEventName:S,setFrom:b,setTo:$,to:v,uniqueEventNames:g}=de(),h=f.data??[],p=m.data??[],r=n.data,E=()=>{const c=[["day","event_name","district_id","school_id","event_count"].join(",")];for(const a of h)c.push([u(a.day),u(a.event_name),u(a.district_id??""),u(a.school_id??""),u(a.event_count??0)].join(","));J(`teachmo_analytics_rollups_${o}_${v}.csv`,c.join(`
28`),"text/csv")},i=()=>{const c=[["event_ts","event_name","actor_user_id","entity_type","entity_id","metadata"].join(",")];for(const a of p)c.push([u(a.event_ts),u(a.event_name),u(a.actor_user_id??""),u(a.entity_type??""),u(a.entity_id??""),u(JSON.stringify(a.metadata??{}))].join(","));J(`teachmo_analytics_events_${d}_${o}_${v}.csv`,c.join(`
29`),"text/csv")},I=[{id:"day",header:"Day",accessor:"day"},{id:"event_name",header:"Event",accessor:"event_name"},{id:"school_id",header:"School",accessor:s=>s.school_id??"—"},{id:"event_count",header:"Count",accessor:s=>s.event_count??0},{id:"actions",header:"Action",accessor:s=>s.event_name,cell:s=>e.jsx(j,{size:"sm",variant:"secondary",onClick:()=>y(s.event_name??null),children:"Drill down"})}],V=[{id:"event_ts",header:"Time",accessor:s=>
29new Date(s.event_ts).toLocaleString()},{id:"actor_user_id",header:"User",accessor:s=>s.actor_user_id??"—"},{id:"entity",header:"Entity",accessor:s=>(s.entity_type??"")+(s.entity_id?`:${s.entity_id}`:"")},{id:"metadata",header:"Metadata",accessor:s=>JSON.stringify(s.metadata??{})}],P=()=>{const s=`
30      <html>
31        <head>
32          <title>Teachmo Analytics Export</title>
33          <style>
34            body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; padding: 24px; }
35            h1 { font-size: 18px; margin: 0 0 8px; }
36            h2 { font-size: 14px; margin: 18px 0 8px; }
37            table { width: 100%; border-collapse: collapse; }
38            th, td { border: 1px solid #ddd; padding: 6px 8px; font-size: 12px; }
39            th { text-align: left; background: #f6f6f6; }
40            .meta { color: #555; font-size: 12px; margin-bottom: 8px; }
41          </style>
42        </head>
43        <body>
44          <h1>Teachmo Analytics Export</h1>
45          <div class="meta">Range: ${o} → ${v}</div>
46          <h2>Rollups (daily)</h2>
47          <table>
48            <thead>
49              <tr><th>Day</th><th>Event</th><th>District</th><th>School</th><th>Count</th></tr>
50            </thead>
51            <tbody>
52              ${h.map(a=>`<tr><td>${a.day??""}</td><td>${a.event_name??""}</td><td>${a.district_id??""}</td><td>${a.school_id??""}</td><td>${a.event_count??0}</td></tr>`).join("")}
53            </tbody>
54          </table>
55          ${d?`
56            <h2>Drilldown: ${d}</h2>
57            <table>
58              <thead>
59                <tr><th>Time</th><th>User</th><th>Entity</th><th>Metadata</th></tr>
60              </thead>
61              <tbody>
62                ${p.map(a=>`<tr><td>${new Date(a.event_ts).toLocaleString()}</td><td>${a.actor_user_id??""}</td><td>${(a.entity_type??"")+(a.entity_id?":"+a.entity_id:"")}</td><td><pre style="margin:0; white-space: pre-wrap;">${JSON.stringify(a.metadata??{},null,2)}</pre></td></tr>`).join("")}
63              </tbody>
64            </table>
65          `:""}
66        </body>
67      </html>
68    `,c=window.open("","_blank");c&&(c.document.open(),c.document.write(s),c.document.close(),c.focus(),c.print())};return e.jsxs(Y,{eyebrow:"Insights",title:"Analytics",description:"Tenant-scoped rollups, AI usage, and event drilldowns are presented as procurement-ready evidence with exports for review.",badges:["Tenant scoped","Export ready","AI usage","Drilldown"],metrics:[{label:"Rollup rows",value:String(h.length),badge:"Daily",trend:"flat"},{label:"Drilldown rows",value:String(p.length),badge:d?"Selected":"Choose event",trend:"flat"},{label:"AI calls",value:String(((q=r==null?void 0:r.totals)==null?void 0:q.calls)??0),badge:"Governed",trend:"flat"},{label:"Scope",value:l!=null&&l.districtId?"District":"Pending",badge:"RBAC",trend:"flat"}],actions:e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(j,{variant:"secondary",onClick:P,disabled:f.isLoading,children:"Export PDF"}),e.jsx(j,{onClick:E,disabled:f.isLoading,children:"Export Rollups CSV"})]}),children:[e.jsx(ee,{title:"Filters",description:"Narrow analytics before exporting or drilling into event evidence.",children:e.jsxs("div",{className:"grid grid-cols-1 gap-3 md:grid-cols-4",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-muted-foreground mb-1",children:"From"}),e.jsx(M,{"aria-label":"Analytics start date",type:"date",value:o,onChange:s=>b(H(s.target.value))})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-muted-foreground mb-1",children:"To"}),e.jsx(M,{"aria-label":"Analytics end date",type:"date",value:v,onChange:s=>$(H(s.target.value))})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-muted-foreground mb-1",children:"Event"}),e.jsxs(B,{value:_,onValueChange:S,children:[e.jsx(O,{"aria-label":"Analytics event filter",children:e.jsx(Q,{placeholder:"All events"})}),e.jsxs(z,{children:[e.jsx(N,{value:"all",children:"All events"}),g.map(s=>e.jsx(N,{value:s,children:s},s))]})]})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-muted-foreground mb-1",children:"Actor role (drilldown)"}),e.jsxs(B,{value:t,onValueChange:A,children:[e.jsx(O,{"aria-label":"Analytics actor role filter",children:e.jsx(Q,{placeholder:"All roles"})}),e.jsxs(z,{children:[e.jsx(N,{value:"all",children:"All roles"}),["parent","teacher","school_admin","district_admin","system_admin"].map(s=>e.jsx(N,{value:s,children:s},s))]})]})]})]})}),e.jsxs(C,{className:"border-[var(--enterprise-border)] bg-[var(--enterprise-surface)]",children:[e.jsxs(D,{className:"flex flex-row items-center justify-between",children:[e.jsx(R,{children:"Daily rollups"}),e.jsx("div",{className:"text-xs text-muted-foreground",children:l!=null&&l.districtId?`District: ${l.districtId}${l.schoolId?` • School: ${l.schoolId}`:""}`:"Loading tenant scope…"})]}),e.jsx(T,{children:f.isLoading?e.jsx("div",{className:"text-sm text-muted-foreground",children:"Loading rollups…"}
68):h.length?e.jsx(K,{ariaLabel:"Daily analytics rollups",rows:h,columns:I,storageKey:"teachmo_admin_analytics_rollup_views",enableCsvExport:!1,height:380,rowHeight:64}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"No events in this range yet."})})]}),e.jsxs(C,{className:"border-[var(--enterprise-border)] bg-[var(--enterprise-surface)]",children:[e.jsx(D,{children:e.jsx(R,{children:"AI Usage Summary"})}),e.jsx(T,{className:"space-y-3 text-sm",children:n.isLoading?e.jsx("div",{className:"text-muted-foreground",children:"Loading AI usage…"}):(L=r==null?void 0:r.byModel)!=null&&L.length?e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex justify-between text-muted-foreground",children:[e.jsx("span",{children:"Total calls"}),e.jsx("span",{children:((k=r==null?void 0:r.totals)==null?void 0:k.calls)??0})]}),e.jsxs("div",{className:"flex justify-between text-muted-foreground",children:[e.jsx("span",{children:"Estimated spend"}),e.jsxs("span",{children:["$",Number(((F=r==null?void 0:r.totals)==null?void 0:F.cost_usd)||0).toFixed(2)]})]}),e.jsx("div",{className:"overflow-auto",children:e.jsxs("table",{className:"min-w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left border-b",children:[e.jsx("th",{className:"p-2",children:"Model"}),e.jsx("th",{className:"p-2",children:"Calls"}),e.jsx("th",{className:"p-2",children:"Tokens"}),e.jsx("th",{className:"p-2",children:"Cost"})]})}),e.jsx("tbody",{children:r.byModel.map(s=>e.jsxs("tr",{className:"border-b",children:[e.jsx("td",{className:"p-2 font-mono text-xs",children:s.model||"—"}),e.jsx("td",{className:"p-2",children:s.calls}),e.jsx("td",{className:"p-2",children:s.tokens??0}),e.jsxs("td",{className:"p-2",children:["$",Number(s.cost_usd||0).toFixed(2)]})]},s.model??"unknown"))})]})})]}):e.jsx("div",{className:"text-muted-foreground",children:"No AI usage recorded yet."})})]}),null,e.jsxs(C,{className:"border-[var(--enterprise-border)] bg-[var(--enterprise-surface)]",children:[e.jsxs(D,{className:"flex flex-row items-center justify-between",children:[e.jsxs(R,{children:["Drilldown ",d?`• ${d}`:""]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(j,{variant:"secondary",onClick:()=>y(null),disabled:!d,children:"Clear"}),e.jsx(j,{onClick:i,disabled:!d||m.isLoading,children:"Export Events CSV"})]})]}),e.jsx(T,{children:d?m.isLoading?e.jsx("div",{className:"text-sm text-muted-foreground",children:"Loading events…"}):p.length?e.jsx(K,{ariaLabel:"Analytics event drilldown",rows:p,columns:V,storageKey:"teachmo_admin_analytics_drilldown_views",enableCsvExport:!1,height:420,rowHeight:64}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"No matching events."}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"Select an event in rollups to drill down."})})]})]})}export{Ae 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.