SarahXia0405 commited on
Commit
daa9a58
·
verified ·
1 Parent(s): ca9e2d0

Update web/src/App.tsx

Browse files
Files changed (1) hide show
  1. web/src/App.tsx +175 -168
web/src/App.tsx CHANGED
@@ -12,7 +12,7 @@ import { Button } from "./components/ui/button";
12
  import { Toaster } from "./components/ui/sonner";
13
  import { toast } from "sonner";
14
 
15
- // ✅ NEW: backend API bindings
16
  import { apiChat, apiUpload, apiMemoryline } from "./lib/api";
17
 
18
  export interface Message {
@@ -848,186 +848,193 @@ function App() {
848
  if (showOnboarding && user) return <Onboarding user={user} onComplete={handleOnboardingComplete} onSkip={handleOnboardingSkip} />;
849
 
850
  return (
851
- <div className="h-screen overflow-hidden bg-background flex flex-col">
852
  <Toaster />
853
 
854
- {/* Header fixed */}
855
- <div className="flex-shrink-0">
856
- <Header
857
- user={user}
858
- onMenuClick={() => setLeftSidebarOpen(!leftSidebarOpen)}
859
- onUserClick={() => {}}
860
- isDarkMode={isDarkMode}
861
- onToggleDarkMode={() => setIsDarkMode(!isDarkMode)}
862
- language={language}
863
- onLanguageChange={setLanguage}
864
- workspaces={workspaces}
865
- currentWorkspace={currentWorkspace}
866
- onWorkspaceChange={setCurrentWorkspaceId}
867
- onCreateWorkspace={handleCreateWorkspace}
868
- onLogout={() => setUser(null)}
869
- availableCourses={availableCourses}
870
- onUserUpdate={setUser}
871
- />
872
- </div>
 
 
873
 
874
- {showProfileEditor && user && <ProfileEditor user={user} onSave={setUser} onClose={() => setShowProfileEditor(false)} />}
875
 
876
- {/* Review banner fixed */}
877
- {showReviewBanner && (
878
- <div className="flex-shrink-0 w-full bg-background border-b border-border relative z-50">
879
- <ReviewBanner onReview={handleReviewClick} onDismiss={handleDismissReviewBanner} />
880
- </div>
881
- )}
882
-
883
- {/* Main area: NO page scroll, only inner panes scroll */}
884
- <div className="flex-1 min-h-0 flex overflow-hidden relative" style={{ overscrollBehavior: "none" }}>
885
- {!leftPanelVisible && (
886
- <Button
887
- variant="secondary"
888
- size="icon"
889
- onClick={() => setLeftPanelVisible(true)}
890
- className="hidden lg:flex absolute z-[100] h-8 w-5 shadow-lg rounded-full bg-card border border-border transition-all duration-200 ease-in-out hover:translate-x-[10px]"
891
- style={{ left: "-5px", top: "1rem" }}
892
- title="Open panel"
893
- >
894
- <ChevronRight className="h-3 w-3" />
895
- </Button>
896
  )}
897
 
898
- {leftSidebarOpen && <div className="fixed inset-0 bg-black/50 z-40 lg:hidden" onClick={() => setLeftSidebarOpen(false)} />}
899
-
900
- {/* Desktop left panel */}
901
- {leftPanelVisible ? (
902
- <aside className="hidden lg:flex w-80 bg-card border-r border-border flex-col min-h-0 overflow-hidden relative">
903
  <Button
904
  variant="secondary"
905
  size="icon"
906
- onClick={() => setLeftPanelVisible(false)}
907
- className="absolute z-[70] h-8 w-5 shadow-lg rounded-full bg-card border border-border"
908
- style={{ right: "-10px", top: "1rem" }}
909
- title="Close panel"
910
  >
911
- <ChevronLeft className="h-3 w-3" />
912
  </Button>
913
-
914
- {/* IMPORTANT: do NOT wrap LeftSidebar with another overflow container */}
915
- <LeftSidebar
916
- learningMode={learningMode}
917
- language={language}
918
- onLearningModeChange={setLearningMode}
919
- onLanguageChange={setLanguage}
920
- spaceType={spaceType}
921
- groupMembers={groupMembers}
922
- user={user}
923
- onLogin={setUser}
924
- onLogout={() => setUser(null)}
925
- isLoggedIn={!!user}
926
- onEditProfile={() => setShowProfileEditor(true)}
927
- savedItems={savedItems}
928
- recentlySavedId={recentlySavedId}
929
- onUnsave={handleUnsave}
930
- onSave={handleSave}
931
- savedChats={savedChats}
932
- onLoadChat={handleLoadChat}
933
- onDeleteSavedChat={handleDeleteSavedChat}
934
- onRenameSavedChat={handleRenameSavedChat}
935
- currentWorkspaceId={currentWorkspaceId}
936
- workspaces={workspaces}
937
- selectedCourse={currentCourseId}
938
- availableCourses={availableCourses}
939
- />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940
  </aside>
941
- ) : null}
942
-
943
- {/* Mobile left drawer */}
944
- <aside
945
- className={`
946
- fixed lg:hidden inset-y-0 left-0 z-50
947
- w-80 bg-card border-r border-border
948
- transform transition-transform duration-300 ease-in-out
949
- ${leftSidebarOpen ? "translate-x-0" : "-translate-x-full"}
950
- flex flex-col
951
- mt-16
952
- h-[calc(100vh-4rem)]
953
- min-h-0
954
- overflow-hidden
955
- `}
956
- >
957
- <div className="p-4 border-b border-border flex justify-between items-center flex-shrink-0">
958
- <h3>Settings & Guide</h3>
959
- <Button variant="ghost" size="icon" onClick={() => setLeftSidebarOpen(false)}>
960
- <X className="h-5 w-5" />
961
- </Button>
962
- </div>
963
 
964
- <LeftSidebar
965
- learningMode={learningMode}
966
- language={language}
967
- onLearningModeChange={setLearningMode}
968
- onLanguageChange={setLanguage}
969
- spaceType={spaceType}
970
- groupMembers={groupMembers}
971
- user={user}
972
- onLogin={setUser}
973
- onLogout={() => setUser(null)}
974
- isLoggedIn={!!user}
975
- onEditProfile={() => setShowProfileEditor(true)}
976
- savedItems={savedItems}
977
- recentlySavedId={recentlySavedId}
978
- onUnsave={handleUnsave}
979
- onSave={handleSave}
980
- savedChats={savedChats}
981
- onLoadChat={handleLoadChat}
982
- onDeleteSavedChat={handleDeleteSavedChat}
983
- currentWorkspaceId={currentWorkspaceId}
984
- workspaces={workspaces}
985
- selectedCourse={currentCourseId}
986
- availableCourses={availableCourses}
987
- />
988
- </aside>
989
-
990
- {/* Chat column: overflow hidden, ChatArea handles its own inner scroll */}
991
- <main className="flex-1 min-w-0 min-h-0 flex flex-col overflow-hidden">
992
- <ChatArea
993
- messages={messages}
994
- onSendMessage={handleSendMessage}
995
- uploadedFiles={uploadedFiles}
996
- onFileUpload={handleFileUpload}
997
- onRemoveFile={handleRemoveFile}
998
- onFileTypeChange={handleFileTypeChange}
999
- memoryProgress={memoryProgress}
1000
- isLoggedIn={!!user}
1001
- learningMode={learningMode}
1002
- onClearConversation={() => setShowClearDialog(true)}
1003
- onSaveChat={handleSaveChat}
1004
- onLearningModeChange={setLearningMode}
1005
- spaceType={spaceType}
1006
- chatMode={chatMode}
1007
- onChatModeChange={setChatMode}
1008
- onNextQuestion={handleNextQuestion}
1009
- onStartQuiz={handleStartQuiz}
1010
- quizState={quizState}
1011
- isTyping={isTyping}
1012
- showClearDialog={showClearDialog}
1013
- onConfirmClear={(shouldSave) => {
1014
- handleClearConversation(shouldSave);
1015
- setShowClearDialog(false);
1016
- }}
1017
- onCancelClear={() => setShowClearDialog(false)}
1018
- savedChats={savedChats}
1019
- workspaces={workspaces}
1020
- currentWorkspaceId={currentWorkspaceId}
1021
- onSaveFile={(content, type, _format, targetWorkspaceId) =>
1022
- handleSave(content, type, false, (_format ?? "text") as "pdf" | "text", targetWorkspaceId)
1023
- }
1024
- leftPanelVisible={leftPanelVisible}
1025
- currentCourseId={currentCourseId}
1026
- onCourseChange={setCurrentCourseId}
1027
- availableCourses={availableCourses}
1028
- showReviewBanner={showReviewBanner}
1029
- />
1030
- </main>
1031
  </div>
1032
  </div>
1033
  );
 
12
  import { Toaster } from "./components/ui/sonner";
13
  import { toast } from "sonner";
14
 
15
+ // ✅ backend API bindings
16
  import { apiChat, apiUpload, apiMemoryline } from "./lib/api";
17
 
18
  export interface Message {
 
848
  if (showOnboarding && user) return <Onboarding user={user} onComplete={handleOnboardingComplete} onSkip={handleOnboardingSkip} />;
849
 
850
  return (
851
+ <div className="appShell bg-background">
852
  <Toaster />
853
 
854
+ {/* APP: column layout (header + main) */}
855
+ <div className="col flex-1 min-w-0">
856
+ {/* Header fixed */}
857
+ <div className="colFixed flex-shrink-0">
858
+ <Header
859
+ user={user}
860
+ onMenuClick={() => setLeftSidebarOpen(!leftSidebarOpen)}
861
+ onUserClick={() => {}}
862
+ isDarkMode={isDarkMode}
863
+ onToggleDarkMode={() => setIsDarkMode(!isDarkMode)}
864
+ language={language}
865
+ onLanguageChange={setLanguage}
866
+ workspaces={workspaces}
867
+ currentWorkspace={currentWorkspace}
868
+ onWorkspaceChange={setCurrentWorkspaceId}
869
+ onCreateWorkspace={handleCreateWorkspace}
870
+ onLogout={() => setUser(null)}
871
+ availableCourses={availableCourses}
872
+ onUserUpdate={setUser}
873
+ />
874
+ </div>
875
 
876
+ {showProfileEditor && user && <ProfileEditor user={user} onSave={setUser} onClose={() => setShowProfileEditor(false)} />}
877
 
878
+ {/* Review banner fixed */}
879
+ {showReviewBanner && (
880
+ <div className="colFixed flex-shrink-0 w-full bg-background border-b border-border relative z-50">
881
+ <ReviewBanner onReview={handleReviewClick} onDismiss={handleDismissReviewBanner} />
882
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
  )}
884
 
885
+ {/* Main area: NO page scroll, only inner panes scroll */}
886
+ <div className="colFlex flex min-h-0 overflow-hidden relative" style={{ overscrollBehavior: "none" }}>
887
+ {!leftPanelVisible && (
 
 
888
  <Button
889
  variant="secondary"
890
  size="icon"
891
+ onClick={() => setLeftPanelVisible(true)}
892
+ className="hidden lg:flex absolute z-[100] h-8 w-5 shadow-lg rounded-full bg-card border border-border transition-all duration-200 ease-in-out hover:translate-x-[10px]"
893
+ style={{ left: "-5px", top: "1rem" }}
894
+ title="Open panel"
895
  >
896
+ <ChevronRight className="h-3 w-3" />
897
  </Button>
898
+ )}
899
+
900
+ {leftSidebarOpen && <div className="fixed inset-0 bg-black/50 z-40 lg:hidden" onClick={() => setLeftSidebarOpen(false)} />}
901
+
902
+ {/* Desktop left panel */}
903
+ {leftPanelVisible ? (
904
+ <aside className="hidden lg:flex w-80 bg-card border-r border-border col min-h-0 overflow-hidden relative">
905
+ <Button
906
+ variant="secondary"
907
+ size="icon"
908
+ onClick={() => setLeftPanelVisible(false)}
909
+ className="absolute z-[70] h-8 w-5 shadow-lg rounded-full bg-card border border-border"
910
+ style={{ right: "-10px", top: "1rem" }}
911
+ title="Close panel"
912
+ >
913
+ <ChevronLeft className="h-3 w-3" />
914
+ </Button>
915
+
916
+ {/* LeftSidebar 内部自己控制滚动(Saved list),外层绝不滚 */}
917
+ <LeftSidebar
918
+ learningMode={learningMode}
919
+ language={language}
920
+ onLearningModeChange={setLearningMode}
921
+ onLanguageChange={setLanguage}
922
+ spaceType={spaceType}
923
+ groupMembers={groupMembers}
924
+ user={user}
925
+ onLogin={setUser}
926
+ onLogout={() => setUser(null)}
927
+ isLoggedIn={!!user}
928
+ onEditProfile={() => setShowProfileEditor(true)}
929
+ savedItems={savedItems}
930
+ recentlySavedId={recentlySavedId}
931
+ onUnsave={handleUnsave}
932
+ onSave={handleSave}
933
+ savedChats={savedChats}
934
+ onLoadChat={handleLoadChat}
935
+ onDeleteSavedChat={handleDeleteSavedChat}
936
+ onRenameSavedChat={handleRenameSavedChat}
937
+ currentWorkspaceId={currentWorkspaceId}
938
+ workspaces={workspaces}
939
+ selectedCourse={currentCourseId}
940
+ availableCourses={availableCourses}
941
+ />
942
+ </aside>
943
+ ) : null}
944
+
945
+ {/* Mobile left drawer */}
946
+ <aside
947
+ className={`
948
+ fixed lg:hidden inset-y-0 left-0 z-50
949
+ w-80 bg-card border-r border-border
950
+ transform transition-transform duration-300 ease-in-out
951
+ ${leftSidebarOpen ? "translate-x-0" : "-translate-x-full"}
952
+ col
953
+ mt-16
954
+ h-[calc(100vh-4rem)]
955
+ min-h-0
956
+ overflow-hidden
957
+ `}
958
+ >
959
+ <div className="colFixed p-4 border-b border-border flex justify-between items-center flex-shrink-0">
960
+ <h3>Settings & Guide</h3>
961
+ <Button variant="ghost" size="icon" onClick={() => setLeftSidebarOpen(false)}>
962
+ <X className="h-5 w-5" />
963
+ </Button>
964
+ </div>
965
+
966
+ <div className="colFlex min-h-0 overflow-hidden">
967
+ <LeftSidebar
968
+ learningMode={learningMode}
969
+ language={language}
970
+ onLearningModeChange={setLearningMode}
971
+ onLanguageChange={setLanguage}
972
+ spaceType={spaceType}
973
+ groupMembers={groupMembers}
974
+ user={user}
975
+ onLogin={setUser}
976
+ onLogout={() => setUser(null)}
977
+ isLoggedIn={!!user}
978
+ onEditProfile={() => setShowProfileEditor(true)}
979
+ savedItems={savedItems}
980
+ recentlySavedId={recentlySavedId}
981
+ onUnsave={handleUnsave}
982
+ onSave={handleSave}
983
+ savedChats={savedChats}
984
+ onLoadChat={handleLoadChat}
985
+ onDeleteSavedChat={handleDeleteSavedChat}
986
+ currentWorkspaceId={currentWorkspaceId}
987
+ workspaces={workspaces}
988
+ selectedCourse={currentCourseId}
989
+ availableCourses={availableCourses}
990
+ />
991
+ </div>
992
  </aside>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
993
 
994
+ {/* Chat column: outer never scroll, ChatArea controls internal scroll */}
995
+ <main className="flex-1 min-w-0 col min-h-0 overflow-hidden">
996
+ <div className="colFlex min-h-0 overflow-hidden">
997
+ <ChatArea
998
+ messages={messages}
999
+ onSendMessage={handleSendMessage}
1000
+ uploadedFiles={uploadedFiles}
1001
+ onFileUpload={handleFileUpload}
1002
+ onRemoveFile={handleRemoveFile}
1003
+ onFileTypeChange={handleFileTypeChange}
1004
+ memoryProgress={memoryProgress}
1005
+ isLoggedIn={!!user}
1006
+ learningMode={learningMode}
1007
+ onClearConversation={() => setShowClearDialog(true)}
1008
+ onSaveChat={handleSaveChat}
1009
+ onLearningModeChange={setLearningMode}
1010
+ spaceType={spaceType}
1011
+ chatMode={chatMode}
1012
+ onChatModeChange={setChatMode}
1013
+ onNextQuestion={handleNextQuestion}
1014
+ onStartQuiz={handleStartQuiz}
1015
+ quizState={quizState}
1016
+ isTyping={isTyping}
1017
+ showClearDialog={showClearDialog}
1018
+ onConfirmClear={(shouldSave) => {
1019
+ handleClearConversation(shouldSave);
1020
+ setShowClearDialog(false);
1021
+ }}
1022
+ onCancelClear={() => setShowClearDialog(false)}
1023
+ savedChats={savedChats}
1024
+ workspaces={workspaces}
1025
+ currentWorkspaceId={currentWorkspaceId}
1026
+ onSaveFile={(content, type, _format, targetWorkspaceId) =>
1027
+ handleSave(content, type, false, (_format ?? "text") as "pdf" | "text", targetWorkspaceId)
1028
+ }
1029
+ leftPanelVisible={leftPanelVisible}
1030
+ currentCourseId={currentCourseId}
1031
+ onCourseChange={setCurrentCourseId}
1032
+ availableCourses={availableCourses}
1033
+ showReviewBanner={showReviewBanner}
1034
+ />
1035
+ </div>
1036
+ </main>
1037
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1038
  </div>
1039
  </div>
1040
  );