1import{r as l,j as e}from"./react-vendor-Crdm9tML.js";import{Y as Q,t as W,af as he,ah as C,ai as T,aj as L,ak as D,al as v,S as A,s as B}from"./index-DP4F5GEh.js";import{T as pe,a as ge,b as O,c as w,d as fe,e as _}from"./table-CTWdUCDm.js";import{T as je,a as _e,b as z}from"./tabs-COxby6ki.js";import{D as ke}from"./database-BvJnHv7F.js";import{C as ye}from"./coins-D_t-uHyS.js";import{T as Ne}from"./timer-BsxtmrxY.js";import{R as H,X as Y,Y as F,T as G,Z as be,B as ee}from"./generateCategoricalChart-DFjFx66p.js";import{A as Se}from"./AreaChart-XIdWcn61.js";import{C as V}from"./CartesianGrid-Ds3Z24kS.js";import{A as se}from"./Area-BJVdM1mb.js";import{B as te}from"./BarChart-CiPcHjC0.js";import{f as ae}from"./format-D26AjvIU.js";import{s as we}from"./subDays-DpkGndpk.js";import{e as Ce}from"./es-DzTFRDdD.js";import"./supabase-j14IZ-fx.js";import"./value-DqH_WevB.js";import"./en-US-DEmH3qd1.js";function M(f,k,i,h){const j=h[f];return j?(k*j.in+i*j.out)/1e6:null}const Te={"24h":1,"7d":7,"30d":30,"90d":90};function Ge(){const[f,k]=l.useState("7d"),[i,h]=l.useState([]),[j,oe]=l.useState({}),[r,X]=l.useState(!0),[p,R]=l.useState({}),[re,E]=l.useState(!1),[ne,P]=l.useState(!1),[ie,U]=l.useState(0),[le,q]=l.useState(!1),K=async()=>{X(!0),q(!1);const t=we(new Date,Te[f]).toISOString();try{const[s,a]=await Promise.all([B.from("ai_model_usage").select("organization_id, conversation_id, model_used, model_type, input_tokens, output_tokens, response_time_ms, created_at",{count:"exact"}).gte("created_at",t).order("created_at",{ascending:!1}).limit(5e4),B.from("ai_model_prices").select("model, input_price_per_mtok, output_price_per_mtok")]);if(a.error)console.error("[AdminAIUsage] prices load error:",a.error),E(!0),R({});else{const u={};(a.data??[]).forEach(m=>{m.input_price_per_mtok!=null&&m.output_price_per_mtok!=null&&(u[m.model]={in:Number(m.input_price_per_mtok),out:Number(m.output_price_per_mtok)})}),E(!1),R(u)}const{data:o,error:n,count:g}=s;if(n)console.error("[AdminAIUsage] load error:",n),h([]),P(!1),U(0);else{const u=o||[];h(u),U(g??u.length),P((g??u.length)>u.length);const m=Array.from(new Set((o||[]).map(x=>x.organization_id).filter(Boolean)));if(m.length){const{data:x}=await B.from("organizations").select("id, name").in("id",m),b={};(x||[]).forEach(S=>{b[S.id]=S.name}),oe(b)}}}catch(s){console.error("[AdminAIUsage] load exception:",s),q(!0),h([]),R({}),E(!1),P(!1),U(0)}finally{X(!1)}};l.useEffect(()=>{K()},[f]);const d=l.useMemo(()=>{let t=0,s=0,a=0,o=0,n=0,g=0,u=0,m=0;for(const x of i){const b=x.input_tokens||0,S=x.output_tokens||0;t+=b,s+=S;const J=M(x.model_used,b,S,p);J==null?m++:(a+=J,u++),x.response_time_ms&&(o+=x.response_time_ms,n++),g++}return{inTok:t,outTok:s,totalTok:t+s,cost:a,avgLatency:n?o/n:0,calls:g,rowsPriced:u,rowsUnpriced:m}},[i,p]),c=!r&&d.rowsPriced>0,y=!r&&d.rowsPriced===0,$=re?"No se pudieron cargar las tarifas (error de consulta).":"Costo no disponible: falta cargar tarifas en ai_model_prices.",ce=l.useMemo(()=>{const t=new Map;for(const s of i){const a=ae(new Date(s.created_at),"yyyy-MM-dd"),o=t.get(a)||{date:a,tokens:0,cost:0,calls:0,latSum:0,latN:0},n=s.input_tokens||0,g=s.output_tokens||0;o.tokens+=n+g,o.cost+=M(s.model_used,n,g,p)??0,o.calls++,s.response_time_ms&&(o.latSum+=s.response_time_ms,o.latN++),t.set(a,o)}return Array.from(t.values()).map(s=>({...s,avgLatency:s.latN?Math.round(s.latSum/s.latN):0,cost:+s.cost.toFixed(4)})).sort((s,a)=>s.date.localeCompare(a.date))},[i,p]),N=l.useMemo(()=>{const t=new Map;for(const s of i){if(!s.organization_id)continue;const a=t.get(s.organization_id)||{orgId:s.organization_id,tokens:0,cost:0,calls:0,latSum:0,latN:0},o=s.input_tokens||0,n=s.output_tokens||0;a.tokens+=o+n,a.cost+=M(s.model_used,o,n,p)??0,a.calls++,s.response_time_ms&&(a.latSum+=s.response_time_ms,a.latN++),t.set(s.organization_id,a)}return Array.from(t.values()).map(s=>({...s,name:j[s.orgId]||s.orgId.slice(0,8),avgLatency:s.latN?Math.round(s.latSum/s.latN):0,cost:+s.cost.toFixed(4)})).sort((s,a)=>c?a.cost-s.cost:a.tokens-s.tokens)},[i,j,p,c]),de=l.useMemo(()=>{const t=new Map;for(const s of i){const a=t.get(s.model_used)||{model:s.model_used,tokens:0,cost:0,calls:0}
1,o=s.input_tokens||0,n=s.output_tokens||0;a.tokens+=o+n,a.cost+=M(s.model_used,o,n,p)??0,a.calls++,t.set(s.model_used,a)}return Array.from(t.values()).map(s=>({...s,cost:+s.cost.toFixed(4)})).sort((s,a)=>a.tokens-s.tokens)},[i,p]),Z=!r&&d.rowsPriced>0&&d.rowsUnpriced>0,me=Z?"Costo parcial (USD)":"Costo (USD)",ue=r?null:y?"â":`$${d.cost.toFixed(3)}`,xe=r?"":y?$:Z?`${d.rowsUnpriced} de ${d.calls} llamadas sin tarifa â el costo real es mayor`:`${d.calls.toLocaleString("es-CL")} llamadas`;return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex flex-col gap-3 md:flex-row md:items-end md:justify-between",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"flex items-center gap-2 text-3xl font-bold",children:[e.jsx(Q,{className:"h-7 w-7 text-primary"}),"Consumo de IA"]}),e.jsx("p",{className:"text-muted-foreground",children:"Tokens y latencia por organización. El costo se muestra solo para modelos con tarifa cargada en ai_model_prices."}),le&&e.jsxs("div",{className:"mt-2 flex items-center gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive",children:[e.jsx("span",{children:"No se pudieron cargar los datos."}),e.jsx(W,{variant:"outline",size:"sm",onClick:K,disabled:r,children:"Reintentar"})]}),ne&&e.jsx("span",{title:`Datos incompletos: se muestran ${i.length.toLocaleString("es-CL")} de ${ie.toLocaleString("es-CL")} filas. El consumo real es mayor.`,className:"mt-2 inline-flex items-center gap-1 rounded-full bg-amber-500/15 px-2 py-0.5 text-xs font-medium text-amber-600 dark:text-amber-400 cursor-help",children:"â datos incompletos"})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(je,{value:f,onValueChange:t=>k(t),children:e.jsxs(_e,{children:[e.jsx(z,{value:"24h",children:"24h"}),e.jsx(z,{value:"7d",children:"7d"}),e.jsx(z,{value:"30d",children:"30d"}),e.jsx(z,{value:"90d",children:"90d"})]})}),e.jsx(W,{variant:"outline",size:"icon",onClick:K,disabled:r,children:e.jsx(he,{className:`h-4 w-4 ${r?"animate-spin":""}`})})]})]}),e.jsxs("div",{className:"grid gap-4 md:grid-cols-2 lg:grid-cols-4",children:[e.jsx(I,{icon:e.jsx(ke,{className:"h-4 w-4"}),label:"Tokens totales",value:r?null:d.totalTok.toLocaleString("es-CL"),hint:r?"":`${d.inTok.toLocaleString("es-CL")} in · ${d.outTok.toLocaleString("es-CL")} out`}),e.jsx(I,{icon:e.jsx(ye,{className:"h-4 w-4"}),label:me,value:ue,hint:xe}),e.jsx(I,{icon:e.jsx(Ne,{className:"h-4 w-4"}),label:"Latencia promedio",value:r?null:`${Math.round(d.avgLatency)} ms`,hint:"Tiempo respuesta IA"}),e.jsx(I,{icon:e.jsx(Q,{className:"h-4 w-4"}),label:"Orgs activas",value:r?null:N.length.toString(),hint:"Con consumo en el rango"})]}),e.jsxs(C,{children:[e.jsxs(T,{children:[e.jsx(L,{children:c?"Tokens y costo por dÃa":"Tokens por dÃa"}),e.jsxs(D,{children:["Consumo agregado de todas las organizaciones.",y&&` ${$}`]})]}),e.jsx(v,{className:"h-[320px]",children:r?e.jsx(A,{className:"h-full w-full"}):e.jsx(H,{width:"100%",height:"100%",children:e.jsxs(Se,{data:ce,children:[e.jsx(V,{strokeDasharray:"3 3",className:"opacity-30"}),e.jsx(Y,{dataKey:"date",tickFormatter:t=>ae(new Date(t),"dd MMM",{locale:Ce})}),e.jsx(F,{yAxisId:"left",tickFormatter:t=>t>=1e3?`${(t/1e3).toFixed(0)}k`:t}),c&&e.jsx(F,{yAxisId:"right",orientation:"right",tickFormatter:t=>`$${t.toFixed(2)}`}),e.jsx(G,{formatter:(t,s)=>s==="cost"?`$${Number(t).toFixed(4)}`:Number(t).toLocaleString("es-CL")}),e.jsx(be,{}
1),e.jsx(se,{yAxisId:"left",type:"monotone",dataKey:"tokens",stroke:"hsl(var(--primary))",fill:"hsl(var(--primary) / 0.2)",name:"Tokens"}),c&&e.jsx(se,{yAxisId:"right",type:"monotone",dataKey:"cost",stroke:"hsl(var(--accent))",fill:"hsl(var(--accent) / 0.2)",name:"Costo (USD)"})]})})})]}),e.jsxs("div",{className:"grid gap-4 lg:grid-cols-2",children:[e.jsxs(C,{children:[e.jsxs(T,{children:[e.jsx(L,{children:c?"Top organizaciones por costo":"Top organizaciones por tokens"}),e.jsxs(D,{children:["Las 10 organizaciones que más consumen.",y&&` ${$}`]})]}),e.jsx(v,{className:"h-[320px]",children:r?e.jsx(A,{className:"h-full w-full"}):N.length===0?e.jsx("div",{className:"flex h-full items-center justify-center text-sm text-muted-foreground",children:"Sin consumo en el rango."}):e.jsx(H,{width:"100%",height:"100%",children:e.jsxs(te,{data:N.slice(0,10),layout:"vertical",margin:{left:60},children:[e.jsx(V,{strokeDasharray:"3 3",className:"opacity-30"}),e.jsx(Y,{type:"number",tickFormatter:t=>c?`$${t.toFixed(2)}`:t>=1e3?`${(t/1e3).toFixed(0)}k`:String(t)}),e.jsx(F,{type:"category",dataKey:"name",width:120,tick:{fontSize:11}}),e.jsx(G,{formatter:t=>c?`$${Number(t).toFixed(4)}`:Number(t).toLocaleString("es-CL")}),e.jsx(ee,{dataKey:c?"cost":"tokens",fill:"hsl(var(--primary))",radius:[0,4,4,0]})]})})})]}),e.jsxs(C,{children:[e.jsxs(T,{children:[e.jsx(L,{children:"Distribución por modelo"}),e.jsx(D,{children:"Tokens por modelo de IA usado."})]}),e.jsx(v,{className:"h-[320px]",children:r?e.jsx(A,{className:"h-full w-full"}):e.jsx(H,{width:"100%",height:"100%",children:e.jsxs(te,{data:de,children:[e.jsx(V,{strokeDasharray:"3 3",className:"opacity-30"}),e.jsx(Y,{dataKey:"model",tick:{fontSize:10},interval:0,angle:-15,textAnchor:"end",height:70}),e.jsx(F,{tickFormatter:t=>t>=1e3?`${(t/1e3).toFixed(0)}k`:t}),e.jsx(G,{}),e.jsx(ee,{dataKey:"tokens",fill:"hsl(var(--accent))",radius:[4,4,0,0]})]})})})]})]}),e.jsxs(C,{children:[e.jsxs(T,{children:[e.jsx(L,{children:"Detalle por organización"}),e.jsxs(D,{children:["Tokens",c?", costo":""," y latencia agregados en el rango seleccionado.",y&&` ${$}`]})]}),e.jsx(v,{children:r?e.jsx(A,{className:"h-64 w-full"}):e.jsxs(pe,{children:[e.jsx(ge,{children:e.jsxs(O,{children:[e.jsx(w,{children:"Organización"}),e.jsx(w,{className:"text-right",children:"Llamadas"}),e.jsx(w,{className:"text-right",children:"Tokens"}),c&&e.jsx(w,{className:"text-right",children:"Costo (USD)"}),e.jsx(w,{className:"text-right",children:"Latencia prom."})]})}),e.jsx(fe,{children:N.length===0?e.jsx(O,{children:e.jsx(_,{colSpan:c?5:4,className:"text-center text-muted-foreground py-8",children:"Sin consumo en el rango."})}):N.map(t=>e.jsxs(O,{children:[e.jsx(_,{className:"font-medium",children:t.name}),e.jsx(_,{className:"text-right tabular-nums",children:t.calls.toLocaleString("es-CL")}),e.jsx(_,{className:"text-right tabular-nums",children:t.tokens.toLocaleString("es-CL")}),c&&e.jsxs(_,{className:"text-right tabular-nums",children:["$",t.cost.toFixed(4)]}),e.jsxs(_,{className:"text-right tabular-nums",children:[t.avgLatency," ms"]})]},t.orgId))})]})})]})]})}function I({icon:f,label:k,value:i,hint:h}){return e.jsxs(C,{children:[e.jsxs(T,{className:"flex flex-row items-center justify-between space-y-0 pb-2",children:[e.jsx(L,{className:"text-sm font-medium text-muted-foreground",children:k}),e.jsx("span",{className:"text-muted-foreground",children:f})]}),e.jsxs(v,{children:[i===null?e.jsx(A,{className:"h-7 w-24"}):e.jsx("div",{className:"text-2xl font-bold",children:i}),h&&e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:h})]})]})}export{Ge 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.