<script crossorigin src="https://unpkg.com/react@18.2.0/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.23.5/babel.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
background: linear-gradient(to bottom right, #dbeafe, #fae8ff, #fce7f3);
min-height: 100vh;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 1rem;
}
.card {
background: white;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
padding: 2rem;
margin-bottom: 1.5rem;
}
.header {
text-align: center;
}
.title {
font-size: 2.5rem;
font-weight: bold;
background: linear-gradient(to right, #9333ea, #2563eb);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
}
.subtitle {
color: #4b5563;
font-size: 1.125rem;
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.metric-card {
background: white;
border-radius: 0.5rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
}
.metric-card.blue { border-left: 4px solid #3b82f6; }
.metric-card.green { border-left: 4px solid #10b981; }
.metric-card.yellow { border-left: 4px solid #f59e0b; }
.metric-card.red { border-left: 4px solid #ef4444; }
.metric-label {
font-size: 0.875rem;
color: #6b7280;
margin-bottom: 0.5rem;
}
.metric-value {
font-size: 2rem;
font-weight: bold;
}
.metric-value.blue { color: #3b82f6; }
.metric-value.green { color: #10b981; }
.metric-value.yellow { color: #f59e0b; }
.metric-value.red { color: #ef4444; }
.filters-section h2 {
font-size: 1.5rem;
font-weight: bold;
color: #1f2937;
margin-bottom: 1rem;
}
.filters-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
.filter-group label {
display: block;
font-size: 0.875rem;
font-weight: 600;
color: #374151;
margin-bottom: 0.5rem;
}
.filter-group select {
width: 100%;
padding: 0.75rem;
border: 2px solid #d1d5db;
border-radius: 0.5rem;
font-size: 1rem;
outline: none;
transition: border-color 0.2s;
}
.filter-group select:focus {
border-color: #9333ea;
}
.reset-btn {
padding: 0.75rem 1.5rem;
background: linear-gradient(to right, #9333ea, #2563eb);
color: white;
border: none;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s;
}
.reset-btn:hover {
transform: translateY(-2px);
}
.viz-card {
background: white;
border: 2px solid #e5e7eb;
border-radius: 0.5rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.viz-title {
font-size: 1.5rem;
font-weight: bold;
color: #1f2937;
margin-bottom: 1rem;
}
.top-bottom-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.strength-card {
background: #f0fdf4;
border: 2px solid #bbf7d0;
border-radius: 0.5rem;
padding: 1.5rem;
}
.strength-card h3 {
font-size: 1.25rem;
font-weight: bold;
color: #166534;
margin-bottom: 1rem;
}
.growth-card {
background: #fff7ed;
border: 2px solid #fed7aa;
border-radius: 0.5rem;
padding: 1.5rem;
}
.growth-card h3 {
font-size: 1.25rem;
font-weight: bold;
color: #9a3412;
margin-bottom: 1rem;
}
.trait-item {
margin-bottom: 1rem;
}
.trait-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.trait-name {
font-weight: 600;
color: #1f2937;
}
.trait-score {
font-size: 1.125rem;
font-weight: bold;
}
.trait-score.green { color: #16a34a; }
.trait-score.orange { color: #ea580c; }
.progress-bar {
width: 100%;
height: 0.75rem;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.progress-fill {
height: 100%;
transition: width 0.3s;
}
.progress-fill.green { background: #16a34a; }
.progress-fill.orange { background: #ea580c; }
.all-traits-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
}
.trait-card {
background: linear-gradient(to bottom right, #faf5ff, #dbeafe);
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
padding: 1rem;
transition: box-shadow 0.2s;
}
.trait-card:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.trait-card-name {
font-size: 0.875rem;
font-weight: 600;
color: #374151;
margin-bottom: 0.5rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.trait-card-score {
font-size: 1.5rem;
font-weight: bold;
color: #9333ea;
margin-bottom: 0.5rem;
}
.trait-card-progress {
width: 100%;
height: 0.5rem;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.trait-card-progress-fill {
height: 100%;
background: linear-gradient(to right, #9333ea, #2563eb);
}
.heatmap-container {
overflow-x: auto;
margin-top: 1rem;
}
.heatmap-table {
width: 100%;
border-collapse: collapse;
font-size: 0.75rem;
}
.heatmap-table th,
.heatmap-table td {
border: 1px solid #d1d5db;
padding: 0.5rem;
text-align: center;
}
.heatmap-table th {
background: #f3f4f6;
font-weight: 600;
position: sticky;
top: 0;
}
.heatmap-table .student-name {
background: white;
position: sticky;
left: 0;
font-weight: 600;
text-align: left;
}
.heat-red { background: #ef4444; color: white; }
.heat-orange-dark { background: #f97316; color: white; }
.heat-orange { background: #fb923c; color: white; }
.heat-yellow { background: #fbbf24; color: black; }
.heat-green-light { background: #86efac; color: black; }
.heat-green { background: #22c55e; color: white; }
.student-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1rem;
}
.student-item {
border-radius: 0.5rem;
padding: 1rem;
border: 2px solid;
}
.student-item.leader {
background: linear-gradient(to bottom right, #fef3c7, #fde68a);
border-color: #fbbf24;
}
.student-item.intervention {
background: #fee2e2;
border-color: #fca5a5;
}
.student-item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.student-item-name {
font-weight: bold;
font-size: 1.125rem;
}
.student-item-class {
font-size: 0.875rem;
color: #6b7280;
}
.trait-list {
margin-top: 0.5rem;
}
.trait-list-item {
display: flex;
justify-content: space-between;
padding: 0.5rem;
background: white;
border-radius: 0.25rem;
margin-bottom: 0.25rem;
border: 1px solid;
}
.trait-list-item.low {
border-color: #fca5a5;
}
.trait-list-item.high {
border-color: #86efac;
}
.bar-chart-container {
margin-top: 1rem;
}
.bar-item {
margin-bottom: 1rem;
}
.bar-label {
display: flex;
justify-content: space-between;
margin-bottom: 0.25rem;
font-size: 0.875rem;
}
.bar-background {
width: 100%;
height: 2rem;
background: #f3f4f6;
border-radius: 0.25rem;
overflow: hidden;
position: relative;
}
.bar-fill {
height: 100%;
display: flex;
align-items: center;
padding-left: 0.5rem;
color: white;
font-weight: 600;
font-size: 0.875rem;
transition: width 0.3s;
}
.distribution-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.distribution-card {
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
padding: 1rem;
}
.distribution-card h4 {
font-size: 0.875rem;
font-weight: bold;
margin-bottom: 0.5rem;
color: #374151;
}
.distribution-bars {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.dist-bar {
display: flex;
align-items: center;
gap: 0.5rem;
}
.dist-label {
font-size: 0.75rem;
width: 4rem;
color: #6b7280;
}
.dist-bar-bg {
flex: 1;
height: 1.5rem;
background: #f3f4f6;
border-radius: 0.25rem;
overflow: hidden;
}
.dist-bar-fill {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.75rem;
font-weight: 600;
}
.radar-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 600px;
}
.radar-svg {
max-width: 100%;
height: auto;
}
.radar-legend {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1rem;
padding: 1rem;
background: #f9fafb;
border-radius: 0.5rem;
}
.radar-legend-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.radar-legend-box {
width: 1.5rem;
height: 1.5rem;
border-radius: 0.25rem;
}
.footer {
background: #1f2937;
color: white;
text-align: center;
padding: 1.5rem;
border-radius: 0.5rem;
margin-top: 1.5rem;
}
.footer p {
font-size: 0.875rem;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
}
.spinner {
border: 4px solid #f3f4f6;
border-top: 4px solid #9333ea;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
margin-top: 1rem;
color: #6b7280;
}
.empty-state {
text-align: center;
padding: 3rem;
color: #6b7280;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.legend-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.legend-box {
width: 1.5rem;
height: 1.5rem;
border-radius: 0.25rem;
}
@media print {
.reset-btn { display: none; }
body { background: white; }
}
</style>
<div id="loading" class="loading">
<div class="spinner"></div>
<p class="loading-text">Loading Dashboard...</p>
</div>
<div id="root"></div>
<script type="text/babel">
const { useState, useMemo } = React;
window.addEventListener('load', () => {
setTimeout(() => {
const loading = document.getElementById('loading');
if (loading) loading.style.display = 'none';
}, 500);
});
const VIA_CATEGORIES = {
'Wisdom & Knowledge': ['Creativity', 'Judgement', 'Love of Learning', 'Curiosity', 'Perspective'],
'Courage': ['Bravery', 'Perseverance', 'Honesty', 'Zest'],
'Humanity': ['Love', 'Kindness', 'Social Intelligence'],
'Justice': ['Teamwork', 'Fairness', 'Leadership'],
'Temperance': ['Forgiveness', 'Humility', 'Prudence', 'Self Regulation'],
'Transcendence': ['Appreciation of Beauty & Excellence', 'Gratitude', 'Hope', 'Humour', 'Spirituality']
};
const ALL_TRAITS = [
'Creativity', 'Judgement', 'Love of Learning', 'Curiosity', 'Perspective',
'Social Intelligence', 'Honesty', 'Perseverance', 'Zest', 'Bravery',
'Self Regulation', 'Prudence', 'Forgiveness', 'Gratitude', 'Spirituality',
'Appreciation of Beauty & Excellence', 'Humour', 'Hope', 'Kindness',
'Love', 'Teamwork', 'Leadership', 'Fairness', 'Humility'
];
const COLORS = ['#8b5cf6', '#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#ec4899'];
function VIADashboard() {
const csvData = `Student Name,Class,Creativity,Judgement,Love of Learning,Curiosity,Perspective,Social Intelligence,Honesty,Perseverance,Zest,Bravery,Self Regulation,Prudence,Forgiveness,Gratitude,Spirituality,Appreciation of Beauty & Excellence,Humour,Hope,Kindness,Love,Teamwork,Leadership,Fairness,Humility
Johan,Sec 1 SD7,3.0,3.0,4.5,4.5,2.8,3.5,3.5,4.0,3.5,3.0,4.0,3.0,2.5,4.0,3.0,3.0,4.5,4.0,3.6666666666666665,3.5,2.5,2.5,4.0,3.0
Lee Dao Teng Taylen ,Sec 1 SD7,5.0,4.333333333333333,5.0,5.0,4.2,4.75,4.5,5.0,5.0,5.0,4.0,3.6666666666666665,4.0,5.0,4.0,4.0,5.0,5.0,5.0,5.0,5.0,5.0,4.5,3.5
Qiara atilia,Sec 1 SD7,2.5,3.0,2.5,2.5,4.6,4.25,4.5,3.0,2.0,1.0,4.0,2.6666666666666665,4.5,5.0,2.0,5.0,3.5,2.0,4.0,4.5,3.0,3.0,4.5,2.5
Katrina ,Sec 1 SD4,4.0,4.0,3.0,3.0,3.8,3.75,4.0,3.0,2.5,2.0,4.0,3.6666666666666665,3.5,4.0,2.0,4.0,4.0,3.0,3.6666666666666665,4.0,4.0,4.0,4.0,3.5
Laaibah,Sec 1 SD4,5.0,4.333333333333333,4.5,4.5,4.8,5.0,4.5,4.0,4.0,4.0,4.0,4.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,4.5,4.5,5.0,4.0
Lee Ming Yao,Sec 1 SD7,3.5,2.333333333333333,1.5,1.5,2.0,2.5,2.0,3.0,3.0,3.0,3.0,1.6666666666666667,1.5,3.0,2.0,2.0,3.5,2.0,2.6666666666666665,3.5,1.0,1.0,2.0,2.0
Malcolm ,Sec 1 SD7,5.0,3.6666666666666665,3.5,3.5,3.8,3.75,3.5,3.0,3.5,4.0,4.0,2.6666666666666665,3.5,5.0,2.0,1.0,5.0,5.0,3.6666666666666665,4.5,3.5,3.5,3.5,3.0
Janice teo,Sec 1 SD8,4.5,4.0,5.0,5.0,4.4,4.75,3.5,3.0,3.5,4.0,4.0,4.0,4.0,5.0,2.0,5.0,5.0,2.0,4.666666666666667,5.0,3.5,3.5,4.5,4.0
siti raushni quraisy,Sec 1 SD7,3.5,3.333333333333333,3.5,3.5,3.6,3.75,3.0,4.0,3.5,3.0,4.0,3.333333333333333,3.5,4.0,3.0,4.0,3.5,4.0,3.6666666666666665,4.0,3.5,3.5,3.5,3.5
Yiu To Ka,Sec 1 SD4,4.5,4.333333333333333,3.0,3.0,4.8,5.0,3.0,3.0,2.5,2.0,5.0,4.0,4.5,5.0,5.0,5.0,5.0,3.0,4.666666666666667,4.5,2.5,2.5,4.0,4.0
Jonathan Kevin Tionanda,Sec 1 SD7,3.5,4.333333333333333,5.0,5.0,3.4,3.0,4.0,5.0,4.5,4.0,2.0,4.666666666666667,3.0,4.0,3.0,2.0,3.0,3.0,4.0,4.5,2.5,2.5,4.0,5.0
Lyu Si Ruo,Sec 1 SD7,4.5,3.6666666666666665,5.0,5.0,3.8,4.75,4.5,5.0,5.0,5.0,3.0,4.0,3.0,5.0,3.0,5.0,5.0,4.0,5.0,5.0,4.0,4.0,5.0,4.5
Ng Yu Qing ,Sec 1 SD7,4.5,3.6666666666666665,4.5,4.5,3.8,4.25,4.0,4.0,3.5,3.0,5.0,3.6666666666666665,3.5,5.0,3.0,5.0,4.5,4.0,5.0,5.0,4.0,4.0,5.0,4.0
Akshaya,Sec 1 SD7,3.0,3.333333333333333,3.0,3.0,3.2,3.25,4.0,4.0,3.5,3.0,3.0,3.6666666666666665,3.0,5.0,3.0,4.0,3.0,3.0,4.0,4.5,3.5,3.5,4.0,3.5
Triston ong,Sec 1 SD4,3.0,3.0,3.0,3.0,3.2,3.5,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.5,3.0,3.0,3.0,3.0,3.0,3.0,3.0
Ivanka,Sec 1 SD4,5.0,3.6666666666666665,3.5,3.5,3.0,3.75,2.5,3.0,3.0,3.0,4.0,3.6666666666666665,2.5,5.0,1.0,4.0,4.5,1.0,4.0,4.0,3.5,3.5,4.0,4.0
Aaila sabrina,Sec 1 SD7,3.0,3.0,3.0,3.0,3.2,3.0,4.0,3.0,3.0,3.0,3.0,3.6666666666666665,3.5,4.0,3.0,4.0,3.0,3.0,3.333333333333333,3.5,3.0,3.0,3.5,4.0
Joelle,Sec 1 SD7,4.0,3.6666666666666665,2.5,2.5,5.0,5.0,5.0,3.0,3.0,3.0,4.0,4.333333333333333,5.0,5.0,3.0,5.0,5.0,3.0,5.0,5.0,5.0,5.0,5.0,5.0
Sanchi Pai,Sec 1 SD7,3.5,3.333333333333333,3.5,3.5,3.6,3.5,3.5,2.0,1.5,1.0,4.0,4.333333333333333,4.0,5.0,5.0,4.0,3.0,1.0,4.333333333333333,5.0,2.5,2.5,4.0,4.5
Saravanavel nishitha,Sec 1 SD7,4.5,4.666666666666667,5.0,5.0,4.2,4.25,3.0,3.0,3.0,3.0,4.0,4.0,4.0,4.0,5.0,5.0,4.5,5.0,4.333333333333333,4.5,4.0,4.0,4.5,4.0
LeeXingJian,Sec 1 SD8,4.5,4.333333333333333,3.5,3.5,4.4,4.75,4.0,4.0,3.5,3.0,5.0,4.666666666666667,4.0,4.0,5.0,4.0,5.0,1.0,4.333333333333333,4.0,4.5,4.5,4.0,5.0
NG LIANG QI Ronald,Sec 1 SD7,4.0,2.6666666666666665,3.0,3.0,4.2,4.25,4.0,3.0,3.0,3.0,5.0,2.6666666666666665,4.5,5.0,3.0,3.0,5.0,5.0,4.666666666666667,5.0,2.0,2.0,4.5,3.5
Ryan ,Sec 1 SD8,3.0,4.666666666666667,4.5,4.5,2.4,1.25,1.5,1.0,1.0,1.0,4.0,2.333333333333333,2.0,1.0,5.0,4.0,1.0,1.0,2.333333333333333,3.0,1.5,1.5,3.5,1.5
Arielle teo,Sec 1 SD7,3.0,3.0,3.5,3.5,3.6,4.0,3.5,3.0,3.0,3.0,3.0,3.0,3.5,5.0,4.0,5.0,4.0,3.0,4.0,4.5,2.5,2.5,4.0,3.0
Sharwin,Sec 1 SD7,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0
Danial,Sec 1 SD4,2.5,3.333333333333333,2.0,2.0,3.8,3.5,3.5,4.0,4.0,4.0,4.0,3.6666666666666665,4.0,5.0,3.0,4.0,3.0,3.0,4.0,4.0,3.0,3.0,4.0,3.5
AQEEL MUIZZ,Sec 1 SD4,4.0,3.6666666666666665,4.5,4.5,3.2,3.5,2.5,3.0,3.0,3.0,3.0,4.0,3.5,4.0,5.0,5.0,4.5,2.0,3.6666666666666665,4.0,4.5,4.5,3.0,4.0
Zakwan,Sec 1 SD4,3.0,3.6666666666666665,3.5,3.5,3.8,4.5,4.5,4.0,3.5,3.0,3.0,3.6666666666666665,3.5,4.0,3.0,3.0,4.0,4.0,4.666666666666667,4.5,3.0,3.0,4.5,3.5
Aden,Sec 1 SD4,4.0,3.0,2.5,2.5,3.2,5.0,2.5,5.0,4.5,4.0,4.0,3.333333333333333,1.5,4.0,3.0,3.0,5.0,5.0,5.0,5.0,2.0,2.0,4.0,3.5
Lucus,Sec 1 SD7,3.0,4.0,4.0,4.0,3.6,3.25,4.0,4.0,4.5,5.0,5.0,3.6666666666666665,4.0,5.0,4.0,4.0,3.0,5.0,3.333333333333333,3.0,3.5,3.5,3.0,4.0
alyscha,Sec 1 SD7,2.5,4.0,3.0,3.0,4.4,4.25,4.5,4.0,3.5,3.0,5.0,4.0,4.0,5.0,5.0,3.0,3.5,4.0,4.666666666666667,5.0,3.5,3.5,5.0,3.5
miette,Sec 1 SD7,3.0,3.333333333333333,3.0,3.0,3.2,3.0,3.0,3.0,3.5,4.0,3.0,3.333333333333333,3.0,4.0,3.0,4.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.5
felise kang,Sec 1 SD7,2.5,3.333333333333333,3.0,3.0,3.2,3.75,3.5,2.0,2.0,2.0,4.0,4.0,3.0,4.0,3.0,3.0,3.0,2.0,3.6666666666666665,3.5,3.0,3.0,4.0,4.0
Nicholas chew,Sec 1 SD8,3.0,3.333333333333333,3.5,3.5,2.4,2.5,3.0,3.0,2.5,2.0,3.0,3.0,2.5,4.0,4.0,2.0,2.5,3.0,3.6666666666666665,4.0,3.5,3.5,3.0,3.0
Yang Zekun ,Sec 1 SD4,3.0,3.0,3.5,3.5,2.8,2.75,2.5,3.0,3.0,3.0,3.0,3.0,3.0,3.0,4.0,4.0,3.0,3.0,3.333333333333333,3.5,3.0,3.0,3.0,3.0
Nur athirah,Sec 1 SD7,4.0,3.333333333333333,4.0,4.0,3.4,4.0,3.5,3.0,3.0,3.0,3.0,3.6666666666666665,3.0,4.0,3.0,4.0,4.0,3.0,4.333333333333333,4.5,4.0,4.0,3.5,4.0
Jacob Leow ,Sec 1 SD8,5.0,4.0,4.0,4.0,4.6,5.0,4.0,4.0,3.5,3.0,5.0,3.6666666666666665,4.0,4.0,5.0,5.0,5.0,3.0,4.666666666666667,4.5,4.5,4.5,5.0,4.5
Jacob Lim Shao Yang ,Sec 1 SD8,4.5,3.6666666666666665,4.5,4.5,3.6,3.5,3.0,3.0,3.0,3.0,4.0,3.0,3.5,5.0,5.0,4.0,3.5,3.0,4.0,4.5,2.5,2.5,3.5,3.0
Zavier ,Sec 1 SD7,4.0,4.0,4.5,4.5,4.4,4.75,4.0,5.0,4.5,4.0,3.0,4.333333333333333,4.5,4.0,4.0,4.0,4.5,4.0,4.666666666666667,4.5,4.0,4.0,4.5,4.5
lyvia sim ,Sec 1 SD4,3.5,3.333333333333333,2.5,2.5,3.6,4.5,3.5,3.0,3.0,3.0,3.0,3.6666666666666665,3.0,3.0,2.0,5.0,4.5,3.0,4.0,3.5,4.0,4.0,4.5,4.0
Nur Elsa Diana Binte Mohammad Helme,Sec 1 SD8,3.5,4.0,3.5,3.5,4.2,4.5,4.0,5.0,5.0,5.0,5.0,5.0,4.0,5.0,4.0,5.0,4.0,4.0,5.0,5.0,3.0,3.0,4.5,5.0
nazeera,Sec 1 SD8,3.0,3.6666666666666665,3.5,3.5,3.8,3.75,4.0,4.0,3.5,3.0,3.0,3.6666666666666665,3.5,4.0,3.0,4.0,4.0,5.0,4.0,4.5,3.5,3.5,4.5,4.0
Charis Liew,Sec 1 SD8,3.0,3.333333333333333,3.0,3.0,3.8,3.75,4.0,4.0,3.5,3.0,4.0,4.0,4.0,4.0,3.0,4.0,3.0,3.0,4.333333333333333,4.5,2.5,2.5,4.0,4.0
Aneeq,Sec 1 SD8,3.0,3.6666666666666665,3.5,3.5,4.0,4.0,3.5,4.0,4.0,4.0,3.0,4.0,4.0,4.0,3.0,3.0,3.5,3.0,4.0,4.0,3.5,3.5,3.5,4.0
Keith Chiang Zhi Loong,Sec 1 SD4,2.5,2.333333333333333,2.0,2.0,4.2,3.25,1.0,3.0,2.0,1.0,1.0,1.6666666666666667,5.0,5.0,2.0,5.0,1.5,5.0,4.666666666666667,5.0,3.0,3.0,2.0,2.0
Wildan,Sec 1 SD7,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0
aira adelea,Sec 1 SD4,3.0,3.0,3.5,3.5,3.4,3.75,3.5,3.0,2.5,2.0,3.0,3.333333333333333,3.5,5.0,3.0,3.0,3.5,3.0,3.6666666666666665,3.5,3.0,3.0,4.0,3.5
Dhia aleeya,Sec 1 SD4,4.5,4.0,4.5,4.5,2.8,3.75,2.5,5.0,5.0,5.0,5.0,3.6666666666666665,1.5,3.0,5.0,5.0,4.5,5.0,4.0,4.5,4.5,4.5,3.0,3.0
Avril,Sec 1 SD4,4.5,3.6666666666666665,2.5,2.5,4.8,4.5,4.5,5.0,4.0,3.0,3.0,3.6666666666666665,5.0,4.0,3.0,5.0,4.0,4.0,5.0,5.0,4.0,4.0,4.0,4.0
Eeliya,Sec 1 SD7,3.5,4.333333333333333,3.5,3.5,4.6,4.75,3.5,4.0,4.0,4.0,5.0,4.333333333333333,4.0,5.0,1.0,5.0,4.0,4.0,4.666666666666667,5.0,4.0,4.0,4.5,4.5
Chan Jyxuan,Sec 1 SD4,4.5,4.666666666666667,4.0,4.0,3.6,4.0,5.0,5.0,4.5,4.0,3.0,4.666666666666667,3.0,5.0,5.0,5.0,4.0,5.0,4.333333333333333,4.5,5.0,5.0,4.5,4.5
Ang Wei Jing,Sec 1 SD4,3.0,3.333333333333333,3.5,3.5,3.2,3.0,2.5,4.0,3.5,3.0,2.0,3.6666666666666665,3.5,4.0,3.0,4.0,3.0,4.0,3.6666666666666665,4.0,4.5,4.5,3.5,3.5
zavier peh,Sec 1 SD7,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0
Harris ,Sec 1 SD8,4.0,4.0,4.5,4.5,4.2,4.5,3.5,4.0,3.5,3.0,4.0,4.333333333333333,4.0,4.0,4.0,4.0,4.0,4.0,4.333333333333333,4.0,3.5,3.5,4.0,4.5
Alfie Ho,Sec 1 SD8,2.5,2.6666666666666665,3.0,3.0,2.6,2.5,2.5,4.0,4.0,4.0,2.0,3.333333333333333,2.5,5.0,4.0,3.0,2.0,1.0,3.333333333333333,4.0,2.0,2.0,2.5,3.5
Mohamad rifaie bin mohamad najib,Sec 1 SD7,1.5,1.6666666666666667,3.5,3.5,2.2,1.5,2.0,1.0,2.0,3.0,3.0,2.6666666666666665,2.5,5.0,4.0,2.0,1.0,5.0,1.6666666666666667,2.0,1.5,1.5,2.5,3.5
rika ,Sec 1 SD8,3.0,3.0,2.0,2.0,4.2,4.25,4.0,3.0,3.0,3.0,4.0,4.0,4.5,4.0,3.0,2.0,3.0,3.0,5.0,5.0,2.0,2.0,4.5,4.5
Cheryl Mak hui si,Sec 1 SD4,3.0,2.6666666666666665,2.0,2.0,3.4,3.5,3.5,5.0,4.0,3.0,4.0,3.333333333333333,4.0,5.0,4.0,5.0,3.0,5.0,4.0,4.0,4.0,4.0,3.5,3.5
Tay En Xin,Sec 1 SD8,3.0,3.333333333333333,3.5,3.5,3.2,3.25,4.0,3.0,2.5,2.0,3.0,3.0,2.5,5.0,4.0,4.0,3.0,4.0,4.0,4.5,3.0,3.0,3.5,3.0
Arsyil,Sec 1 SD8,4.0,4.333333333333333,2.0,2.0,4.2,3.75,3.0,5.0,4.0,3.0,4.0,4.666666666666667,4.5,5.0,3.0,4.0,3.5,3.0,4.0,4.0,3.0,3.0,4.0,4.5
Bradley Ho,Sec 1 SD8,1.5,3.333333333333333,1.5,1.5,3.8,3.75,5.0,4.0,2.5,1.0,3.0,4.666666666666667,3.0,4.0,2.0,4.0,2.5,3.0,3.0,3.0,1.5,1.5,4.5,4.5
Leland Ong Tian le,Sec 1 SD8,4.5,4.333333333333333,3.0,3.0,4.8,5.0,5.0,4.0,4.0,4.0,4.0,4.666666666666667,4.5,5.0,5.0,5.0,5.0,4.0,5.0,5.0,5.0,5.0,5.0,5.0
Iann seah,Sec 1 SD7,3.0,3.333333333333333,3.0,3.0,4.2,4.25,3.5,3.0,2.5,2.0,4.0,4.0,4.0,5.0,2.0,4.0,4.0,3.0,4.666666666666667,5.0,3.5,3.5,4.0,4.0
Flavia Chan Hui Zhi,Sec 1 SD4,4.0,4.0,3.5,3.5,4.8,5.0,4.5,4.0,3.5,3.0,4.0,4.0,5.0,5.0,3.0,5.0,4.5,4.0,5.0,5.0,4.0,4.0,4.0,4.0
Chua Jia Yu Bruce ,Sec 1 SD4,4.5,4.333333333333333,4.5,4.5,3.8,4.5,4.0,4.0,4.0,4.0,3.0,4.0,3.5,5.0,4.0,4.0,4.5,4.0,4.333333333333333,4.0,4.0,4.0,4.5,4.0
Princeton,Sec 1 SD4,3.5,3.333333333333333,3.0,3.0,3.8,4.25,3.5,3.0,3.0,3.0,3.0,3.0,3.5,4.0,4.0,5.0,4.5,2.0,4.0,4.0,4.0,4.0,3.5,3.0
Isabel loke,Sec 1 SD4,4.0,4.333333333333333,4.5,4.5,4.4,4.75,4.0,5.0,5.0,5.0,4.0,5.0,4.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0
Terh ren chuan,Sec 1 SD4,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0
aleesya,Sec 1 SD8,3.5,3.333333333333333,3.0,3.0,4.2,4.5,4.5,3.0,3.0,3.0,4.0,4.333333333333333,4.5,4.0,3.0,5.0,4.5,5.0,4.333333333333333,4.5,4.0,4.0,5.0,4.5
Rayny soon,Sec 1 SD8,3.0,3.0,2.0,2.0,3.8,4.25,2.5,3.0,2.5,2.0,3.0,3.0,4.0,4.0,1.0,5.0,4.0,3.0,4.0,4.0,2.5,2.5,3.0,3.0
Vanvelno,Sec 1 SD8,1.5,2.6666666666666665,3.0,3.0,3.8,4.0,4.0,5.0,3.0,1.0,5.0,3.6666666666666665,4.5,5.0,3.0,4.0,3.5,4.0,4.333333333333333,4.5,4.0,4.0,3.5,3.5
Ng Zhi Xuan,Sec 1 SD8,4.5,4.333333333333333,4.0,4.0,4.4,5.0,4.5,3.0,3.0,3.0,1.0,4.333333333333333,3.5,5.0,5.0,5.0,5.0,3.0,5.0,5.0,4.0,4.0,5.0,4.5
Aqid,Sec 1 SD7,4.5,4.333333333333333,5.0,5.0,4.8,5.0,4.0,5.0,4.5,4.0,5.0,4.0,4.5,5.0,4.0,5.0,5.0,5.0,5.0,5.0,3.5,3.5,5.0,4.0
abel,Sec 1 SD8,5.0,4.333333333333333,1.0,1.0,4.2,5.0,4.5,3.0,3.5,4.0,2.0,2.333333333333333,3.0,3.0,5.0,5.0,5.0,1.0,5.0,5.0,5.0,5.0,4.5,2.0
Michelle,Sec 1 SD4,4.5,3.333333333333333,3.0,3.0,3.6,4.75,3.0,3.0,3.5,4.0,3.0,3.333333333333333,3.0,5.0,1.0,5.0,5.0,4.0,5.0,5.0,5.0,5.0,4.0,3.5
Aleesya Arianna,Sec 1 SD8,2.5,3.0,3.0,3.0,3.4,4.5,3.5,3.0,3.0,3.0,2.0,3.6666666666666665,2.5,5.0,3.0,4.0,3.5,2.0,4.666666666666667,4.5,3.5,3.5,4.0,3.5
Liang Haiqiao,Sec 1 SD4,3.0,3.0,3.0,3.0,3.6,4.25,3.5,3.0,3.0,3.0,4.0,4.0,3.5,4.0,3.0,4.0,4.5,4.0,4.0,4.0,3.5,3.5,4.0,4.0
Isabella,Sec 1 SD8,3.5,3.0,3.5,3.5,4.2,4.75,3.0,3.0,3.0,3.0,3.0,3.333333333333333,4.0,5.0,3.0,3.0,4.5,3.0,4.0,4.0,3.5,3.5,3.5,3.5
NG yi xuan Eileen ,Sec 1 SD8,3.0,3.333333333333333,3.0,3.0,3.6,3.5,3.0,3.0,4.0,5.0,1.0,3.333333333333333,3.5,4.0,2.0,4.0,3.5,3.0,3.6666666666666665,4.0,2.5,2.5,3.5,3.5
Zoey lakhani,Sec 1 SD8,4.5,4.333333333333333,4.5,4.5,4.2,4.75,4.5,5.0,4.0,3.0,3.0,4.666666666666667,4.0,5.0,4.0,5.0,5.0,4.0,5.0,5.0,3.0,3.0,5.0,4.5
Samuel goh zhuo jun,Sec 1 SD4,4.5,4.666666666666667,5.0,5.0,4.8,4.75,4.5,5.0,5.0,5.0,4.0,4.666666666666667,5.0,5.0,5.0,5.0,4.5,5.0,5.0,5.0,4.5,4.5,4.5,5.0
Amir hamzah,Sec 1 SD8,3.5,3.6666666666666665,3.5,3.5,3.2,3.0,3.0,3.0,3.0,3.0,3.0,3.6666666666666665,3.5,2.0,4.0,5.0,4.0,4.0,3.0,3.5,3.5,3.5,2.5,4.0
Ayden,Sec 1 SD8,4.5,4.333333333333333,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0
Kloe,Sec 1 SD8,4.0,4.0,3.0,3.0,3.4,3.5,4.0,3.0,2.0,1.0,2.0,3.6666666666666665,2.5,5.0,2.0,4.0,4.0,4.0,3.6666666666666665,4.5,4.0,4.0,4.0,3.5
Danmien,Sec 1 SD8,5.0,3.6666666666666665,2.5,2.5,3.8,3.75,4.0,5.0,3.5,2.0,5.0,4.333333333333333,5.0,5.0,5.0,4.0,4.0,5.0,4.333333333333333,4.0,3.5,3.5,3.5,4.5
Jingkai,Sec 1 SD8,4.0,4.0,4.0,4.0,4.6,5.0,3.5,5.0,5.0,5.0,3.0,4.333333333333333,5.0,5.0,5.0,4.0,4.5,5.0,5.0,5.0,5.0,5.0,4.5,4.0
keshmita,Sec 1 SD8,4.0,3.333333333333333,4.0,4.0,3.6,3.75,3.5,2.0,2.5,3.0,3.0,3.0,4.0,5.0,2.0,5.0,4.5,4.0,4.0,4.5,3.5,3.5,2.5,3.0
Naim,Sec 1 SD8,4.0,4.0,4.0,4.0,3.8,3.5,3.5,3.0,3.0,3.0,4.0,4.0,4.0,4.0,3.0,5.0,4.0,4.0,3.6666666666666665,4.0,3.5,3.5,4.0,4.0
juwai,Sec 1 SD8,3.5,3.6666666666666665,3.5,3.5,3.4,3.5,3.5,4.0,4.0,4.0,4.0,4.0,4.0,5.0,4.0,3.0,3.5,3.0,4.333333333333333,4.5,3.0,3.0,4.0,4.0
Yu Xuan,Sec 1 SD8,3.0,2.0,3.0,3.0,3.4,4.0,3.0,2.0,2.5,3.0,4.0,2.0,3.5,4.0,3.0,4.0,4.5,2.0,3.6666666666666665,4.0,3.0,3.0,3.0,2.0
Jairus lee taneo,Sec 1 SD4,3.0,3.333333333333333,4.0,4.0,3.6,3.75,3.0,3.0,3.5,4.0,3.0,3.333333333333333,3.5,5.0,4.0,4.0,3.5,4.0,3.6666666666666665,3.5,4.0,4.0,3.5,3.5
Shalaina,Sec 1 SD1,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.333333333333333,3.5,3.0,3.0,3.0,3.0
Tan Leann,Sec 1 SD1,3.0,2.6666666666666665,3.5,3.5,3.2,3.75,3.5,3.0,3.0,3.0,3.0,3.333333333333333,3.0,4.0,4.0,5.0,3.5,3.0,4.0,4.5,3.5,3.5,3.5,3.5
Sarah,Sec 1 SD1,3.5,3.0,3.0,3.0,3.2,3.5,3.5,3.0,3.0,3.0,3.0,3.0,3.0,5.0,4.0,3.0,3.5,3.0,4.333333333333333,4.5,3.5,3.5,4.0,3.0
Adam,Sec 1 SD1,3.5,4.333333333333333,4.0,4.0,5.0,5.0,3.0,4.0,4.0,4.0,5.0,4.333333333333333,5.0,5.0,5.0,5.0,4.5,3.0,5.0,5.0,5.0,5.0,5.0,4.0
Jayden Tey,Sec 1 SD1,3.5,3.6666666666666665,3.0,3.0,3.4,4.0,4.0,4.0,3.0,2.0,3.0,3.6666666666666665,3.0,5.0,3.0,5.0,4.5,3.0,4.333333333333333,4.5,3.0,3.0,4.0,3.5
Chung Cheng Yang,Sec 1 SD1,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0
Isaac,Sec 1 SD1,4.0,3.6666666666666665,4.0,4.0,3.0,4.25,3.5,5.0,4.0,3.0,5.0,4.0,2.0,5.0,4.0,3.0,4.5,5.0,4.333333333333333,4.5,5.0,5.0,4.5,4.0
Ayden,Sec 1 SD1,3.0,4.666666666666667,3.0,3.0,4.0,3.25,3.0,1.0,1.0,1.0,3.0,4.0,5.0,5.0,4.0,1.0,1.5,1.0,5.0,5.0,2.0,2.0,5.0,3.5
Jolene,Sec 1 SD1,2.0,2.6666666666666665,2.5,2.5,3.4,4.0,3.5,3.0,2.5,2.0,3.0,3.333333333333333,2.5,3.0,2.0,5.0,2.5,2.0,3.6666666666666665,3.5,1.5,1.5,3.5,3.5
Alyson,Sec 1 SD4,2.5,2.6666666666666665,2.5,2.5,3.0,3.0,3.5,3.0,2.5,2.0,4.0,2.6666666666666665,3.0,4.0,2.0,4.0,3.0,3.0,3.333333333333333,3.5,3.0,3.0,3.5,2.5
Xander yen yong xiang,Sec 1 SD1,1.0,1.6666666666666667,1.5,1.5,1.0,1.25,1.0,2.0,1.5,1.0,1.0,1.6666666666666667,1.0,3.0,1.0,1.0,1.5,1.0,1.6666666666666667,2.0,1.5,1.5,1.0,1.0
Aydin luiz,Sec 1 SD1,3.0,3.6666666666666665,4.0,4.0,3.6,4.25,4.0,4.0,3.5,3.0,3.0,3.6666666666666665,3.5,5.0,4.0,5.0,3.0,4.0,4.333333333333333,4.0,3.0,3.0,3.5,3.5
Natalie Aw,Sec 1 SD1,4.5,4.666666666666667,3.0,3.0,4.0,4.0,4.5,5.0,5.0,5.0,5.0,4.0,4.0,5.0,5.0,3.0,4.5,5.0,4.666666666666667,4.5,4.5,4.5,4.0,3.5
Bernice,Sec 1 SD1,3.5,3.6666666666666665,4.0,4.0,3.4,3.5,3.0,3.0,3.5,4.0,4.0,4.0,3.0,4.0,3.0,4.0,3.5,4.0,4.0,4.0,3.5,3.5,3.5,4.0
Aynn,Sec 1 SD1,3.0,3.333333333333333,3.5,3.5,3.6,4.0,4.0,3.0,3.0,3.0,4.0,3.6666666666666665,3.5,5.0,5.0,4.0,4.0,3.0,4.0,4.5,3.0,3.0,4.0,3.5
Khym,Sec 1 SD1,5.0,4.666666666666667,4.5,4.5,5.0,4.75,4.5,5.0,4.5,4.0,5.0,4.333333333333333,5.0,5.0,5.0,5.0,5.0,5.0,4.666666666666667,5.0,4.5,4.5,5.0,4.5
Lim Yu Tong,Sec 1 SD1,1.0,2.6666666666666665,1.5,1.5,4.4,4.75,2.0,3.0,3.0,3.0,4.0,4.0,4.0,5.0,3.0,5.0,2.5,4.0,4.666666666666667,4.5,1.0,1.0,4.0,4.5
Alethea Ang,Sec 1 SD1,3.5,3.6666666666666665,3.0,3.0,4.8,4.5,3.0,3.0,3.0,3.0,5.0,3.6666666666666665,5.0,4.0,4.0,5.0,4.5,3.0,4.333333333333333,4.0,4.5,4.5,3.5,3.5
gysel,Sec 1 SD1,3.5,4.333333333333333,4.0,4.0,4.4,4.25,3.5,4.0,3.5,3.0,5.0,4.333333333333333,3.5,4.0,4.0,4.0,2.5,4.0,4.666666666666667,4.5,4.0,4.0,4.5,4.5
Gareth,Sec 1 SD1,2.5,2.6666666666666665,2.0,2.0,1.8,2.0,2.5,3.0,3.0,3.0,3.0,3.0,1.5,2.0,2.0,4.0,1.0,1.0,2.333333333333333,2.0,3.0,3.0,3.0,3.5
Sharie,Sec 1 SD1,3.5,3.0,3.5,3.5,3.4,4.25,4.0,3.0,3.0,3.0,3.0,3.0,2.5,5.0,4.0,4.0,4.5,3.0,4.333333333333333,4.5,4.0,4.0,3.5,3.5
FENG ZICHEN ,Sec 1 SD1,4.0,4.0,4.0,4.0,4.0,4.0,3.5,4.0,4.0,4.0,4.0,3.6666666666666665,4.0,4.0,3.0,4.0,4.0,4.0,4.0,4.0,3.5,3.5,4.0,3.5
Firas Akid,Sec 1 SD1,2.0,2.333333333333333,2.0,2.0,2.0,2.25,4.0,1.0,2.5,4.0,1.0,1.3333333333333333,1.5,5.0,2.0,4.0,2.5,4.0,3.333333333333333,4.0,1.0,1.0,2.0,1.0
jovie,Sec 1 SD1,4.0,3.6666666666666665,3.5,3.5,4.2,4.5,3.5,4.0,3.5,3.0,4.0,4.0,4.5,4.0,4.0,5.0,5.0,5.0,4.666666666666667,4.5,5.0,5.0,4.0,4.0
Harald,Sec 1 SD2,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0
danish,Sec 1 SD1,4.5,4.333333333333333,4.5,4.5,3.6,4.0,3.0,4.0,4.0,4.0,5.0,3.6666666666666665,3.0,5.0,5.0,3.0,4.0,5.0,4.666666666666667,5.0,4.5,4.5,3.0,3.5
Tan Hong kai,Sec 1 SD2,3.5,3.6666666666666665,3.0,3.0,3.2,3.5,3.5,4.0,4.0,4.0,4.0,3.6666666666666665,3.0,4.0,3.0,3.0,4.0,3.0,3.6666666666666665,3.5,3.5,3.5,3.0,3.5
Annabel ,Sec 1 SD1,4.0,4.333333333333333,5.0,5.0,4.2,4.5,4.0,5.0,5.0,5.0,5.0,4.0,5.0,5.0,5.0,5.0,4.0,5.0,5.0,5.0,5.0,5.0,4.0,3.5
Loh Jia Wen Ewan,Sec 1 SD2,4.5,5.0,5.0,5.0,4.6,5.0,5.0,5.0,4.5,4.0,5.0,5.0,4.0,5.0,5.0,5.0,4.5,4.0,5.0,5.0,5.0,5.0,5.0,5.0
Jerez,Sec 1 SD2,5.0,5.0,5.0,5.0,5.0,4.25,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,4.0,5.0,5.0,4.0,5.0,5.0,5.0,5.0,5.0
Aryan,Sec 1 SD1,4.0,4.0,4.5,4.5,4.4,4.5,5.0,4.0,3.5,3.0,4.0,4.0,4.5,5.0,4.0,4.0,4.0,4.0,4.666666666666667,4.5,4.0,4.0,4.5,4.0
Matthew,Sec 1 SD2,2.0,2.333333333333333,4.5,4.5,2.6,3.25,3.0,2.0,2.5,3.0,4.0,3.6666666666666665,2.0,4.0,2.0,4.0,3.5,2.0,3.333333333333333,3.5,3.0,3.0,3.5,4.0
Lukas Wang,Sec 1 SD2,4.5,4.333333333333333,4.0,4.0,4.4,4.75,4.5,5.0,5.0,5.0,5.0,4.666666666666667,4.5,5.0,5.0,4.0,5.0,5.0,5.0,5.0,4.0,4.0,5.0,4.5
Ellis,Sec 1 SD2,3.5,3.333333333333333,5.0,5.0,3.8,4.5,4.0,4.0,3.5,3.0,5.0,4.333333333333333,4.0,4.0,3.0,5.0,4.5,5.0,4.666666666666667,4.5,4.5,4.5,5.0,4.5
SN Qusyaira MN,Sec 1 SD2,3.0,3.0,3.0,3.0,2.6,3.0,3.0,3.0,3.0,3.0,3.0,3.0,2.0,4.0,3.0,4.0,3.0,3.0,3.0,3.0,2.5,2.5,3.0,3.0
Zara,Sec 1 SD2,3.0,2.6666666666666665,3.0,3.0,3.4,3.25,2.5,3.0,2.5,2.0,4.0,2.6666666666666665,4.0,3.0,3.0,2.0,3.0,4.0,4.333333333333333,4.5,2.5,2.5,3.0,3.0
Rosanne ,Sec 1 SD2,4.5,4.0,3.0,3.0,3.6,4.5,3.0,3.0,3.5,4.0,3.0,3.6666666666666665,3.0,5.0,2.0,4.0,5.0,5.0,5.0,5.0,3.0,3.0,4.0,3.5
isabelle ,Sec 1 SD2,2.5,2.6666666666666665,2.0,2.0,3.6,3.5,3.0,3.0,3.0,3.0,3.0,3.0,4.0,3.0,3.0,5.0,3.5,4.0,3.6666666666666665,4.0,4.0,4.0,3.5,3.0
Lim Yu En,Sec 1 SD1,2.5,2.6666666666666665,3.0,3.0,3.4,3.75,4.5,4.0,3.5,3.0,3.0,3.0,3.0,4.0,3.0,4.0,2.5,3.0,4.333333333333333,4.0,4.0,4.0,4.0,3.0
Sheeya Lau Shih-ya,Sec 1 SD2,4.0,4.666666666666667,3.5,3.5,5.0,4.5,5.0,5.0,4.5,4.0,5.0,5.0,5.0,5.0,4.0,5.0,3.5,3.0,5.0,5.0,4.5,4.5,5.0,5.0
Nayla ,Sec 1 SD2,3.5,3.6666666666666665,2.0,2.0,4.6,4.25,2.0,2.0,3.0,4.0,3.0,2.6666666666666665,5.0,3.0,3.0,3.0,3.5,3.0,4.666666666666667,5.0,4.0,4.0,3.0,3.5
Sherry,Sec 1 SD2,4.0,3.6666666666666665,4.0,4.0,3.8,4.5,3.5,4.0,3.5,3.0,4.0,4.0,3.5,4.0,4.0,5.0,4.5,3.0,5.0,5.0,3.5,3.5,4.0,4.5
Ayra,Sec 1 SD1,4.0,3.6666666666666665,3.0,3.0,3.8,3.75,3.5,4.0,4.0,4.0,4.0,3.6666666666666665,3.5,4.0,3.0,4.0,3.5,4.0,4.333333333333333,4.5,4.0,4.0,4.0,4.0
Mooi enqi,Sec 1 SD2,1.0,2.6666666666666665,1.5,1.5,2.0,1.0,3.0,1.0,1.0,1.0,1.0,2.6666666666666665,2.5,3.0,1.0,2.0,1.0,3.0,1.6666666666666667,2.0,1.0,1.0,2.5,2.0
Lucas Kumar,Sec 1 SD2,4.5,3.6666666666666665,4.5,4.5,4.2,5.0,5.0,4.0,4.0,4.0,2.0,4.0,4.0,5.0,4.0,5.0,5.0,3.0,5.0,5.0,5.0,5.0,5.0,4.0
Roshan,Sec 1 SD2,3.0,2.0,1.0,1.0,2.6,2.5,1.5,5.0,3.0,1.0,1.0,1.3333333333333333,3.0,2.0,3.0,3.0,2.0,2.0,2.0,1.5,3.0,3.0,1.5,1.0
Rajamohan Pradeep Shansthitaa ,Sec 1 SD2,3.5,4.333333333333333,5.0,5.0,4.2,4.5,4.0,5.0,3.5,2.0,5.0,4.0,3.5,5.0,2.0,5.0,2.5,5.0,5.0,5.0,3.0,3.0,4.5,4.0
Maryam,Sec 1 SD1,4.0,3.6666666666666665,4.0,4.0,4.2,4.75,4.5,3.0,3.5,4.0,4.0,3.0,4.0,5.0,4.0,3.0,4.5,4.0,4.333333333333333,4.0,4.5,4.5,4.5,3.0
Belinda,Sec 1 SD2,3.5,3.6666666666666665,3.5,3.5,3.8,3.75,4.0,3.0,3.5,4.0,3.0,3.333333333333333,3.5,3.0,3.0,4.0,3.5,4.0,3.6666666666666665,4.0,3.5,3.5,4.0,3.5
Ravilla naveen gowtham krishna,Sec 1 SD1,3.0,3.6666666666666665,3.0,3.0,3.0,3.25,4.5,4.0,4.0,4.0,4.0,4.0,2.5,5.0,4.0,4.0,3.5,3.0,3.6666666666666665,4.0,4.5,4.5,4.0,4.0
Ang En Ning,Sec 1 SD1,4.0,3.6666666666666665,3.0,3.0,3.6,3.75,4.0,4.0,4.0,4.0,5.0,4.0,3.0,5.0,3.0,4.0,3.0,2.0,4.666666666666667,4.5,3.0,3.0,4.0,4.5
Katrice Koo,Sec 1 SD2,5.0,4.333333333333333,3.0,3.0,4.4,4.75,4.5,4.0,4.5,5.0,4.0,4.333333333333333,4.5,5.0,2.0,4.0,5.0,5.0,5.0,5.0,4.0,4.0,5.0,4.5
Gladys Tan,Sec 1 SD2,3.0,2.333333333333333,3.5,3.5,3.8,4.0,4.0,3.0,3.0,3.0,3.0,2.333333333333333,4.0,4.0,3.0,4.0,4.0,3.0,4.333333333333333,4.5,2.5,2.5,4.0,2.5
Renu,Sec 1 SD2,4.5,4.333333333333333,5.0,5.0,5.0,5.0,4.5,5.0,4.5,4.0,3.0,4.666666666666667,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,4.5,4.5,5.0,5.0
Aqil Ashraf,Sec 1 SD1,5.0,4.333333333333333,4.5,4.5,4.2,4.25,4.0,4.0,4.0,4.0,4.0,4.0,4.5,5.0,4.0,5.0,5.0,4.0,4.333333333333333,4.5,4.5,4.5,4.5,4.0
Joelle,Sec 1 SD2,2.5,2.6666666666666665,3.0,3.0,3.0,3.25,3.0,3.0,3.0,3.0,3.0,3.333333333333333,3.0,4.0,4.0,2.0,3.0,3.0,3.6666666666666665,3.5,3.0,3.0,3.5,3.5
Tharshini,Sec 1 SD1,5.0,3.0,5.0,5.0,4.4,5.0,4.5,3.0,4.0,5.0,5.0,3.333333333333333,5.0,5.0,5.0,2.0,5.0,5.0,5.0,5.0,4.5,4.5,5.0,4.0
Jowell Lee ,Sec 1 SD1,2.5,3.333333333333333,3.5,3.5,3.6,3.75,4.5,3.0,2.5,2.0,3.0,4.0,3.0,4.0,2.0,5.0,3.0,4.0,4.666666666666667,5.0,3.0,3.0,5.0,4.0
Naufal,Sec 1 SD2,3.0,3.333333333333333,2.5,2.5,3.2,3.0,3.0,3.0,3.0,3.0,3.0,3.6666666666666665,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.5,3.5,3.0,4.0
Trevis Ho Zhen Hao,Sec 1 SD2,2.5,3.333333333333333,3.0,3.0,3.2,3.5,3.5,3.0,3.0,3.0,4.0,3.0,2.5,4.0,2.0,3.0,3.0,3.0,3.333333333333333,3.5,2.5,2.5,3.5,3.0
Tay Hao yu ,Sec 1 SD2,3.5,3.0,3.0,3.0,2.8,2.75,3.5,4.0,3.5,3.0,2.0,2.6666666666666665,3.0,3.0,3.0,2.0,4.0,3.0,3.333333333333333,4.0,2.5,2.5,3.0,2.5
Ariq Aryan,Sec 1 SD2,3.5,3.6666666666666665,4.0,4.0,4.2,4.25,4.5,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,5.0,4.0,4.0,4.666666666666667,5.0,3.5,3.5,5.0,4.0
Breyonn,Sec 1 SD1,4.0,3.6666666666666665,3.0,3.0,3.2,3.0,2.5,3.0,3.0,3.0,4.0,3.6666666666666665,3.5,4.0,3.0,4.0,3.5,3.0,3.333333333333333,3.5,3.0,3.0,3.5,3.5
Zach Adriel,Sec 1 SD2,4.5,5.0,4.0,4.0,5.0,5.0,4.5,4.0,3.5,3.0,5.0,4.333333333333333,5.0,5.0,5.0,5.0,4.5,5.0,4.666666666666667,4.5,5.0,5.0,5.0,4.0
Nur Qaireen Airis,Sec 1 SD2,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0
Janice,Sec 1 SD2,3.0,3.0,3.0,3.0,3.4,3.0,3.5,3.0,3.0,3.0,3.0,2.6666666666666665,4.0,3.0,3.0,3.0,3.0,3.0,3.333333333333333,3.5,2.5,2.5,3.0,2.5
Velencia ,Sec 1 SD2,3.0,3.333333333333333,3.0,3.0,3.2,2.75,3.5,4.0,3.5,3.0,4.0,3.333333333333333,4.0,3.0,3.0,4.0,3.0,3.0,3.6666666666666665,4.0,3.5,3.5,3.5,3.0
Jazel Pang,Sec 1 SD2,3.0,3.333333333333333,4.0,4.0,3.8,4.5,4.0,4.0,3.5,3.0,4.0,3.333333333333333,3.5,3.0,3.0,5.0,4.0,4.0,5.0,5.0,5.0,5.0,4.0,3.5
Jayden ng,Sec 1 SD2,2.5,3.333333333333333,4.0,4.0,2.4,3.0,2.0,1.0,1.5,2.0,3.0,2.6666666666666665,1.5,2.0,3.0,2.0,2.0,4.0,3.6666666666666665,3.5,2.0,2.0,2.0,2.0
Teresa,Sec 1 SD5,2.5,3.0,3.0,3.0,3.8,3.75,3.5,3.0,3.0,3.0,2.0,3.333333333333333,4.0,4.0,3.0,3.0,2.5,2.0,4.0,4.0,3.5,3.5,4.0,3.5
Kayden,Sec 1 SD3,3.0,3.6666666666666665,2.0,2.0,3.2,3.75,3.0,4.0,4.5,5.0,3.0,3.0,1.5,3.0,2.0,1.0,3.0,3.0,2.6666666666666665,2.0,4.0,4.0,3.5,3.0
Puteri Nur Qaisarah,Sec 1 SD3,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0
Ethan lim,Sec 1 SD5,3.5,3.0,4.5,4.5,3.4,4.25,5.0,3.0,2.5,2.0,4.0,2.333333333333333,2.5,4.0,4.0,3.0,2.0,5.0,5.0,5.0,3.5,3.5,4.5,2.5
Sophie,Sec 1 SD5,3.0,3.333333333333333,3.0,3.0,3.6,3.75,3.5,4.0,3.5,3.0,4.0,4.0,3.5,5.0,3.0,3.0,3.5,3.0,3.6666666666666665,4.0,4.0,4.0,4.0,4.0
Elizabeth Summers Leow,Sec 1 SD5,3.5,3.333333333333333,3.5,3.5,3.0,3.25,3.5,3.0,3.0,3.0,3.0,3.6666666666666665,3.0,4.0,3.0,4.0,3.5,3.0,4.0,4.0,3.5,3.5,4.0,3.5
Carin,Sec 1 SD5,4.0,4.0,4.5,4.5,4.2,3.75,3.5,4.0,4.0,4.0,3.0,4.0,4.5,5.0,4.0,5.0,3.5,3.0,4.333333333333333,4.5,3.0,3.0,4.5,4.0
Javier,Sec 1 SD5,3.0,3.0,3.5,3.5,3.2,3.25,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.333333333333333,3.5,3.0,3.0,3.0,3.0
ayra,Sec 1 SD3,5.0,4.333333333333333,5.0,5.0,4.6,4.0,4.5,5.0,4.0,3.0,5.0,4.333333333333333,5.0,5.0,5.0,4.0,4.0,5.0,4.666666666666667,5.0,4.5,4.5,4.5,4.5
Shessan Singaperumal,Sec 1 SD5,2.0,2.0,2.0,2.0,2.2,2.5,2.0,2.0,2.0,2.0,2.0,2.0,2.0,4.0,2.0,2.0,2.0,2.0,2.333333333333333,2.0,2.0,2.0,2.0,2.0
Kao Kai Xiang Nicholas,Sec 1 SD3,5.0,4.666666666666667,5.0,5.0,4.4,5.0,5.0,4.0,4.0,4.0,4.0,4.666666666666667,3.5,5.0,4.0,5.0,5.0,5.0,5.0,5.0,4.5,4.5,5.0,5.0
Andrei,Sec 1 SD5,4.0,3.6666666666666665,3.5,3.5,3.8,3.75,3.0,5.0,4.5,4.0,5.0,3.6666666666666665,4.0,5.0,5.0,3.0,4.0,5.0,4.0,4.0,4.5,4.5,3.5,4.0
Zeyric,Sec 1 SD3,3.5,4.0,3.0,3.0,3.8,4.0,4.0,4.0,3.5,3.0,4.0,4.0,3.5,5.0,3.0,4.0,3.5,4.0,4.0,4.0,3.5,3.5,4.0,4.0
Tan Teck yang,Sec 1 SD5,3.5,3.0,2.5,2.5,2.4,2.75,4.0,4.0,3.0,2.0,2.0,2.0,1.5,3.0,2.0,5.0,3.0,3.0,2.333333333333333,2.5,2.5,2.5,4.0,2.0
Lim Zheng kai,Sec 1 SD5,3.0,2.6666666666666665,3.0,3.0,2.8,3.5,2.0,3.0,3.0,3.0,4.0,2.333333333333333,2.5,5.0,4.0,4.0,4.0,3.0,3.333333333333333,3.5,3.0,3.0,3.5,2.5
Alaina fam Li yi ,Sec 1 SD3,4.5,4.333333333333333,4.5,4.5,4.2,5.0,4.5,3.0,2.5,2.0,4.0,4.666666666666667,3.5,5.0,3.0,5.0,5.0,3.0,5.0,5.0,5.0,5.0,4.0,4.5
careda,Sec 1 SD5,5.0,5.0,4.0,4.0,3.4,5.0,4.0,5.0,5.0,5.0,5.0,4.666666666666667,1.0,5.0,3.0,5.0,5.0,2.0,5.0,5.0,4.5,4.5,5.0,4.5
Shernice,Sec 1 SD5,3.5,3.0,3.0,3.0,3.2,4.0,3.5,3.0,3.5,4.0,3.0,3.333333333333333,2.5,4.0,3.0,4.0,4.0,3.0,4.0,4.0,4.0,4.0,3.5,3.5
Rachel Liew Rui Qi,Sec 1 SD3,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.5,3.5,3.0,3.0
Khalifa,Sec 1 SD3,4.0,4.0,4.0,4.0,3.6,4.25,3.5,4.0,4.0,4.0,4.0,4.0,3.0,4.0,4.0,3.0,4.5,4.0,4.0,4.0,4.0,4.0,4.0,4.0
Fatima binte mohammad syukor,Sec 1 SD3,5.0,5.0,4.0,4.0,4.4,4.5,5.0,3.0,3.5,4.0,5.0,4.666666666666667,3.5,5.0,3.0,2.0,5.0,3.0,4.333333333333333,5.0,5.0,5.0,5.0,4.5
Herlizia ,Sec 1 SD3,3.0,3.0,3.0,3.0,3.4,3.75,3.5,3.0,3.0,3.0,4.0,3.6666666666666665,3.5,4.0,4.0,4.0,3.5,3.0,4.333333333333333,4.5,3.5,3.5,4.0,4.0
Chris Joby,Sec 1 SD5,4.5,4.0,4.5,4.5,4.6,4.75,3.5,3.0,3.5,4.0,4.0,4.333333333333333,4.5,5.0,2.0,5.0,5.0,3.0,4.333333333333333,4.5,4.5,4.5,4.0,4.5
aarti suresh,Sec 1 SD3,3.0,3.333333333333333,3.5,3.5,4.8,5.0,4.5,5.0,4.0,3.0,5.0,4.666666666666667,5.0,5.0,3.0,4.0,4.5,3.0,5.0,5.0,3.0,3.0,5.0,5.0
Zaira,Sec 1 SD3,4.0,3.6666666666666665,3.0,3.0,3.6,3.5,3.0,3.0,3.0,3.0,4.0,3.333333333333333,4.0,5.0,4.0,4.0,4.0,3.0,4.333333333333333,5.0,4.5,4.5,4.0,3.0
Sharlyn,Sec 1 SD5,4.0,4.0,4.0,4.0,4.8,4.75,4.5,4.0,4.0,4.0,4.0,4.0,5.0,4.0,4.0,4.0,4.0,4.0,4.666666666666667,4.5,4.5,4.5,4.5,4.0
nur nazeerah,Sec 1 SD5,4.0,4.0,4.0,4.0,4.2,5.0,4.0,4.0,3.5,3.0,4.0,5.0,4.0,5.0,3.0,5.0,4.5,4.0,5.0,5.0,3.5,3.5,4.5,5.0
Iman,Sec 1 SD5,4.5,4.333333333333333,4.0,4.0,4.4,4.25,4.5,4.0,3.5,3.0,5.0,4.666666666666667,4.5,5.0,5.0,5.0,4.5,5.0,4.666666666666667,5.0,3.0,3.0,4.5,4.5
Shahina naureen,Sec 1 SD3,4.5,4.0,4.5,4.5,4.4,4.25,5.0,5.0,5.0,5.0,3.0,4.666666666666667,4.5,5.0,5.0,3.0,4.0,4.0,5.0,5.0,4.0,4.0,4.5,5.0
Josie,Sec 1 SD3,3.0,3.333333333333333,3.5,3.5,3.0,3.5,2.5,3.0,3.0,3.0,2.0,3.6666666666666665,2.5,4.0,3.0,5.0,3.5,3.0,4.333333333333333,4.5,3.5,3.5,3.5,4.0
Lipika Thishana,Sec 1 SD3,3.5,3.333333333333333,4.0,4.0,4.2,4.0,3.5,3.0,2.5,2.0,4.0,2.6666666666666665,5.0,4.0,2.0,4.0,4.5,3.0,4.666666666666667,5.0,3.0,3.0,3.5,2.5
Sofea eliyanti binte mohamed omar,Sec 1 SD3,4.5,3.0,3.5,3.5,2.8,4.0,3.0,2.0,2.5,3.0,4.0,3.6666666666666665,2.5,5.0,4.0,4.0,5.0,3.0,4.333333333333333,4.5,3.0,3.0,3.0,4.0
Zeng yin,Sec 1 SD3,3.0,2.6666666666666665,3.0,3.0,2.8,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,4.0,3.0,3.0,3.0,3.0,4.0,4.5,4.0,4.0,3.0,3.0
Lydia,Sec 1 SD5,3.0,2.6666666666666665,3.0,3.0,3.4,4.0,5.0,4.0,3.5,3.0,5.0,3.6666666666666665,3.5,5.0,4.0,5.0,3.5,5.0,4.333333333333333,4.5,3.0,3.0,4.5,4.0
Wayne,Sec 1 SD5,4.5,4.0,3.5,3.5,3.0,2.5,2.0,4.0,3.5,3.0,3.0,3.6666666666666665,3.5,2.0,3.0,2.0,3.0,2.0,2.333333333333333,2.0,2.0,2.0,2.5,3.5
matin,Sec 1 SD5,1.0,2.0,2.0,2.0,2.6,4.0,3.0,4.0,4.0,4.0,1.0,3.6666666666666665,2.0,5.0,3.0,1.0,3.0,2.0,4.666666666666667,5.0,2.0,2.0,3.0,4.0
Junyu,Sec 1 SD3,2.5,2.333333333333333,3.0,3.0,3.2,2.75,3.0,3.0,2.5,2.0,3.0,3.0,3.5,4.0,2.0,2.0,2.5,2.0,3.0,3.0,2.5,2.5,3.0,3.5
joel lam,Sec 1 SD3,4.0,2.6666666666666665,4.0,4.0,3.6,4.25,2.5,3.0,2.0,1.0,5.0,2.333333333333333,4.0,4.0,2.0,4.0,4.5,4.0,4.333333333333333,4.5,3.5,3.5,3.5,2.5
Low Jia rui,Sec 1 SD3,3.5,3.0,5.0,5.0,3.6,3.25,2.5,3.0,2.5,2.0,3.0,3.6666666666666665,4.0,5.0,5.0,2.0,4.5,4.0,3.0,4.0,4.5,4.5,3.0,4.0
Mia,Sec 1 SD5,3.5,2.6666666666666665,2.0,2.0,3.6,4.0,3.0,2.0,2.0,2.0,4.0,3.333333333333333,4.0,5.0,2.0,2.0,5.0,2.0,4.333333333333333,4.5,4.0,4.0,3.5,3.5
Krystal,Sec 1 SD3,3.5,3.333333333333333,3.0,3.0,3.6,4.5,4.0,4.0,4.5,5.0,4.0,3.6666666666666665,2.5,5.0,3.0,5.0,4.0,3.0,5.0,5.0,3.5,3.5,4.0,4.0
Zhi Ying,Sec 1 SD5,5.0,2.333333333333333,2.5,2.5,2.6,1.5,4.0,1.0,1.5,2.0,5.0,2.6666666666666665,4.0,5.0,1.0,5.0,3.0,5.0,2.6666666666666665,3.5,2.0,2.0,3.0,3.5
Yu yan,Sec 1 SD3,3.5,4.0,3.5,3.5,4.4,4.75,4.0,4.0,3.5,3.0,4.0,4.333333333333333,4.0,3.0,4.0,4.0,3.5,4.0,4.333333333333333,4.0,3.0,3.0,4.5,4.5
Rania,Sec 1 SD5,1.5,2.333333333333333,2.5,2.5,2.6,2.75,3.5,2.0,2.0,2.0,1.0,3.333333333333333,2.0,4.0,1.0,4.0,2.0,3.0,3.333333333333333,3.5,2.5,2.5,3.5,3.5
Muhammad Shaqyl Izz,Sec 1 SD3,2.0,2.333333333333333,2.5,2.5,2.4,2.75,2.5,3.0,3.0,3.0,2.0,4.0,2.5,4.0,2.0,1.0,2.5,3.0,3.0,3.0,2.0,2.0,2.5,4.5
Nur Marsya ,Sec 1 SD5,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0
Paidipati dhathri,Sec 1 SD3,5.0,4.333333333333333,4.5,4.5,3.6,4.0,4.5,4.0,4.5,5.0,4.0,3.6666666666666665,3.0,5.0,4.0,5.0,4.5,3.0,4.333333333333333,4.5,4.0,4.0,4.5,3.5
Dinie izdihar,Sec 1 SD5,3.0,3.333333333333333,3.0,3.0,3.4,3.75,3.5,3.0,3.0,3.0,4.0,3.6666666666666665,3.0,4.0,3.0,4.0,3.0,4.0,4.0,3.5,3.0,3.0,3.0,4.0
Tiffany Tan,Sec 1 SD5,3.0,3.333333333333333,4.0,4.0,3.8,3.75,3.5,3.0,3.5,4.0,4.0,3.0,3.5,4.0,4.0,5.0,3.5,3.0,4.0,4.5,4.0,4.0,3.5,3.0
dharan,Sec 1 SD1,4.0,4.666666666666667,5.0,5.0,4.6,5.0,5.0,5.0,5.0,5.0,5.0,4.666666666666667,4.0,5.0,5.0,5.0,4.5,5.0,4.666666666666667,4.5,4.5,4.5,5.0,4.5
Alishba,Sec 1 SD5,1.5,1.3333333333333333,1.5,1.5,1.6,1.25,2.5,2.0,1.5,1.0,1.0,1.6666666666666667,2.0,4.0,2.0,4.0,2.0,2.0,2.6666666666666665,3.5,1.0,1.0,2.0,2.0
Darren Yap Wen Le,Sec 1 SD5,3.0,3.333333333333333,3.0,3.0,3.4,3.25,3.5,4.0,3.5,3.0,3.0,4.0,4.0,5.0,4.0,4.0,3.5,3.0,4.0,4.5,3.0,3.0,4.0,4.0
Rayyan Hakim,Sec 1 SD5,3.0,3.0,2.5,2.5,3.2,3.5,3.0,4.0,3.5,3.0,3.0,3.333333333333333,3.0,4.0,5.0,3.0,3.0,4.0,3.6666666666666665,3.5,3.5,3.5,3.5,3.5
Aqil,Sec 1 SD5,2.5,3.333333333333333,2.5,2.5,3.4,2.75,3.0,3.0,2.5,2.0,3.0,3.333333333333333,3.5,3.0,2.0,5.0,2.0,4.0,4.0,4.0,3.5,3.5,4.0,3.0
Xavier,Sec 1 SD3,3.5,3.0,3.5,3.5,3.0,3.25,3.0,3.0,3.0,3.0,3.0,3.0,3.0,4.0,3.0,5.0,4.0,3.0,3.333333333333333,3.5,3.0,3.0,3.0,3.0
Shaw,Sec 1 SD3,3.0,3.0,3.0,3.0,3.2,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.5,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0
gui gee sen,Sec 1 SD6,1.0,2.0,2.5,2.5,2.8,2.25,4.5,4.0,4.0,4.0,3.0,3.6666666666666665,2.5,4.0,2.0,3.0,1.0,3.0,3.0,3.0,1.0,1.0,4.5,5.0
Mateen haikal ,Sec 1 SD6,5.0,4.0,3.0,3.0,4.0,4.5,4.0,3.0,3.0,3.0,5.0,2.333333333333333,3.5,5.0,5.0,3.0,5.0,3.0,4.666666666666667,4.5,4.0,4.0,5.0,2.5
kaley loh,Sec 1 SD6,5.0,5.0,5.0,5.0,5.0,5.0,5.0,5.0,4.5,4.0,5.0,5.0,5.0,5.0,5.0,4.0,5.0,5.0,5.0,5.0,4.5,4.5,5.0,5.0
Shun long ,Sec 1 SD3,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0
Vernise Ong Jie Ning,Sec 1 SD6,4.5,3.333333333333333,2.0,2.0,3.4,3.0,3.0,3.0,3.0,3.0,1.0,2.333333333333333,3.0,4.0,3.0,5.0,3.5,3.0,2.333333333333333,2.5,2.0,2.0,2.5,3.0
Tan kristel,Sec 1 SD6,4.5,4.0,4.0,4.0,4.8,5.0,4.0,3.0,3.0,3.0,5.0,4.333333333333333,5.0,5.0,4.0,5.0,5.0,4.0,4.666666666666667,4.5,2.5,2.5,5.0,4.5
Hariaksha,Sec 1 SD3,3.0,4.333333333333333,4.0,4.0,4.4,4.25,4.5,5.0,4.5,4.0,5.0,4.666666666666667,3.5,5.0,3.0,5.0,3.0,5.0,4.666666666666667,5.0,3.5,3.5,5.0,4.5
Natsumi,Sec 1 SD6,2.5,3.0,2.0,2.0,3.0,3.5,3.5,2.0,2.0,2.0,2.0,4.333333333333333,2.5,4.0,2.0,5.0,3.5,2.0,4.0,4.5,1.5,1.5,4.0,4.5
Yaadevee sri,Sec 1 SD6,5.0,5.0,5.0,5.0,4.6,4.5,4.5,4.0,4.5,5.0,2.0,5.0,4.5,5.0,5.0,5.0,4.5,4.0,4.666666666666667,4.5,5.0,5.0,4.0,5.0
Dian Juwita Amani Binte Jumahat,Sec 1 SD6,4.5,3.333333333333333,4.0,4.0,4.2,4.5,4.5,5.0,4.5,4.0,5.0,4.333333333333333,4.5,5.0,5.0,4.0,5.0,5.0,4.666666666666667,5.0,5.0,5.0,5.0,5.0
Sravi suryadevara,Sec 1 SD6,5.0,4.333333333333333,4.5,4.5,4.6,5.0,5.0,4.0,4.0,4.0,4.0,2.6666666666666665,4.0,5.0,2.0,4.0,5.0,3.0,5.0,5.0,4.0,4.0,5.0,2.5
Iden lim,Sec 1 SD6,3.0,4.0,3.0,3.0,3.4,3.75,4.0,4.0,3.5,3.0,4.0,4.333333333333333,3.0,5.0,3.0,3.0,3.5,3.0,4.333333333333333,4.5,4.0,4.0,4.0,4.0
Delwin chong ,Sec 1 SD6,4.0,4.0,4.0,4.0,4.0,3.5,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,3.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0
Isabel Teo Kai Xin,Sec 1 SD6,3.5,3.333333333333333,3.5,3.5,3.6,4.25,4.0,4.0,3.5,3.0,3.0,4.0,3.0,5.0,3.0,5.0,4.5,3.0,4.333333333333333,4.5,3.5,3.5,4.5,4.5
Jayden chia,Sec 1 SD6,3.5,3.333333333333333,4.5,4.5,4.2,4.5,3.5,3.0,2.5,2.0,4.0,4.0,4.0,4.0,3.0,3.0,4.5,4.0,4.333333333333333,4.5,3.0,3.0,4.0,4.5
Amirah Binte Mohammad Ansor ,Sec 1 SD6,3.5,3.333333333333333,2.5,2.5,4.8,5.0,3.5,4.0,3.0,2.0,3.0,4.333333333333333,5.0,5.0,5.0,5.0,4.5,5.0,4.666666666666667,4.5,3.0,3.0,4.0,5.0
Low yu cheng ethan,Sec 1 SD6,3.0,3.6666666666666665,3.5,3.5,3.8,2.75,3.5,4.0,3.0,2.0,4.0,3.6666666666666665,4.5,4.0,3.0,3.0,3.0,3.0,2.333333333333333,2.5,3.5,3.5,3.0,3.5
Lara ,Sec 1 SD6,2.5,2.6666666666666665,3.0,3.0,3.0,3.75,4.0,2.0,2.5,3.0,1.0,3.333333333333333,2.5,4.0,3.0,4.0,4.0,4.0,3.6666666666666665,4.0,5.0,5.0,3.5,3.5
jaeren,Sec 1 SD5,5.0,4.333333333333333,5.0,5.0,3.4,4.25,4.0,4.0,4.5,5.0,5.0,3.6666666666666665,2.5,5.0,4.0,4.0,5.0,5.0,4.0,4.0,5.0,5.0,4.0,3.5
Neo jun chen,Sec 1 SD6,2.0,3.0,3.0,3.0,2.8,3.0,3.5,5.0,3.5,2.0,4.0,3.333333333333333,3.0,4.0,3.0,5.0,2.0,4.0,4.666666666666667,5.0,2.0,2.0,4.0,3.5
Aislinn Tan,Sec 1 SD6,4.0,4.0,4.5,4.5,5.0,5.0,5.0,4.0,4.0,4.0,4.0,4.333333333333333,5.0,5.0,4.0,5.0,5.0,4.0,5.0,5.0,4.5,4.5,5.0,4.5
Yu Zit,Sec 1 SD6,3.0,3.333333333333333,3.0,3.0,2.6,3.0,4.0,3.0,2.5,2.0,3.0,3.333333333333333,2.0,4.0,3.0,3.0,3.0,3.0,3.6666666666666665,4.0,3.0,3.0,4.0,3.0
Kooguosen,Sec 1 SD6,2.5,3.333333333333333,3.5,3.5,3.4,3.5,3.5,3.0,3.5,4.0,4.0,3.333333333333333,3.5,4.0,4.0,2.0,2.5,4.0,4.333333333333333,4.5,3.5,3.5,3.0,3.0
Varman,Sec 1 SD6,3.5,3.6666666666666665,2.0,2.0,4.0,3.5,2.5,5.0,4.5,4.0,3.0,3.333333333333333,3.5,5.0,4.0,5.0,3.0,3.0,4.0,4.5,4.0,4.0,4.0,4.0
Janna sim,Sec 1 SD6,3.0,3.0,3.0,3.0,3.0,2.5,3.0,2.0,2.0,2.0,2.0,2.333333333333333,3.0,5.0,3.0,3.0,3.0,2.0,3.6666666666666665,4.5,2.0,2.0,3.0,2.5
halina,Sec 1 SD6,3.5,3.0,4.0,4.0,4.0,4.25,3.0,5.0,5.0,5.0,3.0,3.333333333333333,4.0,5.0,5.0,4.0,5.0,4.0,3.333333333333333,3.5,4.0,4.0,3.0,3.0
Keshav Bhardwaj,Sec 1 SD6,4.0,4.0,4.0,4.0,4.0,4.25,4.0,3.0,3.5,4.0,5.0,4.666666666666667,4.0,4.0,4.0,4.0,4.0,3.0,4.333333333333333,4.0,3.5,3.5,4.0,5.0
Li en ,Sec 1 SD6,4.0,4.0,3.5,3.5,3.6,4.0,3.5,4.0,3.0,2.0,3.0,4.0,3.0,4.0,4.0,2.0,4.0,4.0,3.6666666666666665,3.5,4.0,4.0,3.0,4.0
Jaylon,Sec 1 SD6,3.0,3.6666666666666665,3.0,3.0,3.2,3.25,3.0,2.0,2.0,2.0,1.0,3.333333333333333,2.5,5.0,3.0,1.0,3.0,1.0,3.0,3.5,3.0,3.0,3.0,3.5
Soleha,Sec 1 SD6,3.5,3.0,3.0,3.0,3.8,4.5,2.5,3.0,2.0,1.0,2.0,4.0,3.5,4.0,2.0,5.0,4.5,3.0,4.0,3.5,3.0,3.0,4.0,4.0
Lee khai jie ,Sec 1 SD3,3.5,3.6666666666666665,4.0,4.0,3.4,3.75,4.0,4.0,4.0,4.0,4.0,4.0,3.5,4.0,3.0,3.0,4.5,3.0,4.0,4.0,3.5,3.5,4.0,4.0
Myra narayanan,Sec 1 SD5,2.0,2.6666666666666665,1.5,1.5,3.4,3.0,3.5,2.0,2.0,2.0,2.0,4.0,4.5,4.0,2.0,3.0,2.5,2.0,4.0,4.0,3.0,3.0,4.0,3.5
Wan Zun Ho,Sec 1 SD6,4.0,4.666666666666667,4.5,4.5,4.4,4.75,4.0,4.0,3.5,3.0,5.0,4.666666666666667,4.0,5.0,4.0,5.0,4.5,4.0,5.0,5.0,4.5,4.5,4.0,4.5
Darynn Lee Xin Hui,Sec 1 SD6,4.5,4.0,3.5,3.5,2.8,3.25,2.0,4.0,3.5,3.0,3.0,2.6666666666666665,2.5,4.0,3.0,3.0,4.0,2.0,4.0,4.0,3.5,3.5,3.0,2.5
Ashton goh,Sec 1 SD6,3.5,3.6666666666666665,4.0,4.0,3.2,3.25,3.5,4.0,4.0,4.0,3.0,4.0,3.0,4.0,3.0,5.0,2.5,3.0,4.666666666666667,5.0,2.0,2.0,4.0,4.0
Herlina,Sec 1 SD6,3.0,3.0,3.0,3.0,3.2,3.0,3.0,3.0,3.0,3.0,5.0,3.0,3.5,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0,3.0
Zi yi,Sec 1 SD6,3.0,1.6666666666666667,1.5,1.5,1.2,1.25,1.5,5.0,5.0,5.0,2.0,2.333333333333333,1.0,5.0,5.0,1.0,2.0,3.0,3.333333333333333,4.5,2.0,2.0,2.5,3.0
`;
const [visualType, setVisualType] = useState('📊 Overview & Insights');
const [selectedCategory, setSelectedCategory] = useState('All');
const [selectedClass, setSelectedClass] = useState('All');
const [selectedStudent, setSelectedStudent] = useState('All');
const students = useMemo(() => {
try {
const lines = csvData.trim().split('\n');
const headers = lines[0].split(',');
const parsedStudents = lines.slice(1).map(line => {
const values = line.split(',');
const student = {
name: values[0]?.trim() || '',
class: values[1]?.trim() || ''
};
headers.slice(2).forEach((header, idx) => {
student[header.trim()] = parseFloat(values[idx + 2]) || 0;
});
return student;
}).filter(s => s.name && s.class);
console.log('✅ Loaded', parsedStudents.length, 'students');
return parsedStudents;
} catch (err) {
console.error('❌ Error:', err);
return [];
}
}, []);
const metrics = useMemo(() => {
if (students.length === 0) return { totalSubmissions: 0, classes: 0, potentialLeaders: 0, interventionCases: 0 };
const totalSubmissions = students.length;
const classes = new Set(students.map(s => s.class)).size;
const potentialLeaders = students.filter(s => {
const avg = ALL_TRAITS.reduce((sum, trait) => sum + (s[trait] || 0), 0) / ALL_TRAITS.length;
return avg > 4.0;
}).length;
const interventionCases = students.filter(s =>
ALL_TRAITS.some(trait => (s[trait] || 0) < 2.5)
).length;
return { totalSubmissions, classes, potentialLeaders, interventionCases };
}, [students]);
const filteredStudents = useMemo(() => {
let filtered = students;
if (selectedClass !== 'All') {
filtered = filtered.filter(s => s.class === selectedClass);
}
if (selectedStudent !== 'All') {
filtered = filtered.filter(s => s.name === selectedStudent);
}
return filtered;
}, [students, selectedClass, selectedStudent]);
const classList = useMemo(() =>
['All', ...Array.from(new Set(students.map(s => s.class)))].sort(),
[students]
);
const studentList = useMemo(() => {
const baseStudents = selectedClass === 'All'
? students
: students.filter(s => s.class === selectedClass);
return ['All', ...baseStudents.map(s => s.name)];
}, [students, selectedClass]);
const resetFilters = () => {
setVisualType('📊 Overview & Insights');
setSelectedCategory('All');
setSelectedClass('All');
setSelectedStudent('All');
};
// FIXED: VIA Category filter now works properly
const getTraitsByCategory = () => {
if (selectedCategory === 'All') return ALL_TRAITS;
return VIA_CATEGORIES[selectedCategory] || [];
};
const getHeatColor = (value) => {
if (value >= 4.5) return 'heat-green';
if (value >= 4.0) return 'heat-green-light';
if (value >= 3.5) return 'heat-yellow';
if (value >= 3.0) return 'heat-orange';
if (value >= 2.5) return 'heat-orange-dark';
return 'heat-red';
};
const isSingleStudent = selectedStudent !== 'All' && filteredStudents.length === 1;
// 1. Overview & Insights - USES VIA CATEGORY FILTER
const renderOverview = () => {
const traits = getTraitsByCategory(); // FIXED: Now uses category filter
const avgScores = traits.map(trait => ({
trait,
avg: filteredStudents.reduce((sum, s) => sum + (s[trait] || 0), 0) / filteredStudents.length
})).sort((a, b) => b.avg - a.avg);
const top3 = avgScores.slice(0, 3);
const bottom3 = avgScores.slice(-3).reverse();
return (
<div>
<div className="top-bottom-grid">
<div className="strength-card">
<h3>🏆 Top 3 Strengths</h3>
{top3.map((item, idx) => (
<div key={idx} className="trait-item">
<div className="trait-header">
<span className="trait-name">{item.trait}</span>
<span className="trait-score green">{item.avg.toFixed(2)}</span>
</div>
<div className="progress-bar">
<div className="progress-fill green" style={{ width: `${(item.avg / 5) * 100}%` }} />
</div>
</div>
))}
</div>
<div className="growth-card">
<h3>📈 Growth Opportunities</h3>
{bottom3.map((item, idx) => (
<div key={idx} className="trait-item">
<div className="trait-header">
<span className="trait-name">{item.trait}</span>
<span className="trait-score orange">{item.avg.toFixed(2)}</span>
</div>
<div className="progress-bar">
<div className="progress-fill orange" style={{ width: `${(item.avg / 5) * 100}%` }} />
</div>
</div>
))}
</div>
</div>
<div className="viz-card">
<h3 className="viz-title">📊 All Traits Overview</h3>
<div className="all-traits-grid">
{avgScores.map((item, idx) => (
<div key={idx} className="trait-card">
<div className="trait-card-name" title={item.trait}>{item.trait}</div>
<div className="trait-card-score">{item.avg.toFixed(2)}</div>
<div className="trait-card-progress">
<div className="trait-card-progress-fill" style={{ width: `${(item.avg / 5) * 100}%` }} />
</div>
</div>
))}
</div>
</div>
</div>
);
};
// 2. Full Heatmap - USES VIA CATEGORY FILTER
const renderHeatmap = () => {
const traits = getTraitsByCategory(); // FIXED: Now uses category filter
return (
<div className="viz-card">
<h3 className="viz-title">🔥 Full Heatmap - All Traits × All Students</h3>
<div className="legend">
<div className="legend-item">
<div className="legend-box heat-red"></div>
<span><2.5</span>
</div>
<div className="legend-item">
<div className="legend-box heat-orange-dark"></div>
<span>2.5-3.0</span>
</div>
<div className="legend-item">
<div className="legend-box heat-orange"></div>
<span>3.0-3.5</span>
</div>
<div className="legend-item">
<div className="legend-box heat-yellow"></div>
<span>3.5-4.0</span>
</div>
<div className="legend-item">
<div className="legend-box heat-green-light"></div>
<span>4.0-4.5</span>
</div>
<div className="legend-item">
<div className="legend-box heat-green"></div>
<span>≥4.5</span>
</div>
</div>
<div className="heatmap-container">
<table className="heatmap-table">
<thead>
<tr>
<th className="student-name">Student</th>
{traits.map((trait, idx) => (
<th key={idx}>{trait}</th>
))}
<th>Avg</th>
</tr>
</thead>
<tbody>
{filteredStudents.map((student, idx) => {
const avg = traits.reduce((sum, trait) => sum + (student[trait] || 0), 0) / traits.length;
return (
<tr key={idx}>
<td className="student-name">{student.name}</td>
{traits.map((trait, tidx) => {
const value = student[trait] || 0;
return (
<td key={tidx} className={getHeatColor(value)}>
{value.toFixed(1)}
</td>
);
})}
<td style={{background: '#dbeafe', fontWeight: 'bold'}}>{avg.toFixed(2)}</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
};
// 3. Intervention Needed - Uses ALL traits (not filtered by category)
const renderIntervention = () => {
const interventionStudents = filteredStudents.map(student => {
const lowTraits = ALL_TRAITS.filter(trait => (student[trait] || 0) < 2.5);
const lowTraitAvg = lowTraits.length > 0
? lowTraits.reduce((sum, trait) => sum + (student[trait] || 0), 0) / lowTraits.length
: 0;
const lowestScore = lowTraits.length > 0
? Math.min(...lowTraits.map(trait => student[trait] || 0))
: 0;
return { ...student, lowTraits, lowTraitAvg, lowestScore };
}).filter(s => s.lowTraits.length > 0)
.sort((a, b) => {
if (b.lowTraits.length !== a.lowTraits.length) {
return b.lowTraits.length - a.lowTraits.length;
}
if (a.lowTraitAvg !== b.lowTraitAvg) {
return a.lowTraitAvg - b.lowTraitAvg;
}
if (a.lowestScore !== b.lowestScore) {
return a.lowestScore - b.lowestScore;
}
return a.name.localeCompare(b.name);
});
return (
<div className="viz-card">
<h3 className="viz-title">⚠️ Students Requiring Intervention (Traits < 2.5)</h3>
{interventionStudents.length === 0 ? (
<div className="empty-state">No students requiring intervention in selected filters</div>
) : (
<div className="student-list">
{interventionStudents.map((student, idx) => (
<div key={idx} className="student-item intervention">
<div className="student-item-header">
<div>
<div className="student-item-name">{student.name}</div>
<div className="student-item-class">{student.class}</div>
</div>
<div style={{fontSize: '2rem'}}>⚠️</div>
</div>
<div className="trait-list">
<strong>Low Traits ({student.lowTraits.length}) - Avg: {student.lowTraitAvg.toFixed(2)}</strong>
{student.lowTraits.sort((a, b) => (student[a] || 0) - (student[b] || 0)).map((trait, tidx) => (
<div key={tidx} className="trait-list-item low">
<span>{trait}</span>
<span style={{color: '#dc2626', fontWeight: 'bold'}}>{student[trait].toFixed(2)}</span>
</div>
))}
</div>
</div>
))}
</div>
)}
</div>
);
};
// 4. Potential Leaders - Uses ALL traits (not filtered by category)
const renderLeaders = () => {
const leaders = filteredStudents.map(student => {
const allTraitAvg = ALL_TRAITS.reduce((sum, trait) => sum + (student[trait] || 0), 0) / ALL_TRAITS.length;
const highTraits = ALL_TRAITS.filter(trait => (student[trait] || 0) >= 4.5);
const highTraitAvg = highTraits.length > 0
? highTraits.reduce((sum, trait) => sum + (student[trait] || 0), 0) / highTraits.length
: 0;
const highestScore = highTraits.length > 0
? Math.max(...highTraits.map(trait => student[trait] || 0))
: 0;
const allTraitsWithScores = ALL_TRAITS.map(trait => ({
name: trait,
score: student[trait] || 0
}));
return {
...student,
allTraitAvg,
highTraits,
highTraitAvg,
highestScore,
allTraitsWithScores
};
}).filter(s => s.allTraitAvg > 4.0)
.sort((a, b) => {
if (b.highTraits.length !== a.highTraits.length) {
return b.highTraits.length - a.highTraits.length;
}
if (b.highTraitAvg !== a.highTraitAvg) {
return b.highTraitAvg - a.highTraitAvg;
}
if (b.highestScore !== a.highestScore) {
return b.highestScore - a.highestScore;
}
return a.name.localeCompare(b.name);
});
return (
<div className="viz-card">
<h3 className="viz-title">⭐ Potential Leaders (Overall Average > 4.0)</h3>
{leaders.length === 0 ? (
<div className="empty-state">No potential leaders in selected filters</div>
) : (
<div className="student-list">
{leaders.map((student, idx) => (
<div key={idx} className="student-item leader">
<div className="student-item-header">
<div>
<div className="student-item-name">{student.name}</div>
<div className="student-item-class">{student.class}</div>
</div>
<div style={{fontSize: '2rem'}}>🌟</div>
</div>
<div style={{marginTop: '0.5rem', marginBottom: '0.5rem'}}>
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
<span style={{fontWeight: 600}}>Overall Average:</span>
<span style={{fontSize: '1.5rem', fontWeight: 'bold', color: '#f59e0b'}}>{student.allTraitAvg.toFixed(2)}</span>
</div>
<div style={{fontSize: '0.875rem', color: '#6b7280', marginTop: '0.25rem'}}>
High Traits (≥4.5): {student.highTraits.length}
</div>
</div>
<div className="trait-list">
<strong>All 24 Traits:</strong>
{student.allTraitsWithScores
.sort((a, b) => b.score - a.score)
.map((trait, tidx) => (
<div
key={tidx}
className={`trait-list-item ${trait.score >= 4.5 ? 'high' : ''}`}
style={{
borderColor: trait.score >= 4.5 ? '#86efac' : '#e5e7eb',
background: trait.score >= 4.5 ? '#f0fdf4' : 'white'
}}
>
<span>{trait.name}</span>
<span style={{
color: trait.score >= 4.5 ? '#16a34a' : '#6b7280',
fontWeight: 'bold'
}}>
{trait.score.toFixed(2)}
</span>
</div>
))
}
</div>
</div>
))}
</div>
)}
</div>
);
};
// 6. Trait Rankings - USES VIA CATEGORY FILTER
const renderRankings = () => {
const traits = getTraitsByCategory(); // FIXED: Now uses category filter
const avgScores = traits.map(trait => {
const sum = filteredStudents.reduce((sum, s) => sum + (s[trait] || 0), 0);
const count = filteredStudents.length;
const avg = count > 0 ? sum / count : 0;
return { trait, avg };
}).sort((a, b) => b.avg - a.avg);
const displayContext = isSingleStudent
? `Individual: ${filteredStudents[0].name}`
: `Average of ${filteredStudents.length} student(s)`;
return (
<div className="viz-card">
<h3 className="viz-title">📈 Trait Rankings - Horizontal Bar Comparison</h3>
<p style={{color: '#6b7280', marginBottom: '1rem', fontSize: '0.875rem'}}>
{displayContext}
</p>
<div className="bar-chart-container">
{avgScores.map((item, idx) => (
<div key={idx} className="bar-item">
<div className="bar-label">
<span style={{fontWeight: 600}}>{item.trait}</span>
<span style={{fontWeight: 'bold', color: COLORS[idx % COLORS.length]}}>{item.avg.toFixed(2)}</span>
</div>
<div className="bar-background">
<div
className="bar-fill"
style={{
width: `${(item.avg / 5) * 100}%`,
background: COLORS[idx % COLORS.length]
}}
>
{item.avg.toFixed(2)}
</div>
</div>
</div>
))}
</div>
</div>
);
};
// 7. Student Profile - IMPROVED RADAR CHART LOGIC
const renderProfile = () => {
const traits = getTraitsByCategory(); // Uses category filter
// Determine comparison based on filters
let comparisonStudents = students; // Default to full cohort
let comparisonLabel = 'Full Cohort';
let primaryLabel = 'Filter Average';
// Logic for comparison:
// If specific student selected AND specific class selected → compare student vs class
// If specific student selected AND class is All → compare student vs cohort
// If student is All AND specific class selected → compare class vs cohort
// If both are All → show cohort only
if (selectedStudent !== 'All' && selectedClass !== 'All') {
// Student vs Class
comparisonStudents = students.filter(s => s.class === selectedClass);
comparisonLabel = `Class ${selectedClass} Average`;
primaryLabel = filteredStudents[0].name;
} else if (selectedStudent !== 'All' && selectedClass === 'All') {
// Student vs Cohort
comparisonStudents = students;
comparisonLabel = 'Full Cohort Average';
primaryLabel = filteredStudents[0].name;
} else if (selectedStudent === 'All' && selectedClass !== 'All') {
// Class vs Cohort
comparisonStudents = students;
comparisonLabel = 'Full Cohort Average';
primaryLabel = `Class ${selectedClass} Average`;
} else {
// Both All - show cohort only
comparisonStudents = students;
comparisonLabel = 'Full Cohort Average';
primaryLabel = 'Full Cohort Average';
}
// Calculate filter average
const filterAvgs = traits.map(trait => {
const sum = filteredStudents.reduce((sum, s) => sum + (s[trait] || 0), 0);
return sum / filteredStudents.length;
});
// Calculate comparison average
const comparisonAvgs = traits.map(trait => {
const sum = comparisonStudents.reduce((sum, s) => sum + (s[trait] || 0), 0);
return sum / comparisonStudents.length;
});
const size = 500;
const center = size / 2;
const maxRadius = size / 2 - 80;
const numTraits = traits.length;
const filterPoints = filterAvgs.map((avg, i) => {
const angle = (i / numTraits) * 2 * Math.PI - Math.PI / 2;
const radius = (avg / 5) * maxRadius;
return {
x: center + radius * Math.cos(angle),
y: center + radius * Math.sin(angle)
};
});
const comparisonPoints = comparisonAvgs.map((avg, i) => {
const angle = (i / numTraits) * 2 * Math.PI - Math.PI / 2;
const radius = (avg / 5) * maxRadius;
return {
x: center + radius * Math.cos(angle),
y: center + radius * Math.sin(angle)
};
});
const filterPath = filterPoints.map((p, i) =>
`${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`
).join(' ') + ' Z';
const comparisonPath = comparisonPoints.map((p, i) =>
`${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`
).join(' ') + ' Z';
const gridLevels = [1, 2, 3, 4, 5];
// Don't show comparison if both are the same
const showComparison = primaryLabel !== comparisonLabel;
return (
<div className="viz-card">
<h3 className="viz-title">🎯 Trait Profile - Radar Chart</h3>
<p style={{color: '#6b7280', marginBottom: '1rem', fontSize: '0.875rem'}}>
Comparing: {primaryLabel} {showComparison ? `vs ${comparisonLabel}` : ''}
</p>
{showComparison && (
<div className="radar-legend">
<div className="radar-legend-item">
<div className="radar-legend-box" style={{background: '#9333ea'}}></div>
<span>{primaryLabel}</span>
</div>
<div className="radar-legend-item">
<div className="radar-legend-box" style={{background: '#3b82f6'}}></div>
<span>{comparisonLabel}</span>
</div>
</div>
)}
<div className="radar-container">
<svg viewBox={`0 0 ${size} ${size}`} className="radar-svg" style={{width: '100%', maxWidth: '600px'}}>
{gridLevels.map(level => (
<circle
key={level}
cx={center}
cy={center}
r={(level / 5) * maxRadius}
fill="none"
stroke="#e5e7eb"
strokeWidth="1"
/>
))}
{traits.map((trait, i) => {
const angle = (i / numTraits) * 2 * Math.PI - Math.PI / 2;
const endX = center + maxRadius * Math.cos(angle);
const endY = center + maxRadius * Math.sin(angle);
return (
<line
key={i}
x1={center}
y1={center}
x2={endX}
y2={endY}
stroke="#d1d5db"
strokeWidth="1"
/>
);
})}
{showComparison && (
<>
<path
d={comparisonPath}
fill="#3b82f6"
fillOpacity="0.2"
stroke="#3b82f6"
strokeWidth="2"
/>
{comparisonPoints.map((point, i) => (
<circle
key={`comparison-${i}`}
cx={point.x}
cy={point.y}
r="3"
fill="#3b82f6"
/>
))}
</>
)}
<path
d={filterPath}
fill="#9333ea"
fillOpacity="0.3"
stroke="#9333ea"
strokeWidth="2"
/>
{filterPoints.map((point, i) => (
<circle
key={`filter-${i}`}
cx={point.x}
cy={point.y}
r="4"
fill="#9333ea"
/>
))}
{traits.map((trait, i) => {
const angle = (i / numTraits) * 2 * Math.PI - Math.PI / 2;
const labelRadius = maxRadius + 40;
const x = center + labelRadius * Math.cos(angle);
const y = center + labelRadius * Math.sin(angle);
return (
<text
key={i}
x={x}
y={y}
textAnchor="middle"
dominantBaseline="middle"
fontSize="10"
fontWeight="600"
fill="#374151"
>
<tspan x={x} dy="0">{trait.substring(0, 15)}</tspan>
{trait.length > 15 && <tspan x={x} dy="12">{trait.substring(15)}</tspan>}
<tspan x={x} dy="12" fill="#9333ea" fontWeight="bold">
{filterAvgs[i].toFixed(2)}
</tspan>
{showComparison && (
<tspan x={x} dy="10" fill="#3b82f6" fontSize="8">
({comparisonAvgs[i].toFixed(2)})
</tspan>
)}
</text>
);
})}
{gridLevels.map(level => (
<text
key={level}
x={center + 5}
y={center - (level / 5) * maxRadius}
fontSize="10"
fill="#6b7280"
>
{level.toFixed(1)}
</text>
))}
</svg>
</div>
</div>
);
};
// 8. Score Distribution - USES VIA CATEGORY FILTER
const renderDistribution = () => {
const traits = getTraitsByCategory(); // FIXED: Now uses category filter
const distributionData = traits.map(trait => {
const scores = filteredStudents.map(s => s[trait] || 0);
const bins = { '1.0-1.9': 0, '2.0-2.9': 0, '3.0-3.9': 0, '4.0-4.9': 0, '5.0': 0 };
scores.forEach(score => {
if (score < 2) bins['1.0-1.9']++;
else if (score < 3) bins['2.0-2.9']++;
else if (score < 4) bins['3.0-3.9']++;
else if (score < 5) bins['4.0-4.9']++;
else bins['5.0']++;
});
return { trait, bins };
});
const displayContext = isSingleStudent
? `Individual: ${filteredStudents[0].name}`
: `Distribution of ${filteredStudents.length} student(s)`;
return (
<div className="viz-card">
<h3 className="viz-title">📉 Score Distribution - Histograms for All Traits</h3>
<p style={{color: '#6b7280', marginBottom: '1rem', fontSize: '0.875rem'}}>
{displayContext}
</p>
<div className="distribution-grid">
{distributionData.map((data, idx) => {
const maxCount = Math.max(...Object.values(data.bins), 1);
return (
<div key={idx} className="distribution-card">
<h4>{data.trait}</h4>
<div className="distribution-bars">
{Object.entries(data.bins).map(([range, count], bidx) => (
<div key={bidx} className="dist-bar">
<span className="dist-label">{range}</span>
<div className="dist-bar-bg">
<div
className="dist-bar-fill"
style={{
width: maxCount > 0 ? `${(count / maxCount) * 100}%` : '0%',
background: COLORS[bidx % COLORS.length]
}}
>
{count > 0 ? count : ''}
</div>
</div>
</div>
))}
</div>
</div>
);
})}
</div>
</div>
);
};
// 9. VIA Categories - ALWAYS SHOWS ALL CATEGORIES
const renderCategories = () => {
const categoryData = Object.entries(VIA_CATEGORIES).map(([category, traits]) => {
const avgScore = traits.reduce((sum, trait) => {
return sum + filteredStudents.reduce((s, student) => s + (student[trait] || 0), 0) / filteredStudents.length;
}, 0) / traits.length;
return { category, avgScore, traits };
}).sort((a, b) => b.avgScore - a.avgScore);
const displayContext = isSingleStudent
? `Individual: ${filteredStudents[0].name}`
: `Average of ${filteredStudents.length} student(s)`;
return (
<div className="viz-card">
<h3 className="viz-title">🎨 VIA Categories - Grouped by 6 Character Categories</h3>
<p style={{color: '#6b7280', marginBottom: '1rem', fontSize: '0.875rem'}}>
{displayContext}
</p>
{categoryData.map((cat, idx) => (
<div key={idx} style={{
background: 'linear-gradient(to right, #faf5ff, #dbeafe)',
padding: '1.5rem',
borderRadius: '0.5rem',
border: '2px solid #c4b5fd',
marginBottom: '1rem'
}}>
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1rem'}}>
<h4 style={{fontSize: '1.25rem', fontWeight: 'bold'}}>{cat.category}</h4>
<div style={{fontSize: '1.875rem', fontWeight: 'bold', color: '#9333ea'}}>{cat.avgScore.toFixed(2)}</div>
</div>
<div className="all-traits-grid">
{cat.traits.map((trait, tidx) => {
const traitAvg = filteredStudents.reduce((sum, s) => sum + (s[trait] || 0), 0) / filteredStudents.length;
return (
<div key={tidx} style={{background: 'white', padding: '0.75rem', borderRadius: '0.25rem', border: '1px solid #e5e7eb'}}>
<div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center'}}>
<span style={{fontSize: '0.875rem', fontWeight: 600}}>{trait}</span>
<span style={{fontWeight: 'bold', color: '#9333ea'}}>{traitAvg.toFixed(2)}</span>
</div>
<div className="progress-bar" style={{marginTop: '0.5rem', height: '0.5rem'}}>
<div style={{
height: '100%',
background: 'linear-gradient(to right, #9333ea, #2563eb)',
width: `${(traitAvg / 5) * 100}%`
}} />
</div>
</div>
);
})}
</div>
</div>
))}
</div>
);
};
const renderVisualization = () => {
switch (visualType) {
case '📊 Overview & Insights': return renderOverview();
case '🔥 Full Heatmap': return renderHeatmap();
case '⚠️ Intervention Needed': return renderIntervention();
case '⭐ Potential Leaders': return renderLeaders();
case '📈 Trait Rankings': return renderRankings();
case '🎯 Student Profile': return renderProfile();
case '📉 Score Distribution': return renderDistribution();
case '🎨 VIA Categories': return renderCategories();
default: return renderOverview();
}
};
if (students.length === 0) {
return (
<div className="loading">
<div style={{background: 'white', padding: '2rem', borderRadius: '0.5rem', textAlign: 'center'}}>
<h2 style={{color: '#dc2626', marginBottom: '1rem'}}>No Data Available</h2>
<p>Failed to load student data. Check console for details.</p>
</div>
</div>
);
}
return (
<div className="container">
<div className="card header">
<h1 className="title">Discovering The Hidden Me Dashboard</h1>
<p className="subtitle">
VIA Diagnostic profiles of Sec 1 (2026 cohort) students from Serangoon Secondary School
</p>
</div>
<div className="metrics-grid">
<div className="metric-card blue">
<div className="metric-label">Total Submissions</div>
<div className="metric-value blue">{metrics.totalSubmissions}</div>
</div>
<div className="metric-card green">
<div className="metric-label">Number of Classes</div>
<div className="metric-value green">{metrics.classes}</div>
</div>
<div className="metric-card yellow">
<div className="metric-label">Potential Leaders</div>
<div className="metric-value yellow">{metrics.potentialLeaders}</div>
</div>
<div className="metric-card red">
<div className="metric-label">Intervention Cases</div>
<div className="metric-value red">{metrics.interventionCases}</div>
</div>
</div>
<div className="card filters-section">
<h2>🔍 Filters & Views</h2>
<div className="filters-grid">
<div className="filter-group">
<label>Visualization Type</label>
<select value={visualType} onChange={(e) => setVisualType(e.target.value)}>
<option>📊 Overview & Insights</option>
<option>🔥 Full Heatmap</option>
<option>⚠️ Intervention Needed</option>
<option>⭐ Potential Leaders</option>
<option>📈 Trait Rankings</option>
<option>🎯 Student Profile</option>
<option>📉 Score Distribution</option>
<option>🎨 VIA Categories</option>
</select>
</div>
<div className="filter-group">
<label>VIA Category</label>
<select value={selectedCategory} onChange={(e) => setSelectedCategory(e.target.value)}>
<option>All</option>
{Object.keys(VIA_CATEGORIES).map(cat => (
<option key={cat}>{cat}</option>
))}
</select>
</div>
<div className="filter-group">
<label>Student Class</label>
<select
value={selectedClass}
onChange={(e) => {
setSelectedClass(e.target.value);
setSelectedStudent('All');
}}
>
{classList.map(cls => (
<option key={cls}>{cls}</option>
))}
</select>
</div>
<div className="filter-group">
<label>Student Name</label>
<select value={selectedStudent} onChange={(e) => setSelectedStudent(e.target.value)}>
{studentList.map(name => (
<option key={name}>{name}</option>
))}
</select>
</div>
</div>
<button onClick={resetFilters} className="reset-btn">
🔄 Reset All Filters
</button>
</div>
{renderVisualization()}
<div className="footer">
<p>© 2026 Wan.Cognition Training Consultancy. All Rights Reserved.</p>
</div>
</div>
);
}
try {
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<VIADashboard />);
console.log('✅ Dashboard loaded - VIA Category filter and Radar chart comparison logic fixed!');
} catch (err) {
console.error('❌ Failed:', err);
document.getElementById('loading').innerHTML = '<div style="background: white; padding: 2rem; border-radius: 0.5rem;"><h2 style="color: #dc2626;">Error</h2><p>' + err.message + '</p></div>';
}
</script>