dvc890 commited on
Commit
62ec62d
·
verified ·
1 Parent(s): 9700a4d

Upload 45 files

Browse files
Files changed (3) hide show
  1. pages/CourseList.tsx +20 -3
  2. pages/Dashboard.tsx +29 -16
  3. pages/Games.tsx +8 -1
pages/CourseList.tsx CHANGED
@@ -51,8 +51,16 @@ export const CourseList: React.FC = () => {
51
  setCourses(filteredCourses);
52
  setSubjects(s);
53
  setTeachers(t);
54
- // Safety check and sort
55
- setClasses(Array.isArray(cls) ? cls.sort(sortClasses) : []);
 
 
 
 
 
 
 
 
56
  } catch (e) { console.error(e); } finally { setLoading(false); }
57
  };
58
 
@@ -106,6 +114,12 @@ export const CourseList: React.FC = () => {
106
  setIsModalOpen(true);
107
  };
108
 
 
 
 
 
 
 
109
  return (
110
  <div className="space-y-6">
111
  <div className="flex flex-col md:flex-row justify-between items-center gap-4">
@@ -188,7 +202,10 @@ export const CourseList: React.FC = () => {
188
  <label className="text-sm font-bold text-gray-500 mb-1 block">教学班级</label>
189
  <select className="w-full border p-2 rounded-lg focus:ring-2 focus:ring-indigo-500 outline-none" value={formData.className} onChange={e=>setFormData({...formData, className:e.target.value})} required>
190
  <option value="">-- 选择班级 --</option>
191
- {classes.map(cls => <option key={cls._id} value={cls.grade + cls.className}>{cls.grade}{cls.className}</option>)}
 
 
 
192
  </select>
193
  </div>
194
 
 
51
  setCourses(filteredCourses);
52
  setSubjects(s);
53
  setTeachers(t);
54
+
55
+ // Safety check: Ensure cls is array and sort safely
56
+ const safeClasses = Array.isArray(cls) ? cls : [];
57
+ try {
58
+ safeClasses.sort(sortClasses);
59
+ } catch (e) {
60
+ console.warn("Class sort failed", e);
61
+ }
62
+ setClasses(safeClasses);
63
+
64
  } catch (e) { console.error(e); } finally { setLoading(false); }
65
  };
66
 
 
114
  setIsModalOpen(true);
115
  };
116
 
117
+ // Helper to ensure class name is consistent
118
+ const getFullClassName = (c: ClassInfo) => {
119
+ if (c.className && c.grade && c.className.startsWith(c.grade)) return c.className;
120
+ return (c.grade || '') + (c.className || '');
121
+ };
122
+
123
  return (
124
  <div className="space-y-6">
125
  <div className="flex flex-col md:flex-row justify-between items-center gap-4">
 
202
  <label className="text-sm font-bold text-gray-500 mb-1 block">教学班级</label>
203
  <select className="w-full border p-2 rounded-lg focus:ring-2 focus:ring-indigo-500 outline-none" value={formData.className} onChange={e=>setFormData({...formData, className:e.target.value})} required>
204
  <option value="">-- 选择班级 --</option>
205
+ {classes.map(cls => {
206
+ const fullName = getFullClassName(cls);
207
+ return <option key={cls._id} value={fullName}>{fullName}</option>;
208
+ })}
209
  </select>
210
  </div>
211
 
pages/Dashboard.tsx CHANGED
@@ -20,27 +20,33 @@ export const sortGrades = (a: string, b: string) => (gradeOrder[a] || 99) - (gra
20
 
21
  // Helper to extract grade from class string (e.g. "一年级(1)班" -> "一年级")
22
  const extractGrade = (s: string) => {
 
23
  const keys = Object.keys(gradeOrder);
24
- // Sort keys by length desc to match "七年级" before "七" if needed, though simple find is usually ok
25
  return keys.find(g => s.startsWith(g)) || '';
26
  };
27
 
28
- export const sortClasses = (a: string, b: string) => {
 
 
 
 
 
 
 
29
  // 1. Sort by Grade First
30
- const gradeA = extractGrade(a);
31
- const gradeB = extractGrade(b);
32
 
33
  if (gradeA && gradeB && gradeA !== gradeB) {
34
  return sortGrades(gradeA, gradeB);
35
  }
36
 
37
- // 2. Sort by Class Number (extract digits inside parens or generally)
38
- // Matches (1) or 1班
39
  const getNum = (str: string) => {
40
  const match = str.match(/(\d+)/);
41
  return match ? parseInt(match[1]) : 0;
42
  };
43
- return getNum(a) - getNum(b);
44
  };
45
 
46
  export const Dashboard: React.FC<DashboardProps> = ({ onNavigate }) => {
@@ -86,14 +92,17 @@ export const Dashboard: React.FC<DashboardProps> = ({ onNavigate }) => {
86
  api.users.getAll({ role: 'TEACHER' })
87
  ]);
88
  setStats(summary);
89
- setClassList(classes);
 
 
 
 
90
  setSubjects(subs);
91
  setTeachers(userList);
92
 
93
- if (isAdmin && classes.length > 0) {
94
- const grades = Array.from(new Set((classes as ClassInfo[]).map((c: ClassInfo) => c.grade))).sort(sortGrades);
95
  if (grades.length > 0) {
96
- // IMPORTANT: Default viewGrade to the first available grade so schedule isn't empty
97
  setViewGrade(grades[0] as string);
98
  }
99
  }
@@ -190,11 +199,15 @@ export const Dashboard: React.FC<DashboardProps> = ({ onNavigate }) => {
190
 
191
  const uniqueGrades = Array.from(new Set(classList.map(c => c.grade))).sort(sortGrades);
192
 
193
- // Filter class options in the modal based on currently selected viewGrade (if Admin)
194
- // Ensure strict sorting
195
- const modalClassOptions = isAdmin && viewGrade
196
- ? classList.filter(c => c.grade === viewGrade).map(c => c.grade + c.className).sort(sortClasses)
197
- : classList.map(c => c.grade + c.className).sort(sortClasses);
 
 
 
 
198
 
199
  const cards = [
200
  { label: '在校学生', value: stats.studentCount, icon: Users, color: 'bg-blue-500', trend: '实时' },
 
20
 
21
  // Helper to extract grade from class string (e.g. "一年级(1)班" -> "一年级")
22
  const extractGrade = (s: string) => {
23
+ if (!s) return '';
24
  const keys = Object.keys(gradeOrder);
 
25
  return keys.find(g => s.startsWith(g)) || '';
26
  };
27
 
28
+ // Robust sort function
29
+ export const sortClasses = (a: ClassInfo | string, b: ClassInfo | string) => {
30
+ // Handle both ClassInfo objects and string names
31
+ const nameA = typeof a === 'string' ? a : (a.grade + a.className);
32
+ const nameB = typeof b === 'string' ? b : (b.grade + b.className);
33
+
34
+ if (!nameA || !nameB) return 0;
35
+
36
  // 1. Sort by Grade First
37
+ const gradeA = extractGrade(nameA);
38
+ const gradeB = extractGrade(nameB);
39
 
40
  if (gradeA && gradeB && gradeA !== gradeB) {
41
  return sortGrades(gradeA, gradeB);
42
  }
43
 
44
+ // 2. Sort by Class Number
 
45
  const getNum = (str: string) => {
46
  const match = str.match(/(\d+)/);
47
  return match ? parseInt(match[1]) : 0;
48
  };
49
+ return getNum(nameA) - getNum(nameB);
50
  };
51
 
52
  export const Dashboard: React.FC<DashboardProps> = ({ onNavigate }) => {
 
92
  api.users.getAll({ role: 'TEACHER' })
93
  ]);
94
  setStats(summary);
95
+
96
+ // Safety check for classes
97
+ const safeClasses = Array.isArray(classes) ? classes : [];
98
+ setClassList(safeClasses);
99
+
100
  setSubjects(subs);
101
  setTeachers(userList);
102
 
103
+ if (isAdmin && safeClasses.length > 0) {
104
+ const grades = Array.from(new Set(safeClasses.map((c: ClassInfo) => c.grade))).sort(sortGrades);
105
  if (grades.length > 0) {
 
106
  setViewGrade(grades[0] as string);
107
  }
108
  }
 
199
 
200
  const uniqueGrades = Array.from(new Set(classList.map(c => c.grade))).sort(sortGrades);
201
 
202
+ // Robust class mapping for modal
203
+ const modalClassOptions = classList
204
+ .filter(c => !isAdmin || !viewGrade || c.grade === viewGrade)
205
+ .map(c => ({
206
+ id: c._id,
207
+ name: (c.className.startsWith(c.grade) ? c.className : c.grade + c.className)
208
+ }))
209
+ .sort((a,b) => sortClasses(a.name, b.name))
210
+ .map(c => c.name);
211
 
212
  const cards = [
213
  { label: '在校学生', value: stats.studentCount, icon: Users, color: 'bg-blue-500', trend: '实时' },
pages/Games.tsx CHANGED
@@ -29,6 +29,13 @@ export const Games: React.FC = () => {
29
  loadContext();
30
  }, []);
31
 
 
 
 
 
 
 
 
32
  const loadContext = async () => {
33
  if (isTeacher) {
34
  try {
@@ -36,7 +43,7 @@ export const Games: React.FC = () => {
36
  const allClasses = await api.classes.getAll();
37
  const homerooms = allClasses
38
  .filter((c: ClassInfo) => c.homeroomTeacherIds?.includes(currentUser._id || '') || c.teacherName?.includes(currentUser.trueName || currentUser.username))
39
- .map((c: ClassInfo) => c.grade + c.className);
40
 
41
  // Fetch Courses where I am Subject Teacher
42
  const allCourses = await api.courses.getAll();
 
29
  loadContext();
30
  }, []);
31
 
32
+ const getFullClassName = (c: ClassInfo) => {
33
+ // Logic to prevent double grade names like "一年级一年级(1)班"
34
+ // If className already starts with grade, use it as is.
35
+ if (c.className && c.grade && c.className.startsWith(c.grade)) return c.className;
36
+ return (c.grade || '') + (c.className || '');
37
+ };
38
+
39
  const loadContext = async () => {
40
  if (isTeacher) {
41
  try {
 
43
  const allClasses = await api.classes.getAll();
44
  const homerooms = allClasses
45
  .filter((c: ClassInfo) => c.homeroomTeacherIds?.includes(currentUser._id || '') || c.teacherName?.includes(currentUser.trueName || currentUser.username))
46
+ .map(getFullClassName);
47
 
48
  // Fetch Courses where I am Subject Teacher
49
  const allCourses = await api.courses.getAll();