SarahXia0405 commited on
Commit
8bf06de
·
verified ·
1 Parent(s): 7c40469

Update web/src/App.tsx

Browse files
Files changed (1) hide show
  1. web/src/App.tsx +73 -25
web/src/App.tsx CHANGED
@@ -1,7 +1,7 @@
1
  // web/src/App.tsx
2
  import React, { useState, useEffect, useRef, useMemo } from "react";
3
  import { Header } from "./components/Header";
4
- import { LeftSidebar } from "./components/LeftSidebar";
5
  import { ChatArea } from "./components/ChatArea";
6
  import { LoginScreen } from "./components/LoginScreen";
7
  import { ProfileEditor } from "./components/ProfileEditor";
@@ -11,10 +11,7 @@ import { X, ChevronLeft, ChevronRight } from "lucide-react";
11
  import { Button } from "./components/ui/button";
12
  import { Toaster } from "./components/ui/sonner";
13
  import { toast } from "sonner";
14
- // import { LeftSidebar } from "./components/sidebar/LeftSidebar";
15
-
16
- // import { COURSE_DIRECTORY } from "./lib/courseDirectory";
17
-
18
 
19
  // backend API bindings
20
  import { apiChat, apiUpload, apiMemoryline } from "./lib/api";
@@ -394,6 +391,64 @@ function App() {
394
 
395
  const spaceType: SpaceType = currentWorkspace?.type || "individual";
396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  useEffect(() => {
398
  if (!currentWorkspace) return;
399
 
@@ -674,9 +729,8 @@ function App() {
674
  const newFiles: UploadedFile[] = files.map((file) => ({ file, type: "other" as FileType }));
675
  setUploadedFiles((prev) => [...prev, ...newFiles]);
676
  };
677
-
678
 
679
- const handleRemoveFile = (file: File) => setUploadedFiles(prev => prev.filter(u => u.file !== file));
680
 
681
  // ✅ CRITICAL FIX:
682
  // - use functional setState to avoid stale closure
@@ -723,11 +777,7 @@ function App() {
723
 
724
  return chat.messages.every((savedMsg, idx) => {
725
  const currentMsg = messages[idx];
726
- return (
727
- savedMsg.id === currentMsg.id &&
728
- savedMsg.role === currentMsg.role &&
729
- savedMsg.content === currentMsg.content
730
- );
731
  });
732
  }) || null
733
  );
@@ -756,7 +806,10 @@ function App() {
756
  return;
757
  }
758
 
759
- const title = `Chat - ${chatMode === "ask" ? "Ask" : chatMode === "review" ? "Review" : "Quiz"} - ${new Date().toLocaleDateString()}`;
 
 
 
760
  const newChat: SavedChat = {
761
  id: Date.now().toString(),
762
  title,
@@ -891,12 +944,7 @@ function App() {
891
  toast.success("Removed from saved items");
892
  };
893
 
894
- const handleCreateWorkspace = (payload: {
895
- name: string;
896
- category: "course" | "personal";
897
- courseId?: string;
898
- invites: string[];
899
- }) => {
900
  const id = `group-${Date.now()}`;
901
  const avatar = `https://api.dicebear.com/7.x/shapes/svg?seed=${encodeURIComponent(payload.name)}`;
902
 
@@ -1059,8 +1107,8 @@ function App() {
1059
  language={language}
1060
  onLearningModeChange={setLearningMode}
1061
  onLanguageChange={setLanguage}
1062
- spaceType={spaceType}
1063
- groupMembers={groupMembers}
1064
  user={user}
1065
  onLogin={setUser}
1066
  onLogout={() => setUser(null)}
@@ -1075,7 +1123,7 @@ function App() {
1075
  onDeleteSavedChat={handleDeleteSavedChat}
1076
  onRenameSavedChat={handleRenameSavedChat}
1077
  currentWorkspaceId={currentWorkspaceId}
1078
- workspaces={workspaces}
1079
  selectedCourse={currentCourseId}
1080
  availableCourses={availableCourses}
1081
  />
@@ -1106,8 +1154,8 @@ function App() {
1106
  language={language}
1107
  onLearningModeChange={setLearningMode}
1108
  onLanguageChange={setLanguage}
1109
- spaceType={spaceType}
1110
- groupMembers={groupMembers}
1111
  user={user}
1112
  onLogin={setUser}
1113
  onLogout={() => setUser(null)}
@@ -1122,7 +1170,7 @@ function App() {
1122
  onDeleteSavedChat={handleDeleteSavedChat}
1123
  onRenameSavedChat={handleRenameSavedChat}
1124
  currentWorkspaceId={currentWorkspaceId}
1125
- workspaces={workspaces}
1126
  selectedCourse={currentCourseId}
1127
  availableCourses={availableCourses}
1128
  />
 
1
  // web/src/App.tsx
2
  import React, { useState, useEffect, useRef, useMemo } from "react";
3
  import { Header } from "./components/Header";
4
+ // import { LeftSidebar } from "./components/LeftSidebar";
5
  import { ChatArea } from "./components/ChatArea";
6
  import { LoginScreen } from "./components/LoginScreen";
7
  import { ProfileEditor } from "./components/ProfileEditor";
 
11
  import { Button } from "./components/ui/button";
12
  import { Toaster } from "./components/ui/sonner";
13
  import { toast } from "sonner";
14
+ import { LeftSidebar } from "./components/sidebar/LeftSidebar";
 
 
 
15
 
16
  // backend API bindings
17
  import { apiChat, apiUpload, apiMemoryline } from "./lib/api";
 
391
 
392
  const spaceType: SpaceType = currentWorkspace?.type || "individual";
393
 
394
+ // =========================
395
+ // ✅ Scheme 1: "My Space" uses Group-like sidebar view model
396
+ // - Only affects LeftSidebar rendering (NOT ChatArea / Header logic)
397
+ // =========================
398
+ const mySpaceCourseInfo = useMemo(() => {
399
+ return availableCourses.find((c) => c.id === currentCourseId);
400
+ }, [availableCourses, currentCourseId]);
401
+
402
+ const mySpaceUserMember: GroupMember | null = useMemo(() => {
403
+ if (!user) return null;
404
+ return {
405
+ id: user.email,
406
+ name: user.name,
407
+ email: user.email,
408
+ avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(user.email)}`,
409
+ };
410
+ }, [user]);
411
+
412
+ const clareMember: GroupMember = useMemo(
413
+ () => ({ id: "clare", name: "Clare AI", email: "clare@ai.assistant", isAI: true }),
414
+ []
415
+ );
416
+
417
+ // For sidebar only: treat "My Space" as a course/group-like workspace so Group UI blocks can render.
418
+ const sidebarWorkspaces: Workspace[] = useMemo(() => {
419
+ if (!workspaces?.length) return workspaces;
420
+
421
+ // If user is not ready, do not synthesize
422
+ if (!mySpaceUserMember) return workspaces;
423
+
424
+ return workspaces.map((w) => {
425
+ if (w.id !== "individual") return w;
426
+
427
+ return {
428
+ ...w,
429
+ // keep type as "individual" to avoid breaking other list logic,
430
+ // but fill the fields that Group UI depends on
431
+ category: "course",
432
+ courseName: mySpaceCourseInfo?.name || w.courseName || "My Course",
433
+ courseInfo: mySpaceCourseInfo,
434
+ members: [clareMember, mySpaceUserMember],
435
+ };
436
+ });
437
+ }, [workspaces, mySpaceCourseInfo, mySpaceUserMember, clareMember]);
438
+
439
+ // Some sidebars use spaceType to gate rendering; for My Space we present it as "group" in sidebar only.
440
+ const sidebarSpaceType: SpaceType = useMemo(() => {
441
+ return currentWorkspaceId === "individual" ? "group" : spaceType;
442
+ }, [currentWorkspaceId, spaceType]);
443
+
444
+ // Sidebar-only "members" list
445
+ const sidebarGroupMembers: GroupMember[] = useMemo(() => {
446
+ if (currentWorkspaceId === "individual" && mySpaceUserMember) {
447
+ return [clareMember, mySpaceUserMember];
448
+ }
449
+ return groupMembers;
450
+ }, [currentWorkspaceId, mySpaceUserMember, clareMember, groupMembers]);
451
+
452
  useEffect(() => {
453
  if (!currentWorkspace) return;
454
 
 
729
  const newFiles: UploadedFile[] = files.map((file) => ({ file, type: "other" as FileType }));
730
  setUploadedFiles((prev) => [...prev, ...newFiles]);
731
  };
 
732
 
733
+ const handleRemoveFile = (file: File) => setUploadedFiles((prev) => prev.filter((u) => u.file !== file));
734
 
735
  // ✅ CRITICAL FIX:
736
  // - use functional setState to avoid stale closure
 
777
 
778
  return chat.messages.every((savedMsg, idx) => {
779
  const currentMsg = messages[idx];
780
+ return savedMsg.id === currentMsg.id && savedMsg.role === currentMsg.role && savedMsg.content === currentMsg.content;
 
 
 
 
781
  });
782
  }) || null
783
  );
 
806
  return;
807
  }
808
 
809
+ const title = `Chat - ${
810
+ chatMode === "ask" ? "Ask" : chatMode === "review" ? "Review" : "Quiz"
811
+ } - ${new Date().toLocaleDateString()}`;
812
+
813
  const newChat: SavedChat = {
814
  id: Date.now().toString(),
815
  title,
 
944
  toast.success("Removed from saved items");
945
  };
946
 
947
+ const handleCreateWorkspace = (payload: { name: string; category: "course" | "personal"; courseId?: string; invites: string[] }) => {
 
 
 
 
 
948
  const id = `group-${Date.now()}`;
949
  const avatar = `https://api.dicebear.com/7.x/shapes/svg?seed=${encodeURIComponent(payload.name)}`;
950
 
 
1107
  language={language}
1108
  onLearningModeChange={setLearningMode}
1109
  onLanguageChange={setLanguage}
1110
+ spaceType={sidebarSpaceType}
1111
+ groupMembers={sidebarGroupMembers}
1112
  user={user}
1113
  onLogin={setUser}
1114
  onLogout={() => setUser(null)}
 
1123
  onDeleteSavedChat={handleDeleteSavedChat}
1124
  onRenameSavedChat={handleRenameSavedChat}
1125
  currentWorkspaceId={currentWorkspaceId}
1126
+ workspaces={sidebarWorkspaces}
1127
  selectedCourse={currentCourseId}
1128
  availableCourses={availableCourses}
1129
  />
 
1154
  language={language}
1155
  onLearningModeChange={setLearningMode}
1156
  onLanguageChange={setLanguage}
1157
+ spaceType={sidebarSpaceType}
1158
+ groupMembers={sidebarGroupMembers}
1159
  user={user}
1160
  onLogin={setUser}
1161
  onLogout={() => setUser(null)}
 
1170
  onDeleteSavedChat={handleDeleteSavedChat}
1171
  onRenameSavedChat={handleRenameSavedChat}
1172
  currentWorkspaceId={currentWorkspaceId}
1173
+ workspaces={sidebarWorkspaces}
1174
  selectedCourse={currentCourseId}
1175
  availableCourses={availableCourses}
1176
  />