Lashtw commited on
Commit
f9b747d
·
verified ·
1 Parent(s): 54ca71d

Upload 8 files

Browse files
Files changed (1) hide show
  1. src/views/InstructorView.js +74 -7
src/views/InstructorView.js CHANGED
@@ -874,13 +874,51 @@ export function setupInstructorEvents() {
874
  data-id="${p.id}"
875
  onchange="handlePromptSelection(this)">
876
  </div>
877
- <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">${p.prompt}</div>
878
- <div class="text-[10px] text-gray-500 text-right mt-1">${p.time}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
879
  `;
880
  container.appendChild(card);
881
  });
882
  };
883
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
884
  // Selection Logic
885
  let selectedPrompts = []; // Stores IDs
886
 
@@ -916,7 +954,6 @@ export function setupInstructorEvents() {
916
  btn.classList.add('opacity-50', 'cursor-not-allowed');
917
  }
918
  }
919
-
920
  // Comparison Logic
921
  const compareBtn = document.getElementById('btn-compare-prompts');
922
  if (compareBtn) {
@@ -945,10 +982,40 @@ export function setupInstructorEvents() {
945
  });
946
  }
947
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
948
  window.openComparisonView = (items) => {
949
  const modal = document.getElementById('comparison-modal');
950
  const grid = document.getElementById('comparison-grid');
951
 
 
 
 
 
 
 
 
 
 
952
  // Setup Grid Rows (Vertical Stacking)
953
  let rowClass = 'grid-rows-1';
954
  if (items.length === 2) rowClass = 'grid-rows-2';
@@ -963,7 +1030,7 @@ export function setupInstructorEvents() {
963
  col.className = 'flex flex-row h-full bg-gray-900 p-4 overflow-hidden';
964
  col.innerHTML = `
965
  <div class="w-48 flex-shrink-0 border-r border-gray-700 pr-4 mr-4 flex flex-col justify-center">
966
- <h3 class="text-xl font-bold text-cyan-400 mb-1">${item.author}</h3>
967
  <p class="text-md text-gray-400 truncate" title="${item.title}">${item.title}</p>
968
  </div>
969
  <!-- Prompt Content: Larger Text -->
@@ -1135,10 +1202,10 @@ function renderTransposedHeatmap(students) {
1135
 
1136
  if (p) {
1137
  if (p.status === 'completed') {
1138
- 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)]';
1139
  content = '✅';
1140
- // Pass only IDs, lookup logic is in the modal function now to avoid escaping issues
1141
- action = `onclick = "window.showBroadcastModal('${student.id}', '${c.id}')"`;
1142
  } else if (p.status === 'started') {
1143
  // Check stuck
1144
  const startedAt = p.timestamp ? p.timestamp.toDate() : new Date();
 
874
  data-id="${p.id}"
875
  onchange="handlePromptSelection(this)">
876
  </div>
877
+ <!-- Prompt Content -->
878
+ <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}</div>
879
+
880
+ <!-- Footer: Time + Actions -->
881
+ <div class="flex justify-between items-center text-[10px] text-gray-500 mt-auto">
882
+ <span>${p.time}</span>
883
+ <div class="flex space-x-2">
884
+ <button onclick="window.confirmReset('${p.studentId}', '${p.challengeId}', '${p.title}')" class="px-2 py-1 bg-red-900/30 hover:bg-red-800 text-red-400 rounded transition-colors text-xs border border-red-800/50" title="退回重做">
885
+ 退回
886
+ </button>
887
+ <button onclick="window.broadcastPrompt('${p.studentId}', '${p.challengeId}')" class="px-2 py-1 bg-cyan-900/30 hover:bg-cyan-800 text-cyan-400 rounded transition-colors text-xs border border-cyan-800/50" title="投放此作品">
888
+ 投放
889
+ </button>
890
+ </div>
891
+ </div>
892
  `;
893
  container.appendChild(card);
894
  });
895
  };
896
 
897
+ // Helper Actions
898
+ window.confirmReset = async (userId, challengeId, title) => {
899
+ if (confirm(`確定要退回 ${title} 嗎?此動作將清除學員目前的進度。`)) {
900
+ const roomCode = localStorage.getItem('vibecoding_instructor_room');
901
+ if (userId && challengeId && roomCode) {
902
+ try {
903
+ const { resetProgress } = await import("../services/classroom.js");
904
+ await resetProgress(userId, roomCode, challengeId);
905
+ // Refresh current list if open? (It will stay open but might not update immediately if realtime check isn't hooked to modal content. But subscriptions update `currentStudents`. We might need to refresh list)
906
+ // For now, simple alert or auto-close
907
+ alert("已退回");
908
+ // close modal to refresh data context
909
+ document.getElementById('prompt-list-modal').classList.add('hidden');
910
+ } catch (e) {
911
+ console.error(e);
912
+ alert("退回失敗");
913
+ }
914
+ }
915
+ }
916
+ };
917
+
918
+ window.broadcastPrompt = (userId, challengeId) => {
919
+ window.showBroadcastModal(userId, challengeId);
920
+ };
921
+
922
  // Selection Logic
923
  let selectedPrompts = []; // Stores IDs
924
 
 
954
  btn.classList.add('opacity-50', 'cursor-not-allowed');
955
  }
956
  }
 
957
  // Comparison Logic
958
  const compareBtn = document.getElementById('btn-compare-prompts');
959
  if (compareBtn) {
 
982
  });
983
  }
984
 
985
+ let isAnonymous = false;
986
+
987
+ window.toggleAnonymous = (btn) => {
988
+ isAnonymous = !isAnonymous;
989
+ btn.textContent = isAnonymous ? '🙈 顯示姓名' : '👀 隱藏姓名';
990
+ btn.classList.toggle('bg-gray-700');
991
+ btn.classList.toggle('bg-purple-700');
992
+
993
+ // Update DOM
994
+ document.querySelectorAll('.comparison-author').forEach(el => {
995
+ if (isAnonymous) {
996
+ el.dataset.original = el.textContent;
997
+ el.textContent = '學員';
998
+ el.classList.add('blur-sm'); // Optional Effect
999
+ setTimeout(() => el.classList.remove('blur-sm'), 300);
1000
+ } else {
1001
+ if (el.dataset.original) el.textContent = el.dataset.original;
1002
+ }
1003
+ });
1004
+ };
1005
+
1006
  window.openComparisonView = (items) => {
1007
  const modal = document.getElementById('comparison-modal');
1008
  const grid = document.getElementById('comparison-grid');
1009
 
1010
+ // Reset Anonymous State
1011
+ isAnonymous = false;
1012
+ const anonBtn = document.getElementById('btn-anonymous-toggle');
1013
+ if (anonBtn) {
1014
+ anonBtn.textContent = '👀 隱藏姓名';
1015
+ anonBtn.classList.remove('bg-purple-700');
1016
+ anonBtn.classList.add('bg-gray-700');
1017
+ }
1018
+
1019
  // Setup Grid Rows (Vertical Stacking)
1020
  let rowClass = 'grid-rows-1';
1021
  if (items.length === 2) rowClass = 'grid-rows-2';
 
1030
  col.className = 'flex flex-row h-full bg-gray-900 p-4 overflow-hidden';
1031
  col.innerHTML = `
1032
  <div class="w-48 flex-shrink-0 border-r border-gray-700 pr-4 mr-4 flex flex-col justify-center">
1033
+ <h3 class="text-xl font-bold text-cyan-400 mb-1 comparison-author">${item.author}</h3>
1034
  <p class="text-md text-gray-400 truncate" title="${item.title}">${item.title}</p>
1035
  </div>
1036
  <!-- Prompt Content: Larger Text -->
 
1202
 
1203
  if (p) {
1204
  if (p.status === 'completed') {
1205
+ statusClass = 'bg-green-500/20 border-green-500/50 hover:bg-green-500/40 cursor-default shadow-[0_0_10px_rgba(34,197,94,0.1)]';
1206
  content = '✅';
1207
+ // Action removed: Moved to prompt list view
1208
+ action = `title="完成 - 請點擊標題查看詳情"`;
1209
  } else if (p.status === 'started') {
1210
  // Check stuck
1211
  const startedAt = p.timestamp ? p.timestamp.toDate() : new Date();