Lashtw commited on
Commit
a57f0f7
·
verified ·
1 Parent(s): abfdb2a

Upload 9 files

Browse files
Files changed (1) hide show
  1. src/views/InstructorView.js +61 -60
src/views/InstructorView.js CHANGED
@@ -1792,25 +1792,26 @@ if (addInstBtn) {
1792
  ctx.clearRect(0, 0, canvas.width, canvas.height);
1793
  }
1794
  };
 
1795
 
1796
 
1797
- /**
1798
- * Renders the Transposed Heatmap (Rows=Challenges, Cols=Students)
1799
- */
1800
- function renderTransposedHeatmap(students) {
1801
- const thead = document.getElementById('heatmap-header');
1802
- const tbody = document.getElementById('heatmap-body');
1803
 
1804
- if (students.length === 0) {
1805
- thead.innerHTML = '<th class="p-4 text-left">等待資料...</th>';
1806
- tbody.innerHTML = '<tr><td class="p-10 text-center text-gray-500">尚無學員加入</td></tr>';
1807
- return;
1808
- }
1809
 
1810
- // 1. Render Header (Students)
1811
- // Sticky Top for Header Row
1812
- // Sticky Left for the first cell ("Challenge/Student")
1813
- let headerHtml = `
1814
  <th class="p-3 text-left sticky left-0 top-0 bg-gray-800 z-30 border-b border-gray-600 min-w-[200px] border-r border-gray-700 shadow-md">
1815
  <div class="flex justify-between items-end">
1816
  <span class="text-sm text-gray-400">題目</span>
@@ -1819,8 +1820,8 @@ if (addInstBtn) {
1819
  </th>
1820
  `;
1821
 
1822
- students.forEach(student => {
1823
- headerHtml += `
1824
  <th class="p-2 text-center sticky top-0 bg-gray-800 z-20 border-b border-gray-700 min-w-[80px] group">
1825
  <div class="flex flex-col items-center space-y-2 py-2">
1826
  <div class="w-8 h-8 rounded-full bg-gradient-to-br from-gray-700 to-gray-600 flex items-center justify-center text-xs font-bold text-white uppercase border border-gray-500 shadow-sm relative">
@@ -1839,59 +1840,59 @@ if (addInstBtn) {
1839
  </div>
1840
  </th>
1841
  `;
1842
- });
1843
- thead.innerHTML = headerHtml;
1844
 
1845
- // 2. Render Body (Challenges as Rows)
1846
- if (cachedChallenges.length === 0) {
1847
- tbody.innerHTML = '<tr><td colspan="100" class="text-center py-4">沒有題目資料</td></tr>';
1848
- return;
1849
- }
1850
 
1851
- tbody.innerHTML = cachedChallenges.map((c, index) => {
1852
- const colors = { beginner: 'cyan', intermediate: 'blue', advanced: 'purple' };
1853
- const color = colors[c.level] || 'gray';
1854
-
1855
- // Build Row Cells (One per student)
1856
- const rowCells = students.map(student => {
1857
- const p = student.progress?.[c.id];
1858
- let statusClass = 'bg-gray-800/30 border-gray-800'; // Default
1859
- let content = '';
1860
- let action = '';
1861
-
1862
- if (p) {
1863
- if (p.status === 'completed') {
1864
- 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)]';
1865
- content = '✅';
1866
- // Action removed: Moved to prompt list view
1867
- action = `title="完成 - 請點擊標題查看詳情"`;
1868
- } else if (p.status === 'started') {
1869
- // Check stuck
1870
- const startedAt = p.timestamp ? p.timestamp.toDate() : new Date();
1871
- const now = new Date();
1872
- const diffMins = (now - startedAt) / 1000 / 60;
1873
-
1874
- if (diffMins > 5) {
1875
- statusClass = 'bg-red-900/50 border-red-500 animate-pulse cursor-help';
1876
- content = '🆘';
1877
- } else {
1878
- statusClass = 'bg-blue-600/20 border-blue-500';
1879
- content = '🔵';
1880
- }
1881
  }
1882
  }
 
1883
 
1884
- return `
1885
  <td class="p-1 border border-gray-800/50 text-center align-middle h-14 hover:bg-white/5 transition-colors">
1886
  <div class="mx-auto w-10 h-10 rounded-lg border flex items-center justify-center ${statusClass} transition-all duration-300" ${action}>
1887
  ${content}
1888
  </div>
1889
  </td>
1890
  `;
1891
- }).join('');
1892
 
1893
- // Row Header (Challenge Title)
1894
- return `
1895
  <tr class="hover:bg-gray-800/50 transition-colors">
1896
  <td class="p-3 sticky left-0 bg-gray-900 z-10 border-r border-b border-gray-700 shadow-md">
1897
  <div class="flex items-center justify-between">
@@ -1908,7 +1909,7 @@ if (addInstBtn) {
1908
  ${rowCells}
1909
  </tr>
1910
  `;
1911
- }).join('');
1912
- }
1913
 
1914
 
 
1792
  ctx.clearRect(0, 0, canvas.width, canvas.height);
1793
  }
1794
  };
1795
+ }
1796
 
1797
 
1798
+ /**
1799
+ * Renders the Transposed Heatmap (Rows=Challenges, Cols=Students)
1800
+ */
1801
+ function renderTransposedHeatmap(students) {
1802
+ const thead = document.getElementById('heatmap-header');
1803
+ const tbody = document.getElementById('heatmap-body');
1804
 
1805
+ if (students.length === 0) {
1806
+ thead.innerHTML = '<th class="p-4 text-left">等待資料...</th>';
1807
+ tbody.innerHTML = '<tr><td class="p-10 text-center text-gray-500">尚無學員加入</td></tr>';
1808
+ return;
1809
+ }
1810
 
1811
+ // 1. Render Header (Students)
1812
+ // Sticky Top for Header Row
1813
+ // Sticky Left for the first cell ("Challenge/Student")
1814
+ let headerHtml = `
1815
  <th class="p-3 text-left sticky left-0 top-0 bg-gray-800 z-30 border-b border-gray-600 min-w-[200px] border-r border-gray-700 shadow-md">
1816
  <div class="flex justify-between items-end">
1817
  <span class="text-sm text-gray-400">題目</span>
 
1820
  </th>
1821
  `;
1822
 
1823
+ students.forEach(student => {
1824
+ headerHtml += `
1825
  <th class="p-2 text-center sticky top-0 bg-gray-800 z-20 border-b border-gray-700 min-w-[80px] group">
1826
  <div class="flex flex-col items-center space-y-2 py-2">
1827
  <div class="w-8 h-8 rounded-full bg-gradient-to-br from-gray-700 to-gray-600 flex items-center justify-center text-xs font-bold text-white uppercase border border-gray-500 shadow-sm relative">
 
1840
  </div>
1841
  </th>
1842
  `;
1843
+ });
1844
+ thead.innerHTML = headerHtml;
1845
 
1846
+ // 2. Render Body (Challenges as Rows)
1847
+ if (cachedChallenges.length === 0) {
1848
+ tbody.innerHTML = '<tr><td colspan="100" class="text-center py-4">沒有題目資料</td></tr>';
1849
+ return;
1850
+ }
1851
 
1852
+ tbody.innerHTML = cachedChallenges.map((c, index) => {
1853
+ const colors = { beginner: 'cyan', intermediate: 'blue', advanced: 'purple' };
1854
+ const color = colors[c.level] || 'gray';
1855
+
1856
+ // Build Row Cells (One per student)
1857
+ const rowCells = students.map(student => {
1858
+ const p = student.progress?.[c.id];
1859
+ let statusClass = 'bg-gray-800/30 border-gray-800'; // Default
1860
+ let content = '';
1861
+ let action = '';
1862
+
1863
+ if (p) {
1864
+ if (p.status === 'completed') {
1865
+ 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)]';
1866
+ content = '✅';
1867
+ // Action removed: Moved to prompt list view
1868
+ action = `title="完成 - 請點擊標題查看詳情"`;
1869
+ } else if (p.status === 'started') {
1870
+ // Check stuck
1871
+ const startedAt = p.timestamp ? p.timestamp.toDate() : new Date();
1872
+ const now = new Date();
1873
+ const diffMins = (now - startedAt) / 1000 / 60;
1874
+
1875
+ if (diffMins > 5) {
1876
+ statusClass = 'bg-red-900/50 border-red-500 animate-pulse cursor-help';
1877
+ content = '🆘';
1878
+ } else {
1879
+ statusClass = 'bg-blue-600/20 border-blue-500';
1880
+ content = '🔵';
 
1881
  }
1882
  }
1883
+ }
1884
 
1885
+ return `
1886
  <td class="p-1 border border-gray-800/50 text-center align-middle h-14 hover:bg-white/5 transition-colors">
1887
  <div class="mx-auto w-10 h-10 rounded-lg border flex items-center justify-center ${statusClass} transition-all duration-300" ${action}>
1888
  ${content}
1889
  </div>
1890
  </td>
1891
  `;
1892
+ }).join('');
1893
 
1894
+ // Row Header (Challenge Title)
1895
+ return `
1896
  <tr class="hover:bg-gray-800/50 transition-colors">
1897
  <td class="p-3 sticky left-0 bg-gray-900 z-10 border-r border-b border-gray-700 shadow-md">
1898
  <div class="flex items-center justify-between">
 
1909
  ${rowCells}
1910
  </tr>
1911
  `;
1912
+ }).join('');
1913
+ }
1914
 
1915