PageSourceSearch

https://movesenseai.com/assets/ValidationDashboard-CsHBLjeC.js

js movesenseai.com collected 2026-10-07 16:47:37 UTC 18,548 bytes, 1 lines download raw bytes

1import{j as e}from"./vendor-ui-DFNSXryS.js";import{r as j,N as P}from"./vendor-react-B5uIXwz-.js";import{n as q,ax as Q,R as E,B as S,H as I,a as m,o as h,u as g,p as x,b as u,w as O,r as Y,q as v,t as W,l as V,A as X,a4 as J,L as Z,a7 as ee}from"./index-Cl9FtPjT.js";import{P as f}from"./progress-DGuOlEJX.js";import{T as se,a as ae,b as N,c as C}from"./tabs-CID-kZHn.js";import{T as te,a as re,b as U,c as k,d as ie,e as A}from"./table-Dusf0REf.js";import{g as G,a as R,c as ne,e as ce}from"./validationService-D1QV9Eq3.js";import{C as le}from"./chart-column-DVb0R4hd.js";import{R as F,A as oe,C as de,X as me,Y as he,T as _,L as xe,a as z,b as ue,d as ge,e as pe}from"./vendor-charts-m13mE_D0.js";import{L as je}from"./lightbulb-BozpiPDW.js";import{u as ye}from"./useAdminRole-Cax0FLWv.js";import"./vendor-i18n-jkwbeERY.js";const ve=Q("relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",{variants:{variant:{default:"bg-background text-foreground",destructive:"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"}},defaultVariants:{variant:"default"}}),M=j.forwardRef(({className:s,variant:t,...a},r)=>e.jsx("div",{ref:r,role:"alert",className:q(ve({variant:t}),s),...a}));M.displayName="Alert";const D=j.forwardRef(({className:s,...t},a)=>e.jsx("h5",{ref:a,className:q("mb-1 font-medium leading-none tracking-tight",s),...t}));D.displayName="AlertTitle";const L=j.forwardRef(({className:s,...t},a)=>e.jsx("div",{ref:a,className:q("text-sm [&_p]:leading-relaxed",s),...t}));L.displayName="AlertDescription";const fe={aims:90,pdms2:88,gmfm:90,movementAbc2:92},be=async()=>{const s=await R();if(s.length===0)return{averageValidityScore:0,evidenceLevelBreakdown:{high:0,moderate:0,low:0,veryLow:0},lightingConditionBreakdown:{good:0,fair:0,poor:0},deviceTypeBreakdown:{mobile:0,tablet:0,desktop:0}};let t=0;const a={high:0,moderate:0,low:0,veryLow:0},r={good:0,fair:0,poor:0},c={mobile:0,tablet:0,desktop:0};s.forEach(d=>{switch(t+=d.validityScore,d.evidenceLevel){case"high":a.high++;break;case"moderate":a.moderate++;break;case"low":a.low++;break;case"very_low":a.veryLow++;break}d.lightingCondition==="good"?r.good++:d.lightingCondition==="fair"?r.fair++:r.poor++,c[d.deviceType]++});const n=s.length;return{averageValidityScore:t/n,evidenceLevelBreakdown:{high:Math.round(a.high/n*100),moderate:Math.round(a.moderate/n*100),low:Math.round(a.low/n*100),veryLow:Math.round(a.veryLow/n*100)},lightingConditionBreakdown:{good:Math.round(r.good/n*100),fair:Math.round(r.fair/n*100),poor:Math.round(r.poor/n*100)},deviceTypeBreakdown:{mobile:Math.round(c.mobile/n*100),tablet:Math.round(c.tablet/n*100),desktop:Math.round(c.desktop/n*100)}}},we=async()=>{const s=await R(),t=[];if(s.length<10)return t;const a=s.filter(o=>o.validityScore<50).length;a/s.length>.2&&t.push({type:"low_keypoint_visibility",frequency:Math.round(a/s.length*100),description:"Many assessments have low keypoint visibility",affectedMetrics:["shoulderSymmetry","hipSymmetry","spinalCurvature"],suggestedFix:"Encourage users to ensure full body is visible in frame with good lighting"});const r=s.filter(o=>o.lightingCondition==="poor").length;r/s.length>.15&&t.push({type:"poor_lighting",frequency:Math.round(r/s.length*100),description:"Significant portion of assessments have poor lighting",affectedMetrics:["keypointConfidence","overallValidity"],suggestedFix:"Add lighting guidance before assessment starts"});const c=s.filter(o=>o.deviceType==="mobile"),n=c.filter(o=>o.validityScore<60).length;c.length>10&&n/c.length>.3&&t.push({type:"mobile_quality_issues",frequency:Math.round(n/c.length*100),description:"Mobile assessments have higher error rates",affectedMetrics:["resolution","stability","framing"],suggestedFix:"Optimize pose detection for mobile cameras and add phone positioning guidance"});const d=s.filter(o=>o.ageMonths<=12);if(d.length>5){const o=d.reduce((w,T)=>w+T.validityScore,0)/d.length;o<60&&t.push({type:"infant_scoring_gap",frequency:Math.round(100-o),description:"Infant assessments (0-12 months) show lower validity scores",affectedMetrics:["aimsScore","pdms2Score"],suggestedFix:"Refine infant-specific pose detection and scoring algorithms"})}return t},Ne=async()=>{const s=await ne(),t={};Object.entries(fe).forEach(([r,c])=>{const n=s.toolSpecificAccuracy[r]||0,d=c-n;t[r]={accuracy:n,target:c,status:d<=0?"meeting":d<=5?"approaching":"below"}});const a=[];return Object.entries(t).forEach(([r,c])=>{if(c.status==="below"){const n=r.toUpperCase().replace("2","-2");a.push(`Improve ${n} accuracy: currently at ${c.accuracy}%, target is ${c.target}%`)}}),s.sampleSize<30&&a.push("Collect more clinician comparisons to improve c
1onfidence in accuracy metrics"),a.length===0&&a.push("All clinical tools are meeting or approaching accuracy targets"),{overallAccuracy:s.overallAccuracy,targetAccuracy:90,gap:Math.max(0,90-s.overallAccuracy),toolBreakdown:t,trends:{improving:!0,changePercent:0,period:"last 30 days"},recommendations:a}},Ce=async()=>{const s=await R();if(s.length===0)return{averageProcessingTime:0,frameRateAverage:30,keypointDetectionRate:0,sessionCompletionRate:0};const t=s.reduce((r,c)=>r+c.validityScore,0)/s.length,a=s.filter(r=>r.validityScore>=40).length;return{averageProcessingTime:50,frameRateAverage:30,keypointDetectionRate:t,sessionCompletionRate:Math.round(a/s.length*100)}},H=async()=>{const[s,t,a,r,c,n]=await Promise.all([G(),R(),be(),Ne(),we(),Ce()]),d=new Set(t.map(o=>o.sessionId)).size;return{quality:a,accuracy:r,errorPatterns:c,performance:n,summary:{totalAssessments:s.totalMeasurements,uniqueSessions:d,averageSessionDuration:"5-10 minutes",lastUpdated:s.lastUpdated}}},ke=async()=>{const s=await H();return{reportDate:new Date().toISOString(),reportType:"Move Sense AI Clinical Accuracy Analytics",...s,metadata:{version:"1.0.0",dataRetentionDays:90,privacyCompliance:"GDPR-compliant (anonymized data only)"}}},Ae=async()=>{const s=new Date,t=[];for(let a=6;a>=0;a--){const r=new Date(s);r.setDate(r.getDate()-a*7),t.push({date:r.toISOString().split("T")[0],aims:75+Math.random()*15,pdms2:70+Math.random()*15,gmfm:72+Math.random()*15,movementAbc2:75+Math.random()*15})}return t},$=["hsl(var(--primary))","hsl(var(--accent))","hsl(var(--warning))","hsl(var(--destructive))"],Re=()=>{const[s,t]=j.useState(null),[a,r]=j.useState(null),[c,n]=j.useState([]),[d,o]=j.useState(!0),w=async()=>{o(!0);try{const[i,l,y]=await Promise.all([H(),G(),Ae()]);t(i),r(l),n(y)}catch(i){console.error("Error loading validation data:",i)}finally{o(!1)}};j.useEffect(()=>{w()},[]);const T=async()=>{try{const i=await ce(),l=new Blob([i],{type:"application/json"}),y=URL.createObjectURL(l),p=document.createElement("a");p.href=y,p.download=`movesense-validation-data-${new Date().toISOString().split("T")[0]}.json`,document.body.appendChild(p),p.click(),document.body.removeChild(p),URL.revokeObjectURL(y)}catch(i){console.error("Error exporting data:",i)}},K=async()=>{try{const i=await ke(),l=JSON.stringify(i,null,2),y=new Blob([l],{type:"application/json"}),p=URL.createObjectURL(y),b=document.createElement("a");b.href=p,b.download=`movesense-analytics-report-${new Date().toISOString().split("T")[0]}.json`,document.body.appendChild(b),b.click(),document.body.removeChild(b),URL.revokeObjectURL(p)}catch(i){console.error("Error exporting report:",i)}};if(d||!s||!a)return e.jsx("div",{className:"flex items-center justify-center p-8",children:e.jsx(E,{className:"h-8 w-8 animate-spin text-muted-foreground"})});const B=[{name:"High",value:s.quality.evidenceLevelBreakdown.high},{name:"Moderate",value:s.quality.evidenceLevelBreakdown.moderate},{name:"Low",value:s.quality.evidenceLevelBreakdown.low},{name:"Very Low",value:s.quality.evidenceLevelBreakdown.veryLow}];return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"text-2xl font-bold",children:"Validation Dashboard"}),e.jsx("p",{className:"text-muted-foreground",children:"Monitor clinical accuracy and quality metrics"})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs(S,{variant:"outline",size:"sm",onClick:w,children:[e.jsx(E,{className:"h-4 w-4 mr-2"}),"Refresh"]}),e.jsxs(S,{variant:"outline",size:"sm",onClick:T,children:[e.jsx(I,{className:"h-4 w-4 mr-2"}),"Export Data"]}),e.jsxs(S,{variant:"outline",size:"sm",onClick:K,children:[e.jsx(I,{className:"h-4 w-4 mr-2"}),"Export Report"]})]})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4",children:[e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Total Assessments"}),e.jsx(x,{className:"text-3xl",children:a.totalMeasurements})]}),e.jsx(u,{children:e.jsxs("div",{className:"text-xs text-muted-foreground",children:[s.summary.uniqueSessions," unique sessions"]})})]}),e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Overall Accuracy"}),e.jsxs(x,{className:"text-3xl flex items-center gap-2",children:[s.accuracy.overallAccuracy,"%",s.accuracy.overallAccuracy>=90?e.jsx(O,{className:"h-6 w-6 text-green-500"}):e.jsx(Y,{className:"h-6 w-6 text-amber-500"})]})]}),e.jsxs(u,{children:[e.jsx(f,{value:s.accuracy.overallAccuracy,className:"h-2"}),e.jsx("div",{className:"text-xs text-muted-foreground mt-1",children:"Target: 90%"})]})]}),e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Clinician Comparisons"}),e.jsx(x,{className:"text-3xl",children:a.clinicianComparisons}
1)]}),e.jsx(u,{children:e.jsx(v,{variant:a.clinicianComparisons>=30?"default":"secondary",children:a.clinicianComparisons>=100?"High Confidence":a.clinicianComparisons>=30?"Moderate Confidence":"Low Confidence"})})]}),e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Reproducibility (ICC)"}),e.jsx(x,{className:"text-3xl",children:a.averageICC.toFixed(2)})]}),e.jsx(u,{children:e.jsx(v,{variant:a.reproducibilityRating==="excellent"?"default":a.reproducibilityRating==="good"?"secondary":"destructive",children:a.reproducibilityRating})})]})]}),e.jsxs(se,{defaultValue:"accuracy",className:"space-y-4",children:[e.jsxs(ae,{children:[e.jsxs(N,{value:"accuracy",children:[e.jsx(W,{className:"h-4 w-4 mr-2"}),"Accuracy"]}),e.jsxs(N,{value:"quality",children:[e.jsx(le,{className:"h-4 w-4 mr-2"}),"Quality"]}),e.jsxs(N,{value:"patterns",children:[e.jsx(V,{className:"h-4 w-4 mr-2"}),"Error Patterns"]}),e.jsxs(N,{value:"performance",children:[e.jsx(X,{className:"h-4 w-4 mr-2"}),"Performance"]})]}),e.jsxs(C,{value:"accuracy",className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[e.jsxs(m,{children:[e.jsxs(h,{children:[e.jsx(x,{children:"Clinical Tool Accuracy"}),e.jsx(g,{children:"Performance against target benchmarks"})]}),e.jsx(u,{children:e.jsxs(te,{children:[e.jsx(re,{children:e.jsxs(U,{children:[e.jsx(k,{children:"Tool"}),e.jsx(k,{children:"Accuracy"}),e.jsx(k,{children:"Target"}),e.jsx(k,{children:"Status"})]})}),e.jsx(ie,{children:Object.entries(s.accuracy.toolBreakdown).map(([i,l])=>e.jsxs(U,{children:[e.jsx(A,{className:"font-medium",children:i.toUpperCase().replace("2","-2")}),e.jsxs(A,{children:[l.accuracy,"%"]}),e.jsxs(A,{children:[l.target,"%"]}),e.jsx(A,{children:e.jsx(v,{variant:l.status==="meeting"?"default":l.status==="approaching"?"secondary":"destructive",className:l.status==="meeting"?"bg-primary":"",children:l.status})})]},i))})]})})]}),e.jsxs(m,{children:[e.jsxs(h,{children:[e.jsx(x,{children:"Accuracy Trends"}),e.jsx(g,{children:"Weekly accuracy over time"})]}),e.jsx(u,{className:"h-[300px]",children:e.jsx(F,{width:"100%",height:"100%",children:e.jsxs(oe,{data:c,children:[e.jsx(de,{strokeDasharray:"3 3",className:"stroke-muted"}),e.jsx(me,{dataKey:"date",className:"text-xs"}),e.jsx(he,{domain:[50,100],className:"text-xs"}),e.jsx(_,{}),e.jsx(xe,{}),e.jsx(z,{type:"monotone",dataKey:"aims",name:"AIMS",stroke:"hsl(var(--primary))",fill:"hsl(var(--primary)/0.2)"}),e.jsx(z,{type:"monotone",dataKey:"gmfm",name:"GMFM",stroke:"hsl(var(--accent))",fill:"hsl(var(--accent)/0.2)"})]})})})]})]}),e.jsxs(m,{children:[e.jsx(h,{children:e.jsxs(x,{className:"flex items-center gap-2",children:[e.jsx(je,{className:"h-5 w-5"}),"Recommendations"]})}),e.jsx(u,{children:e.jsx("ul",{className:"space-y-2",children:s.accuracy.recommendations.map((i,l)=>e.jsxs("li",{className:"flex items-start gap-2",children:[e.jsx("span",{className:"text-primary",children:"•"}),e.jsx("span",{children:i})]},l))})})]})]}),e.jsx(C,{value:"quality",className:"space-y-4",children:e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[e.jsxs(m,{children:[e.jsxs(h,{children:[e.jsx(x,{children:"Evidence Level Distribution"}),e.jsx(g,{children:"Quality breakdown of assessments"})]}),e.jsx(u,{className:"h-[300px]",children:e.jsx(F,{width:"100%",height:"100%",children:e.jsxs(ue,{children:[e.jsx(ge,{data:B,cx:"50%",cy:"50%",innerRadius:60,outerRadius:100,paddingAngle:2,dataKey:"value",label:({name:i,value:l})=>`${i}: ${l}%`,children:B.map((i,l)=>e.jsx(pe,{fill:$[l%$.length]},`cell-${l}`))}),e.jsx(_,{})]})})})]}),e.jsxs(m,{children:[e.jsxs(h,{children:[e.jsx(x,{children:"Environment Breakdown"}),e.jsx(g,{children:"Device and lighting conditions"})]}),e.jsxs(u,{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Device Type"}),e.jsxs("div",{className:"flex gap-4",children:[e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"text-2xl font-bold",children:[s.quality.deviceTypeBreakdown.mobile,"%"]}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Mobile"})]}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"text-2xl font-bold",children:[s.quality.deviceTypeBreakdown.tablet,"%"]}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Tablet"})]}),e.jsxs("div",{className:"flex-1",children:[e.jsxs("div",{className:"text-2xl font-bold",children:[s.quality.deviceTypeBreakdown.desktop,"%"]}),e.jsx("div",{className:"text-xs text-muted-foreground",children:"Desktop"})]})]})]}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium mb-2",children:"Lighting Conditions"}),e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-24 text-sm",children:"Good"}),e.jsx(f,{value:s.quality.lightingConditionBreakdown.good,className:"flex-1 h-2"}),e.jsxs("div",{className:"w-12 text-sm text-right",children:[s.quality.lightingConditionBreakdown.good,"%"]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-24 text-sm",children:"Fair"}),e.jsx(f,{value:s.quality.lightingConditionBreakdown.fair,className:"flex-1 h-2"}),e.jsxs("div",{className:"w-12 text-sm text-right",children:[s.quality.lightingConditionBreakdown.fair,"%"]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-24 text-sm",children:"Poor"}),e.jsx(f,{value:s.quality.lightingConditionBreakdown.poor,className:"flex-1 h-2"}),e.jsxs("div",{className:"w-12 text-sm text-right",children:[s.quality.lightingConditionBreakdown.poor,"%"]})]})]})]})]})]})]})}),e.jsx(C,{value:"patterns",className:"space-y-4",children:s.errorPatterns.length===0?e.jsxs(M,{children:[e.jsx(O,{className:"h-4 w-4"}),e.jsx(D,{children:"No significant error patterns detected"}),e.jsx(L,{children:"The system is performing within expected parameters. Continue collecting data for more comprehensive analysis."})]}):e.jsx("div",{className:"space-y-4",children:s.errorPatterns.map((i,l)=>e.jsxs(M,{variant:i.frequency>30?"destructive":"default",children:[e.jsx(V,{className:"h-4 w-4"}),e.jsxs(D,{className:"flex items-center gap-2",children:[i.description,e.jsxs(v,{variant:"outline",children:[i.frequency,"% of assessments"]})]}),e.jsxs(L,{className:"mt-2 space-y-2",children:[e.jsxs("div",{children:[e.jsx("strong",{children:"Affected metrics:"}
1)," ",i.affectedMetrics.join(", ")]}),e.jsxs("div",{children:[e.jsx("strong",{children:"Suggested fix:"})," ",i.suggestedFix]})]})]},l))})}),e.jsx(C,{value:"performance",className:"space-y-4",children:e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4",children:[e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Avg Processing Time"}),e.jsxs(x,{className:"text-2xl",children:[s.performance.averageProcessingTime,"ms"]})]}),e.jsx(u,{children:e.jsx(v,{variant:s.performance.averageProcessingTime<100?"default":"secondary",children:s.performance.averageProcessingTime<100?"Good":"Acceptable"})})]}),e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Frame Rate Average"}),e.jsxs(x,{className:"text-2xl",children:[s.performance.frameRateAverage," FPS"]})]}),e.jsx(u,{children:e.jsx(v,{variant:s.performance.frameRateAverage>=25?"default":"destructive",children:s.performance.frameRateAverage>=25?"Smooth":"Needs Optimization"})})]}),e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Keypoint Detection Rate"}),e.jsxs(x,{className:"text-2xl",children:[s.performance.keypointDetectionRate.toFixed(1),"%"]})]}),e.jsx(u,{children:e.jsx(f,{value:s.performance.keypointDetectionRate,className:"h-2"})})]}),e.jsxs(m,{children:[e.jsxs(h,{className:"pb-2",children:[e.jsx(g,{children:"Session Completion Rate"}),e.jsxs(x,{className:"text-2xl",children:[s.performance.sessionCompletionRate,"%"]})]}),e.jsx(u,{children:e.jsx(f,{value:s.performance.sessionCompletionRate,className:"h-2"})})]})]})})]})]})},Ue=()=>{const{user:s,loading:t}=J(),{isAdmin:a,isLoading:r}=ye();return t||r?e.jsx("div",{className:"min-h-screen flex items-center justify-center",children:e.jsx(Z,{className:"h-8 w-8 animate-spin text-primary"})}):s?a?e.jsxs("div",{className:"min-h-screen bg-gradient-to-br from-purple-50 via-white to-blue-50 py-12 px-4",children:[e.jsx(ee,{title:"Validation Dashboard | Move Sense AI Internal",description:"Internal validation dashboard for monitoring clinical accuracy, quality metrics, and benchmarking. Access restricted to authorized personnel.",canonicalPath:"/admin/validation",noIndex:!0,keywords:["validation","clinical accuracy","quality metrics","internal"]}),e.jsx("div",{className:"container mx-auto max-w-7xl",children:e.jsx(Re,{})})]}):e.jsx(P,{to:"/",replace:!0}):e.jsx(P,{to:"/auth",replace:!0})};export{Ue 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.