Varun10000's picture
Upload 6 files
4207c0e verified
Raw
History Blame Contribute Delete
37.6 kB
const { useState, useMemo, useEffect, createElement: h } = React;
function getOrdinalSuffix(day) {
const mod100 = day % 100;
if (mod100 >= 11 && mod100 <= 13) return 'th';
switch (day % 10) {
case 1: return 'st';
case 2: return 'nd';
case 3: return 'rd';
default: return 'th';
}
}
function formatAssessmentDate(date) {
const day = date.getDate();
const month = date.toLocaleString(undefined, { month: 'long' });
const year = date.getFullYear();
return `${day}${getOrdinalSuffix(day)} ${month} ${year}`;
}
function getDefaultAssessmentName(now = new Date()) {
return `LVMWD - ${formatAssessmentDate(now)}`;
}
// Helper function to get maturity level based on score
function getMaturityLevel(score) {
if (score >= 80) return { name: 'Leading', color: '#3b82f6' };
if (score >= 60) return { name: 'Scaling', color: '#22c55e' };
if (score >= 40) return { name: 'Adopting', color: '#eab308' };
if (score >= 20) return { name: 'Experimenting', color: '#f97316' };
return { name: 'Curious/Aware', color: '#ef4444' };
}
function AssessmentTool() {
const [responses, setResponses] = useState({});
const [notes, setNotes] = useState({});
const [expandedSections, setExpandedSections] = useState(
Object.fromEntries(assessmentData.dimensions.map((_, i) => [i, false]))
);
const [showSaveDialog, setShowSaveDialog] = useState(false);
const [showViewSaved, setShowViewSaved] = useState(false);
const [organizationName, setOrganizationName] = useState('');
const [assessmentName, setAssessmentName] = useState(getDefaultAssessmentName());
const [activeQuestionId, setActiveQuestionId] = useState(null);
const [showValidationErrors, setShowValidationErrors] = useState(false);
const [autosaveState, setAutosaveState] = useState({
status: 'Saved',
lastUpdated: null
});
const draftStorageKey = 'aiMaturityAssessmentDraftV1';
const questionIndex = useMemo(() => {
const order = [];
const indexById = {};
assessmentData.dimensions.forEach((dimension, dimIndex) => {
dimension.questions.forEach((question, qIndex) => {
const globalIndex = order.length;
order.push(question.id);
indexById[question.id] = { dimIndex, qIndex, globalIndex };
});
});
return { order, indexById };
}, []);
const unansweredQuestionIds = useMemo(() => {
return questionIndex.order.filter(id => responses[id] === undefined);
}, [questionIndex.order, responses]);
const scrollToQuestion = (questionId) => {
const meta = questionIndex.indexById[questionId];
if (!meta) return;
setExpandedSections(prev => ({ ...prev, [meta.dimIndex]: true }));
setActiveQuestionId(questionId);
setTimeout(() => {
const el = document.getElementById(questionId);
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
const input = el.querySelector('input');
if (input) input.focus({ preventScroll: true });
}
}, 50);
};
const goToUnanswered = (direction) => {
if (unansweredQuestionIds.length === 0) {
alert('All questions are answered.');
return;
}
const order = questionIndex.order;
const startIndex = activeQuestionId && questionIndex.indexById[activeQuestionId]
? questionIndex.indexById[activeQuestionId].globalIndex
: (direction > 0 ? -1 : order.length);
const step = direction > 0 ? 1 : -1;
const maxIters = order.length;
let i = startIndex;
for (let iter = 0; iter < maxIters; iter++) {
i = (i + step + order.length) % order.length;
const id = order[i];
if (responses[id] === undefined) {
scrollToQuestion(id);
return;
}
}
scrollToQuestion(unansweredQuestionIds[0]);
};
const validateAllAnswered = () => {
const missing = unansweredQuestionIds;
if (missing.length === 0) return { ok: true, missing: [] };
return { ok: false, missing };
};
// Load draft on first mount
useEffect(() => {
try {
const raw = localStorage.getItem(draftStorageKey);
if (!raw) return;
const draft = JSON.parse(raw);
if (draft && typeof draft === 'object') {
if (draft.responses && typeof draft.responses === 'object') setResponses(draft.responses);
if (draft.notes && typeof draft.notes === 'object') setNotes(draft.notes);
if (typeof draft.organizationName === 'string') setOrganizationName(draft.organizationName);
if (typeof draft.assessmentName === 'string' && draft.assessmentName.trim()) {
setAssessmentName(draft.assessmentName);
}
if (draft.updatedAt) {
const d = new Date(draft.updatedAt);
if (!isNaN(d.getTime())) {
setAutosaveState({ status: 'Saved', lastUpdated: d });
}
}
}
} catch (_) {
// Ignore draft load errors
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Calculate scores
const calculations = useMemo(() => {
const dimensionScores = assessmentData.dimensions.map(dimension => {
let earnedPoints = 0;
let totalPossiblePoints = 0;
let answeredQuestions = 0;
dimension.questions.forEach(question => {
const maxPoints = Math.max(...question.options.map((_, i) => question.options[i]?.points || i * 3 + 3));
totalPossiblePoints += maxPoints;
if (responses[question.id] !== undefined) {
earnedPoints += question.options[responses[question.id]]?.points || responses[question.id] * 3 + 3;
answeredQuestions++;
}
});
const percentage = totalPossiblePoints > 0 ? (earnedPoints / totalPossiblePoints) * 100 : 0;
const completionRate = (answeredQuestions / dimension.questions.length) * 100;
return {
name: dimension.name,
score: percentage,
earnedPoints,
totalPossiblePoints,
weight: dimension.weight,
completionRate,
answeredQuestions,
totalQuestions: dimension.questions.length
};
});
let totalWeightedScore = 0;
let totalWeight = 0;
dimensionScores.forEach(dim => {
totalWeightedScore += dim.score * dim.weight;
totalWeight += dim.weight;
});
const overallScore = totalWeight > 0 ? totalWeightedScore / totalWeight : 0;
let maturityLevel = 'Curious/Aware';
let maturityColor = '#ef4444';
if (overallScore >= 80) { maturityLevel = 'Leading'; maturityColor = '#3b82f6'; }
else if (overallScore >= 60) { maturityLevel = 'Scaling'; maturityColor = '#22c55e'; }
else if (overallScore >= 40) { maturityLevel = 'Adopting'; maturityColor = '#eab308'; }
else if (overallScore >= 20) { maturityLevel = 'Experimenting'; maturityColor = '#f97316'; }
const totalAnswered = dimensionScores.reduce((s, d) => s + d.answeredQuestions, 0);
const totalQuestions = dimensionScores.reduce((s, d) => s + d.totalQuestions, 0);
return {
dimensionScores,
overallScore,
maturityLevel,
maturityColor,
totalAnswered,
totalQuestions
};
}, [responses]);
// Initialize Chart.js radar chart
useEffect(() => {
const ctx = document.getElementById('radarChart');
if (!ctx) return;
if (window.radarChartInstance) {
window.radarChartInstance.destroy();
}
window.radarChartInstance = new Chart(ctx.getContext('2d'), {
type: 'radar',
data: {
labels: calculations.dimensionScores.map(d => d.name),
datasets: [{
label: 'Maturity Score',
data: calculations.dimensionScores.map(d => d.score),
fill: true,
backgroundColor: 'rgba(59, 130, 246, 0.2)',
borderColor: 'rgba(59, 130, 246, 1)',
pointBackgroundColor: 'rgba(59, 130, 246, 1)',
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: 'rgba(59, 130, 246, 1)',
pointRadius: 5,
pointHoverRadius: 7
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
r: {
beginAtZero: true,
max: 100,
ticks: {
stepSize: 20,
callback: value => value + '%'
},
pointLabels: {
font: { size: 11 }
}
}
},
plugins: {
legend: { display: false },
tooltip: {
callbacks: {
label: context => context.parsed.r.toFixed(1) + '%'
}
}
}
}
});
return () => {
if (window.radarChartInstance) {
window.radarChartInstance.destroy();
}
};
}, [calculations]);
const handleResponse = (questionId, value) => {
setResponses(prev => ({ ...prev, [questionId]: value }));
setActiveQuestionId(questionId);
};
const toggleSection = (index) => {
setExpandedSections(prev => ({ ...prev, [index]: !prev[index] }));
};
const exportAssessment = () => {
const validation = validateAllAnswered();
if (!validation.ok) {
setShowValidationErrors(true);
scrollToQuestion(validation.missing[0]);
alert(`Please answer all questions before finalizing. Remaining: ${validation.missing.length}`);
return;
}
const data = {
assessmentName: assessmentName || "AI Maturity Assessment",
organizationName: organizationName || "",
timestamp: new Date().toISOString(),
responses: responses,
notes: notes,
scores: calculations
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `assessment-${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const loadAssessment = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
const data = JSON.parse(event.target.result);
if (data.responses) {
setResponses(data.responses);
}
if (data.notes && typeof data.notes === 'object') setNotes(data.notes);
if (typeof data.organizationName === 'string') setOrganizationName(data.organizationName);
if (typeof data.assessmentName === 'string' && data.assessmentName.trim()) setAssessmentName(data.assessmentName);
} catch (error) {
alert('Failed to load file');
}
};
reader.readAsText(file);
};
input.click();
};
const copyToClipboard = () => {
const text = `AI Maturity Assessment Results
Overall Score: ${calculations.overallScore.toFixed(1)}%
Maturity Level: ${calculations.maturityLevel}
Progress: ${calculations.totalAnswered}/${calculations.totalQuestions} questions
Dimension Scores:
${calculations.dimensionScores.map(d =>
`${d.name}: ${d.score.toFixed(1)}% (${d.answeredQuestions}/${d.totalQuestions} answered)`
).join('\n')}`;
navigator.clipboard.writeText(text).then(() => alert('Copied to clipboard!'));
};
const saveAssessmentToCloud = () => {
if (!organizationName.trim()) {
alert('Please enter an organization name');
return;
}
const data = {
assessmentName: assessmentName || getDefaultAssessmentName(),
organizationName: organizationName.trim(),
timestamp: new Date().toISOString(),
responses: responses,
notes: notes,
scores: calculations
};
// Get existing assessments
const saved = JSON.parse(localStorage.getItem('savedAssessments') || '[]');
saved.push(data);
localStorage.setItem('savedAssessments', JSON.stringify(saved));
alert('Assessment saved successfully!');
setShowSaveDialog(false);
// Reset everything for a fresh assessment
setResponses({});
setNotes({});
setOrganizationName('');
setAssessmentName(getDefaultAssessmentName());
setActiveQuestionId(null);
setShowValidationErrors(false);
setExpandedSections(Object.fromEntries(assessmentData.dimensions.map((_, i) => [i, false])));
localStorage.removeItem(draftStorageKey);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const getSavedAssessments = () => {
return JSON.parse(localStorage.getItem('savedAssessments') || '[]');
};
const loadSavedAssessment = (index) => {
const saved = getSavedAssessments();
if (saved[index]) {
setResponses(saved[index].responses);
if (saved[index].notes && typeof saved[index].notes === 'object') setNotes(saved[index].notes);
setOrganizationName(saved[index].organizationName);
if (typeof saved[index].assessmentName === 'string' && saved[index].assessmentName.trim()) {
setAssessmentName(saved[index].assessmentName);
}
setShowViewSaved(false);
alert('Assessment loaded!');
}
};
// Autosave draft (debounced)
useEffect(() => {
setAutosaveState(prev => ({ ...prev, status: 'Saving…' }));
const timer = setTimeout(() => {
try {
const updatedAt = new Date();
const draft = {
assessmentName,
organizationName,
responses,
notes,
updatedAt: updatedAt.toISOString()
};
localStorage.setItem(draftStorageKey, JSON.stringify(draft));
setAutosaveState({ status: 'Saved', lastUpdated: updatedAt });
} catch (_) {
setAutosaveState(prev => ({ ...prev, status: 'Saved' }));
}
}, 500);
return () => clearTimeout(timer);
}, [assessmentName, organizationName, responses, notes]);
const deleteSavedAssessment = (index) => {
if (confirm('Are you sure you want to delete this assessment?')) {
const saved = getSavedAssessments();
saved.splice(index, 1);
localStorage.setItem('savedAssessments', JSON.stringify(saved));
setShowViewSaved(false);
setTimeout(() => setShowViewSaved(true), 0);
}
};
return h('div', { className: 'min-h-screen bg-gray-50' },
h('div', { className: 'max-w-7xl mx-auto p-6' },
// Header Card
h('div', { className: 'bg-white rounded-lg shadow-lg p-4 md:p-6 mb-4' },
h('div', { className: 'flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4' },
h('div', { className: 'flex items-center gap-4' },
h('img', {
src: 'LOGO1.png',
alt: 'Sedna Consulting Group',
className: 'h-8 md:h-10'
}),
h('div', {},
h('h1', { className: 'text-xl md:text-2xl font-bold text-gray-900' }, 'AI Maturity Assessment'),
h('p', { className: 'text-xs md:text-sm text-gray-600' },
`Progress: ${calculations.totalAnswered} of ${calculations.totalQuestions} questions`),
h('div', { className: 'mt-2 flex flex-col sm:flex-row sm:items-center gap-2' },
h('input', {
type: 'text',
value: assessmentName,
onChange: (e) => setAssessmentName(e.target.value),
placeholder: 'Assessment name',
className: 'w-full sm:w-[360px] px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500'
}),
h('div', { className: 'text-xs text-gray-500' },
autosaveState.status === 'Saving…'
? 'Saving…'
: `Saved${autosaveState.lastUpdated ? ` • Last updated ${autosaveState.lastUpdated.toLocaleString()}` : ''}`
)
)
)
),
h('div', { className: 'flex flex-wrap gap-2 no-print' },
h('button', {
onClick: () => setShowSaveDialog(true),
className: 'px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium'
}, 'Save'),
h('button', {
onClick: () => setShowViewSaved(true),
className: 'px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 font-medium'
}, 'Load'),
h('button', {
onClick: exportAssessment,
className: 'px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 font-medium'
}, unansweredQuestionIds.length > 0 ? `Finalize (${unansweredQuestionIds.length} left)` : 'Finalize'),
h('button', {
onClick: () => window.print(),
className: 'px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 font-medium'
}, 'Print')
)
),
h('div', { className: 'text-center py-4' },
h('div', {
className: 'text-4xl md:text-5xl font-bold mb-1',
style: { color: calculations.maturityColor }
}, `${calculations.overallScore.toFixed(1)}%`),
h('div', {
className: 'text-lg md:text-xl font-semibold',
style: { color: calculations.maturityColor }
}, calculations.maturityLevel)
)
),
// Unanswered Navigation (kept above the question sections)
h('div', { className: 'bg-white rounded-lg shadow-md p-3 mb-4 no-print flex flex-wrap gap-2 justify-end items-center' },
h('div', { className: 'text-sm text-gray-600 mr-auto' },
unansweredQuestionIds.length > 0
? `${unansweredQuestionIds.length} unanswered remaining`
: 'All questions answered'
),
h('button', {
onClick: () => goToUnanswered(-1),
className: 'px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 font-medium'
}, 'Previous Unanswered'),
h('button', {
onClick: () => goToUnanswered(1),
className: 'px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 font-medium'
}, 'Next Unanswered')
),
// Radar Chart Card - Smaller
h('div', { className: 'bg-white rounded-lg shadow-lg p-4 md:p-6 mb-4' },
h('h2', { className: 'text-lg font-bold text-gray-900 mb-3 text-center' }, 'Maturity Dimensions'),
h('div', { className: 'max-w-md mx-auto', style: { height: '280px' } },
h('canvas', { id: 'radarChart' })
)
),
// Dimension Summary Cards
h('div', { className: 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mb-4' },
...assessmentData.dimensions.map((dimension, dimIndex) => {
const dimScore = calculations.dimensionScores[dimIndex];
const pct = dimScore.score;
const levelColor = getMaturityLevel(pct).color;
return h('div', {
key: `summary-${dimIndex}`,
className: 'bg-white rounded-lg shadow-md p-4 cursor-pointer hover:shadow-lg transition-shadow',
onClick: () => {
setExpandedSections(prev => ({ ...prev, [dimIndex]: true }));
setTimeout(() => {
const el = document.getElementById(dimension.questions[0].id);
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
}, 100);
}
},
h('div', { className: 'flex items-center justify-between mb-2' },
h('h3', { className: 'font-semibold text-gray-900 text-sm' }, dimension.name),
h('span', {
className: 'text-2xl font-bold',
style: { color: levelColor }
}, `${pct.toFixed(0)}%`)
),
h('p', { className: 'text-xs text-gray-500 mb-2' },
`${dimScore.answeredQuestions}/${dimScore.totalQuestions} questions`),
h('div', { className: 'w-full bg-gray-200 rounded-full h-2' },
h('div', {
className: 'h-2 rounded-full transition-all',
style: { width: `${pct}%`, backgroundColor: levelColor }
})
)
);
})
),
// Save Dialog Modal
showSaveDialog && h('div', {
className: 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50',
onClick: () => setShowSaveDialog(false)
},
h('div', {
className: 'bg-white rounded-lg p-6 max-w-md w-full mx-4',
onClick: (e) => e.stopPropagation()
},
h('h3', { className: 'text-xl font-bold mb-4' }, 'Save Assessment'),
h('input', {
type: 'text',
placeholder: 'Assessment name',
value: assessmentName,
onChange: (e) => setAssessmentName(e.target.value),
className: 'w-full px-4 py-2 border border-gray-300 rounded-lg mb-3 focus:outline-none focus:ring-2 focus:ring-blue-500'
}),
h('input', {
type: 'text',
placeholder: 'Enter organization name',
value: organizationName,
onChange: (e) => setOrganizationName(e.target.value),
className: 'w-full px-4 py-2 border border-gray-300 rounded-lg mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500'
}),
h('div', { className: 'flex gap-2 justify-end' },
h('button', {
onClick: () => setShowSaveDialog(false),
className: 'px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600'
}, 'Cancel'),
h('button', {
onClick: saveAssessmentToCloud,
className: 'px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700'
}, 'Save')
)
)
),
// View Saved Assessments Modal
showViewSaved && h('div', {
className: 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4',
onClick: () => setShowViewSaved(false)
},
h('div', {
className: 'bg-white rounded-lg p-6 max-w-4xl w-full max-h-[80vh] overflow-y-auto',
onClick: (e) => e.stopPropagation()
},
h('div', { className: 'flex justify-between items-center mb-4' },
h('h3', { className: 'text-xl font-bold' }, 'Saved Assessments'),
h('button', {
onClick: () => setShowViewSaved(false),
className: 'text-gray-500 hover:text-gray-700 text-2xl'
}, '×')
),
getSavedAssessments().length === 0
? h('p', { className: 'text-gray-500 text-center py-8' }, 'No saved assessments yet')
: h('div', { className: 'space-y-4' },
...getSavedAssessments().map((assessment, index) =>
h('div', {
key: index,
className: 'border border-gray-200 rounded-lg p-4 hover:bg-gray-50'
},
h('div', { className: 'flex justify-between items-start mb-2' },
h('div', {},
h('h4', { className: 'font-bold text-lg' }, assessment.assessmentName || 'Untitled Assessment'),
h('p', { className: 'text-sm text-gray-700' }, assessment.organizationName),
h('p', { className: 'text-sm text-gray-500' },
new Date(assessment.timestamp).toLocaleString()
)
),
h('div', { className: 'flex gap-2' },
h('button', {
onClick: () => loadSavedAssessment(index),
className: 'px-3 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 text-sm'
}, 'Load'),
h('button', {
onClick: () => deleteSavedAssessment(index),
className: 'px-3 py-1 bg-red-600 text-white rounded hover:bg-red-700 text-sm'
}, 'Delete')
)
),
h('div', { className: 'text-sm' },
h('p', {},
h('span', { className: 'font-semibold' }, 'Score: '),
h('span', { style: { color: assessment.scores.maturityColor } },
`${assessment.scores.overallScore.toFixed(1)}% - ${assessment.scores.maturityLevel}`)
),
h('p', {},
h('span', { className: 'font-semibold' }, 'Progress: '),
`${assessment.scores.totalAnswered}/${assessment.scores.totalQuestions} questions`
)
)
)
)
)
)
),
// Dimensions
...assessmentData.dimensions.map((dimension, dimIndex) =>
h('div', {
key: dimIndex,
className: 'bg-white rounded-lg shadow-md mb-4 overflow-hidden'
},
// Dimension Header
h('div', {
className: 'bg-gray-50 border-b border-gray-200 p-4 md:p-5 cursor-pointer hover:bg-gray-100',
onClick: () => toggleSection(dimIndex)
},
h('div', { className: 'flex items-center justify-between' },
h('div', {},
h('h3', { className: 'text-lg md:text-xl font-bold text-gray-900' }, dimension.name)
),
h('div', { className: 'flex items-center gap-4 text-sm text-gray-600' },
h('span', {},
`${calculations.dimensionScores[dimIndex].answeredQuestions}/${dimension.questions.length} answered`),
h('span', {},
`Score: ${calculations.dimensionScores[dimIndex].score.toFixed(1)}%`),
h('span', {},
`Weight: ${dimension.weight} pts`),
h('svg', {
className: `w-6 h-6 transform transition-transform ${expandedSections[dimIndex] ? 'rotate-180' : ''}`,
fill: 'none',
stroke: 'currentColor',
viewBox: '0 0 24 24'
},
h('path', {
strokeLinecap: 'round',
strokeLinejoin: 'round',
strokeWidth: 2,
d: 'M19 9l-7 7-7-7'
})
)
)
)
),
// Questions
expandedSections[dimIndex] && h('div', { className: 'p-4 md:p-6' },
...dimension.questions.map((question, qIndex) => {
const isUnanswered = responses[question.id] === undefined;
const showRequired = showValidationErrors && isUnanswered;
return h('div', {
id: question.id,
key: question.id,
className: `mb-6 pb-6 border-b border-gray-200 last:border-b-0 last:mb-0 last:pb-0 ${showRequired ? 'ring-2 ring-red-400 rounded-lg p-3 -m-3' : ''}`
},
h('div', { className: 'mb-3' },
h('div', { className: 'flex items-start gap-2' },
h('span', { className: 'text-blue-600 font-semibold flex-shrink-0' }, `${qIndex + 1}.`),
h('div', { className: 'flex-1' },
h('p', { className: `font-medium ${showRequired ? 'text-red-700' : 'text-gray-900'}` }, question.text),
showRequired && h('p', { className: 'text-xs text-red-600 mt-1' }, 'Required')
)
)
),
h('div', { className: 'space-y-2 ml-6' },
...question.options.map((option, optIndex) => {
const isSelected = responses[question.id] === optIndex;
const points = option.points !== undefined ? option.points : optIndex * 3 + 3;
return h('label', {
key: optIndex,
className: `flex items-start gap-3 p-3 border-2 rounded-lg cursor-pointer transition-all ${isSelected
? 'border-blue-500 bg-blue-50'
: 'border-gray-200 hover:border-blue-300 hover:bg-gray-50'
}`
},
h('input', {
type: 'radio',
name: question.id,
value: optIndex,
checked: isSelected,
onChange: () => handleResponse(question.id, optIndex),
className: 'mt-1 w-5 h-5 text-blue-600 flex-shrink-0'
}),
h('div', { className: 'flex-1' },
h('div', { className: 'font-semibold text-gray-900 mb-1' },
option.level || ['Curious/Aware', 'Experimenting', 'Adopting', 'Scaling', 'Leading'][optIndex]
),
h('div', { className: 'text-sm text-gray-600' },
option.description || option
)
),
h('div', { className: 'text-sm font-medium text-gray-500 flex-shrink-0' },
`${points} pts`
)
);
})
),
h('div', { className: 'ml-6 mt-3' },
h('label', { className: 'block text-sm font-medium text-gray-700 mb-1' }, 'Notes'),
h('textarea', {
value: notes[question.id] || '',
onChange: (e) => setNotes(prev => ({ ...prev, [question.id]: e.target.value })),
placeholder: 'Add your notes here...',
rows: 2,
className: 'w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y'
})
)
);
})
)
)
)
)
);
}
// Render the app
ReactDOM.createRoot(document.getElementById('root')).render(h(AssessmentTool));