File size: 7,071 Bytes
8e67c57
 
 
8be522e
8e67c57
 
 
 
 
 
 
 
 
cceb576
 
 
 
8be522e
 
 
 
 
 
 
 
 
 
 
 
 
 
8e67c57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8be522e
 
 
 
 
 
 
 
cceb576
 
8e67c57
cceb576
 
 
 
 
8be522e
 
 
8e67c57
 
cceb576
8e67c57
cceb576
8e67c57
 
cceb576
8be522e
8e67c57
cceb576
8be522e
8e67c57
 
 
 
8be522e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cceb576
 
 
 
 
6170d99
 
 
8be522e
 
 
 
 
 
 
 
 
cceb576
 
 
 
 
 
 
8be522e
 
6170d99
8be522e
 
8e67c57
 
 
8be522e
 
cceb576
8e67c57
8be522e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8e67c57
 
 
 
 
 
cceb576
 
8e67c57
 
 
 
 
 
 
 
 
 
 
 
8be522e
8e67c57
 
 
 
 
 
cceb576
 
8e67c57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
import React, { useMemo } from "react";
import { Separator } from "../ui/separator";

import type { Workspace, SpaceType } from "../../App";
import type { CourseDirectoryItem } from "../../lib/courseDirectory";

function gmailComposeLink(email: string, subject?: string, body?: string) {
  const to = `to=${encodeURIComponent(email)}`;
  const su = subject ? `&su=${encodeURIComponent(subject)}` : "";
  const bd = body ? `&body=${encodeURIComponent(body)}` : "";
  return `https://mail.google.com/mail/?view=cm&fs=1&${to}${su}${bd}`;
}

function norm(s: any) {
  return String(s ?? "").trim().toLowerCase();
}

function pickAny(obj: any, keys: string[]) {
  for (const k of keys) {
    const v = obj?.[k];
    if (v !== undefined && v !== null && String(v).trim() !== "") return v;
  }
  return undefined;
}

function toIntOrFallback(v: any, fb: number) {
  const n = Number(v);
  if (Number.isFinite(n) && n > 0) return Math.floor(n);
  return fb;
}

export function CourseInfoSection({
  currentWorkspaceId,
  workspaces,
  selectedCourse,
  availableCourses,
}: {
  currentWorkspaceId: string;
  workspaces: Workspace[];
  selectedCourse: string;
  availableCourses: CourseDirectoryItem[];
}) {
  const currentWorkspace = useMemo(
    () => workspaces.find((w) => w.id === currentWorkspaceId),
    [workspaces, currentWorkspaceId]
  );

  // 判断当前是否是 Group/Team(用于决定是否展示 Group Name/Number)
  const isTeamSpace = useMemo(() => {
    const ws: any = currentWorkspace as any;
    const st = String((ws?.type as SpaceType) || "").toLowerCase();
    return st === "group" || st === "team";
  }, [currentWorkspace]);

  // --------- CourseInfo resolution ---------
  const courseInfo = useMemo((): CourseDirectoryItem | null => {
    const list = Array.isArray(availableCourses) ? availableCourses : [];

    // 1) selectedCourse: 可能是 id / name(大小写/空格不一致也能匹配)
    const selRaw = (selectedCourse || "").trim();
    const sel = norm(selRaw);
    if (sel) {
      const hit =
        list.find((c: any) => norm(c.id) === sel) ||
        list.find((c: any) => norm(c.name) === sel);
      if (hit) return hit as any;
    }

    // 2) workspace.courseInfo: group workspace 的 courseInfo 可能只带 id/name
    const wsCourse = (currentWorkspace as any)?.courseInfo as
      | { id?: string; name?: string; instructor?: any; teachingAssistant?: any }
      | undefined;

    const wsId = norm(wsCourse?.id);
    if (wsId) return (list.find((c: any) => norm(c.id) === wsId) ?? (wsCourse as any)) as any;

    const wsName = norm(wsCourse?.name);
    if (wsName) return (list.find((c: any) => norm(c.name) === wsName) ?? (wsCourse as any)) as any;

    return null;
  }, [availableCourses, currentWorkspace, selectedCourse]);

  // ---------- Display fields ----------
  const courseName = useMemo(() => {
    return (courseInfo as any)?.name ?? (selectedCourse || "Course");
  }, [courseInfo, selectedCourse]);

  // --------- Group fields (read-only) ---------
  const groupName = useMemo(() => {
    const ws: any = currentWorkspace as any;
    // 优先 groupName,否则退回 workspace 的 name/title
    return String(pickAny(ws, ["groupName", "name", "title"]) || "My Group");
  }, [currentWorkspace]);

  const groupNo = useMemo(() => {
    const ws: any = currentWorkspace as any;
    const raw = ws?.groupNo ?? ws?.groupNumber ?? ws?.groupIndex ?? ws?.group_id ?? ws?.groupId ?? 1;
    return toIntOrFallback(raw, 1);
  }, [currentWorkspace]);

  // ---------- Fallback:不要静默消失 ----------
  if (!courseInfo) {
    return (
      <div className="w-full">
        <div className="px-4 pt-4 pb-3 space-y-2">
          <div className="text-xs text-red-600">COURSEINFO ACTIVE (fallback)</div>
          <div className="font-semibold text-base">No course matched</div>
          <div className="text-xs text-muted-foreground space-y-1">
            <div>
              selectedCourse: <span className="font-medium">{String(selectedCourse || "")}</span>
            </div>
            <div>
              availableCourses: <span className="font-medium">{availableCourses?.length ?? 0}</span>
            </div>
            <div>
              currentWorkspaceId: <span className="font-medium">{String(currentWorkspaceId)}</span>
            </div>
          </div>
        </div>
        <Separator className="bg-[#ECECF1]" />
      </div>
    );
  }

  const instructorName = (courseInfo as any)?.instructor?.name ?? "N/A";
  const instructorEmail = String((courseInfo as any)?.instructor?.email ?? "").trim();

  const taName = (courseInfo as any)?.teachingAssistant?.name ?? "N/A";
  const taEmail = String((courseInfo as any)?.teachingAssistant?.email ?? "").trim();

  return (
    <div className="w-full">
      <div className="px-4 pt-4 pb-3 space-y-3">
        {/* Course Title */}
        <div className="font-semibold text-base">{courseName}</div>

        {/* Group fields (read-only, no pencil) */}
        {isTeamSpace && (
          <div className="space-y-1.5">
            <div className="flex items-center justify-between text-[13px]">
              <span className="text-muted-foreground">Group Name:</span>
              <span className="font-semibold truncate max-w-[60%] text-right">{groupName}</span>
            </div>
            <div className="flex items-center justify-between text-[13px]">
              <span className="text-muted-foreground">Group Number:</span>
              <span className="font-semibold">{groupNo}</span>
            </div>
          </div>
        )}

        {/* Instructor */}
        <div className="text-sm text-muted-foreground">
          Instructor:&nbsp;
          {instructorEmail ? (
            <a
              href={gmailComposeLink(
                instructorEmail,
                `[Clare] Question about ${courseName}`,
                `Hi ${instructorName},\n\nI have a question about ${courseName}:\n\n(Write your question here)\n\nThanks,\n`
              )}
              target="_blank"
              rel="noopener noreferrer"
              className="text-primary hover:underline"
            >
              {instructorName}
            </a>
          ) : (
            <span className="text-muted-foreground/60">{instructorName}</span>
          )}
        </div>

        {/* TA */}
        <div className="text-sm text-muted-foreground">
          TA:&nbsp;
          {taEmail ? (
            <a
              href={gmailComposeLink(
                taEmail,
                `[Clare] Help request for ${courseName}`,
                `Hi ${taName},\n\nI need help with ${courseName}:\n\n(Write your question here)\n\nThanks,\n`
              )}
              target="_blank"
              rel="noopener noreferrer"
              className="text-primary hover:underline"
            >
              {taName}
            </a>
          ) : (
            <span className="text-muted-foreground/60">{taName}</span>
          )}
        </div>
      </div>

      <Separator className="bg-[#ECECF1]" />
    </div>
  );
}