📝 Assessment Management
Comprehensive view of all assessments, submissions, and certificates.
Show code
Auth = window . Auth || {};
IoTClassAPI = window . IoTClassAPI || {};
// Colors
colors = ({
navy : '#2C3E50' ,
teal : '#16A085' ,
gray : '#7F8C8D' ,
green : '#27AE60' ,
orange : '#E67E22' ,
red : '#E74C3C'
})
// Check authentication and admin status
currentUser = {
const user = Auth. getCurrentUser ();
if (! user) return null ;
const isAdmin = Auth. isUserAdmin ();
return { ... user, isAdmin };
}
// Load quiz results via Supabase
allQuizResults = {
if (! currentUser || ! currentUser. isAdmin ) return null ;
try {
const results = await IoTClassAPI. Admin . getAllQuizResults ();
// Map Supabase field names for compatibility
return results. map (r => ({
... r,
username : r. users ?. username || 'Unknown' ,
quizId : r. quiz_id ,
chapterId : r. chapter_path ,
totalQuestions : r. max_score ,
passed : r. percentage >= 80 ,
attemptNumber : r. attempt_number ,
maxAttempts : 3 ,
completedDate : r. created_at
}));
} catch (error) {
console . error ('Error loading quiz results:' , error);
return [];
}
}
// Load lab submissions via Supabase
allLabSubmissions = {
if (! currentUser || ! currentUser. isAdmin ) return null ;
try {
const submissions = await IoTClassAPI. Admin . getAllLabSubmissions ();
// Map Supabase field names for compatibility
return submissions. map (s => ({
... s,
username : s. users ?. username || 'Unknown' ,
labTitle : s. lab_title ,
labType : s. lab_type ,
submissionUrl : s. submission_url ,
submittedDate : s. submitted_at
}));
} catch (error) {
console . error ('Error loading lab submissions:' , error);
return [];
}
}
// Load certificates via Supabase
allCertificates = {
if (! currentUser || ! currentUser. isAdmin ) return null ;
try {
const certificates = await IoTClassAPI. Admin . getAllCertificates ();
// Map Supabase field names for compatibility
return certificates. map (c => ({
... c,
username : c. users ?. username || 'Unknown' ,
certificateId : c. certificate_id ,
achievementName : c. achievement_name ,
issuedDate : c. issued_at
}));
} catch (error) {
console . error ('Error loading certificates:' , error);
return [];
}
}
// Calculate statistics
assessmentStats = {
if (! allQuizResults || ! allLabSubmissions || ! allCertificates) return null ;
// Quiz stats
const totalQuizAttempts = allQuizResults. length ;
const passedQuizzes = allQuizResults. filter (r => r. passed ). length ;
const avgQuizScore = totalQuizAttempts > 0
? Math . round (allQuizResults. reduce ((sum, r) => sum + (r. percentage || 0 ), 0 ) / totalQuizAttempts)
: 0 ;
// Lab stats
const totalLabSubmissions = allLabSubmissions. length ;
const pendingLabs = allLabSubmissions. filter (s => s. status === 'pending' ). length ;
const gradedLabs = allLabSubmissions. filter (s => s. status === 'graded' ). length ;
// Certificate stats
const totalCertificates = allCertificates. length ;
const certificatesByType = {};
allCertificates. forEach (cert => {
certificatesByType[cert. type ] = (certificatesByType[cert. type ] || 0 ) + 1 ;
});
return {
totalQuizAttempts,
passedQuizzes,
avgQuizScore,
totalLabSubmissions,
pendingLabs,
gradedLabs,
totalCertificates,
certificatesByType
};
}
// Active tab
viewState = ({ activeTab : 'quizzes' })
Show code
// Render access denied if not admin
html ` ${ ! Auth. isUserAuthenticated () || ! currentUser?. isAdmin ? `
<div class="access-denied">
<div style="font-size: 72px; margin-bottom: 20px;">🔒</div>
<h2 style="color: ${ colors. navy } ; margin-bottom: 16px;">Access Denied</h2>
<p style="color: ${ colors. gray } ;">
You must be an administrator to access this page.
</p>
</div>
` : '' } `
Show code
// Render assessment management if admin
html ` ${ currentUser?. isAdmin && assessmentStats ? `
<!-- Statistics Row -->
<div class="stats-row">
<div class="stat-box">
<div class="stat-value"> ${ assessmentStats. totalQuizAttempts } </div>
<div class="stat-label">Quiz Attempts</div>
</div>
<div class="stat-box">
<div class="stat-value" style="color: ${ colors. green } ;"> ${ assessmentStats. passedQuizzes } </div>
<div class="stat-label">Passed Quizzes</div>
</div>
<div class="stat-box">
<div class="stat-value"> ${ assessmentStats. avgQuizScore } %</div>
<div class="stat-label">Avg Quiz Score</div>
</div>
<div class="stat-box">
<div class="stat-value"> ${ assessmentStats. totalLabSubmissions } </div>
<div class="stat-label">Lab Submissions</div>
</div>
<div class="stat-box">
<div class="stat-value" style="color: ${ colors. orange } ;"> ${ assessmentStats. pendingLabs } </div>
<div class="stat-label">Pending Review</div>
</div>
<div class="stat-box">
<div class="stat-value" style="color: ${ colors. teal } ;"> ${ assessmentStats. totalCertificates } </div>
<div class="stat-label">Certificates Issued</div>
</div>
</div>
<!-- Tab Navigation -->
<div class="tab-container">
<div class="tab-buttons">
<button class="tab-button ${ viewState. activeTab === 'quizzes' ? 'active' : '' } "
onclick="viewState = { ...viewState, activeTab: 'quizzes' }">
Quiz Results
</button>
<button class="tab-button ${ viewState. activeTab === 'labs' ? 'active' : '' } "
onclick="viewState = { ...viewState, activeTab: 'labs' }">
Lab Submissions
</button>
<button class="tab-button ${ viewState. activeTab === 'certificates' ? 'active' : '' } "
onclick="viewState = { ...viewState, activeTab: 'certificates' }">
Certificates
</button>
</div>
<!-- Quiz Results Tab -->
${ viewState. activeTab === 'quizzes' ? `
<div class="tab-content">
<h3>Quiz Results</h3>
<table class="data-table">
<thead>
<tr>
<th>Student</th>
<th>Quiz</th>
<th>Score</th>
<th>Percentage</th>
<th>Status</th>
<th>Attempt</th>
<th>Date</th>
</tr>
</thead>
<tbody>
${ allQuizResults. length === 0 ? `
<tr>
<td colspan="7" style="text-align: center; padding: 40px; color: ${ colors. gray } ;">
No quiz results yet.
</td>
</tr>
` : allQuizResults. slice (0 , 50 ). map (result => `
<tr>
<td>
<div style="font-weight: 500;"> ${ result. username || 'Unknown' } </div>
</td>
<td>
<div style="font-weight: 500;"> ${ result. quizId || 'N/A' } </div>
<small style="color: ${ colors. gray } ;"> ${ result. chapterId || '' } </small>
</td>
<td style="text-align: center; font-weight: 600;">
${ result. score || 0 } / ${ result. totalQuestions || 0 }
</td>
<td style="text-align: center;">
<div style="font-size: 20px; font-weight: bold; color: ${ result. percentage >= 80 ? colors. green : colors. orange } ;">
${ result. percentage || 0 } %
</div>
</td>
<td>
<span class="status-badge ${ result. passed ? 'status-passed' : 'status-failed' } ">
${ result. passed ? 'Passed' : 'Failed' }
</span>
</td>
<td style="text-align: center;">
${ result. attemptNumber || 1 } / ${ result. maxAttempts || 3 }
</td>
<td style="color: ${ colors. gray } ; font-size: 13px;">
${ new Date (result. completedDate ). toLocaleDateString ()}
</td>
</tr>
` ). join ('' )}
</tbody>
</table>
</div>
` : '' }
<!-- Lab Submissions Tab -->
${ viewState. activeTab === 'labs' ? `
<div class="tab-content">
<h3>Lab Submissions</h3>
<table class="data-table">
<thead>
<tr>
<th>Student</th>
<th>Lab</th>
<th>Status</th>
<th>Submitted</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
${ allLabSubmissions. length === 0 ? `
<tr>
<td colspan="5" style="text-align: center; padding: 40px; color: ${ colors. gray } ;">
No lab submissions yet.
</td>
</tr>
` : allLabSubmissions. slice (0 , 50 ). map (submission => `
<tr>
<td>
<div style="font-weight: 500;"> ${ submission. username || 'Unknown' } </div>
</td>
<td>
<div style="font-weight: 500;"> ${ submission. labTitle || 'N/A' } </div>
<small style="color: ${ colors. gray } ;"> ${ submission. labType || '' } </small>
</td>
<td>
<span class="status-badge status- ${ submission. status } ">
${ submission. status || 'pending' }
</span>
</td>
<td style="color: ${ colors. gray } ; font-size: 13px;">
${ new Date (submission. submittedDate ). toLocaleDateString ()}
</td>
<td>
<a href=" ${ submission. submissionUrl } " target="_blank" class="action-button">
View Submission
</a>
${ submission. status === 'pending' ? `
<button class="action-button" style="margin-left: 8px;">
Grade
</button>
` : '' }
</td>
</tr>
` ). join ('' )}
</tbody>
</table>
</div>
` : '' }
<!-- Certificates Tab -->
${ viewState. activeTab === 'certificates' ? `
<div class="tab-content">
<h3>Certificates Issued</h3>
<table class="data-table">
<thead>
<tr>
<th>Certificate ID</th>
<th>Student</th>
<th>Type</th>
<th>Achievement</th>
<th>Issued Date</th>
</tr>
</thead>
<tbody>
${ allCertificates. length === 0 ? `
<tr>
<td colspan="5" style="text-align: center; padding: 40px; color: ${ colors. gray } ;">
No certificates issued yet.
</td>
</tr>
` : allCertificates. slice (0 , 50 ). map (cert => `
<tr>
<td>
<code style="font-size: 12px; color: ${ colors. teal } ;"> ${ cert. certificateId || 'N/A' } </code>
</td>
<td>
<div style="font-weight: 500;"> ${ cert. username || 'Unknown' } </div>
</td>
<td>
<span style="text-transform: capitalize; font-weight: 600; color: ${ colors. navy } ;">
${ cert. type || 'N/A' }
</span>
</td>
<td>
${ cert. achievementName || 'N/A' }
</td>
<td style="color: ${ colors. gray } ; font-size: 13px;">
${ new Date (cert. issuedDate ). toLocaleDateString ()}
</td>
</tr>
` ). join ('' )}
</tbody>
</table>
</div>
` : '' }
</div>
` : '' } `
Assessment Management Tips
Quiz Results : - View all quiz attempts with scores and pass/fail status - Track attempt numbers (max 3 attempts per quiz) - Monitor average scores across all quizzes
Lab Submissions : - Review pending lab submissions - Access submission URLs (Wokwi projects, GitHub repos) - Grade labs using structured rubrics - Provide detailed feedback to students
Certificates : - View all issued certificates - Track certificate types (course, chapter, quiz, lab) - Verify certificate IDs for authenticity
Future Features (Coming Soon): - Auto-grading for specific lab criteria - Bulk certificate generation - Detailed analytics and reports - Export to CSV/Excel