Spaces:
Running
Running
Upload 9 files
Browse files- src/views/InstructorView.js +29 -13
src/views/InstructorView.js
CHANGED
|
@@ -124,12 +124,9 @@ export async function renderInstructorView() {
|
|
| 124 |
<div class="flex items-center"><div class="w-3 h-3 bg-red-500 animate-pulse rounded-sm mr-1"></div> 卡關 (>5m)</div>
|
| 125 |
</div>
|
| 126 |
|
| 127 |
-
<button id="group-photo-btn" class="bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-500 hover:to-purple-500 text-white font-bold py-2 px-4 rounded-lg transition-all shadow-lg border border-pink-400/30 flex items-center space-x-2">
|
| 128 |
<span>📸 大合照</span>
|
| 129 |
</button>
|
| 130 |
-
<button id="btn-open-gallery" class="bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 rounded-lg transition-all border border-blue-400/30 flex items-center space-x-2">
|
| 131 |
-
<span>👾 怪獸圖鑑</span>
|
| 132 |
-
</button>
|
| 133 |
<button id="nav-admin-btn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg transition-all border border-gray-600">
|
| 134 |
管理題目
|
| 135 |
</button>
|
|
@@ -138,13 +135,20 @@ export async function renderInstructorView() {
|
|
| 138 |
<button id="rejoin-room-btn" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded-lg">重回</button>
|
| 139 |
|
| 140 |
<button id="create-room-btn" class="bg-purple-600 hover:bg-purple-500 text-white font-bold px-4 py-2 rounded-lg shadow-lg">開房</button>
|
| 141 |
-
<button id="logout-btn" class="bg-red-800 hover:bg-red-700 text-white font-bold px-4 py-2 rounded-lg shadow-lg border border-red-600/50" title="登出">
|
| 142 |
-
🚪
|
| 143 |
-
</button>
|
| 144 |
</div>
|
| 145 |
</div>
|
| 146 |
</header>
|
| 147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
<!-- Heatmap Content -->
|
| 149 |
<div id="dashboard-content" class="hidden overflow-x-auto pb-10">
|
| 150 |
<table class="w-full border-collapse">
|
|
@@ -635,6 +639,7 @@ export function setupInstructorEvents() {
|
|
| 635 |
createContainer.classList.add('hidden');
|
| 636 |
roomInfo.classList.remove('hidden');
|
| 637 |
dashboardContent.classList.remove('hidden');
|
|
|
|
| 638 |
displayRoomCode.textContent = roomCode;
|
| 639 |
localStorage.setItem('vibecoding_instructor_room', roomCode);
|
| 640 |
sessionStorage.setItem('vibecoding_instructor_in_room', 'true');
|
|
@@ -786,8 +791,8 @@ function renderTransposedHeatmap(students) {
|
|
| 786 |
if (p.status === 'completed') {
|
| 787 |
statusClass = 'bg-green-500/20 border-green-500/50 hover:bg-green-500/40 cursor-pointer shadow-[0_0_10px_rgba(34,197,94,0.1)]';
|
| 788 |
content = '✅';
|
| 789 |
-
|
| 790 |
-
action = `onclick = "window.showBroadcastModal('${student.id}', '${c.id}'
|
| 791 |
} else if (p.status === 'started') {
|
| 792 |
// Check stuck
|
| 793 |
const startedAt = p.timestamp ? p.timestamp.toDate() : new Date();
|
|
@@ -833,14 +838,25 @@ function renderTransposedHeatmap(students) {
|
|
| 833 |
}
|
| 834 |
|
| 835 |
// Global scope for HTML access
|
| 836 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 837 |
const modal = document.getElementById('broadcast-modal');
|
| 838 |
const content = document.getElementById('broadcast-content');
|
| 839 |
|
| 840 |
-
document.getElementById('broadcast-avatar').textContent = nickname[0];
|
| 841 |
-
document.getElementById('broadcast-author').textContent = nickname;
|
| 842 |
document.getElementById('broadcast-challenge').textContent = title;
|
| 843 |
-
|
|
|
|
| 844 |
|
| 845 |
// Store IDs for actions
|
| 846 |
modal.dataset.userId = userId;
|
|
|
|
| 124 |
<div class="flex items-center"><div class="w-3 h-3 bg-red-500 animate-pulse rounded-sm mr-1"></div> 卡關 (>5m)</div>
|
| 125 |
</div>
|
| 126 |
|
| 127 |
+
<button id="group-photo-btn" class="hidden bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-500 hover:to-purple-500 text-white font-bold py-2 px-4 rounded-lg transition-all shadow-lg border border-pink-400/30 flex items-center space-x-2">
|
| 128 |
<span>📸 大合照</span>
|
| 129 |
</button>
|
|
|
|
|
|
|
|
|
|
| 130 |
<button id="nav-admin-btn" class="bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg transition-all border border-gray-600">
|
| 131 |
管理題目
|
| 132 |
</button>
|
|
|
|
| 135 |
<button id="rejoin-room-btn" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded-lg">重回</button>
|
| 136 |
|
| 137 |
<button id="create-room-btn" class="bg-purple-600 hover:bg-purple-500 text-white font-bold px-4 py-2 rounded-lg shadow-lg">開房</button>
|
|
|
|
|
|
|
|
|
|
| 138 |
</div>
|
| 139 |
</div>
|
| 140 |
</header>
|
| 141 |
|
| 142 |
+
<!-- Fixed Bottom Right Controls -->
|
| 143 |
+
<div class="fixed bottom-6 right-6 z-40 flex flex-col space-y-3">
|
| 144 |
+
<button id="btn-open-gallery" class="bg-blue-600 hover:bg-blue-500 text-white p-4 rounded-full shadow-2xl border border-blue-400/50 hover:scale-110 transition-transform flex items-center justify-center group" title="怪獸圖鑑">
|
| 145 |
+
<span class="text-2xl group-hover:animate-bounce">👾</span>
|
| 146 |
+
</button>
|
| 147 |
+
<button id="logout-btn" class="bg-red-800 hover:bg-red-700 text-white p-4 rounded-full shadow-2xl border border-red-600/50 hover:scale-110 transition-transform flex items-center justify-center" title="登出">
|
| 148 |
+
<span class="text-2xl">🚪</span>
|
| 149 |
+
</button>
|
| 150 |
+
</div>
|
| 151 |
+
|
| 152 |
<!-- Heatmap Content -->
|
| 153 |
<div id="dashboard-content" class="hidden overflow-x-auto pb-10">
|
| 154 |
<table class="w-full border-collapse">
|
|
|
|
| 639 |
createContainer.classList.add('hidden');
|
| 640 |
roomInfo.classList.remove('hidden');
|
| 641 |
dashboardContent.classList.remove('hidden');
|
| 642 |
+
document.getElementById('group-photo-btn').classList.remove('hidden'); // Show photo button
|
| 643 |
displayRoomCode.textContent = roomCode;
|
| 644 |
localStorage.setItem('vibecoding_instructor_room', roomCode);
|
| 645 |
sessionStorage.setItem('vibecoding_instructor_in_room', 'true');
|
|
|
|
| 791 |
if (p.status === 'completed') {
|
| 792 |
statusClass = 'bg-green-500/20 border-green-500/50 hover:bg-green-500/40 cursor-pointer shadow-[0_0_10px_rgba(34,197,94,0.1)]';
|
| 793 |
content = '✅';
|
| 794 |
+
// Pass only IDs, lookup logic is in the modal function now to avoid escaping issues
|
| 795 |
+
action = `onclick = "window.showBroadcastModal('${student.id}', '${c.id}')"`;
|
| 796 |
} else if (p.status === 'started') {
|
| 797 |
// Check stuck
|
| 798 |
const startedAt = p.timestamp ? p.timestamp.toDate() : new Date();
|
|
|
|
| 838 |
}
|
| 839 |
|
| 840 |
// Global scope for HTML access
|
| 841 |
+
// Global scope for HTML access
|
| 842 |
+
window.showBroadcastModal = (userId, challengeId) => {
|
| 843 |
+
const student = currentStudents.find(s => s.id === userId);
|
| 844 |
+
if (!student) return;
|
| 845 |
+
|
| 846 |
+
const p = student.progress?.[challengeId];
|
| 847 |
+
if (!p) return;
|
| 848 |
+
|
| 849 |
+
const challenge = cachedChallenges.find(c => c.id === challengeId);
|
| 850 |
+
const title = challenge ? challenge.title : 'Unknown Challenge'; // Fallback
|
| 851 |
+
|
| 852 |
const modal = document.getElementById('broadcast-modal');
|
| 853 |
const content = document.getElementById('broadcast-content');
|
| 854 |
|
| 855 |
+
document.getElementById('broadcast-avatar').textContent = student.nickname[0];
|
| 856 |
+
document.getElementById('broadcast-author').textContent = student.nickname;
|
| 857 |
document.getElementById('broadcast-challenge').textContent = title;
|
| 858 |
+
// content is already just text, but let's be safe
|
| 859 |
+
document.getElementById('broadcast-prompt').textContent = p.prompt || p.code || ''; // robust fallback
|
| 860 |
|
| 861 |
// Store IDs for actions
|
| 862 |
modal.dataset.userId = userId;
|