dvc890 commited on
Commit
8a0d330
·
verified ·
1 Parent(s): 58b7bd4

Upload 45 files

Browse files
pages/AchievementTeacher.tsx CHANGED
@@ -13,7 +13,7 @@ const PRESET_ICONS = [
13
  { icon: '⏰', label: '守时' }, { icon: '📝', label: '写作' }, { icon: '🧮', label: '计算' }, { icon: '🔬', label: '科学' }
14
  ];
15
 
16
- export const AchievementTeacher: React.FC = () => {
17
  const [loading, setLoading] = useState(true);
18
  const [config, setConfig] = useState<AchievementConfig | null>(null);
19
  const [students, setStudents] = useState<Student[]>([]);
@@ -31,11 +31,12 @@ export const AchievementTeacher: React.FC = () => {
31
  const [selectedAchieveId, setSelectedAchieveId] = useState('');
32
 
33
  const currentUser = api.auth.getCurrentUser();
34
- const homeroomClass = currentUser?.homeroomClass;
 
35
 
36
  useEffect(() => {
37
  loadData();
38
- }, []);
39
 
40
  const loadData = async () => {
41
  if (!homeroomClass) return;
 
13
  { icon: '⏰', label: '守时' }, { icon: '📝', label: '写作' }, { icon: '🧮', label: '计算' }, { icon: '🔬', label: '科学' }
14
  ];
15
 
16
+ export const AchievementTeacher: React.FC<{className?: string}> = ({ className }) => {
17
  const [loading, setLoading] = useState(true);
18
  const [config, setConfig] = useState<AchievementConfig | null>(null);
19
  const [students, setStudents] = useState<Student[]>([]);
 
31
  const [selectedAchieveId, setSelectedAchieveId] = useState('');
32
 
33
  const currentUser = api.auth.getCurrentUser();
34
+ // Use prop or fallback
35
+ const homeroomClass = className || currentUser?.homeroomClass;
36
 
37
  useEffect(() => {
38
  loadData();
39
+ }, [className]); // Reload when className changes
40
 
41
  const loadData = async () => {
42
  if (!homeroomClass) return;
pages/GameMountain.tsx CHANGED
@@ -2,7 +2,7 @@
2
  import React, { useState, useEffect, useRef } from 'react';
3
  import { createPortal } from 'react-dom';
4
  import { api } from '../services/api';
5
- import { GameSession, GameTeam, Student, GameRewardConfig, AchievementConfig } from '../types';
6
  import { Settings, Plus, Minus, Users, CheckSquare, Loader2, Trash2, X, Flag, Gift, Star, Trophy, Maximize, Minimize, Lock } from 'lucide-react';
7
  import { Emoji } from '../components/Emoji';
8
 
@@ -200,16 +200,17 @@ export const GameMountain: React.FC<{className?: string}> = ({ className }) => {
200
  useEffect(() => { loadData(); }, [targetClass]);
201
 
202
  const loadData = async () => {
203
- if (!targetClass || targetClass === 'MY_CLASS' && !currentUser.homeroomClass) return;
 
204
  setLoading(true);
205
 
206
  // Resolve "MY_CLASS" for students
207
  let resolvedClass = targetClass;
208
- if (targetClass === 'MY_CLASS' && currentUser.role === 'STUDENT') {
209
  // We need to fetch student's class first? Actually dashboard loads it.
210
  // Assuming passed prop is correct or we fetch student
211
  const stus = await api.students.getAll();
212
- const me = stus.find((s: Student) => s.name === (currentUser.trueName || currentUser.username));
213
  if(me) resolvedClass = me.className;
214
  }
215
 
@@ -221,7 +222,8 @@ export const GameMountain: React.FC<{className?: string}> = ({ className }) => {
221
 
222
  const filteredStudents = allStudents.filter((s: Student) => s.className === resolvedClass);
223
  // Sort
224
- filteredStudents.sort((a, b) => {
 
225
  const seatA = parseInt(a.seatNo || '99999');
226
  const seatB = parseInt(b.seatNo || '99999');
227
  if (seatA !== seatB) return seatA - seatB;
@@ -254,11 +256,12 @@ export const GameMountain: React.FC<{className?: string}> = ({ className }) => {
254
 
255
  // Permissions Check
256
  if (isAdmin) setCanEdit(true);
257
- else if (isTeacher) {
258
  // Fetch class info to check homeroomTeacherIds
259
- const clsList = await api.classes.getAll();
260
- const cls = clsList.find(c => c.grade + c.className === resolvedClass);
261
- if (cls && (cls.homeroomTeacherIds?.includes(currentUser._id) || cls.teacherName?.includes(currentUser.trueName || currentUser.username))) {
 
262
  setCanEdit(true);
263
  // Only load Ach config if can edit
264
  const ac = await api.achievements.getConfig(resolvedClass);
 
2
  import React, { useState, useEffect, useRef } from 'react';
3
  import { createPortal } from 'react-dom';
4
  import { api } from '../services/api';
5
+ import { GameSession, GameTeam, Student, GameRewardConfig, AchievementConfig, ClassInfo } from '../types';
6
  import { Settings, Plus, Minus, Users, CheckSquare, Loader2, Trash2, X, Flag, Gift, Star, Trophy, Maximize, Minimize, Lock } from 'lucide-react';
7
  import { Emoji } from '../components/Emoji';
8
 
 
200
  useEffect(() => { loadData(); }, [targetClass]);
201
 
202
  const loadData = async () => {
203
+ // FIX: Check for currentUser
204
+ if (!targetClass || targetClass === 'MY_CLASS' && !currentUser?.homeroomClass && currentUser?.role !== 'STUDENT') return;
205
  setLoading(true);
206
 
207
  // Resolve "MY_CLASS" for students
208
  let resolvedClass = targetClass;
209
+ if (targetClass === 'MY_CLASS' && currentUser?.role === 'STUDENT') {
210
  // We need to fetch student's class first? Actually dashboard loads it.
211
  // Assuming passed prop is correct or we fetch student
212
  const stus = await api.students.getAll();
213
+ const me = stus.find((s: Student) => s.name === (currentUser?.trueName || currentUser?.username));
214
  if(me) resolvedClass = me.className;
215
  }
216
 
 
222
 
223
  const filteredStudents = allStudents.filter((s: Student) => s.className === resolvedClass);
224
  // Sort
225
+ // FIX: Add explicit types for sort arguments
226
+ filteredStudents.sort((a: Student, b: Student) => {
227
  const seatA = parseInt(a.seatNo || '99999');
228
  const seatB = parseInt(b.seatNo || '99999');
229
  if (seatA !== seatB) return seatA - seatB;
 
256
 
257
  // Permissions Check
258
  if (isAdmin) setCanEdit(true);
259
+ else if (isTeacher && currentUser) {
260
  // Fetch class info to check homeroomTeacherIds
261
+ const clsList = await api.classes.getAll() as ClassInfo[];
262
+ // FIX: Add type for find callback parameter
263
+ const cls = clsList.find((c: ClassInfo) => c.grade + c.className === resolvedClass);
264
+ if (cls && (cls.homeroomTeacherIds?.includes(currentUser._id || '') || cls.teacherName?.includes(currentUser.trueName || currentUser.username))) {
265
  setCanEdit(true);
266
  // Only load Ach config if can edit
267
  const ac = await api.achievements.getConfig(resolvedClass);
pages/GameRewards.tsx CHANGED
@@ -4,7 +4,7 @@ import { api } from '../services/api';
4
  import { StudentReward, Student } from '../types';
5
  import { Gift, Loader2, Search, Filter, Trash2, Edit, Save, X, ChevronLeft, ChevronRight } from 'lucide-react';
6
 
7
- export const GameRewards: React.FC = () => {
8
  const [rewards, setRewards] = useState<StudentReward[]>([]);
9
  const [total, setTotal] = useState(0);
10
  const [page, setPage] = useState(1);
@@ -32,6 +32,9 @@ export const GameRewards: React.FC = () => {
32
  const currentUser = api.auth.getCurrentUser();
33
  const isStudent = currentUser?.role === 'STUDENT';
34
  const isTeacher = currentUser?.role === 'TEACHER';
 
 
 
35
 
36
  const PAGE_SIZE = 15;
37
 
@@ -54,9 +57,9 @@ export const GameRewards: React.FC = () => {
54
  let targetClass = '';
55
  let filteredStudents = allStus;
56
 
57
- if (isTeacher && currentUser.homeroomClass) {
58
- targetClass = currentUser.homeroomClass;
59
- filteredStudents = allStus.filter((s: Student) => s.className === currentUser.homeroomClass);
60
  }
61
 
62
  const res = await api.rewards.getClassRewards(page, PAGE_SIZE, targetClass);
@@ -77,7 +80,7 @@ export const GameRewards: React.FC = () => {
77
  finally { setLoading(false); }
78
  };
79
 
80
- useEffect(() => { loadData(); }, [page]);
81
 
82
  const handleGrant = async () => {
83
  if(!grantForm.studentId) return alert('请选择学生');
@@ -139,7 +142,7 @@ export const GameRewards: React.FC = () => {
139
  <div className="flex flex-col h-full bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
140
  <div className="p-4 md:p-6 border-b border-gray-100 flex flex-col md:flex-row justify-between items-start md:items-center gap-4 shrink-0">
141
  <h3 className="text-xl font-bold text-gray-800">
142
- {isStudent ? '我的战利品清单' : '班级奖励核销台'}
143
  </h3>
144
 
145
  <div className="flex flex-wrap items-center gap-3 w-full md:w-auto">
 
4
  import { StudentReward, Student } from '../types';
5
  import { Gift, Loader2, Search, Filter, Trash2, Edit, Save, X, ChevronLeft, ChevronRight } from 'lucide-react';
6
 
7
+ export const GameRewards: React.FC<{className?: string}> = ({ className }) => {
8
  const [rewards, setRewards] = useState<StudentReward[]>([]);
9
  const [total, setTotal] = useState(0);
10
  const [page, setPage] = useState(1);
 
32
  const currentUser = api.auth.getCurrentUser();
33
  const isStudent = currentUser?.role === 'STUDENT';
34
  const isTeacher = currentUser?.role === 'TEACHER';
35
+
36
+ // Use prop className or fallback to legacy homeroom
37
+ const homeroomClass = className || currentUser?.homeroomClass;
38
 
39
  const PAGE_SIZE = 15;
40
 
 
57
  let targetClass = '';
58
  let filteredStudents = allStus;
59
 
60
+ if (isTeacher && homeroomClass) {
61
+ targetClass = homeroomClass;
62
+ filteredStudents = allStus.filter((s: Student) => s.className === homeroomClass);
63
  }
64
 
65
  const res = await api.rewards.getClassRewards(page, PAGE_SIZE, targetClass);
 
80
  finally { setLoading(false); }
81
  };
82
 
83
+ useEffect(() => { loadData(); }, [page, className]); // Reload when page or className prop changes
84
 
85
  const handleGrant = async () => {
86
  if(!grantForm.studentId) return alert('请选择学生');
 
142
  <div className="flex flex-col h-full bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
143
  <div className="p-4 md:p-6 border-b border-gray-100 flex flex-col md:flex-row justify-between items-start md:items-center gap-4 shrink-0">
144
  <h3 className="text-xl font-bold text-gray-800">
145
+ {isStudent ? '我的战利品清单' : `${homeroomClass || '全校'} 奖励核销台`}
146
  </h3>
147
 
148
  <div className="flex flex-wrap items-center gap-3 w-full md:w-auto">