Spaces:
Sleeping
Sleeping
| import React, { useMemo } from "react"; | |
| import { Separator } from "../ui/separator"; | |
| import type { Workspace } 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(); | |
| } | |
| 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] | |
| ); | |
| 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) => norm(c.id) === sel) || | |
| list.find((c) => norm(c.name) === sel); | |
| if (hit) return hit; | |
| } | |
| // 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) => norm(c.id) === wsId) ?? (wsCourse as any); | |
| } | |
| const wsName = norm(wsCourse?.name); | |
| if (wsName) { | |
| return list.find((c) => norm(c.name) === wsName) ?? (wsCourse as any); | |
| } | |
| return null; | |
| }, [availableCourses, currentWorkspace, selectedCourse]); | |
| // ---------- 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> | |
| ); | |
| } | |
| // ---------- Display fields ---------- | |
| const courseName = courseInfo.name ?? (selectedCourse || "Course"); | |
| const instructorName = courseInfo?.instructor?.name ?? "N/A"; | |
| const instructorEmail = (courseInfo?.instructor?.email || "").trim(); | |
| const taName = courseInfo?.teachingAssistant?.name ?? "N/A"; | |
| const taEmail = (courseInfo?.teachingAssistant?.email || "").trim(); | |
| return ( | |
| <div className="w-full"> | |
| <div className="px-4 pt-4 pb-3 space-y-2"> | |
| <div className="font-semibold text-base">{courseName}</div> | |
| <div className="text-sm text-muted-foreground"> | |
| Instructor: | |
| {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> | |
| <div className="text-sm text-muted-foreground"> | |
| TA: | |
| {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> | |
| ); | |
| } | |