SarahXia0405 commited on
Commit
8be522e
·
verified ·
1 Parent(s): c27be1f

Update web/src/components/sidebar/CourseInfoSection.tsx

Browse files
web/src/components/sidebar/CourseInfoSection.tsx CHANGED
@@ -1,7 +1,7 @@
1
  import React, { useMemo } from "react";
2
  import { Separator } from "../ui/separator";
3
 
4
- import type { Workspace } from "../../App";
5
  import type { CourseDirectoryItem } from "../../lib/courseDirectory";
6
 
7
  function gmailComposeLink(email: string, subject?: string, body?: string) {
@@ -15,6 +15,20 @@ function norm(s: any) {
15
  return String(s ?? "").trim().toLowerCase();
16
  }
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  export function CourseInfoSection({
19
  currentWorkspaceId,
20
  workspaces,
@@ -31,6 +45,14 @@ export function CourseInfoSection({
31
  [workspaces, currentWorkspaceId]
32
  );
33
 
 
 
 
 
 
 
 
 
34
  const courseInfo = useMemo((): CourseDirectoryItem | null => {
35
  const list = Array.isArray(availableCourses) ? availableCourses : [];
36
 
@@ -39,9 +61,9 @@ export function CourseInfoSection({
39
  const sel = norm(selRaw);
40
  if (sel) {
41
  const hit =
42
- list.find((c) => norm(c.id) === sel) ||
43
- list.find((c) => norm(c.name) === sel);
44
- if (hit) return hit;
45
  }
46
 
47
  // 2) workspace.courseInfo: group workspace 的 courseInfo 可能只带 id/name
@@ -50,18 +72,32 @@ export function CourseInfoSection({
50
  | undefined;
51
 
52
  const wsId = norm(wsCourse?.id);
53
- if (wsId) {
54
- return list.find((c) => norm(c.id) === wsId) ?? (wsCourse as any);
55
- }
56
 
57
  const wsName = norm(wsCourse?.name);
58
- if (wsName) {
59
- return list.find((c) => norm(c.name) === wsName) ?? (wsCourse as any);
60
- }
61
 
62
  return null;
63
  }, [availableCourses, currentWorkspace, selectedCourse]);
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  // ---------- Fallback:不要静默消失 ----------
66
  if (!courseInfo) {
67
  return (
@@ -70,9 +106,15 @@ export function CourseInfoSection({
70
  <div className="text-xs text-red-600">COURSEINFO ACTIVE (fallback)</div>
71
  <div className="font-semibold text-base">No course matched</div>
72
  <div className="text-xs text-muted-foreground space-y-1">
73
- <div>selectedCourse: <span className="font-medium">{String(selectedCourse || "")}</span></div>
74
- <div>availableCourses: <span className="font-medium">{availableCourses?.length ?? 0}</span></div>
75
- <div>currentWorkspaceId: <span className="font-medium">{String(currentWorkspaceId)}</span></div>
 
 
 
 
 
 
76
  </div>
77
  </div>
78
  <Separator className="bg-[#ECECF1]" />
@@ -80,21 +122,33 @@ export function CourseInfoSection({
80
  );
81
  }
82
 
 
 
83
 
84
- // ---------- Display fields ----------
85
- const courseName = courseInfo.name ?? (selectedCourse || "Course");
86
-
87
- const instructorName = courseInfo?.instructor?.name ?? "N/A";
88
- const instructorEmail = (courseInfo?.instructor?.email || "").trim();
89
-
90
- const taName = courseInfo?.teachingAssistant?.name ?? "N/A";
91
- const taEmail = (courseInfo?.teachingAssistant?.email || "").trim();
92
 
93
  return (
94
  <div className="w-full">
95
- <div className="px-4 pt-4 pb-3 space-y-2">
 
96
  <div className="font-semibold text-base">{courseName}</div>
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  <div className="text-sm text-muted-foreground">
99
  Instructor:&nbsp;
100
  {instructorEmail ? (
@@ -115,6 +169,7 @@ export function CourseInfoSection({
115
  )}
116
  </div>
117
 
 
118
  <div className="text-sm text-muted-foreground">
119
  TA:&nbsp;
120
  {taEmail ? (
 
1
  import React, { useMemo } from "react";
2
  import { Separator } from "../ui/separator";
3
 
4
+ import type { Workspace, SpaceType } from "../../App";
5
  import type { CourseDirectoryItem } from "../../lib/courseDirectory";
6
 
7
  function gmailComposeLink(email: string, subject?: string, body?: string) {
 
15
  return String(s ?? "").trim().toLowerCase();
16
  }
17
 
18
+ function pickAny(obj: any, keys: string[]) {
19
+ for (const k of keys) {
20
+ const v = obj?.[k];
21
+ if (v !== undefined && v !== null && String(v).trim() !== "") return v;
22
+ }
23
+ return undefined;
24
+ }
25
+
26
+ function toIntOrFallback(v: any, fb: number) {
27
+ const n = Number(v);
28
+ if (Number.isFinite(n) && n > 0) return Math.floor(n);
29
+ return fb;
30
+ }
31
+
32
  export function CourseInfoSection({
33
  currentWorkspaceId,
34
  workspaces,
 
45
  [workspaces, currentWorkspaceId]
46
  );
47
 
48
+ // 判断当前是否是 Group/Team(用于决定是否展示 Group Name/Number)
49
+ const isTeamSpace = useMemo(() => {
50
+ const ws: any = currentWorkspace as any;
51
+ const st = String((ws?.type as SpaceType) || "").toLowerCase();
52
+ return st === "group" || st === "team";
53
+ }, [currentWorkspace]);
54
+
55
+ // --------- CourseInfo resolution ---------
56
  const courseInfo = useMemo((): CourseDirectoryItem | null => {
57
  const list = Array.isArray(availableCourses) ? availableCourses : [];
58
 
 
61
  const sel = norm(selRaw);
62
  if (sel) {
63
  const hit =
64
+ list.find((c: any) => norm(c.id) === sel) ||
65
+ list.find((c: any) => norm(c.name) === sel);
66
+ if (hit) return hit as any;
67
  }
68
 
69
  // 2) workspace.courseInfo: group workspace 的 courseInfo 可能只带 id/name
 
72
  | undefined;
73
 
74
  const wsId = norm(wsCourse?.id);
75
+ if (wsId) return (list.find((c: any) => norm(c.id) === wsId) ?? (wsCourse as any)) as any;
 
 
76
 
77
  const wsName = norm(wsCourse?.name);
78
+ if (wsName) return (list.find((c: any) => norm(c.name) === wsName) ?? (wsCourse as any)) as any;
 
 
79
 
80
  return null;
81
  }, [availableCourses, currentWorkspace, selectedCourse]);
82
 
83
+ // ---------- Display fields ----------
84
+ const courseName = useMemo(() => {
85
+ return (courseInfo as any)?.name ?? (selectedCourse || "Course");
86
+ }, [courseInfo, selectedCourse]);
87
+
88
+ // --------- Group fields (read-only) ---------
89
+ const groupName = useMemo(() => {
90
+ const ws: any = currentWorkspace as any;
91
+ // 优先 groupName,否则退回 workspace 的 name/title
92
+ return String(pickAny(ws, ["groupName", "name", "title"]) || "My Group");
93
+ }, [currentWorkspace]);
94
+
95
+ const groupNo = useMemo(() => {
96
+ const ws: any = currentWorkspace as any;
97
+ const raw = ws?.groupNo ?? ws?.groupNumber ?? ws?.groupIndex ?? ws?.group_id ?? ws?.groupId ?? 1;
98
+ return toIntOrFallback(raw, 1);
99
+ }, [currentWorkspace]);
100
+
101
  // ---------- Fallback:不要静默消失 ----------
102
  if (!courseInfo) {
103
  return (
 
106
  <div className="text-xs text-red-600">COURSEINFO ACTIVE (fallback)</div>
107
  <div className="font-semibold text-base">No course matched</div>
108
  <div className="text-xs text-muted-foreground space-y-1">
109
+ <div>
110
+ selectedCourse: <span className="font-medium">{String(selectedCourse || "")}</span>
111
+ </div>
112
+ <div>
113
+ availableCourses: <span className="font-medium">{availableCourses?.length ?? 0}</span>
114
+ </div>
115
+ <div>
116
+ currentWorkspaceId: <span className="font-medium">{String(currentWorkspaceId)}</span>
117
+ </div>
118
  </div>
119
  </div>
120
  <Separator className="bg-[#ECECF1]" />
 
122
  );
123
  }
124
 
125
+ const instructorName = (courseInfo as any)?.instructor?.name ?? "N/A";
126
+ const instructorEmail = String((courseInfo as any)?.instructor?.email ?? "").trim();
127
 
128
+ const taName = (courseInfo as any)?.teachingAssistant?.name ?? "N/A";
129
+ const taEmail = String((courseInfo as any)?.teachingAssistant?.email ?? "").trim();
 
 
 
 
 
 
130
 
131
  return (
132
  <div className="w-full">
133
+ <div className="px-4 pt-4 pb-3 space-y-3">
134
+ {/* Course Title */}
135
  <div className="font-semibold text-base">{courseName}</div>
136
 
137
+ {/* Group fields (read-only, no pencil) */}
138
+ {isTeamSpace && (
139
+ <div className="space-y-1.5">
140
+ <div className="flex items-center justify-between text-[13px]">
141
+ <span className="text-muted-foreground">Group Name:</span>
142
+ <span className="font-semibold truncate max-w-[60%] text-right">{groupName}</span>
143
+ </div>
144
+ <div className="flex items-center justify-between text-[13px]">
145
+ <span className="text-muted-foreground">Group Number:</span>
146
+ <span className="font-semibold">{groupNo}</span>
147
+ </div>
148
+ </div>
149
+ )}
150
+
151
+ {/* Instructor */}
152
  <div className="text-sm text-muted-foreground">
153
  Instructor:&nbsp;
154
  {instructorEmail ? (
 
169
  )}
170
  </div>
171
 
172
+ {/* TA */}
173
  <div className="text-sm text-muted-foreground">
174
  TA:&nbsp;
175
  {taEmail ? (