Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
},
{
"files": [
"src/app/components/factorSlider/FactorSlider.tsx"
"src/app/components/factorSlider/FactorSlider.tsx",
"src/app/context/AssessmentContext.tsx"
],
"rules": {
"react-refresh/only-export-components": "off"
Expand Down
Binary file modified public/screenshot_assessment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/screenshot_calc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 9 additions & 6 deletions src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@

import { Route, Routes } from 'react-router-dom';
import Home from './pages/home';
import { AssessmentProvider } from './context/AssessmentContext';

export function App() {
return (
<Routes>
<Route
path="/"
Component={Home}
/>
</Routes>
<AssessmentProvider>
<Routes>
<Route
path="/"
Component={Home}
/>
</Routes>
</AssessmentProvider>
);
}

Expand Down
8 changes: 6 additions & 2 deletions src/app/components/calculator/Calculator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ export interface CalculatorProps {
}

function Calculator(props: CalculatorProps) {

const [factors, setFactors] = useState<Factor[]>(initialFactors || []); // Add fallback
const [factors, setFactors] = useState<Factor[]>(() => (initialFactors || []).map(f => ({ ...f })));

const handleScoreChange = (index: number, value: number) => {
const newFactors = [...factors];
Expand All @@ -27,6 +26,10 @@ function Calculator(props: CalculatorProps) {
}
};

const handleAssessmentReset = () => {
setFactors((initialFactors || []).map(f => ({ ...f })));
};

const calculateCompositeScore = () => {
return (factors || []).reduce((acc, factor) => acc + factor.score * factor.weight, 0);
};
Expand Down Expand Up @@ -73,6 +76,7 @@ function Calculator(props: CalculatorProps) {
<CompositeScoreDisplay
score={calculateCompositeScore()}
onQuestionnaireScoreUpdate={handleQuestionnaireScoreUpdate}
onAssessmentReset={handleAssessmentReset}
/>

{/* Maturity Level Indicator */}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render } from '@testing-library/react';
import { render } from '../../../test-utils';

import CompositeScoreDisplay from './CompositeScoreDisplay';

Expand Down
114 changes: 47 additions & 67 deletions src/app/components/compositeScoreDisplay/CompositeScoreDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,41 @@ import React, { useState } from 'react';
import { Button } from 'primereact/button';
import styles from './CompositeScoreDisplay.module.scss';
import QuestionnaireDialog from '../questionnaire/QuestionnaireDialog';

interface AssessmentResult {
sectionName: string;
score: number;
maxScore: number;
answers: { questionId: string; questionText: string; selectedAnswer: string; pointsEarned: number; maxPoints: number }[];
completedAt: Date;
}
import { useAssessmentContext } from '../../context/AssessmentContext';

export interface CompositeScoreDisplayProps {
score: number;
onQuestionnaireScoreUpdate?: (sectionIndex: number, score: number) => void;
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing documentation: The new onAssessmentReset prop is not documented. Consider adding JSDoc comments to explain when this callback is invoked and what it should do, especially since it's optional and may not be obvious to consumers of this component.

Suggested change
onQuestionnaireScoreUpdate?: (sectionIndex: number, score: number) => void;
onQuestionnaireScoreUpdate?: (sectionIndex: number, score: number) => void;
/**
* Optional callback invoked after the assessment has been reset and the
* guided assessment dialog has been closed. Use this to perform any
* additional cleanup or UI updates when starting a new assessment.
*/

Copilot uses AI. Check for mistakes.
/**
* Optional callback invoked after the guided assessment has been reset
* and the assessment dialog has been closed. Use this to perform any
* additional cleanup or UI updates when starting a new assessment.
*/
onAssessmentReset?: () => void;
'data-testid'?: string;
}

const CompositeScoreDisplay: React.FC<CompositeScoreDisplayProps> = ({ score, onQuestionnaireScoreUpdate }) => {
const CompositeScoreDisplay: React.FC<CompositeScoreDisplayProps> = ({
score,
onQuestionnaireScoreUpdate,
onAssessmentReset,
'data-testid': testId,
}) => {
const [open, setOpen] = useState(false);
const [assessmentResults, setAssessmentResults] = useState<AssessmentResult[]>([]);
const [isAssessmentComplete, setIsAssessmentComplete] = useState(false);

const handleAssessmentComplete = (results: AssessmentResult[]) => {
setAssessmentResults(results);
setIsAssessmentComplete(true);
};

const generateQuickReport = () => {
if (!isAssessmentComplete) return;

const totalScore = assessmentResults.reduce((sum, result) => sum + result.score, 0);
const maxTotalScore = assessmentResults.length * 25;
const overallPercentage = (totalScore / maxTotalScore) * 100;

let maturityLevel = 'Developing';
if (overallPercentage >= 80) maturityLevel = 'Expert';
else if (overallPercentage >= 65) maturityLevel = 'Advanced';
else if (overallPercentage >= 45) maturityLevel = 'Intermediate';
else if (overallPercentage >= 25) maturityLevel = 'Basic';

const quickReport = `# PolydraIQ™ Assessment Summary

**Overall Score:** ${totalScore.toFixed(1)} / ${maxTotalScore} (${overallPercentage.toFixed(1)}%)
**Maturity Level:** ${maturityLevel}
**Completed:** ${new Date().toLocaleDateString()}

${assessmentResults.map(result => `
**${result.sectionName}:** ${result.score.toFixed(1)}/25 (${((result.score / 25) * 100).toFixed(1)}%)`).join('')}

*Complete assessment details available in the Guided Assessment dialog.*`;
const [dialogKey, setDialogKey] = useState(0);
const { isAssessmentComplete, printReport, resetAssessment } = useAssessmentContext();

const printWindow = window.open('', '_blank');
if (printWindow) {
printWindow.document.write(`
<html>
<head>
<title>PolydraIQ Assessment Summary</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 40px auto; padding: 20px; }
h1 { color: #0ea5e9; border-bottom: 2px solid #0ea5e9; padding-bottom: 10px; }
strong { color: #1f2937; }
@media print { body { margin: 0; padding: 15px; } }
</style>
</head>
<body>
<pre style="white-space: pre-wrap; font-family: inherit;">${quickReport.replace(/\\*/g, '').replace(/#/g, '')}</pre>
</body>
</html>
`);
printWindow.document.close();
printWindow.print();
const handleResetAssessment = () => {
resetAssessment();
setDialogKey((k) => k + 1);
setOpen(false);
if (onAssessmentReset) {
onAssessmentReset();
}
};

return (
<div className="composite-score">
<div className="composite-score" data-testid={testId}>
<div style={{ display: 'flex', gap: '16px', justifyContent: 'center', alignItems: 'center', marginBottom: '20px' }}>
<Button
icon="pi pi-compass"
Expand All @@ -88,12 +50,12 @@ ${assessmentResults.map(result => `
icon="pi pi-file-pdf"
size="small"
rounded
label="Quick Report"
label="Print Report"
outlined={!isAssessmentComplete}
severity={isAssessmentComplete ? "success" : "secondary"}
disabled={!isAssessmentComplete}
onClick={generateQuickReport}
tooltip={isAssessmentComplete ? "Generate printable summary" : "Complete the Guided Assessment first"}
onClick={printReport}
tooltip={isAssessmentComplete ? "Print assessment report" : "Complete the Guided Assessment first"}
style={{
opacity: isAssessmentComplete ? 1 : 0.7,
borderWidth: isAssessmentComplete ? '2px' : '1px'
Expand All @@ -102,15 +64,33 @@ ${assessmentResults.map(result => `
</div>
<h2>Composite Quality Score: {score.toFixed(2)}</h2>
{isAssessmentComplete && (
<div style={{ color: '#059669', fontSize: '14px', fontWeight: '500' }}>
✅ Assessment Completed - Detailed report available in Guided Assessment
<div style={{ marginTop: '4px' }}>
<div style={{ color: '#059669', fontSize: '14px', fontWeight: 500 }}>
✅ Assessment Completed - Detailed report available in Guided Assessment
</div>
<button
type="button"
onClick={handleResetAssessment}
style={{
marginTop: '4px',
padding: 0,
border: 'none',
background: 'none',
color: '#2563eb',
fontSize: '12px',
cursor: 'pointer',
textDecoration: 'underline',
}}
>
Start a new assessment
</button>
</div>
)}
<QuestionnaireDialog
key={dialogKey}
open={open}
onClose={() => setOpen(false)}
onScoreUpdate={onQuestionnaireScoreUpdate}
onAssessmentComplete={handleAssessmentComplete}
/>
</div>
);
Expand Down
Loading