SarahXia0405 commited on
Commit
acdaba5
·
verified ·
1 Parent(s): d1e9766

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

Browse files
web/src/components/sidebar/LeftSidebar.tsx CHANGED
@@ -7,7 +7,7 @@ import { Input } from "../ui/input";
7
  import { SavedChatSection } from "./SavedChatSection";
8
  import { GroupMembers } from "../GroupMembers";
9
 
10
- import { Users, Mail, Pencil, Check, X } from "lucide-react";
11
 
12
  import type {
13
  SavedChat,
@@ -23,13 +23,16 @@ import type { CourseDirectoryItem } from "../../lib/courseDirectory";
23
 
24
  /**
25
  * Behavior:
26
- * - spaceType === "group" (Team/Group space): show "Group Members (N)" + Invite button + member list (old design).
27
- * - otherwise (My Space / personal): show simple "Group {groupNo}" + editable "Group {groupName}".
 
 
 
28
  *
29
  * Layout:
30
- * - TOP: Welcome + Course + (Group block: either simple or members panel) => non-scroll
31
  * - MIDDLE: Saved Chat => only scroll
32
- * - BOTTOM: Instructor/TA (click -> Gmail compose) => fixed, non-scroll
33
  */
34
 
35
  type Props = {
@@ -70,11 +73,10 @@ type Props = {
70
  selectedCourse: string;
71
  availableCourses: CourseDirectoryItem[];
72
 
73
- // optional: if you already have invite flow somewhere, wire it here; otherwise UI stays but does nothing.
74
  onInviteGroupMembers?: () => void;
75
 
76
- // optional: if you have backend rename endpoint, wire it here (preferred).
77
- // if not provided, we fallback to localStorage persistence.
78
  onRenameGroupName?: (workspaceId: string, newName: string) => Promise<void> | void;
79
  };
80
 
@@ -167,7 +169,7 @@ export function LeftSidebar(props: Props) {
167
  return (courseInfo as any)?.name ?? (selectedCourse || "Course");
168
  }, [courseInfo, selectedCourse]);
169
 
170
- // --------- Group number/name (My Space view) ---------
171
  const groupNo = useMemo(() => {
172
  const ws: any = currentWorkspace as any;
173
  return (
@@ -182,10 +184,7 @@ export function LeftSidebar(props: Props) {
182
 
183
  const defaultGroupName = useMemo(() => {
184
  const ws: any = currentWorkspace as any;
185
- return (
186
- pickAny(ws, ["groupName", "name", "title"]) ||
187
- "My Group"
188
- );
189
  }, [currentWorkspace]);
190
 
191
  const groupNameStorageKey = useMemo(
@@ -220,7 +219,7 @@ export function LeftSidebar(props: Props) {
220
  await onRenameGroupName(currentWorkspaceId, next);
221
  return;
222
  } catch {
223
- // fallback to localStorage if handler fails
224
  }
225
  }
226
 
@@ -245,7 +244,7 @@ export function LeftSidebar(props: Props) {
245
 
246
  const displayName = useMemo(() => getUserName(user), [user]);
247
 
248
- // Team/Group space detection: we treat "group" as the Team/Group page
249
  const isTeamSpace = useMemo(() => {
250
  const st = String(spaceType || "").toLowerCase();
251
  return st === "group" || st === "team";
@@ -268,7 +267,7 @@ export function LeftSidebar(props: Props) {
268
  <div className="px-4 pt-10 pb-10 space-y-4">
269
  <div className="text-[30px] leading-tight font-semibold">{courseName}</div>
270
 
271
- {/* My Space: simple Group No + Group Name (editable) */}
272
  {!isTeamSpace ? (
273
  <div className="space-y-3">
274
  <div className="text-[30px] leading-tight font-semibold">
@@ -327,18 +326,68 @@ export function LeftSidebar(props: Props) {
327
  </div>
328
  </div>
329
  ) : (
330
- // Team/Group Space: "Group Members (N)" + Invite + list
331
- <div className="rounded-xl border bg-[#0B0B0C] text-white overflow-hidden">
332
  <div className="px-4 py-3 flex items-center justify-between">
333
- <div className="flex items-center gap-2 text-[14px] font-medium">
334
- <Users className="w-4 h-4 opacity-90" />
335
- <span>Group Members ({(groupMembers || []).length})</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  </div>
337
 
338
  <Button
339
  type="button"
340
  variant="secondary"
341
- className="h-8 px-3 text-[13px] bg-white/10 hover:bg-white/15 text-white border border-white/15"
342
  onClick={onInviteGroupMembers}
343
  >
344
  <Mail className="w-4 h-4 mr-2" />
@@ -347,7 +396,7 @@ export function LeftSidebar(props: Props) {
347
  </div>
348
 
349
  <div className="px-3 pb-3">
350
- {/* Reuse existing GroupMembers component (old UI) */}
351
  <GroupMembers members={groupMembers as any} />
352
  </div>
353
  </div>
 
7
  import { SavedChatSection } from "./SavedChatSection";
8
  import { GroupMembers } from "../GroupMembers";
9
 
10
+ import { Mail, Pencil, Check, X } from "lucide-react";
11
 
12
  import type {
13
  SavedChat,
 
23
 
24
  /**
25
  * Behavior:
26
+ * - My Space: show "Group {groupNo}" + editable "Group {Name}"
27
+ * - Team/Group Space: show a WHITE card with:
28
+ * - title: "Group Name" + editable group name (pencil)
29
+ * - right: Invite button
30
+ * - body: GroupMembers list
31
  *
32
  * Layout:
33
+ * - TOP: Welcome + Course + Group block => non-scroll
34
  * - MIDDLE: Saved Chat => only scroll
35
+ * - BOTTOM: Instructor/TA => fixed, non-scroll
36
  */
37
 
38
  type Props = {
 
73
  selectedCourse: string;
74
  availableCourses: CourseDirectoryItem[];
75
 
 
76
  onInviteGroupMembers?: () => void;
77
 
78
+ // preferred: wire to backend rename (POST /api/workspaces/{id}/rename)
79
+ // fallback: localStorage
80
  onRenameGroupName?: (workspaceId: string, newName: string) => Promise<void> | void;
81
  };
82
 
 
169
  return (courseInfo as any)?.name ?? (selectedCourse || "Course");
170
  }, [courseInfo, selectedCourse]);
171
 
172
+ // --------- Group number/name ---------
173
  const groupNo = useMemo(() => {
174
  const ws: any = currentWorkspace as any;
175
  return (
 
184
 
185
  const defaultGroupName = useMemo(() => {
186
  const ws: any = currentWorkspace as any;
187
+ return pickAny(ws, ["groupName", "name", "title"]) || "My Group";
 
 
 
188
  }, [currentWorkspace]);
189
 
190
  const groupNameStorageKey = useMemo(
 
219
  await onRenameGroupName(currentWorkspaceId, next);
220
  return;
221
  } catch {
222
+ // fallback below
223
  }
224
  }
225
 
 
244
 
245
  const displayName = useMemo(() => getUserName(user), [user]);
246
 
247
+ // Team/Group space detection
248
  const isTeamSpace = useMemo(() => {
249
  const st = String(spaceType || "").toLowerCase();
250
  return st === "group" || st === "team";
 
267
  <div className="px-4 pt-10 pb-10 space-y-4">
268
  <div className="text-[30px] leading-tight font-semibold">{courseName}</div>
269
 
270
+ {/* My Space: simple Group No + Group Name */}
271
  {!isTeamSpace ? (
272
  <div className="space-y-3">
273
  <div className="text-[30px] leading-tight font-semibold">
 
326
  </div>
327
  </div>
328
  ) : (
329
+ // Team/Group Space: white card + "Group Name" header + pencil rename + Invite
330
+ <div className="rounded-2xl border bg-white overflow-hidden">
331
  <div className="px-4 py-3 flex items-center justify-between">
332
+ <div className="flex items-center gap-2 min-w-0">
333
+ <div className="text-[14px] font-semibold text-foreground whitespace-nowrap">
334
+ Group Name
335
+ </div>
336
+
337
+ {!editingGroupName ? (
338
+ <div className="flex items-center gap-2 min-w-0">
339
+ <div className="text-[14px] font-medium text-muted-foreground truncate max-w-[180px]">
340
+ {groupName}
341
+ </div>
342
+ <button
343
+ type="button"
344
+ className="inline-flex items-center text-muted-foreground hover:text-foreground"
345
+ onClick={() => setEditingGroupName(true)}
346
+ aria-label="Edit group name"
347
+ >
348
+ <Pencil className="w-4 h-4" />
349
+ </button>
350
+ </div>
351
+ ) : (
352
+ <div className="flex items-center gap-2 min-w-0">
353
+ <Input
354
+ value={draftGroupName}
355
+ onChange={(e) => setDraftGroupName(e.target.value)}
356
+ onKeyDown={(e) => {
357
+ if (e.key === "Enter") saveGroupName();
358
+ if (e.key === "Escape") cancelGroupName();
359
+ }}
360
+ className="h-8 w-[180px]"
361
+ autoFocus
362
+ />
363
+ <Button
364
+ type="button"
365
+ size="icon"
366
+ variant="ghost"
367
+ className="h-8 w-8"
368
+ onClick={saveGroupName}
369
+ aria-label="Save group name"
370
+ >
371
+ <Check className="w-4 h-4" />
372
+ </Button>
373
+ <Button
374
+ type="button"
375
+ size="icon"
376
+ variant="ghost"
377
+ className="h-8 w-8"
378
+ onClick={cancelGroupName}
379
+ aria-label="Cancel edit group name"
380
+ >
381
+ <X className="w-4 h-4" />
382
+ </Button>
383
+ </div>
384
+ )}
385
  </div>
386
 
387
  <Button
388
  type="button"
389
  variant="secondary"
390
+ className="h-9 px-3 text-[13px]"
391
  onClick={onInviteGroupMembers}
392
  >
393
  <Mail className="w-4 h-4 mr-2" />
 
396
  </div>
397
 
398
  <div className="px-3 pb-3">
399
+ {/* Old GroupMembers list UI */}
400
  <GroupMembers members={groupMembers as any} />
401
  </div>
402
  </div>