1import{r as k,j as e}from"./vendor-react-DKbe1Umz.js";import{u as T}from"./vendor-data-CjSe2wkl.js";import{S as A,g as C,h as S,i as D,j as x,aG as E,C as t,s as c,w as r,t as d,v as l,l as w}from"./index-DZ74KeH3.js";import{au as L,d9 as R,c$ as O,da as B,az as g,ay as f,J as I}from"./vendor-ui-DMaW9-Ov.js";import{R as h,B as y,C as m,X as j,Y as u,T as p,a as N,b as P,L as W,d as b}from"./vendor-charts-DWX9qznF.js";import{f as z}from"./vendor-dates-5UNeOyFW.js";import"./vendor-forms-C2vWC2ab.js";const K=48*60*60*1e3,$={WRONG_TRADE:"Wrong trade",DUPLICATE:"Duplicate",NOT_PRESENT:"Not present",WRONG_QUANTITY:"Wrong quantity",WRONG_LOCATION:"Wrong location",OTHER:"Other"};function _(i){return`${Math.round(i*100)}%`}function Q(){const[i,o]=k.useState("30"),{data:s,isLoading:n}=T({queryKey:[`/api/ai-features/takeoff/accuracy?days=${i}`]});return e.jsxs("div",{className:"container mx-auto p-6 space-y-6","data-testid":"page-ai-takeoff-accuracy",children:[e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"text-2xl font-bold flex items-center gap-2",children:[e.jsx(L,{className:"w-6 h-6 text-primary"}),"AI Takeoff Accuracy"]}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"How often your team accepts the AI's detected items, broken down by trade and confidence."})]}),e.jsx("div",{className:"w-44",children:e.jsxs(A,{value:i,onValueChange:o,children:[e.jsx(C,{"data-testid":"select-window-days",children:e.jsx(S,{})}),e.jsxs(D,{children:[e.jsx(x,{value:"7",children:"Last 7 days"}),e.jsx(x,{value:"30",children:"Last 30 days"}),e.jsx(x,{value:"90",children:"Last 90 days"}),e.jsx(x,{value:"180",children:"Last 180 days"})]})]})})]}),n&&e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-4",children:[0,1,2,3].map(a=>e.jsx(E,{className:"h-28 w-full"},a))}),!n&&s&&e.jsxs(e.Fragment,{children:[e.jsx(G,{lastCalibratedAt:s.lastCalibratedAt}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-4 gap-4",children:[e.jsxs(t,{"data-testid":"card-overall-accuracy",children:[e.jsxs(c,{className:"pb-2",children:[e.jsx(r,{children:"Overall accuracy"}),e.jsx(d,{className:"text-3xl",children:_(s.accuracy)})]}),e.jsx(l,{children:e.jsxs("p",{className:"text-xs text-muted-foreground",children:[s.reviewed," reviewed of ",s.totals.PENDING+s.reviewed," total"]})})]}),e.jsx(t,{"data-testid":"card-totals-accepted",children:e.jsxs(c,{className:"pb-2",children:[e.jsxs(r,{className:"flex items-center gap-1.5",children:[e.jsx(R,{className:"w-3.5 h-3.5"})," Accepted"]}),e.jsx(d,{className:"text-3xl",children:s.totals.ACCEPTED})]})}),e.jsx(t,{"data-testid":"card-totals-edited",children:e.jsxs(c,{className:"pb-2",children:[e.jsxs(r,{className:"flex items-center gap-1.5",children:[e.jsx(O,{className:"w-3.5 h-3.5"})," Edited"]}),e.jsx(d,{className:"text-3xl",children:s.totals.EDITED})]})}),e.jsx(t,{"data-testid":"card-totals-rejected",children:e.jsxs(c,{className:"pb-2",children:[e.jsxs(r,{className:"flex items-center gap-1.5",children:[e.jsx(B,{className:"w-3.5 h-3.5"})," Rejected"]}),e.jsx(d,{className:"text-3xl",children:s.totals.REJECTED})]})})]}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[e.jsxs(t,{children:[e.jsxs(c,{children:[e.jsxs(d,{className:"text-base flex items-center gap-2",children:[e.jsx(g,{className:"w-4 h-4"}),"Accuracy by trade"]}),e.jsx(r,{children:"Accepted + edited as a share of reviewed."})]}),e.jsx(l,{children:s.accuracyByTrade.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No reviewed suggestions in the selected window."}):e.jsx("div",{className:"h-72",children:e.jsx(h,{width:"100%",height:"100%",children:e.jsxs(y,{data:s.accuracyByTrade.map(a=>({...a,accuracyPct:Math.round(a.accuracy*100)})),children:[e.jsx(m,{strokeDasharray:"3 3"}),e.jsx(j,{dataKey:"tradeType",tick:{fontSize:10},interval:0,angle:-30,textAnchor:"end",height:70}),e.jsx(u,{tick:{fontSize:10},domain:[0,100],unit:"%"}),e.jsx(p,{formatter:a=>`${a}%`}),e.jsx(N,{dataKey:"accuracyPct",fill:"#7c3aed",name:"Accuracy"})]})})})})]}),e.jsxs(t,{children:[e.jsxs(c,{children:[e.jsxs(d,{className:"text-base flex items-center gap-2",children:[e.jsx(g,{className:"w-4 h-4"}),"Accuracy by reported confidence"]}),e.jsx(r,{children:"How well each model-reported confidence bucket maps to acceptance. Used by the nightly calibration job."})]}),e.jsx(l,{children:e.jsx("div",{className:"h-72",children:e.jsx(h,{width:"100%",height:"100%",children:e.jsxs(y,{data:s.accuracyByBucket.map(a=>({bucket:`${a.bucket}-${a.bucket+9}%`,accuracyPct:Math.round(a.accuracy*100),reviewed:a.accepted+a.edited+a.rejected})),children:[e.jsx(m,{strokeDasharray:"3 3"}),e.jsx(j,{dataKey:"bucket",tick:{fontSize:11}}),e.jsx(u,{tick:{fontSize:10},domain:[0,100],unit:"%"}),e.jsx(p,{formatter:a=>`${a}%`}),e.jsx(N,{dataKey:"accuracyPct",fill:"#0ea5e9",name:"Accuracy"})]})})})})]})]}),e.jsxs(t,{children:[e.jsxs(c,{children:[e.jsxs(d,{className:"text-base flex items-center gap-2",children:[e.jsx(f,{className:"w-4 h-4"}),"Daily review trend"]}),e.jsx(r,{children:"Accepted (incl. edited) vs. rejected per day."})]}),e.jsx(l,{children:s.trend.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No reviewed suggestions in the selected window."}):e.jsx("div",{className:"h-64",children:e.jsx(h,{width:"100%",height:"100%",children:e.jsxs(P,{data:s.trend,children:[e.jsx(m,{strokeDasharray:"3 3"}),e.jsx(j,{dataKey:"day",tick:{fontSize:10}}),e.jsx(u,{tick:{fontSize:10}}),e.jsx(p,{}),e.jsx(W,{}),e.jsx(b,{type:"monotone",dataKey:"accepted",stroke:"#16a34a",strokeWidth:2,name:"Accepted (incl. edited)"}),e.jsx(b,{type:"monotone",dataKey:"rejected",stroke:"#ef4444",strokeWidth:2,name:"Rejected"})]})})})})]}),e.jsxs(t,{children:[e.jsxs(c,{children:[e.jsx(d,{className:"text-base",children:"Top rejection reasons"}),e.jsx(r,{children:"Why the team most often turns down a suggestion."})]}),e.jsx(l,{children:s.rejectReasons.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No rejections in the selected window."}):e.jsx("div",{className:"space-y-2",children:s.rejectReasons.map(a=>e.jsxs("div",{className:"flex items-center justify-between","data-testid":`row-reason-${a.reason}`,children:[e.jsx("span",{className:"text-sm",children:$[a.reason]??a.reason}),e.jsx(w,{variant:"secondary",children:a.count})]},a.reason))})})]})]})]})}function G({lastCalibratedAt:i}){if(!i)return e.jsx(t,{className:"border-dashed","data-testid":"card-calibration-freshness",children:e.jsxs(l,{className:"py-3 px-4 flex items-center gap-2 text-sm text-muted-foreground",children:[e.jsx(f,{className:"w-4 h-4"}),e.jsx("span",{"data-testid":"text-calibration-status",children:"Confidence calibration has not run yet for this organization."})]})});const o=new Date(i),n=Date.now()-o.getTime()>K,a=z(o,{addSuffix:!0}
1),v=o.toLocaleString();return e.jsx(t,{className:n?"border-amber-300 bg-amber-50 dark:border-amber-800 dark:bg-amber-950/40":void 0,"data-testid":"card-calibration-freshness",children:e.jsxs(l,{className:"py-3 px-4 flex items-center justify-between gap-3 text-sm",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[n?e.jsx(I,{className:"w-4 h-4 text-amber-600 dark:text-amber-400","aria-hidden":"true"}):e.jsx(f,{className:"w-4 h-4 text-muted-foreground","aria-hidden":"true"}),e.jsxs("span",{className:n?"text-amber-800 dark:text-amber-200":"text-muted-foreground","data-testid":"text-calibration-status",children:["Confidence calibration last updated"," ",e.jsx("span",{className:"font-medium",title:v,"data-testid":"text-calibration-relative",children:a}),"."]})]}),n&&e.jsx(w,{variant:"outline",className:"border-amber-400 text-amber-800 dark:border-amber-600 dark:text-amber-200","data-testid":"badge-calibration-stale",children:"Stale"})]})})}export{Q 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.