diff --git a/package.json b/package.json index bc2b957736..60d1c5cddd 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "generate-schemas": "yarn generate-schema:StudyConfig && yarn generate-schema:GlobalConfig && yarn generate-schema:LibraryConfig", "test": "playwright test", "unittest": "vitest", - "preinstall": "node -e 'if(!/yarn\\.js$/.test(process.env.npm_execpath))throw new Error(\"Use yarn\")'", + "preinstall": "node -e \"if(!/yarn\\.js$/.test(process.env.npm_execpath))throw new Error('Use yarn')\"", "postinstall": "husky" }, "lint-staged": { diff --git a/src/analysis/individualStudy/stats/StatsView.tsx b/src/analysis/individualStudy/stats/StatsView.tsx index 0ff52ceb3a..6f3c786066 100644 --- a/src/analysis/individualStudy/stats/StatsView.tsx +++ b/src/analysis/individualStudy/stats/StatsView.tsx @@ -10,6 +10,11 @@ import { StudyConfig } from '../../../parser/types'; import { TrialVisualization } from './TrialVisualization'; import { ComponentBlockWithOrderPath, StepsPanel } from '../../../components/interface/StepsPanel'; import { addPathToComponentBlock } from '../../../utils/getSequenceFlatMap'; +import { OverviewStats } from '../summary/OverviewStats'; +import { + calculateParticipantCounts, calculateCorrectnessStats, calculateTimeStats, calculateDateStats, calculateComponentStats, +} from '../summary/utils'; +import { OverviewData } from '../../types'; export function StatsView( { @@ -29,9 +34,52 @@ export function StatsView( const { trialId } = useParams(); + const filteredParticipants = useMemo(() => { + // Before selecting a trial, show all participants + if (!trialId || trialId === 'end') return visibleParticipants; + + return visibleParticipants + .map((p) => { + const filteredAnswers = Object.fromEntries( + Object.entries(p.answers).filter(([key]) => { + const parts = key.split('_'); + const component = parts.length === 4 ? parts[2] : parts[0]; + return component === trialId; + }), + ); + return { ...p, answers: filteredAnswers } as ParticipantData; + }) + .filter((p) => Object.keys(p.answers).length > 0); + }, [visibleParticipants, trialId]); + + const overviewData = useMemo(() => { + if (filteredParticipants.length === 0) return null; + + const participantCounts = calculateParticipantCounts(filteredParticipants); + const { avgTime, avgCleanTime } = calculateTimeStats(filteredParticipants); + const { startDate, endDate } = calculateDateStats(filteredParticipants); + const correctnessStats = calculateCorrectnessStats(filteredParticipants); + + // Calculate component data + const componentStats = calculateComponentStats(visibleParticipants); + const componentData = componentStats.map((stat) => ({ + component: stat.name, + participants: stat.participantCount, + avgTime: Number.isFinite(stat.avgTime) ? `${stat.avgTime.toFixed(1)}s` : 'N/A', + avgCleanTime: Number.isFinite(stat.avgCleanTime) ? `${stat.avgCleanTime.toFixed(1)}s` : 'N/A', + correctness: !Number.isNaN(stat.correctness) ? `${stat.correctness.toFixed(1)}%` : 'N/A', + })); + + return { + participantCounts, avgTime, avgCleanTime, startDate, endDate, correctnessStats, componentData, + }; + }, [filteredParticipants, studyConfig, trialId]); + return ( - - { + <> + + + { (visibleParticipants.length === 0) ? ( @@ -52,6 +100,7 @@ export function StatsView( ) } - + + ); } diff --git a/src/public/libraries/virtual-chinrest/assets/ViewingDistanceCalibration.tsx b/src/public/libraries/virtual-chinrest/assets/ViewingDistanceCalibration.tsx index a5dc622062..0c07a55a5e 100644 --- a/src/public/libraries/virtual-chinrest/assets/ViewingDistanceCalibration.tsx +++ b/src/public/libraries/virtual-chinrest/assets/ViewingDistanceCalibration.tsx @@ -171,14 +171,14 @@ export default function ViewingDistanceCalibration({ parameters, setAnswer }: St - Put your left hand on the + Put your left hand on the  space bar . Cover your right eye with your right hand. Using your left eye, focus on the black square. Keep your focus on the black square. - The + The  red ball {' '} will disappear as it moves from right to left.