mynewi/GradingApp
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />6 <title>Academic Evaluator Pro</title>7 <link rel="preconnect" href="https://fonts.googleapis.com">8 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>9 <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">10 11 <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>12 <script src="https://unpkg.com/jszip/dist/jszip.min.js"></script>13 <script src="https://unpkg.com/docx-preview/dist/docx-preview.min.js"></script>14 <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>15 <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>16 <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>17 <link rel="stylesheet" href="style.css" />18</head>19<body>20 <header class="app-header">21 <div class="brand-area">22 <div class="brand-title">Academic Evaluator <span>Pro</span></div>23 <div class="brand-subtitle">Flexible local grading workspace</div>24 </div>25 <div class="header-actions">26 <input type="file" id="file-upload" accept="application/pdf,.pdf,.docx" hidden />27 <button class="btn btn-primary" id="upload-btn">📄 Upload Assignment</button>28 <button class="btn btn-secondary" id="config-btn">⚙ Configurations</button>29 <button class="btn btn-secondary" id="new-batch-btn">🗂 New Student Batch</button>30 <button class="btn btn-secondary" id="gradebook-btn">👥 Gradebook</button>31 <button class="btn btn-highlight" id="export-btn">📊 Export Final Scores</button>32 <button class="btn btn-ghost" id="backup-btn">💾 Save / Backup Session</button>33 </div>34 </header>35 36 <main class="app-shell">37 <section class="document-pane" id="document-pane">38 <div class="viewer-statusbar">39 <div>40 <strong id="viewer-file-name">No assignment loaded</strong>41 <span id="viewer-file-meta">PDF and DOCX supported · Word files are converted locally for viewing</span>42 </div>43 <span id="autosave-pill" class="autosave-pill">✓ Auto-save active</span>44 </div>45 46 <div class="empty-state" id="empty-state">47 <div class="empty-icon">📚</div>48 <h2>Ready to grade</h2>49 <p>Upload a PDF or DOCX submission. Configurations, student records and unfinished grading entries are automatically saved in this browser.</p>50 <button class="btn btn-primary" id="empty-upload-btn">Upload Assignment</button>51 <div class="privacy-note"><strong>Local-first:</strong> this app does not send student grades to a grading server.</div>52 </div>53 54 <div id="custom-pdf-viewer" class="viewer-wrap" hidden>55 <div class="doc-toolbar">56 <div class="toolbar-group">57 <button class="btn btn-sm" id="pdf-prev">◀ Prev</button>58 <span class="page-info">Page <span id="page_num">1</span> / <span id="page_count">1</span></span>59 <button class="btn btn-sm" id="pdf-next">Next ▶</button>60 <span class="toolbar-divider"></span>61 <button class="btn btn-sm" id="pdf-zoom-out">−</button>62 <span class="zoom-info"><span id="zoom_val">120</span>%</span>63 <button class="btn btn-sm" id="pdf-zoom-in">+</button>64 <span class="toolbar-tip">Ctrl + scroll to zoom · scroll past page edge to change page</span>65 </div>66 <button class="btn btn-danger-soft btn-sm" id="drop-paper-pdf">✕ Drop Paper</button>67 </div>68 <div class="canvas-wrapper" id="canvas-wrapper">69 <canvas id="pdf-canvas"></canvas>70 </div>71 </div>72 73 <div id="conversion-state" class="conversion-state" hidden>74 <div class="conversion-card">75 <div class="conversion-spinner"></div>76 <h3>Preparing Word document</h3>77 <p id="conversion-message">Converting DOCX to a local PDF preview…</p>78 </div>79 </div>80 <div id="docx-conversion-host" aria-hidden="true"><div id="docx-conversion-content"></div></div>81 </section>82 83 <aside class="grading-pane">84 <section class="grading-section grading-top">85 <div class="section-title-row">86 <div>87 <div class="eyebrow">EVALUATION PANEL</div>88 <h2>Grade Submission</h2>89 </div>90 <span class="batch-chip" id="current-batch-chip">Batch will be set on first save</span>91 </div>92 93 <label class="field-label" for="active-config">Configuration</label>94 <select id="active-config" class="field-control"></select>95 96 <label class="field-label" for="active-assignment">Assignment</label>97 <select id="active-assignment" class="field-control"></select>98 <div id="assignment-meta" class="assignment-meta"></div>99 100 <div class="student-grid">101 <div>102 <label class="field-label" for="student-name">Student Name</label>103 <input id="student-name" class="field-control" autocomplete="off" placeholder="Student name" />104 </div>105 <div>106 <label class="field-label" for="student-id">Student ID</label>107 <input id="student-id" class="field-control" autocomplete="off" placeholder="Student ID" />108 </div>109 </div>110 <div id="group-field-wrap" hidden>111 <label class="field-label" for="group-number">Group #</label>112 <input id="group-number" class="field-control" autocomplete="off" placeholder="e.g. G03" />113 </div>114 115 <div class="progress-wrap">116 <div class="progress-label"><span>Rubric progress</span><span id="rubric-progress-text">0 / 0 criteria touched</span></div>117 <div class="progress-track"><div id="rubric-progress-bar" class="progress-bar"></div></div>118 </div>119 </section>120 121 <div id="sliders-container" class="sliders-container"></div>122 123 <section class="feedback-section">124 <label class="field-label" for="lecturer-feedback">Lecturer Feedback / Notes</label>125 <textarea id="lecturer-feedback" rows="4" placeholder="Overall feedback or notes..."></textarea>126 </section>127 128 <section class="score-dashboard">129 <div class="score-card">130 <span>Rubric Score</span>131 <strong id="rubric-score">0</strong><small>/ 100</small>132 </div>133 <div class="score-card">134 <span>Assignment Weight</span>135 <strong id="assignment-weight">0</strong><small>%</small>136 </div>137 <div class="score-card highlight">138 <span>Final Contribution</span>139 <strong id="weighted-score">0.00</strong><small>/ <span id="weighted-max">0</span></small>140 </div>141 </section>142 143 <section class="save-zone">144 <button class="btn btn-primary btn-full" id="save-record-btn">📥 Save Student Record & Next</button>145 <button class="btn btn-outline btn-full top-gap" id="group-import-open" hidden>👥 Import Group Assignment Grades</button>146 <div class="save-helper">Student ID is the primary key within the current batch.</div>147 </section>148 </aside>149 </main>150 151 <!-- CONFIGURATION -->152 <div class="modal-overlay" id="config-modal" hidden>153 <div class="modal-card modal-xl">154 <div class="modal-header">155 <div><div class="eyebrow">SAVED SETUPS</div><h2>Configurations</h2><p>Each configuration must total 100% across assignments, and every assignment rubric must total 100%.</p></div>156 <button class="icon-btn" data-close="config-modal">✕</button>157 </div>158 <div class="config-layout">159 <aside class="config-sidebar">160 <button class="btn btn-primary btn-full" id="new-config-btn">+ New Configuration</button>161 <div id="config-list" class="config-list"></div>162 <div class="sidebar-actions">163 <button class="btn btn-outline btn-full" id="duplicate-config-btn">Duplicate</button>164 <button class="btn btn-danger-soft btn-full" id="delete-config-btn">Delete</button>165 </div>166 </aside>167 <section class="config-editor">168 <div class="editor-heading-grid">169 <div><label class="field-label">Configuration Name</label><input id="config-name" class="field-control" /></div>170 <div><label class="field-label">Subject / Code (optional)</label><input id="config-code" class="field-control" /></div>171 </div>172 <div id="config-summary" class="config-summary"></div>173 <div id="assignment-editor-list"></div>174 <button class="btn btn-outline btn-full" id="add-assignment-btn">+ Add Assignment</button>175 </section>176 </div>177 <div class="modal-footer">178 <span class="footer-note">Saved configurations remain available in this browser for reuse across terms.</span>179 <button class="btn btn-secondary-dark" data-close="config-modal">Cancel</button>180 <button class="btn btn-primary" id="save-config-btn">Save Configuration</button>181 </div>182 </div>183 </div>184 185 <!-- FIRST BATCH SETUP -->186 <div class="modal-overlay" id="first-batch-modal" hidden>187 <div class="modal-card modal-sm">188 <div class="modal-header"><div><div class="eyebrow">FIRST STUDENT RECORD</div><h2>Set up this student batch</h2><p>This appears only when the first record is saved/imported for a new batch.</p></div></div>189 <div class="modal-body">190 <label class="field-label" for="first-batch-name">Batch / Term Name</label>191 <input id="first-batch-name" class="field-control" placeholder="e.g. September 2026 Intake" />192 <div class="info-box"><strong>Auto-save is already active in this browser.</strong> The batch name and grading records will continue to save automatically. Use “Save / Backup Session” whenever you want a separate backup file on your computer.</div>193 </div>194 <div class="modal-footer stacked-mobile">195 <button class="btn btn-outline" id="first-batch-cancel">Cancel</button>196 <button class="btn btn-primary" id="first-batch-browser">Start Batch & Save</button>197 </div>198 </div>199 </div>200 201 <!-- NEW BATCH -->202 <div class="modal-overlay" id="new-batch-modal" hidden>203 <div class="modal-card modal-sm">204 <div class="modal-header"><div><div class="eyebrow">NEW STUDENT BATCH</div><h2>Start a Fresh Batch</h2><p>The current configuration is reused; existing records are not deleted.</p></div><button class="icon-btn" data-close="new-batch-modal">✕</button></div>205 <div class="modal-body">206 <label class="field-label" for="new-batch-name">Batch / Term Name</label>207 <input id="new-batch-name" class="field-control" placeholder="e.g. January 2027 Intake" />208 <div class="info-box">This creates a new empty student record set for <strong id="new-batch-config-name"></strong>. Previous batches remain preserved in browser storage and in backups.</div>209 </div>210 <div class="modal-footer"><button class="btn btn-secondary-dark" data-close="new-batch-modal">Cancel</button><button class="btn btn-primary" id="create-batch-btn">Create New Batch</button></div>211 </div>212 </div>213 214 <!-- GROUP IMPORT -->215 <div class="modal-overlay" id="group-import-modal" hidden>216 <div class="modal-card modal-lg">217 <div class="modal-header"><div><div class="eyebrow">GROUP ASSIGNMENT</div><h2>Import Group Grades</h2><p>One uploaded row becomes one student assignment record.</p></div><button class="icon-btn" data-close="group-import-modal">✕</button></div>218 <div class="modal-body">219 <div class="info-box"><strong>Required columns:</strong> Student Name, Student ID, Grade, Group #. Grade is always entered out of 100. Student ID and Grade cannot be blank.</div>220 <div class="group-import-actions">221 <button class="btn btn-outline" id="download-group-template">⬇ Download Excel Template</button>222 <label class="btn btn-primary file-label">⬆ Upload Excel / CSV<input id="group-file-input" type="file" accept=".xlsx,.xls,.csv" hidden></label>223 </div>224 <div class="or-separator">OR PASTE FROM EXCEL / GOOGLE SHEETS</div>225 <textarea id="group-paste-area" rows="9" placeholder="Student Name Student ID Grade Group #"></textarea>226 <button class="btn btn-outline btn-full top-gap" id="import-pasted-group">Import Pasted Rows</button>227 <div id="group-import-result" class="import-result" hidden></div>228 </div>229 </div>230 </div>231 232 <!-- GRADEBOOK -->233 <div class="modal-overlay" id="gradebook-modal" hidden>234 <div class="modal-card modal-xxl">235 <div class="modal-header"><div><div class="eyebrow">CURRENT BATCH</div><h2>Gradebook</h2><p id="gradebook-subtitle"></p></div><button class="icon-btn" data-close="gradebook-modal">✕</button></div>236 <div class="gradebook-toolbar">237 <input id="gradebook-search" class="field-control" placeholder="Search student name or ID" />238 <select id="gradebook-sort" class="field-control"><option value="id">Sort by Student ID</option><option value="name">Sort by Name</option><option value="score-desc">Highest Final Score</option><option value="score-asc">Lowest Final Score</option></select>239 <button class="btn btn-outline" id="gradebook-refresh">Refresh</button>240 <button class="btn btn-outline" id="print-reports">🖨 Student Reports</button>241 <button class="btn btn-primary" id="gradebook-export">📊 Export Excel</button>242 </div>243 <div class="gradebook-table-wrap"><table class="gradebook-table" id="gradebook-table"></table></div>244 </div>245 </div>246 247 <!-- STUDENT REPORT -->248 <div class="modal-overlay" id="student-report-modal" hidden>249 <div class="modal-card modal-lg">250 <div class="modal-header"><div><div class="eyebrow">STUDENT RECORD</div><h2 id="student-report-title">Student</h2><p id="student-report-subtitle"></p></div><button class="icon-btn" data-close="student-report-modal">✕</button></div>251 <div class="modal-body" id="student-report-body"></div>252 </div>253 </div>254 255 <!-- BACKUP -->256 <div class="modal-overlay" id="backup-modal" hidden>257 <div class="modal-card modal-sm">258 <div class="modal-header"><div><div class="eyebrow">LOCAL SAFETY COPY</div><h2>Save / Backup Your Session</h2><p>Your browser auto-saves continuously. Use these options for an extra recoverable copy.</p></div><button class="icon-btn" data-close="backup-modal">✕</button></div>259 <div class="modal-body">260 <div id="backup-status-card" class="storage-status-card"></div>261 <button class="btn btn-primary btn-full" id="export-backup-btn">⬇ Save Session Backup File (.json)</button>262 <label class="btn btn-outline btn-full top-gap file-label">⬆ Restore Session Backup<input id="restore-backup-file" type="file" accept="application/json" hidden></label>263 <p class="microcopy">Browser auto-save is the working copy. The JSON backup is a separate safety copy saved through your browser’s normal download process, so it also works when the app is embedded in Hugging Face.</p>264 </div>265 </div>266 </div>267 268 <div id="toast" class="toast" hidden></div>269 <script src="app.js"></script>270</body>271</html>272 