Lashtw commited on
Commit
fb00883
·
verified ·
1 Parent(s): 86248bc

Upload 9 files

Browse files
src/services/classroom.js CHANGED
@@ -11,9 +11,9 @@ import {
11
  where,
12
  getDocs,
13
  orderBy,
14
- deleteDoc,
15
  updateDoc,
16
- getCountFromServer
 
17
  } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
18
 
19
  // Collection references
@@ -509,3 +509,12 @@ export function subscribeToUserProgress(userId, callback) {
509
  callback(progressMap);
510
  });
511
  }
 
 
 
 
 
 
 
 
 
 
11
  where,
12
  getDocs,
13
  orderBy,
 
14
  updateDoc,
15
+ getCountFromServer,
16
+ deleteDoc
17
  } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js";
18
 
19
  // Collection references
 
509
  callback(progressMap);
510
  });
511
  }
512
+
513
+ /**
514
+ * Removes a user from the classroom (Kick)
515
+ * @param {string} userId
516
+ */
517
+ export async function removeUser(userId) {
518
+ if (!userId) return;
519
+ await deleteDoc(doc(db, USERS_COLLECTION, userId));
520
+ }
src/views/InstructorView.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createRoom, subscribeToRoom, getChallenges, resetProgress } from "../services/classroom.js";
2
 
3
  import { generateMonsterSVG, getNextMonster } from "../utils/monsterUtils.js";
4
 
@@ -378,6 +378,18 @@ export function setupInstructorEvents() {
378
  const students = [...currentStudents].sort(() => Math.random() - 0.5);
379
  const total = students.length;
380
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  students.forEach((s, index) => {
382
  const progressMap = s.progress || {};
383
  const totalLikes = Object.values(progressMap).reduce((acc, p) => acc + (p.likes || 0), 0);
@@ -398,24 +410,36 @@ export function setupInstructorEvents() {
398
  const deg = finalAngle * (180 / Math.PI) % 360;
399
  const normalizedDeg = deg < 0 ? deg + 360 : deg;
400
 
401
- // Avoid "South" (approx 60 to 120 degrees) where the label sticks out
402
- // Also push further away if near the bottom
403
  if (normalizedDeg > 60 && normalizedDeg < 120) {
404
  // Push angle strictly out of the zone
405
  const distTo60 = Math.abs(normalizedDeg - 60);
406
  const distTo120 = Math.abs(normalizedDeg - 120);
407
 
408
  if (distTo60 < distTo120) {
409
- finalAngle = (60 - 10) * (Math.PI / 180); // Move to 50 deg
410
  } else {
411
- finalAngle = (120 + 10) * (Math.PI / 180); // Move to 130 deg
412
  }
413
  }
414
 
415
- // Radius: Random within range (Push out further if still somewhat south)
 
 
 
 
416
  let radius = minR + Math.random() * (maxR - minR);
417
- if (normalizedDeg > 45 && normalizedDeg < 135) {
418
- radius += 60; // Extra buffer for bottom elements
 
 
 
 
 
 
 
 
 
419
  }
420
 
421
  const xOff = Math.cos(finalAngle) * radius;
@@ -444,7 +468,7 @@ export function setupInstructorEvents() {
444
  </div>
445
 
446
  <!-- Monster Image -->
447
- <div class="monster-img-container relative w-20 h-20 md:w-24 md:h-24 flex items-center justify-center transform group-hover/card:scale-125 transition-transform duration-300" style="animation: float 3s ease-in-out infinite; animation-delay: -${floatDelay}s;">
448
  <div class="w-full h-full pixel-art drop-shadow-md filter group-hover/card:brightness-110 transition-all">
449
  ${generateMonsterSVG(monster)}
450
  </div>
@@ -609,9 +633,14 @@ function renderTransposedHeatmap(students) {
609
  <!-- Online Indicator (Simulated) -->
610
  <div class="absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 border-2 border-gray-800 rounded-full"></div>
611
  </div>
612
- <span class="text-xs text-gray-300 font-medium truncate max-w-[80px] writing-vertical-lr" style="writing-mode: vertical-rl; text-orientation: mixed;">
613
- ${student.nickname}
614
- </span>
 
 
 
 
 
615
  </div>
616
  </th>
617
  `;
 
1
+ import { createRoom, subscribeToRoom, getChallenges, resetProgress, removeUser } from "../services/classroom.js";
2
 
3
  import { generateMonsterSVG, getNextMonster } from "../utils/monsterUtils.js";
4
 
 
378
  const students = [...currentStudents].sort(() => Math.random() - 0.5);
379
  const total = students.length;
380
 
381
+ // --- Dynamic Sizing Logic ---
382
+ let sizeClass = 'w-20 h-20 md:w-24 md:h-24'; // Default (Size 100%)
383
+ let scaleFactor = 1.0;
384
+
385
+ if (total >= 40) {
386
+ sizeClass = 'w-12 h-12 md:w-14 md:h-14'; // Size 60%
387
+ scaleFactor = 0.6;
388
+ } else if (total >= 20) {
389
+ sizeClass = 'w-16 h-16 md:w-20 md:h-20'; // Size 80%
390
+ scaleFactor = 0.8;
391
+ }
392
+
393
  students.forEach((s, index) => {
394
  const progressMap = s.progress || {};
395
  const totalLikes = Object.values(progressMap).reduce((acc, p) => acc + (p.likes || 0), 0);
 
410
  const deg = finalAngle * (180 / Math.PI) % 360;
411
  const normalizedDeg = deg < 0 ? deg + 360 : deg;
412
 
413
+ // 1. Avoid Instructor Label (Bottom Center: 60-120 deg)
 
414
  if (normalizedDeg > 60 && normalizedDeg < 120) {
415
  // Push angle strictly out of the zone
416
  const distTo60 = Math.abs(normalizedDeg - 60);
417
  const distTo120 = Math.abs(normalizedDeg - 120);
418
 
419
  if (distTo60 < distTo120) {
420
+ finalAngle = (60 - 15) * (Math.PI / 180); // Move to 45 deg
421
  } else {
422
+ finalAngle = (120 + 15) * (Math.PI / 180); // Move to 135 deg
423
  }
424
  }
425
 
426
+ // 2. Recalculate degree after shift
427
+ const finalDeg = finalAngle * (180 / Math.PI) % 360;
428
+ const normFinalDeg = finalDeg < 0 ? finalDeg + 360 : finalDeg;
429
+
430
+ // Radius: Random within range
431
  let radius = minR + Math.random() * (maxR - minR);
432
+
433
+ // 3. Avoid Watermark (Bottom Right: 0-60 deg)
434
+ // If in this sector, pull them in closer to center to avoid the corner watermark
435
+ if (normFinalDeg >= 0 && normFinalDeg < 60) {
436
+ radius = minR + Math.random() * 40; // Max radius restricted to minR + 40 (approx 260px)
437
+ }
438
+
439
+ // 4. Extra space for bottom area (outside watermark/label zones)
440
+ // If 120-150 deg (Bottom Left), can go further out
441
+ if (normFinalDeg > 120 && normFinalDeg < 150) {
442
+ radius += 40;
443
  }
444
 
445
  const xOff = Math.cos(finalAngle) * radius;
 
468
  </div>
469
 
470
  <!-- Monster Image -->
471
+ <div class="monster-img-container relative ${sizeClass} flex items-center justify-center transform group-hover/card:scale-125 transition-transform duration-300" style="animation: float 3s ease-in-out infinite; animation-delay: -${floatDelay}s;">
472
  <div class="w-full h-full pixel-art drop-shadow-md filter group-hover/card:brightness-110 transition-all">
473
  ${generateMonsterSVG(monster)}
474
  </div>
 
633
  <!-- Online Indicator (Simulated) -->
634
  <div class="absolute -bottom-1 -right-1 w-3 h-3 bg-green-500 border-2 border-gray-800 rounded-full"></div>
635
  </div>
636
+ <div class="flex items-center justify-center space-x-1">
637
+ <span class="text-xs text-gray-300 font-medium truncate max-w-[80px] writing-vertical-lr" style="writing-mode: vertical-rl; text-orientation: mixed;">
638
+ ${student.nickname}
639
+ </span>
640
+ <button onclick="window.confirmKick('${student.id}', '${student.nickname}')" class="text-gray-600 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity" title="踢出學員">
641
+ 🗑️
642
+ </button>
643
+ </div>
644
  </div>
645
  </th>
646
  `;