SarahXia0405 commited on
Commit
056662a
·
verified ·
1 Parent(s): 59e26b5

Update web/src/App.tsx

Browse files
Files changed (1) hide show
  1. web/src/App.tsx +120 -84
web/src/App.tsx CHANGED
@@ -25,7 +25,6 @@ import {
25
  energyPct,
26
  } from "./lib/reviewStar";
27
 
28
-
29
  export type MessageAttachmentKind = "pdf" | "ppt" | "doc" | "image" | "other";
30
 
31
  export interface MessageAttachment {
@@ -36,16 +35,21 @@ export interface MessageAttachment {
36
  fileType?: FileType; // syllabus / lecture-slides / ...
37
  }
38
 
 
 
39
  export interface Message {
40
  id: string;
41
  role: "user" | "assistant";
42
  content: string;
43
  timestamp: Date;
 
 
 
44
  references?: string[];
45
  sender?: GroupMember;
46
  showNextButton?: boolean;
47
 
48
- // ✅ NEW: show files “with” the user message (metadata only)
49
  attachments?: MessageAttachment[];
50
 
51
  questionData?: {
@@ -58,8 +62,6 @@ export interface Message {
58
  };
59
  }
60
 
61
-
62
-
63
  export interface User {
64
  // required identity
65
  name: string;
@@ -155,6 +157,56 @@ function mapLanguagePref(lang: Language): string {
155
  return "Auto";
156
  }
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  // ✅ localStorage helpers for saved chats
159
  function savedChatsStorageKey(email: string) {
160
  return `saved_chats::${email}`;
@@ -654,6 +706,8 @@ function App() {
654
 
655
  if (!hasText && !hasFiles) return;
656
 
 
 
657
  const fileNames = hasFiles ? uploadedFiles.map((f) => f.file.name) : [];
658
  const fileLine = fileNames.length ? `Uploaded files: ${fileNames.join(", ")}` : "";
659
 
@@ -672,29 +726,28 @@ function App() {
672
  ? content
673
  : `📎 Sent ${fileNames.length} file(s)\n${fileNames.map((n) => `- ${n}`).join("\n")}`;
674
 
675
- // ✅ snapshot attachments at send-time
676
- const attachmentsSnapshot: MessageAttachment[] =
677
- uploadedFiles.map((uf) => {
678
- const lower = uf.file.name.toLowerCase();
679
- const kind: MessageAttachmentKind =
680
- lower.endsWith(".pdf")
681
- ? "pdf"
682
- : lower.endsWith(".ppt") || lower.endsWith(".pptx")
683
- ? "ppt"
684
- : lower.endsWith(".doc") || lower.endsWith(".docx")
685
- ? "doc"
686
- : [".jpg", ".jpeg", ".png", ".gif", ".webp"].some((e) => lower.endsWith(e))
687
- ? "image"
688
- : "other";
689
-
690
- return {
691
- name: uf.file.name,
692
- size: uf.file.size,
693
- kind,
694
- fileType: uf.type,
695
- };
696
- });
697
-
698
  const userMessage: Message = {
699
  id: Date.now().toString(),
700
  role: "user",
@@ -720,32 +773,19 @@ function App() {
720
  learning_mode: "quiz",
721
  language_preference: mapLanguagePref(language),
722
  doc_type: docType,
723
- });
724
-
725
- const normalizeRefs = (raw: any): string[] => {
726
- const arr = Array.isArray(raw) ? raw : [];
727
- return arr
728
- .map((x) => {
729
- if (typeof x === "string") {
730
- const s = x.trim();
731
- return s ? s : null;
732
- }
733
- const a = x?.source_file ? String(x.source_file) : "";
734
- const b = x?.section ? String(x.section) : "";
735
- const s = `${a}${a && b ? " — " : ""}${b}`.trim();
736
- return s || null;
737
- })
738
- .filter(Boolean) as string[];
739
- };
740
 
741
- const refs = normalizeRefs((r as any).refs ?? (r as any).references);
 
742
 
743
  const assistantMessage: Message = {
744
  id: (Date.now() + 1).toString(),
745
  role: "assistant",
746
- content: r.reply || "",
747
  timestamp: new Date(),
748
- references: refs.length ? refs : undefined,
 
749
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
750
  showNextButton: false,
751
  };
@@ -786,23 +826,19 @@ function App() {
786
  learning_mode: learningMode,
787
  language_preference: mapLanguagePref(language),
788
  doc_type: docType,
789
- });
 
790
 
791
- const refs = (r.refs || [])
792
- .map((x: any) => {
793
- const a = x?.source_file ? String(x.source_file) : "";
794
- const b = x?.section ? String(x.section) : "";
795
- const s = `${a}${a && b ? " — " : ""}${b}`.trim();
796
- return s || null;
797
- })
798
- .filter(Boolean) as string[];
799
 
800
  const assistantMessage: Message = {
801
  id: (Date.now() + 1).toString(),
802
  role: "assistant",
803
- content: r.reply || "",
804
  timestamp: new Date(),
805
- references: refs.length ? refs : undefined,
 
806
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
807
  };
808
 
@@ -841,6 +877,8 @@ function App() {
841
  const handleNextQuestion = async () => {
842
  if (!user) return;
843
 
 
 
844
  const prompt = "Please give me another question of the same quiz style.";
845
  const sender: GroupMember = {
846
  id: user.email,
@@ -868,23 +906,19 @@ function App() {
868
  learning_mode: "quiz",
869
  language_preference: mapLanguagePref(language),
870
  doc_type: docType,
871
- });
 
872
 
873
- const refs = (r.refs || [])
874
- .map((x: any) => {
875
- const a = x?.source_file ? String(x.source_file) : "";
876
- const b = x?.section ? String(x.section) : "";
877
- const s = `${a}${a && b ? " — " : ""}${b}`.trim();
878
- return s || null;
879
- })
880
- .filter(Boolean) as string[];
881
 
882
  const assistantMessage: Message = {
883
  id: (Date.now() + 1).toString(),
884
  role: "assistant",
885
- content: r.reply || "",
886
  timestamp: new Date(),
887
- references: refs.length ? refs : undefined,
 
888
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
889
  showNextButton: false,
890
  };
@@ -919,6 +953,8 @@ function App() {
919
  const handleStartQuiz = async () => {
920
  if (!user) return;
921
 
 
 
922
  setIsTyping(true);
923
  try {
924
  const docType = getCurrentDocTypeForChat();
@@ -928,23 +964,19 @@ function App() {
928
  language_preference: mapLanguagePref(language),
929
  doc_type: docType,
930
  learning_mode: "quiz",
931
- });
 
932
 
933
- const refs = (r.refs || [])
934
- .map((x: any) => {
935
- const a = x?.source_file ? String(x.source_file) : "";
936
- const b = x?.section ? String(x.section) : "";
937
- const s = `${a}${a && b ? " — " : ""}${b}`.trim();
938
- return s || null;
939
- })
940
- .filter(Boolean) as string[];
941
 
942
  const assistantMessage: Message = {
943
  id: Date.now().toString(),
944
  role: "assistant",
945
- content: r.reply || "",
946
  timestamp: new Date(),
947
- references: refs.length ? refs : undefined,
 
948
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
949
  showNextButton: false,
950
  };
@@ -974,7 +1006,6 @@ function App() {
974
  // =========================
975
  // File Upload (FIXED)
976
  // =========================
977
-
978
  const handleFileUpload = async (input: File[] | FileList | null | undefined) => {
979
  const files = Array.isArray(input) ? input : input ? Array.from(input) : [];
980
  if (!files.length) return;
@@ -985,6 +1016,8 @@ function App() {
985
 
986
  if (!user) return;
987
 
 
 
988
  for (const f of files) {
989
  const fp = `${f.name}::${f.size}::${f.lastModified}`;
990
  if (uploadedFingerprintsRef.current.has(fp)) continue;
@@ -994,8 +1027,9 @@ function App() {
994
  await apiUpload({
995
  user_id: user.email,
996
  doc_type: DOC_TYPE_MAP["other"] || "Other Course Document",
 
997
  file: f,
998
- });
999
  toast.success(`File uploaded: ${f.name}`);
1000
  } catch (e: any) {
1001
  toast.error(e?.message || `Upload failed: ${f.name}`);
@@ -1054,12 +1088,15 @@ function App() {
1054
  if (uploadedFingerprintsRef.current.has(fp)) return;
1055
  uploadedFingerprintsRef.current.add(fp);
1056
 
 
 
1057
  try {
1058
  await apiUpload({
1059
  user_id: user.email,
1060
  doc_type: DOC_TYPE_MAP[type] || "Other Course Document",
 
1061
  file: target,
1062
- });
1063
  toast.success("File uploaded to backend");
1064
  } catch (e: any) {
1065
  toast.error(e?.message || "Upload failed");
@@ -1523,7 +1560,6 @@ function App() {
1523
  onReviewActivity={handleReviewActivity}
1524
  currentUserId={user?.email}
1525
  docType={"Syllabus"}
1526
- // ✅ bio is still allowed to be updated by chat/Clare
1527
  onProfileBioUpdate={(bio) => updateUser({ bio })}
1528
  />
1529
  </div>
 
25
  energyPct,
26
  } from "./lib/reviewStar";
27
 
 
28
  export type MessageAttachmentKind = "pdf" | "ppt" | "doc" | "image" | "other";
29
 
30
  export interface MessageAttachment {
 
35
  fileType?: FileType; // syllabus / lecture-slides / ...
36
  }
37
 
38
+ type RefObj = { source_file: string; section?: string };
39
+
40
  export interface Message {
41
  id: string;
42
  role: "user" | "assistant";
43
  content: string;
44
  timestamp: Date;
45
+ // ✅ NEW: structured refs returned by backend
46
+ refs?: RefObj[];
47
+ // legacy string list (keep)
48
  references?: string[];
49
  sender?: GroupMember;
50
  showNextButton?: boolean;
51
 
52
+ // ✅ show files “with” the user message (metadata only)
53
  attachments?: MessageAttachment[];
54
 
55
  questionData?: {
 
62
  };
63
  }
64
 
 
 
65
  export interface User {
66
  // required identity
67
  name: string;
 
157
  return "Auto";
158
  }
159
 
160
+ // ✅ map UI course ids to backend course_id
161
+ const BACKEND_COURSE_ID_MAP: Record<string, string> = {
162
+ course1: "course_ist345",
163
+ course2: "course_ist345",
164
+ course3: "course_ist345",
165
+ course4: "course_ist345",
166
+ };
167
+
168
+ function getBackendCourseId(uiCourseId: string): string {
169
+ return BACKEND_COURSE_ID_MAP[uiCourseId] || "course_ist345";
170
+ }
171
+
172
+ function normalizeApiRefs(raw: any): RefObj[] {
173
+ const arr = Array.isArray(raw) ? raw : [];
174
+ return arr
175
+ .map((x) => {
176
+ // object form
177
+ if (x && typeof x === "object") {
178
+ const source = x.source_file ? String(x.source_file).trim() : "";
179
+ const section = x.section ? String(x.section).trim() : "";
180
+ if (!source && !section) return null;
181
+ return { source_file: source || "Unknown file", section: section || undefined };
182
+ }
183
+
184
+ // legacy string
185
+ if (typeof x === "string") {
186
+ const s = x.trim();
187
+ if (!s) return null;
188
+ const parts = s.split("—").map((p) => p.trim()).filter(Boolean);
189
+ if (parts.length >= 2) {
190
+ return { source_file: parts[0] || "Unknown file", section: parts.slice(1).join(" — ") || undefined };
191
+ }
192
+ return { source_file: s, section: undefined };
193
+ }
194
+
195
+ return null;
196
+ })
197
+ .filter(Boolean) as RefObj[];
198
+ }
199
+
200
+ function refsToLegacyStrings(refs: RefObj[]): string[] {
201
+ return (refs || [])
202
+ .map((r) => {
203
+ const a = (r.source_file || "").trim();
204
+ const b = (r.section || "").trim();
205
+ return b ? `${a} — ${b}` : a;
206
+ })
207
+ .filter(Boolean);
208
+ }
209
+
210
  // ✅ localStorage helpers for saved chats
211
  function savedChatsStorageKey(email: string) {
212
  return `saved_chats::${email}`;
 
706
 
707
  if (!hasText && !hasFiles) return;
708
 
709
+ const backendCourseId = getBackendCourseId(currentCourseId);
710
+
711
  const fileNames = hasFiles ? uploadedFiles.map((f) => f.file.name) : [];
712
  const fileLine = fileNames.length ? `Uploaded files: ${fileNames.join(", ")}` : "";
713
 
 
726
  ? content
727
  : `📎 Sent ${fileNames.length} file(s)\n${fileNames.map((n) => `- ${n}`).join("\n")}`;
728
 
729
+ // ✅ snapshot attachments at send-time
730
+ const attachmentsSnapshot: MessageAttachment[] = uploadedFiles.map((uf) => {
731
+ const lower = uf.file.name.toLowerCase();
732
+ const kind: MessageAttachmentKind =
733
+ lower.endsWith(".pdf")
734
+ ? "pdf"
735
+ : lower.endsWith(".ppt") || lower.endsWith(".pptx")
736
+ ? "ppt"
737
+ : lower.endsWith(".doc") || lower.endsWith(".docx")
738
+ ? "doc"
739
+ : [".jpg", ".jpeg", ".png", ".gif", ".webp"].some((e) => lower.endsWith(e))
740
+ ? "image"
741
+ : "other";
742
+
743
+ return {
744
+ name: uf.file.name,
745
+ size: uf.file.size,
746
+ kind,
747
+ fileType: uf.type,
748
+ };
749
+ });
750
+
 
751
  const userMessage: Message = {
752
  id: Date.now().toString(),
753
  role: "user",
 
773
  learning_mode: "quiz",
774
  language_preference: mapLanguagePref(language),
775
  doc_type: docType,
776
+ course_id: backendCourseId,
777
+ } as any);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
778
 
779
+ const refsObj = normalizeApiRefs((r as any).refs ?? (r as any).references);
780
+ const refsLegacy = refsToLegacyStrings(refsObj);
781
 
782
  const assistantMessage: Message = {
783
  id: (Date.now() + 1).toString(),
784
  role: "assistant",
785
+ content: (r as any).reply || "",
786
  timestamp: new Date(),
787
+ refs: refsObj.length ? refsObj : undefined,
788
+ references: refsLegacy.length ? refsLegacy : undefined,
789
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
790
  showNextButton: false,
791
  };
 
826
  learning_mode: learningMode,
827
  language_preference: mapLanguagePref(language),
828
  doc_type: docType,
829
+ course_id: backendCourseId,
830
+ } as any);
831
 
832
+ const refsObj = normalizeApiRefs((r as any).refs ?? (r as any).references);
833
+ const refsLegacy = refsToLegacyStrings(refsObj);
 
 
 
 
 
 
834
 
835
  const assistantMessage: Message = {
836
  id: (Date.now() + 1).toString(),
837
  role: "assistant",
838
+ content: (r as any).reply || "",
839
  timestamp: new Date(),
840
+ refs: refsObj.length ? refsObj : undefined,
841
+ references: refsLegacy.length ? refsLegacy : undefined,
842
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
843
  };
844
 
 
877
  const handleNextQuestion = async () => {
878
  if (!user) return;
879
 
880
+ const backendCourseId = getBackendCourseId(currentCourseId);
881
+
882
  const prompt = "Please give me another question of the same quiz style.";
883
  const sender: GroupMember = {
884
  id: user.email,
 
906
  learning_mode: "quiz",
907
  language_preference: mapLanguagePref(language),
908
  doc_type: docType,
909
+ course_id: backendCourseId,
910
+ } as any);
911
 
912
+ const refsObj = normalizeApiRefs((r as any).refs ?? (r as any).references);
913
+ const refsLegacy = refsToLegacyStrings(refsObj);
 
 
 
 
 
 
914
 
915
  const assistantMessage: Message = {
916
  id: (Date.now() + 1).toString(),
917
  role: "assistant",
918
+ content: (r as any).reply || "",
919
  timestamp: new Date(),
920
+ refs: refsObj.length ? refsObj : undefined,
921
+ references: refsLegacy.length ? refsLegacy : undefined,
922
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
923
  showNextButton: false,
924
  };
 
953
  const handleStartQuiz = async () => {
954
  if (!user) return;
955
 
956
+ const backendCourseId = getBackendCourseId(currentCourseId);
957
+
958
  setIsTyping(true);
959
  try {
960
  const docType = getCurrentDocTypeForChat();
 
964
  language_preference: mapLanguagePref(language),
965
  doc_type: docType,
966
  learning_mode: "quiz",
967
+ course_id: backendCourseId,
968
+ } as any);
969
 
970
+ const refsObj = normalizeApiRefs((r as any).refs ?? (r as any).references);
971
+ const refsLegacy = refsToLegacyStrings(refsObj);
 
 
 
 
 
 
972
 
973
  const assistantMessage: Message = {
974
  id: Date.now().toString(),
975
  role: "assistant",
976
+ content: (r as any).reply || "",
977
  timestamp: new Date(),
978
+ refs: refsObj.length ? refsObj : undefined,
979
+ references: refsLegacy.length ? refsLegacy : undefined,
980
  sender: spaceType === "group" ? groupMembers.find((m) => m.isAI) : undefined,
981
  showNextButton: false,
982
  };
 
1006
  // =========================
1007
  // File Upload (FIXED)
1008
  // =========================
 
1009
  const handleFileUpload = async (input: File[] | FileList | null | undefined) => {
1010
  const files = Array.isArray(input) ? input : input ? Array.from(input) : [];
1011
  if (!files.length) return;
 
1016
 
1017
  if (!user) return;
1018
 
1019
+ const backendCourseId = getBackendCourseId(currentCourseId);
1020
+
1021
  for (const f of files) {
1022
  const fp = `${f.name}::${f.size}::${f.lastModified}`;
1023
  if (uploadedFingerprintsRef.current.has(fp)) continue;
 
1027
  await apiUpload({
1028
  user_id: user.email,
1029
  doc_type: DOC_TYPE_MAP["other"] || "Other Course Document",
1030
+ course_id: backendCourseId,
1031
  file: f,
1032
+ } as any);
1033
  toast.success(`File uploaded: ${f.name}`);
1034
  } catch (e: any) {
1035
  toast.error(e?.message || `Upload failed: ${f.name}`);
 
1088
  if (uploadedFingerprintsRef.current.has(fp)) return;
1089
  uploadedFingerprintsRef.current.add(fp);
1090
 
1091
+ const backendCourseId = getBackendCourseId(currentCourseId);
1092
+
1093
  try {
1094
  await apiUpload({
1095
  user_id: user.email,
1096
  doc_type: DOC_TYPE_MAP[type] || "Other Course Document",
1097
+ course_id: backendCourseId,
1098
  file: target,
1099
+ } as any);
1100
  toast.success("File uploaded to backend");
1101
  } catch (e: any) {
1102
  toast.error(e?.message || "Upload failed");
 
1560
  onReviewActivity={handleReviewActivity}
1561
  currentUserId={user?.email}
1562
  docType={"Syllabus"}
 
1563
  onProfileBioUpdate={(bio) => updateUser({ bio })}
1564
  />
1565
  </div>