SarahXia0405 commited on
Commit
cd2318c
·
verified ·
1 Parent(s): 825ec95

Update web/src/components/LeftSidebar.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/LeftSidebar.tsx +31 -42
web/src/components/LeftSidebar.tsx CHANGED
@@ -1,6 +1,5 @@
1
  // web/src/components/LeftSidebar.tsx
2
- import React, { useEffect, useRef, useState } from "react";
3
- import { LearningModeSelector } from "./LearningModeSelector";
4
  import { Label } from "./ui/label";
5
  import { Button } from "./ui/button";
6
  import { LogIn, Bookmark, Download, Copy } from "lucide-react";
@@ -20,16 +19,10 @@ import type {
20
  import { toast } from "sonner";
21
  import { Document, HeadingLevel, Packer, Paragraph, TextRun } from "docx";
22
  import { jsPDF } from "jspdf";
23
- import {
24
- Dialog,
25
- DialogContent,
26
- DialogDescription,
27
- DialogHeader,
28
- DialogTitle,
29
- } from "./ui/dialog";
30
  import type { CourseInfo } from "../App";
31
 
32
- // ✅ NEW: extracted section
33
  import { SavedChatSection } from "./sidebar/SavedChatSection";
34
 
35
  interface LeftSidebarProps {
@@ -37,21 +30,26 @@ interface LeftSidebarProps {
37
  language: Language;
38
  onLearningModeChange: (mode: LearningMode) => void;
39
  onLanguageChange: (lang: Language) => void;
 
40
  spaceType: SpaceType;
41
  groupMembers: GroupMember[];
 
42
  user: UserType | null;
43
  onLogin: (user: UserType) => void;
44
  onLogout: () => void;
45
  isLoggedIn: boolean;
46
  onEditProfile: () => void;
 
47
  savedItems: SavedItem[];
48
  recentlySavedId: string | null;
49
  onUnsave: (id: string) => void;
50
  onSave: (content: string, type: "export" | "quiz" | "summary") => void;
 
51
  savedChats: SavedChat[];
52
  onLoadChat: (chat: SavedChat) => void;
53
  onDeleteSavedChat: (id: string) => void;
54
  onRenameSavedChat?: (id: string, newTitle: string) => void;
 
55
  currentWorkspaceId: string;
56
  workspaces?: Array<{
57
  id: string;
@@ -63,6 +61,7 @@ interface LeftSidebarProps {
63
  isEditable?: boolean;
64
  name?: string;
65
  }>;
 
66
  selectedCourse?: string;
67
  courses?: Array<{ id: string; name: string }>;
68
  availableCourses?: CourseInfo[];
@@ -97,12 +96,16 @@ export function LeftSidebar({
97
  const [showLoginForm, setShowLoginForm] = useState(false);
98
  const [name, setName] = useState("");
99
  const [email, setEmail] = useState("");
 
100
  const [selectedItem, setSelectedItem] = useState<SavedItem | null>(null);
101
  const [isDialogOpen, setIsDialogOpen] = useState(false);
102
 
103
  const [isDownloading, setIsDownloading] = useState(false);
104
  const [copied, setCopied] = useState(false);
105
 
 
 
 
106
  const handleLogin = () => {
107
  if (!name.trim() || !email.trim()) {
108
  toast.error("Please fill in all fields");
@@ -140,8 +143,7 @@ export function LeftSidebar({
140
  };
141
 
142
  const getDefaultFilenameBase = (item: SavedItem) => {
143
- const kind =
144
- item.type === "export" ? "export" : item.type === "summary" ? "summary" : "quiz";
145
  return `clare-${kind}-${formatDateStamp(item.timestamp)}`;
146
  };
147
 
@@ -240,9 +242,7 @@ export function LeftSidebar({
240
  setTimeout(() => setCopied(false), 2000);
241
  };
242
 
243
- const handleUnsaveItem = (id: string) => {
244
- onUnsave(id);
245
- };
246
 
247
  const isItemSaved = selectedItem
248
  ? savedItems.some((item) => {
@@ -286,9 +286,6 @@ export function LeftSidebar({
286
 
287
  const currentWorkspace = workspaces?.find((w) => w.id === currentWorkspaceId);
288
 
289
- const [editableTitle, setEditableTitle] = useState("Untitled");
290
- const [isEditingTitle, setIsEditingTitle] = useState(false);
291
-
292
  const getCourseDisplayInfo = () => {
293
  if (!currentWorkspace) return null;
294
 
@@ -343,9 +340,9 @@ export function LeftSidebar({
343
  const courseDisplayInfo = getCourseDisplayInfo();
344
 
345
  return (
346
- // LeftSidebar itself never scrolls; only Saved list scrolls.
347
  <div className="h-full min-h-0 flex flex-col overflow-hidden">
348
- {/* Top fixed blocks */}
349
  {isLoggedIn && courseDisplayInfo && (
350
  <div className="p-4 border-b border-border flex-shrink-0">
351
  {courseDisplayInfo.type === "course" ? (
@@ -354,19 +351,13 @@ export function LeftSidebar({
354
  <div className="space-y-2 text-sm">
355
  <div>
356
  <span className="text-muted-foreground">Instructor: </span>
357
- <a
358
- href={`mailto:${courseDisplayInfo.instructor.email}`}
359
- className="text-primary hover:underline"
360
- >
361
  {courseDisplayInfo.instructor.name}
362
  </a>
363
  </div>
364
  <div>
365
  <span className="text-muted-foreground">TA: </span>
366
- <a
367
- href={`mailto:${courseDisplayInfo.teachingAssistant.email}`}
368
- className="text-primary hover:underline"
369
- >
370
  {courseDisplayInfo.teachingAssistant.name}
371
  </a>
372
  </div>
@@ -476,14 +467,16 @@ export function LeftSidebar({
476
  </div>
477
  )}
478
 
479
- {/* Saved Chat moved out (only this scrolls) */}
480
- <SavedChatSection
481
- isLoggedIn={isLoggedIn}
482
- savedChats={savedChats}
483
- onLoadChat={onLoadChat}
484
- onDeleteSavedChat={onDeleteSavedChat}
485
- onRenameSavedChat={onRenameSavedChat}
486
- />
 
 
487
 
488
  {/* Saved Item Dialog (unchanged) */}
489
  <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
@@ -491,12 +484,8 @@ export function LeftSidebar({
491
  <DialogHeader>
492
  <DialogTitle>{selectedItem?.title}</DialogTitle>
493
  <DialogDescription>
494
- {selectedItem?.type === "export"
495
- ? "Export"
496
- : selectedItem?.type === "quiz"
497
- ? "Quiz"
498
- : "Summary"}{" "}
499
- • {selectedItem?.timestamp.toLocaleDateString()}
500
  </DialogDescription>
501
  </DialogHeader>
502
 
 
1
  // web/src/components/LeftSidebar.tsx
2
+ import React, { useEffect, useState } from "react";
 
3
  import { Label } from "./ui/label";
4
  import { Button } from "./ui/button";
5
  import { LogIn, Bookmark, Download, Copy } from "lucide-react";
 
19
  import { toast } from "sonner";
20
  import { Document, HeadingLevel, Packer, Paragraph, TextRun } from "docx";
21
  import { jsPDF } from "jspdf";
22
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog";
 
 
 
 
 
 
23
  import type { CourseInfo } from "../App";
24
 
25
+ // ✅ extracted section
26
  import { SavedChatSection } from "./sidebar/SavedChatSection";
27
 
28
  interface LeftSidebarProps {
 
30
  language: Language;
31
  onLearningModeChange: (mode: LearningMode) => void;
32
  onLanguageChange: (lang: Language) => void;
33
+
34
  spaceType: SpaceType;
35
  groupMembers: GroupMember[];
36
+
37
  user: UserType | null;
38
  onLogin: (user: UserType) => void;
39
  onLogout: () => void;
40
  isLoggedIn: boolean;
41
  onEditProfile: () => void;
42
+
43
  savedItems: SavedItem[];
44
  recentlySavedId: string | null;
45
  onUnsave: (id: string) => void;
46
  onSave: (content: string, type: "export" | "quiz" | "summary") => void;
47
+
48
  savedChats: SavedChat[];
49
  onLoadChat: (chat: SavedChat) => void;
50
  onDeleteSavedChat: (id: string) => void;
51
  onRenameSavedChat?: (id: string, newTitle: string) => void;
52
+
53
  currentWorkspaceId: string;
54
  workspaces?: Array<{
55
  id: string;
 
61
  isEditable?: boolean;
62
  name?: string;
63
  }>;
64
+
65
  selectedCourse?: string;
66
  courses?: Array<{ id: string; name: string }>;
67
  availableCourses?: CourseInfo[];
 
96
  const [showLoginForm, setShowLoginForm] = useState(false);
97
  const [name, setName] = useState("");
98
  const [email, setEmail] = useState("");
99
+
100
  const [selectedItem, setSelectedItem] = useState<SavedItem | null>(null);
101
  const [isDialogOpen, setIsDialogOpen] = useState(false);
102
 
103
  const [isDownloading, setIsDownloading] = useState(false);
104
  const [copied, setCopied] = useState(false);
105
 
106
+ const [editableTitle, setEditableTitle] = useState("Untitled");
107
+ const [isEditingTitle, setIsEditingTitle] = useState(false);
108
+
109
  const handleLogin = () => {
110
  if (!name.trim() || !email.trim()) {
111
  toast.error("Please fill in all fields");
 
143
  };
144
 
145
  const getDefaultFilenameBase = (item: SavedItem) => {
146
+ const kind = item.type === "export" ? "export" : item.type === "summary" ? "summary" : "quiz";
 
147
  return `clare-${kind}-${formatDateStamp(item.timestamp)}`;
148
  };
149
 
 
242
  setTimeout(() => setCopied(false), 2000);
243
  };
244
 
245
+ const handleUnsaveItem = (id: string) => onUnsave(id);
 
 
246
 
247
  const isItemSaved = selectedItem
248
  ? savedItems.some((item) => {
 
286
 
287
  const currentWorkspace = workspaces?.find((w) => w.id === currentWorkspaceId);
288
 
 
 
 
289
  const getCourseDisplayInfo = () => {
290
  if (!currentWorkspace) return null;
291
 
 
340
  const courseDisplayInfo = getCourseDisplayInfo();
341
 
342
  return (
343
+ // Sidebar itself does NOT scroll; only the Saved Chat list section scrolls.
344
  <div className="h-full min-h-0 flex flex-col overflow-hidden">
345
+ {/* ========== FIXED TOP (no scroll) ========== */}
346
  {isLoggedIn && courseDisplayInfo && (
347
  <div className="p-4 border-b border-border flex-shrink-0">
348
  {courseDisplayInfo.type === "course" ? (
 
351
  <div className="space-y-2 text-sm">
352
  <div>
353
  <span className="text-muted-foreground">Instructor: </span>
354
+ <a href={`mailto:${courseDisplayInfo.instructor.email}`} className="text-primary hover:underline">
 
 
 
355
  {courseDisplayInfo.instructor.name}
356
  </a>
357
  </div>
358
  <div>
359
  <span className="text-muted-foreground">TA: </span>
360
+ <a href={`mailto:${courseDisplayInfo.teachingAssistant.email}`} className="text-primary hover:underline">
 
 
 
361
  {courseDisplayInfo.teachingAssistant.name}
362
  </a>
363
  </div>
 
467
  </div>
468
  )}
469
 
470
+ {/* ========== SCROLL AREA (Saved Chats ONLY) ========== */}
471
+ <div className="flex-1 min-h-0 overflow-y-auto">
472
+ <SavedChatSection
473
+ isLoggedIn={isLoggedIn}
474
+ savedChats={savedChats}
475
+ onLoadChat={onLoadChat}
476
+ onDeleteSavedChat={onDeleteSavedChat}
477
+ onRenameSavedChat={onRenameSavedChat}
478
+ />
479
+ </div>
480
 
481
  {/* Saved Item Dialog (unchanged) */}
482
  <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
 
484
  <DialogHeader>
485
  <DialogTitle>{selectedItem?.title}</DialogTitle>
486
  <DialogDescription>
487
+ {selectedItem?.type === "export" ? "Export" : selectedItem?.type === "quiz" ? "Quiz" : "Summary"} •{" "}
488
+ {selectedItem?.timestamp.toLocaleDateString()}
 
 
 
 
489
  </DialogDescription>
490
  </DialogHeader>
491