Merge branch 'main' of https://git.levimclean.me/Levi/IWD2-02
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome with Live Server",
|
||||
"url": "http://127.0.0.1:5500/index.html",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,20 +14,73 @@ export function getPassingStudents(students, passMark = 70) {
|
||||
return passingStudents;
|
||||
}
|
||||
|
||||
function calculateCourseAverages(students) {
|
||||
|
||||
export function calculateCourseAverages(students) {
|
||||
const groupedCourses = students.reduce((accumulator, { course, grade }) => {
|
||||
if (!accumulator[course]) {
|
||||
accumulator[course] = { total: 0, count: 0 };
|
||||
}
|
||||
|
||||
accumulator[course].total += Number(grade);
|
||||
accumulator[course].count += 1;
|
||||
|
||||
return accumulator;
|
||||
}, {});
|
||||
|
||||
return Object.entries(groupedCourses).reduce((accumulator, [course, values]) => {
|
||||
accumulator[course] = values.count === 0 ? 0 : values.total / values.count;
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function getTopPerCourse(students) {
|
||||
export function getTopPerCourse(students) {
|
||||
const topStudentsByCourse = students.reduce((accumulator, student) => {
|
||||
const currentTopStudent = accumulator[student.course];
|
||||
|
||||
if (!currentTopStudent || student.grade > currentTopStudent.grade) {
|
||||
accumulator[student.course] = student;
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}, {});
|
||||
|
||||
return Object.values(topStudentsByCourse);
|
||||
}
|
||||
|
||||
function calculateOverallStatistics(students) {
|
||||
const summary = document.getElementById("summary");
|
||||
summary.innerHTML += `<p>Total Students: ${students.length}</p><br>
|
||||
<p>Average Grade: </p><br>
|
||||
<p>Highest Grade: </p><br>
|
||||
<p>Lowest Grade: </p><br>
|
||||
<p>Average Attendance: %</p><br>
|
||||
<p>Unique Courses: </p><br>`;
|
||||
export function calculateOverallStatistics(students) {
|
||||
if (students.length === 0) {
|
||||
return {
|
||||
totalStudents: 0,
|
||||
averageGrade: 0,
|
||||
highestGrade: 0,
|
||||
lowestGrade: 0,
|
||||
averageAttendance: 0
|
||||
};
|
||||
}
|
||||
|
||||
const totals = students.reduce((accumulator, { grade, attendance }) => {
|
||||
const numericGrade = Number(grade);
|
||||
const numericAttendance = Number(attendance);
|
||||
|
||||
accumulator.totalStudents += 1;
|
||||
accumulator.gradeTotal += numericGrade;
|
||||
accumulator.attendanceTotal += numericAttendance;
|
||||
accumulator.highestGrade = Math.max(accumulator.highestGrade, numericGrade);
|
||||
accumulator.lowestGrade = Math.min(accumulator.lowestGrade, numericGrade);
|
||||
|
||||
return accumulator;
|
||||
}, {
|
||||
totalStudents: 0,
|
||||
gradeTotal: 0,
|
||||
attendanceTotal: 0,
|
||||
highestGrade: -Infinity,
|
||||
lowestGrade: Infinity
|
||||
});
|
||||
|
||||
return {
|
||||
totalStudents: totals.totalStudents,
|
||||
averageGrade: totals.gradeTotal / totals.totalStudents,
|
||||
highestGrade: totals.highestGrade,
|
||||
lowestGrade: totals.lowestGrade,
|
||||
averageAttendance: totals.attendanceTotal / totals.totalStudents
|
||||
};
|
||||
}
|
||||
@@ -9,9 +9,9 @@
|
||||
<body>
|
||||
<h1>Student Performance Analytics Dashboard</h1>
|
||||
<input type="text" placeholder="Search By Name" id="nameSearch">
|
||||
<label for="minimumGrade">Minimum Grade:</label>
|
||||
<input type="range" min="0" max="100" name="minimumGrade" id="minimumGrade">
|
||||
<button type="button">Toggle Theme</button>
|
||||
<label for="minimumGrade">Minimum Grade: <span id="gradeValue">50</span></label>
|
||||
<input type="range" min="0" max="100" value="50" name="minimumGrade" id="minimumGrade">
|
||||
<button type="button" id="themeToggle">Toggle Theme</button>
|
||||
|
||||
<table id="studentTable">
|
||||
<thead>
|
||||
@@ -31,10 +31,5 @@
|
||||
<h2>Summary</h2>
|
||||
</div>
|
||||
<script type="module" src="main.js"></script>
|
||||
<script type="module" src="data.js"></script>
|
||||
<script type="module" src="analytics.js"></script>
|
||||
<script type="module" src="utils.js"></script>
|
||||
<script type="module" src="ui.js"></script>
|
||||
<script type="module" src="theme.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
import { students } from "./data.js";
|
||||
import { calculateOverallStatistics } from "./analytics.js";
|
||||
import { getTopThree, uniqueCourses } from "./utils.js";
|
||||
import { renderTable, renderSummary } from "./ui.js";
|
||||
import { toggleTheme, loadTheme } from "./theme.js";
|
||||
|
||||
let filteredStudents = [...students];
|
||||
|
||||
const nameSearchInput = document.getElementById("nameSearch");
|
||||
const minGradeInput = document.getElementById("minimumGrade");
|
||||
const minGradeValue = document.getElementById("gradeValue");
|
||||
|
||||
const applyFilters = () => {
|
||||
const nameFilter = nameSearchInput.value.toLowerCase();
|
||||
const minGrade = parseInt(minGradeInput.value);
|
||||
|
||||
filteredStudents = students.filter((student) => student.name.toLowerCase().includes(nameFilter) && student.grade >= minGrade);
|
||||
|
||||
const topThree = getTopThree(filteredStudents);
|
||||
renderTable(filteredStudents, topThree);
|
||||
|
||||
const stats = calculateOverallStatistics(filteredStudents);
|
||||
renderSummary(stats, uniqueCourses(filteredStudents).length);
|
||||
};
|
||||
|
||||
nameSearchInput.addEventListener("input", applyFilters);
|
||||
minGradeInput.addEventListener("input", () => {
|
||||
minGradeValue.textContent = minGradeInput.value;
|
||||
applyFilters();
|
||||
});
|
||||
|
||||
document.getElementById("themeToggle").addEventListener("click", toggleTheme);
|
||||
|
||||
loadTheme();
|
||||
applyFilters();
|
||||
@@ -19,4 +19,58 @@ table td, table th {
|
||||
|
||||
table th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 12px;
|
||||
margin: 10px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gold {
|
||||
background-color: gold;
|
||||
}
|
||||
|
||||
.silver {
|
||||
background-color: silver;
|
||||
}
|
||||
|
||||
.bronze {
|
||||
background-color: #cd7f32;
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: #1f1f1f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dark table {
|
||||
background-color: #2b2b2b;
|
||||
color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.dark table td,
|
||||
.dark table th {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.dark table th {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.dark button {
|
||||
background-color: #444;
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.dark input {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.dark #summary {
|
||||
color: white;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export function loadTheme() {
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
|
||||
if (savedTheme === "dark") {
|
||||
document.body.classList.add("dark");
|
||||
}
|
||||
}
|
||||
|
||||
export function toggleTheme() {
|
||||
document.body.classList.toggle("dark");
|
||||
|
||||
if (document.body.classList.contains("dark")) {
|
||||
localStorage.setItem("theme", "dark");
|
||||
} else {
|
||||
localStorage.setItem("theme", "light");
|
||||
}
|
||||
}
|
||||
@@ -6,22 +6,46 @@ const passingStudents = getPassingStudents(students);
|
||||
const passingIds = passingStudents.map(student => student.id);
|
||||
const tableBody = document.getElementById("studentTableBody");
|
||||
|
||||
students.forEach(student => {
|
||||
const row = document.createElement("tr");
|
||||
const name = document.createElement("td");
|
||||
name.textContent = student.name ?? "-";
|
||||
const course = document.createElement("td");
|
||||
course.textContent = student.course ?? "-";
|
||||
const grade = document.createElement("td");
|
||||
grade.textContent = student.grade ?? "-";
|
||||
const attendance = document.createElement("td");
|
||||
attendance.textContent = student.attendance + "%" ?? "-";
|
||||
const assignmentAvg = document.createElement("td");
|
||||
assignmentAvg.textContent = calculateAssignmentAverage(student);
|
||||
const status = document.createElement("td");
|
||||
status.textContent = passingIds.includes(student.id) ? "Pass" : "Fail";
|
||||
const city = document.createElement("td");
|
||||
city.textContent = student.address.city ?? "-";
|
||||
row.append(name, course, grade, attendance, assignmentAvg, status, city);
|
||||
tableBody.appendChild(row);
|
||||
});
|
||||
export function renderTable(students, topThree) {
|
||||
tableBody.innerHTML = "";
|
||||
students.forEach(student => {
|
||||
const row = document.createElement("tr");
|
||||
|
||||
if (topThree[0] && student.id === topThree[0].id) {
|
||||
row.classList.add("gold");
|
||||
} else if (topThree[1] && student.id === topThree[1].id) {
|
||||
row.classList.add("silver");
|
||||
} else if (topThree[2] && student.id === topThree[2].id) {
|
||||
row.classList.add("bronze");
|
||||
}
|
||||
|
||||
const name = document.createElement("td");
|
||||
name.textContent = student.name ?? "-";
|
||||
const course = document.createElement("td");
|
||||
course.textContent = student.course ?? "-";
|
||||
const grade = document.createElement("td");
|
||||
grade.textContent = student.grade ?? "-";
|
||||
const attendance = document.createElement("td");
|
||||
attendance.textContent = student.attendance + "%" ?? "-";
|
||||
const assignmentAvg = document.createElement("td");
|
||||
assignmentAvg.textContent = calculateAssignmentAverage(...student.assignments);
|
||||
const status = document.createElement("td");
|
||||
status.textContent = passingIds.includes(student.id) ? "Pass" : "Fail";
|
||||
const city = document.createElement("td");
|
||||
city.textContent = student.address.city ?? "-";
|
||||
row.append(name, course, grade, attendance, assignmentAvg, status, city);
|
||||
tableBody.appendChild(row);
|
||||
})
|
||||
}
|
||||
|
||||
export function renderSummary(stats, uniqueCourseCount) {
|
||||
const summary = document.getElementById("summary");
|
||||
summary.innerHTML = "";
|
||||
summary.innerHTML += `<h2>Summary</h2>
|
||||
<p>Total Students: ${stats.totalStudents}</p>
|
||||
<p>Average Grade: ${stats.averageGrade.toFixed(2)}</p>
|
||||
<p>Highest Grade: ${stats.highestGrade}</p>
|
||||
<p>Lowest Grade: ${stats.lowestGrade}</p>
|
||||
<p>Average Attendance: ${stats.averageAttendance.toFixed(2)}%</p>
|
||||
<p>Unique Courses: ${uniqueCourseCount}</p>`;
|
||||
}
|
||||
@@ -1,7 +1,17 @@
|
||||
export function calculateAssignmentAverage(student) {
|
||||
if (!student.assignments || student.assignments.length === 0) {
|
||||
return false;
|
||||
export function calculateAssignmentAverage(...grades) {
|
||||
if (grades.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
const total = student.assignments.reduce((sum, score) => sum + score, 0);
|
||||
return total / student.assignments.length;
|
||||
|
||||
const total = grades.reduce((sum, score) => sum + score, 0);
|
||||
return total / grades.length;
|
||||
}
|
||||
|
||||
export function getTopThree(students) {
|
||||
const sorted = [...students].sort((a, b) => b.grade - a.grade);
|
||||
return sorted.slice(0, 3);
|
||||
}
|
||||
|
||||
export function uniqueCourses(students) {
|
||||
return [...new Set(students.map(({ course }) => course))];
|
||||
}
|
||||
Reference in New Issue
Block a user