Spaces:
Running
Running
Upload 9 files
Browse files- src/services/auth.js +9 -1
- src/views/InstructorView.js +23 -4
src/services/auth.js
CHANGED
|
@@ -84,7 +84,15 @@ export async function checkInstructorPermission(user) {
|
|
| 84 |
|
| 85 |
const email = user.email;
|
| 86 |
const instructorRef = doc(db, INSTRUCTORS_COLLECTION, email);
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
// Bootstrap Super Admin
|
| 90 |
if (email === SUPER_ADMIN_EMAIL) {
|
|
|
|
| 84 |
|
| 85 |
const email = user.email;
|
| 86 |
const instructorRef = doc(db, INSTRUCTORS_COLLECTION, email);
|
| 87 |
+
let snap;
|
| 88 |
+
|
| 89 |
+
try {
|
| 90 |
+
snap = await getDoc(instructorRef);
|
| 91 |
+
} catch (error) {
|
| 92 |
+
console.warn("Instructor Permission Check Failed (likely not whitelisted):", error.code);
|
| 93 |
+
// If permission denied, it means they are not allowed to read the doc => likely not in whitelist
|
| 94 |
+
return null;
|
| 95 |
+
}
|
| 96 |
|
| 97 |
// Bootstrap Super Admin
|
| 98 |
if (email === SUPER_ADMIN_EMAIL) {
|
src/views/InstructorView.js
CHANGED
|
@@ -336,6 +336,25 @@ export async function renderInstructorView() {
|
|
| 336 |
}
|
| 337 |
|
| 338 |
export function setupInstructorEvents() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
let roomUnsubscribe = null;
|
| 340 |
let currentInstructor = null;
|
| 341 |
|
|
@@ -1239,7 +1258,7 @@ export function setupInstructorEvents() {
|
|
| 1239 |
document.getElementById('broadcast-content').classList.add('hidden');
|
| 1240 |
const stage = document.getElementById('stage-view');
|
| 1241 |
stage.classList.remove('hidden');
|
| 1242 |
-
document.getElementById('stage-prompt').textContent = (prompt || '')
|
| 1243 |
document.getElementById('stage-author').textContent = author;
|
| 1244 |
};
|
| 1245 |
|
|
@@ -1353,7 +1372,7 @@ export function setupInstructorEvents() {
|
|
| 1353 |
onchange="handlePromptSelection(this)">
|
| 1354 |
</div>
|
| 1355 |
<!-- Prompt Content -->
|
| 1356 |
-
<div class="bg-black/30 rounded p-2 flex-1 overflow-y-auto font-mono text-green-300 whitespace-pre-wrap custom-scrollbar text-base leading-snug group-hover:text-green-200 transaction-colors mb-2">${(p.prompt
|
| 1357 |
|
| 1358 |
<!-- Footer: Time + Actions -->
|
| 1359 |
<div class="flex justify-between items-center text-[10px] text-gray-500 mt-auto">
|
|
@@ -1532,7 +1551,7 @@ export function setupInstructorEvents() {
|
|
| 1532 |
</div>
|
| 1533 |
<!-- Prompt Content: Larger Text (text-4xl) -->
|
| 1534 |
<div class="flex-1 overflow-y-auto font-mono text-green-300 text-3xl leading-relaxed whitespace-pre-wrap p-2 hover:bg-white/5 transition-colors rounded custom-scrollbar text-left">
|
| 1535 |
-
${(item.prompt
|
| 1536 |
</div>
|
| 1537 |
`;
|
| 1538 |
grid.appendChild(col);
|
|
@@ -1848,7 +1867,7 @@ export function setupInstructorEvents() {
|
|
| 1848 |
document.getElementById('broadcast-author').textContent = student.nickname;
|
| 1849 |
document.getElementById('broadcast-challenge').textContent = title;
|
| 1850 |
// content is already just text, but let's be safe
|
| 1851 |
-
document.getElementById('broadcast-prompt').textContent = (p.prompt || p.code || '')
|
| 1852 |
|
| 1853 |
// Store IDs for actions
|
| 1854 |
modal.dataset.userId = userId;
|
|
|
|
| 336 |
}
|
| 337 |
|
| 338 |
export function setupInstructorEvents() {
|
| 339 |
+
// Utility for cleaning prompt indentation
|
| 340 |
+
function stripIndent(str) {
|
| 341 |
+
if (!str) return '';
|
| 342 |
+
// Remove first line if empty (common in template literals)
|
| 343 |
+
const lines = str.replace(/^\n/, '').split('\n');
|
| 344 |
+
|
| 345 |
+
// Find min indentation (ignoring empty lines)
|
| 346 |
+
const minIndent = lines.reduce((min, line) => {
|
| 347 |
+
if (!line.trim()) return min;
|
| 348 |
+
const indent = line.match(/^\s*/)[0].length;
|
| 349 |
+
return Math.min(min, indent);
|
| 350 |
+
}, Infinity);
|
| 351 |
+
|
| 352 |
+
if (minIndent === Infinity) return str.trim(); // No non-empty lines
|
| 353 |
+
|
| 354 |
+
// Strip indent
|
| 355 |
+
return lines.map(line => line.slice(minIndent)).join('\n').trim();
|
| 356 |
+
}
|
| 357 |
+
|
| 358 |
let roomUnsubscribe = null;
|
| 359 |
let currentInstructor = null;
|
| 360 |
|
|
|
|
| 1258 |
document.getElementById('broadcast-content').classList.add('hidden');
|
| 1259 |
const stage = document.getElementById('stage-view');
|
| 1260 |
stage.classList.remove('hidden');
|
| 1261 |
+
document.getElementById('stage-prompt').textContent = stripIndent(prompt || '');
|
| 1262 |
document.getElementById('stage-author').textContent = author;
|
| 1263 |
};
|
| 1264 |
|
|
|
|
| 1372 |
onchange="handlePromptSelection(this)">
|
| 1373 |
</div>
|
| 1374 |
<!-- Prompt Content -->
|
| 1375 |
+
<div class="bg-black/30 rounded p-2 flex-1 overflow-y-auto font-mono text-green-300 whitespace-pre-wrap custom-scrollbar text-base leading-snug group-hover:text-green-200 transaction-colors mb-2">${stripIndent(p.prompt)}</div>
|
| 1376 |
|
| 1377 |
<!-- Footer: Time + Actions -->
|
| 1378 |
<div class="flex justify-between items-center text-[10px] text-gray-500 mt-auto">
|
|
|
|
| 1551 |
</div>
|
| 1552 |
<!-- Prompt Content: Larger Text (text-4xl) -->
|
| 1553 |
<div class="flex-1 overflow-y-auto font-mono text-green-300 text-3xl leading-relaxed whitespace-pre-wrap p-2 hover:bg-white/5 transition-colors rounded custom-scrollbar text-left">
|
| 1554 |
+
${stripIndent(item.prompt)}
|
| 1555 |
</div>
|
| 1556 |
`;
|
| 1557 |
grid.appendChild(col);
|
|
|
|
| 1867 |
document.getElementById('broadcast-author').textContent = student.nickname;
|
| 1868 |
document.getElementById('broadcast-challenge').textContent = title;
|
| 1869 |
// content is already just text, but let's be safe
|
| 1870 |
+
document.getElementById('broadcast-prompt').textContent = stripIndent(p.prompt || p.code || ''); // robust fallback
|
| 1871 |
|
| 1872 |
// Store IDs for actions
|
| 1873 |
modal.dataset.userId = userId;
|