| <!DOCTYPE html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>Student Dashboard</title>
|
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/4.6.2/css/bootstrap.min.css"/>
|
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"/>
|
| <style>
|
| :root {
|
| --primary: #1a4a7c;
|
| --secondary: #3498db;
|
| --success: #2ecc71;
|
| --warning: #f39c12;
|
| --info: #2980b9;
|
| --light: #ecf0f1;
|
| --dark: #2c3e50;
|
| --white: #ffffff;
|
| --gray: #bdc3c7;
|
| --border-radius: 8px;
|
| --font-main: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| }
|
| body {
|
| background: linear-gradient(135deg, #f0f4f8 0%, #d7e1ec 100%);
|
| min-height: 100vh;
|
| font-family: var(--font-main);
|
| padding: 40px 20px;
|
| }
|
| .container {
|
| max-width: 900px;
|
| }
|
| .dashboard-box {
|
| background-color: var(--white);
|
| border-radius: var(--border-radius);
|
| padding: 35px;
|
| width: 100%;
|
| box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
| margin: 20px auto;
|
| border-top: 4px solid var(--primary);
|
| }
|
| h1 {
|
| color: var(--primary);
|
| margin-bottom: 25px;
|
| text-align: center;
|
| font-weight: 600;
|
| font-size: 2.2rem;
|
| }
|
| h4 {
|
| color: var(--primary);
|
| margin: 25px 0 15px;
|
| font-weight: 500;
|
| font-size: 1.4rem;
|
| }
|
| p {
|
| font-size: 16px;
|
| color: var(--dark);
|
| margin-bottom: 15px;
|
| }
|
| .editable {
|
| cursor: pointer;
|
| color: var(--info);
|
| font-size: 0.9rem;
|
| text-decoration: none;
|
| transition: color 0.3s;
|
| display: block;
|
| text-align: center;
|
| margin-top: 5px;
|
| }
|
| .editable:hover {
|
| color: var(--secondary);
|
| text-decoration: underline;
|
| }
|
| .hidden {
|
| display: none;
|
| }
|
| .btn-primary {
|
| background: linear-gradient(to right, var(--primary), var(--secondary));
|
| border: none;
|
| border-radius: 50px;
|
| font-weight: 500;
|
| padding: 12px 25px;
|
| transition: all 0.3s;
|
| box-shadow: 0 4px 10px rgba(26, 74, 124, 0.3);
|
| margin-top: 20px;
|
| }
|
| .btn-primary:hover {
|
| background: linear-gradient(to right, #155785, #2980b9);
|
| transform: translateY(-2px);
|
| box-shadow: 0 6px 12px rgba(26, 74, 124, 0.4);
|
| }
|
| .btn-secondary {
|
| background-color: var(--light);
|
| color: var(--dark);
|
| border: none;
|
| border-radius: 50px;
|
| font-weight: 500;
|
| padding: 12px 25px;
|
| transition: all 0.3s;
|
| margin-top: 10px;
|
| }
|
| .btn-secondary:hover {
|
| background-color: var(--gray);
|
| transform: translateY(-2px);
|
| }
|
| .btn-success {
|
| background: linear-gradient(to right, var(--success), var(--info));
|
| border: none;
|
| border-radius: 50px;
|
| font-weight: 500;
|
| padding: 12px 25px;
|
| transition: all 0.3s;
|
| box-shadow: 0 4px 10px rgba(46, 204, 113, 0.3);
|
| margin-right: 10px;
|
| }
|
| .btn-success:hover {
|
| background: linear-gradient(to right, #27ae60, var(--success));
|
| transform: translateY(-2px);
|
| box-shadow: 0 6px 12px rgba(46, 204, 113, 0.4);
|
| }
|
| .btn-link {
|
| color: var(--gray);
|
| text-decoration: none;
|
| padding: 10px;
|
| display: block;
|
| text-align: center;
|
| transition: color 0.3s;
|
| font-weight: 500;
|
| }
|
| .btn-link:hover {
|
| color: var(--primary);
|
| text-decoration: none;
|
| }
|
| .profile-info {
|
| background-color: var(--light);
|
| border-radius: var(--border-radius);
|
| padding: 25px;
|
| margin-bottom: 25px;
|
| border-left: 4px solid var(--primary);
|
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
| }
|
| .profile-info p {
|
| margin-bottom: 15px;
|
| display: flex;
|
| align-items: center;
|
| line-height: 1.6;
|
| }
|
| .profile-info p strong {
|
| min-width: 130px;
|
| display: inline-block;
|
| }
|
| .profile-info p strong i {
|
| width: 20px;
|
| color: var(--primary);
|
| margin-right: 8px;
|
| }
|
| .form-group label {
|
| font-weight: 500;
|
| color: var(--dark);
|
| margin-bottom: 8px;
|
| }
|
| .form-control {
|
| border-radius: var(--border-radius);
|
| border: 1px solid var(--gray);
|
| padding: 12px;
|
| transition: border 0.3s, box-shadow 0.3s;
|
| margin-bottom: 15px;
|
| }
|
| .form-control:focus {
|
| border-color: var(--secondary);
|
| box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25);
|
| }
|
| select.form-control {
|
| cursor: pointer;
|
| height: 46px;
|
| background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24'%3E%3Cpath fill='%231a4a7c' d='M7 10l5 5 5-5z'/%3E%3C/svg%3E");
|
| background-repeat: no-repeat;
|
| background-position: right 10px center;
|
| padding-right: 30px;
|
| background-size: 20px;
|
| -webkit-appearance: none;
|
| -moz-appearance: none;
|
| appearance: none;
|
| }
|
| .table {
|
| border-radius: var(--border-radius);
|
| overflow: hidden;
|
| box-shadow: 0 4px 10px rgba(0,0,0,0.05);
|
| margin-top: 20px;
|
| margin-bottom: 25px;
|
| border: none;
|
| }
|
| .table thead th {
|
| background-color: rgba(26, 74, 124, 0.1);
|
| color: var(--primary);
|
| border-bottom: 2px solid var(--primary);
|
| font-weight: 600;
|
| padding: 15px;
|
| text-transform: uppercase;
|
| font-size: 0.85rem;
|
| letter-spacing: 0.5px;
|
| }
|
| .table tbody td {
|
| padding: 15px;
|
| border-top: 1px solid #e9ecef;
|
| vertical-align: middle;
|
| }
|
| .table-striped tbody tr:nth-of-type(odd) {
|
| background-color: rgba(248, 249, 250, 0.7);
|
| }
|
| hr {
|
| border-top: 1px solid #e9ecef;
|
| margin: 30px 0;
|
| }
|
| .section-title {
|
| display: flex;
|
| align-items: center;
|
| margin-bottom: 20px;
|
| padding-bottom: 10px;
|
| border-bottom: 2px solid rgba(26, 74, 124, 0.2);
|
| }
|
| .section-title i {
|
| margin-right: 10px;
|
| color: var(--primary);
|
| font-size: 1.2rem;
|
| }
|
| .action-buttons {
|
| display: flex;
|
| justify-content: space-between;
|
| margin-top: 25px;
|
| }
|
| .action-buttons button {
|
| width: 48%;
|
| }
|
| .alert-info {
|
| background-color: rgba(26, 74, 124, 0.1);
|
| border-color: rgba(26, 74, 124, 0.2);
|
| color: var(--primary);
|
| border-radius: var(--border-radius);
|
| padding: 15px;
|
| margin-bottom: 25px;
|
| }
|
| .alert-info i {
|
| color: var(--primary);
|
| margin-right: 8px;
|
| }
|
| .modal {
|
| display: none;
|
| position: fixed;
|
| top: 0;
|
| left: 0;
|
| width: 100%;
|
| height: 100%;
|
| background-color: rgba(0,0,0,0.5);
|
| z-index: 1000;
|
| justify-content: center;
|
| align-items: center;
|
| }
|
| .modal-content {
|
| background-color: var(--white);
|
| border-radius: var(--border-radius);
|
| margin:15% auto;
|
| padding: 30px;
|
| border: none;
|
| width: 90%;
|
| max-width: 500px;
|
| box-shadow: 0 15px 30px rgba(0,0,0,0.2);
|
| position: relative;
|
| }
|
| .close-modal {
|
| position: absolute;
|
| top: 15px;
|
| right: 20px;
|
| color: var(--gray);
|
| font-size: 24px;
|
| font-weight: bold;
|
| cursor: pointer;
|
| transition: color 0.3s;
|
| }
|
| .close-modal:hover {
|
| color: var(--dark);
|
| }
|
| .modal h3 {
|
| color: var(--primary);
|
| margin-bottom: 20px;
|
| padding-bottom: 15px;
|
| border-bottom: 1px solid #e9ecef;
|
| }
|
| .modal button {
|
| background: linear-gradient(to right, var(--primary), var(--secondary));
|
| color: var(--white);
|
| border: none;
|
| border-radius: 50px;
|
| padding: 12px 25px;
|
| margin-top: 20px;
|
| cursor: pointer;
|
| transition: all 0.3s;
|
| font-weight: 500;
|
| box-shadow: 0 4px 10px rgba(26, 74, 124, 0.3);
|
| display: block;
|
| width: 100%;
|
| }
|
| .modal button:hover {
|
| background: linear-gradient(to right, #155785, #2980b9);
|
| transform: translateY(-2px);
|
| box-shadow: 0 6px 12px rgba(26, 74, 124, 0.4);
|
| }
|
| .modal label {
|
| display: block;
|
| margin: 10px 0;
|
| font-weight: 500;
|
| }
|
| .modal select {
|
| width: 100%;
|
| padding: 10px;
|
| border-radius: var(--border-radius);
|
| border: 1px solid var(--gray);
|
| margin-bottom: 20px;
|
| }
|
| #regularSubjectsList,
|
| #specializationSubjectsList,
|
| #practicalsList {
|
| max-height: 300px;
|
| overflow-y: auto;
|
| padding: 10px;
|
| border: 1px solid #eee;
|
| border-radius: var(--border-radius);
|
| margin-bottom: 10px;
|
| }
|
| #regularSubjectsList label,
|
| #specializationSubjectsList label,
|
| #practicalsList label {
|
| display: block;
|
| padding: 8px;
|
| border-bottom: 1px solid #f5f5f5;
|
| margin: 0;
|
| }
|
| #regularSubjectsList label:hover,
|
| #specializationSubjectsList label:hover,
|
| #practicalsList label:hover {
|
| background-color: #f8f9fa;
|
| }
|
| #regularSubjectsList input,
|
| #specializationSubjectsList input,
|
| #practicalsList input {
|
| margin-right: 10px;
|
| }
|
| .subject-selection-button {
|
| display: inline-block;
|
| background-color: var(--light);
|
| color: var(--primary);
|
| border: 1px solid var(--primary);
|
| border-radius: 50px;
|
| padding: 10px 20px;
|
| margin: 10px 0;
|
| font-weight: 500;
|
| transition: all 0.3s;
|
| text-decoration: none;
|
| cursor: pointer;
|
| }
|
| .subject-selection-button:hover {
|
| background-color: rgba(26, 74, 124, 0.1);
|
| transform: translateY(-2px);
|
| box-shadow: 0 4px 8px rgba(0,0,0,0.05);
|
| }
|
| .subject-selection-button i {
|
| margin-right: 5px;
|
| }
|
| .mt-4 {
|
| margin-top: 2rem;
|
| }
|
| .text-center {
|
| text-align: center;
|
| }
|
| @media (max-width: 768px) {
|
| .dashboard-box {
|
| padding: 25px 20px;
|
| }
|
| .action-buttons {
|
| flex-direction: column;
|
| }
|
| .action-buttons button {
|
| width: 100%;
|
| margin-bottom: 10px;
|
| }
|
| .profile-info p strong {
|
| min-width: 105px;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <div class="container">
|
| <div class="dashboard-box">
|
| <h1>
|
| Welcome, <span id="view-fullname">{{ user.full_name }}</span>
|
| <small class="editable" onclick="openEdit()"><i class="fas fa-edit"></i> Edit Profile</small>
|
| </h1>
|
|
|
|
|
| <div id="profile-view" class="profile-info">
|
| <p><strong><i class="fas fa-id-card"></i> Enrollment No:</strong> <span id="view-enroll">{{ user.enrollment_number }}</span></p>
|
| <p><strong><i class="fas fa-envelope"></i> Email:</strong> <span id="view-email">{{ user.email }}</span></p>
|
| <p><strong><i class="fas fa-phone"></i> Student No:</strong> <span id="view-studno">{{ user.student_number or '-' }}</span></p>
|
| <p><strong><i class="fas fa-phone-alt"></i> Parent No:</strong> <span id="view-parentno">{{ user.parent_number or '-' }}</span></p>
|
| <p><strong><i class="fas fa-envelope-open"></i> Parent Email:</strong> <span id="view-parentemail">{{ user.parent_email or '-' }}</span></p>
|
| </div>
|
|
|
|
|
| <div id="profile-edit" class="hidden">
|
| <div class="form-group">
|
| <label>Full Name</label>
|
| <input id="edit-fullname" class="form-control" value="{{ user.full_name }}"/>
|
| </div>
|
| <div class="form-group">
|
| <label>Enrollment No</label>
|
| <input id="edit-enroll" class="form-control" value="{{ user.enrollment_number }}"/>
|
| </div>
|
| <div class="form-group">
|
| <label>Email</label>
|
| <input id="edit-email" class="form-control" value="{{ user.email }}"/>
|
| </div>
|
| <div class="form-group">
|
| <label>Student No</label>
|
| <input id="edit-studno" class="form-control" value="{{ user.student_number }}"/>
|
| </div>
|
| <div class="form-group">
|
| <label>Parent No</label>
|
| <input id="edit-parentno" class="form-control" value="{{ user.parent_number }}"/>
|
| </div>
|
| <div class="form-group">
|
| <label>Parent Email</label>
|
| <input id="edit-parentemail" class="form-control" value="{{ user.parent_email }}"/>
|
| </div>
|
| <div class="action-buttons">
|
| <button class="btn btn-success" onclick="saveProfile()"><i class="fas fa-save"></i> Save</button>
|
| <button class="btn btn-secondary" onclick="cancelEdit()"><i class="fas fa-times"></i> Cancel</button>
|
| </div>
|
| </div>
|
|
|
| <hr/>
|
|
|
| <div class="alert alert-info">
|
| <i class="fas fa-chart-pie"></i>
|
| <strong>Overall Attendance Average:</strong> <span id="overall-average">Calculating...</span>
|
| </div>
|
|
|
|
|
| <div class="section-title">
|
| <i class="fas fa-chalkboard-teacher"></i>
|
| <h4>Class Information</h4>
|
| </div>
|
|
|
| <div class="form-group">
|
| <label>Select Class</label>
|
| <select id="class-select" class="form-control">
|
| <option value="">-- Choose Class --</option>
|
| <option {{ 'selected' if user.selected_class=='SY-19' }}>SY-19</option>
|
| <option {{ 'selected' if user.selected_class=='SY-20' }}>SY-20</option>
|
| <option {{ 'selected' if user.selected_class=='SY-21' }}>SY-21</option>
|
| <option {{ 'selected' if user.selected_class=='SY-22' }}>SY-22</option>
|
| </select>
|
| </div>
|
|
|
|
|
| <div id="subjects-section" class="{{ '' if user.selected_class else 'hidden' }}">
|
| <div class="alert alert-info">
|
| <i class="fas fa-info-circle"></i>
|
| <strong>Current Class:</strong> <span id="cls-label">{{ user.selected_class }}</span>
|
| </div>
|
|
|
|
|
| <div class="section-title">
|
| <i class="fas fa-book"></i>
|
| <h4>Regular Subjects</h4>
|
| </div>
|
|
|
| <button class="subject-selection-button" onclick="openRegularSubjectsModal()">
|
| <i class="fas fa-plus-circle"></i> Select Regular Subjects
|
| </button>
|
|
|
| <table class="table table-striped">
|
| <thead>
|
| <tr>
|
| <th>Subject</th>
|
| <th>Attendance Count</th>
|
| <th>Total Count</th>
|
| <th>Percentage</th>
|
| </tr>
|
| </thead>
|
| <tbody id="tbl-subjects">
|
| {% for subject in selected_regular_subjects %}
|
| <tr>
|
| <td>{{ subject }}</td>
|
| <td>{{ subject_attendance['regular'].get(subject, {}).get('attendance', 0) }}</td>
|
| <td>{{ subject_attendance['regular'].get(subject, {}).get('total', 0) }}</td>
|
| <td>
|
| {% set attendance = subject_attendance['regular'].get(subject, {}).get('attendance', 0) %}
|
| {% set total = subject_attendance['regular'].get(subject, {}).get('total', 0) %}
|
| {% if total > 0 %}
|
| {{ (attendance / total * 100) | round(2) }}%
|
| {% else %}
|
| N/A
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
|
|
|
|
| <div class="section-title">
|
| <i class="fas fa-star"></i>
|
| <h4>Specialization Subjects</h4>
|
| </div>
|
|
|
| <button class="subject-selection-button" onclick="openSpecializationSubjectsModal()">
|
| <i class="fas fa-plus-circle"></i> Select Specialization Subjects
|
| </button>
|
|
|
| <table class="table table-striped">
|
| <thead>
|
| <tr>
|
| <th>Specialization Subject</th>
|
| <th>Attendance Count</th>
|
| <th>Total Count</th>
|
| <th>Percentage</th>
|
| </tr>
|
| </thead>
|
| <tbody id="tbl-spec">
|
| {% for subject in selected_specializations %}
|
| <tr>
|
| <td>{{ subject }}</td>
|
| <td>{{ subject_attendance['specialization'].get(subject, {}).get('attendance', 0) }}</td>
|
| <td>{{ subject_attendance['specialization'].get(subject, {}).get('total', 0) }}</td>
|
| <td>
|
| {% set attendance = subject_attendance['specialization'].get(subject, {}).get('attendance', 0) %}
|
| {% set total = subject_attendance['specialization'].get(subject, {}).get('total', 0) %}
|
| {% if total > 0 %}
|
| {{ (attendance / total * 100) | round(2) }}%
|
| {% else %}
|
| N/A
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
|
|
|
|
| <div class="section-title">
|
| <i class="fas fa-flask"></i>
|
| <h4>Practical Subjects</h4>
|
| </div>
|
|
|
| <button class="subject-selection-button" onclick="openBatchPracticalsModal()">
|
| <i class="fas fa-plus-circle"></i> Select Batch & Practical Subjects
|
| </button>
|
|
|
| <table class="table table-striped">
|
| <thead>
|
| <tr>
|
| <th>Practical Subject</th>
|
| <th>Batch</th>
|
| <th>Attendance Count</th>
|
| <th>Total Count</th>
|
| <th>Percentage</th>
|
| </tr>
|
| </thead>
|
| <tbody id="tbl-prac">
|
| {% for subject in selected_practicals %}
|
| <tr>
|
| <td>{{ subject }}</td>
|
| <td>{{ user.selected_batch or 'N/A' }}</td>
|
| <td>{{ subject_attendance['practical'].get(subject, {}).get('attendance', 0) }}</td>
|
| <td>{{ subject_attendance['practical'].get(subject, {}).get('total', 0) }}</td>
|
| <td>
|
| {% set attendance = subject_attendance['practical'].get(subject, {}).get('attendance', 0) %}
|
| {% set total = subject_attendance['practical'].get(subject, {}).get('total', 0) %}
|
| {% if total > 0 %}
|
| {{ (attendance / total * 100) | round(2) }}%
|
| {% else %}
|
| N/A
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
|
|
|
|
| <div class="section-title">
|
| <i class="fas fa-chalkboard-teacher"></i>
|
| <h4>Class Teacher Details</h4>
|
| </div>
|
| <table class="table table-striped">
|
| <thead>
|
| <tr>
|
| <th>Name</th>
|
| <th>Email</th>
|
| <th>Phone</th>
|
| </tr>
|
| </thead>
|
| <tbody>
|
| {% for teacher in teachers %}
|
| <tr>
|
| <td>{{ teacher.full_name }}</td>
|
| <td>
|
| {% if teacher.email %}
|
| <a href="mailto:{{ teacher.email }}">{{ teacher.email }}</a>
|
| {% else %}
|
| Not provided
|
| {% endif %}
|
| </td>
|
| <td>
|
| {% if teacher.phone_number %}
|
| <a href="tel:{{ teacher.phone_number }}">{{ teacher.phone_number }}</a>
|
| {% else %}
|
| Not provided
|
| {% endif %}
|
| </td>
|
| </tr>
|
| {% endfor %}
|
| </tbody>
|
| </table>
|
| </div>
|
|
|
|
|
| <div class="mt-4 text-center">
|
| {% if face_exists %}
|
| <a href="/register_face" class="btn btn-primary">
|
| <i class="fas fa-camera"></i> Re-register Face
|
| </a>
|
| {% else %}
|
| <a href="/register_face" class="btn btn-secondary">
|
| <i class="fas fa-camera"></i> Register Face
|
| </a>
|
| {% endif %}
|
| </div>
|
|
|
| <a href="/logout" class="btn btn-link mt-2">
|
| <i class="fas fa-sign-out-alt"></i> Logout
|
| </a>
|
| </div>
|
| </div>
|
|
|
|
|
| <div class="modal" id="regularSubjectsModal">
|
| <div class="modal-content">
|
| <span class="close-modal" onclick="closeModal('regularSubjectsModal')">✕</span>
|
| <h3><i class="fas fa-book"></i> Select Regular Subjects</h3>
|
| <div id="regularSubjectsList"></div>
|
| <button onclick="saveRegularSubjects()"><i class="fas fa-save"></i> Save Selections</button>
|
| </div>
|
| </div>
|
|
|
| <div class="modal" id="specializationSubjectsModal">
|
| <div class="modal-content">
|
| <span class="close-modal" onclick="closeModal('specializationSubjectsModal')">✕</span>
|
| <h3><i class="fas fa-star"></i> Select Specialization Subjects</h3>
|
| <div id="specializationSubjectsList"></div>
|
| <button onclick="saveSpecializationSubjects()"><i class="fas fa-save"></i> Save Selections</button>
|
| </div>
|
| </div>
|
|
|
| <div class="modal" id="batchPracticalsModal">
|
| <div class="modal-content">
|
| <span class="close-modal" onclick="closeModal('batchPracticalsModal')">✕</span>
|
| <h3><i class="fas fa-flask"></i> Select Batch & Practical Subjects</h3>
|
| <label for="batchSelectModal"><i class="fas fa-layer-group"></i> Select Batch:</label>
|
| <select id="batchSelectModal"></select>
|
| <div id="practicalsList"></div>
|
| <button onclick="saveBatchAndPracticals()"><i class="fas fa-save"></i> Save Selections</button>
|
| </div>
|
| </div>
|
|
|
| <script>
|
| function openEdit() {
|
| document.getElementById('profile-view').classList.add('hidden');
|
| document.getElementById('profile-edit').classList.remove('hidden');
|
| }
|
|
|
| function cancelEdit() {
|
| document.getElementById('profile-edit').classList.add('hidden');
|
| document.getElementById('profile-view').classList.remove('hidden');
|
| }
|
|
|
| function saveProfile() {
|
| fetch('/api/update_profile', {
|
| method: 'POST',
|
| headers: { 'Content-Type': 'application/json' },
|
| body: JSON.stringify({
|
| full_name: document.getElementById('edit-fullname').value,
|
| enrollment_number: document.getElementById('edit-enroll').value,
|
| email: document.getElementById('edit-email').value,
|
| student_number: document.getElementById('edit-studno').value,
|
| parent_number: document.getElementById('edit-parentno').value,
|
| parent_email: document.getElementById('edit-parentemail').value
|
| })
|
| }).then(r => r.json()).then(resp => {
|
| if (resp.success) location.reload();
|
| else alert(resp.message);
|
| });
|
| }
|
|
|
| let selectedRegularSubjects = {{ selected_regular_subjects | tojson | safe }};
|
| let selectedSpecializations = {{ selected_specializations | tojson | safe }};
|
| let selectedBatch = "{{ user.selected_batch or '' }}";
|
| let selectedPracticals = {{ selected_practicals | tojson | safe }};
|
|
|
| document.getElementById('class-select').addEventListener('change', e => {
|
| const cls = e.target.value;
|
| fetch('/api/select_class', {
|
| method: 'POST',
|
| headers: {'Content-Type': 'application/json'},
|
| body: JSON.stringify({ class: cls })
|
| }).then(_ => {
|
| if (!cls) return location.reload();
|
| document.getElementById('subjects-section').classList.remove('hidden');
|
| document.getElementById('cls-label').innerText = cls;
|
| loadStudentSection(cls);
|
| });
|
| });
|
|
|
| function loadStudentSection(cls) {
|
|
|
| }
|
|
|
| function openRegularSubjectsModal() {
|
| const cls = document.getElementById('class-select').value;
|
| if (!cls) {
|
| alert('Please select a class first');
|
| return;
|
| }
|
| fetch(`/get_config?class=${cls}&type=subject`)
|
| .then(r => r.json())
|
| .then(list => {
|
| const listDiv = document.getElementById('regularSubjectsList');
|
| listDiv.innerHTML = '';
|
| list.forEach(subject => {
|
| const checked = selectedRegularSubjects.includes(subject) ? 'checked' : '';
|
| listDiv.innerHTML += `<label><input type="checkbox" value="${subject}" ${checked}> ${subject}</label><br>`;
|
| });
|
| document.getElementById('regularSubjectsModal').style.display = 'block';
|
| });
|
| }
|
|
|
| function openSpecializationSubjectsModal() {
|
| const cls = document.getElementById('class-select').value;
|
| if (!cls) {
|
| alert('Please select a class first');
|
| return;
|
| }
|
| fetch(`/get_config?class=${cls}&type=specSubject`)
|
| .then(r => r.json())
|
| .then(list => {
|
| const listDiv = document.getElementById('specializationSubjectsList');
|
| listDiv.innerHTML = '';
|
| list.forEach(subject => {
|
| const checked = selectedSpecializations.includes(subject) ? 'checked' : '';
|
| listDiv.innerHTML += `<label><input type="checkbox" value="${subject}" ${checked}> ${subject}</label><br>`;
|
| });
|
| document.getElementById('specializationSubjectsModal').style.display = 'block';
|
| });
|
| }
|
|
|
| function openBatchPracticalsModal() {
|
| const cls = document.getElementById('class-select').value;
|
| if (!cls) {
|
| alert('Please select a class first');
|
| return;
|
| }
|
| fetch(`/get_config?class=${cls}&type=batch`)
|
| .then(r => r.json())
|
| .then(batches => {
|
| const batchSelect = document.getElementById('batchSelectModal');
|
| batchSelect.innerHTML = '<option value="">-- Select Batch --</option>';
|
| batches.forEach(batch => {
|
| const selected = batch === selectedBatch ? 'selected' : '';
|
| batchSelect.innerHTML += `<option value="${batch}" ${selected}>${batch}</option>`;
|
| });
|
| });
|
| fetch(`/get_config?class=${cls}&type=practical`)
|
| .then(r => r.json())
|
| .then(practicals => {
|
| const listDiv = document.getElementById('practicalsList');
|
| listDiv.innerHTML = '';
|
| practicals.forEach(subject => {
|
| const checked = selectedPracticals.includes(subject) ? 'checked' : '';
|
| listDiv.innerHTML += `<label><input type="checkbox" value="${subject}" ${checked}> ${subject}</label><br>`;
|
| });
|
| document.getElementById('batchPracticalsModal').style.display = 'block';
|
| });
|
| }
|
|
|
| function saveRegularSubjects() {
|
| const checkboxes = document.querySelectorAll('#regularSubjectsList input[type="checkbox"]:checked');
|
| const selected = Array.from(checkboxes).map(cb => cb.value);
|
| fetch('/api/update_regular_subjects', {
|
| method: 'POST',
|
| headers: {'Content-Type': 'application/json'},
|
| body: JSON.stringify({subjects: selected})
|
| }).then(() => {
|
| location.reload();
|
| });
|
| }
|
|
|
| function saveSpecializationSubjects() {
|
| const checkboxes = document.querySelectorAll('#specializationSubjectsList input[type="checkbox"]:checked');
|
| const selected = Array.from(checkboxes).map(cb => cb.value);
|
| fetch('/api/update_specializations', {
|
| method: 'POST',
|
| headers: {'Content-Type': 'application/json'},
|
| body: JSON.stringify({subjects: selected})
|
| }).then(() => {
|
| location.reload();
|
| });
|
| }
|
|
|
| function saveBatchAndPracticals() {
|
| const batch = document.getElementById('batchSelectModal').value;
|
| const checkboxes = document.querySelectorAll('#practicalsList input[type="checkbox"]:checked');
|
| const selectedPracticals = Array.from(checkboxes).map(cb => cb.value);
|
| fetch('/api/update_batch', {
|
| method: 'POST',
|
| headers: {'Content-Type': 'application/json'},
|
| body: JSON.stringify({batch: batch})
|
| }).then(() => {
|
| fetch('/api/update_practicals', {
|
| method: 'POST',
|
| headers: {'Content-Type': 'application/json'},
|
| body: JSON.stringify({practicals: selectedPracticals})
|
| }).then(() => {
|
| location.reload();
|
| });
|
| });
|
| }
|
|
|
| function closeModal(modalId) {
|
| document.getElementById(modalId).style.display = 'none';
|
| }
|
|
|
| window.addEventListener('DOMContentLoaded', () => {
|
| const cs = document.getElementById('class-select');
|
| if (cs.value) cs.dispatchEvent(new Event('change'));
|
|
|
|
|
| const percentageCells = document.querySelectorAll('#tbl-subjects td:last-child, #tbl-spec td:last-child, #tbl-prac td:last-child');
|
| let sum = 0;
|
| let count = 0;
|
| percentageCells.forEach(cell => {
|
| const text = cell.textContent.trim();
|
| if (text !== 'N/A') {
|
| const percentage = parseFloat(text.replace('%', ''));
|
| sum += percentage;
|
| count++;
|
| }
|
| });
|
| const average = count > 0 ? (sum / count).toFixed(2) : 'N/A';
|
| document.getElementById('overall-average').textContent = average === 'N/A' ? 'N/A' : average + '%';
|
| });
|
| </script>
|
| </body>
|
| </html> |