diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 21fea5d1..98c57341 100755 Binary files a/frontend/bun.lockb and b/frontend/bun.lockb differ diff --git a/frontend/src/StateProvider.tsx b/frontend/src/StateProvider.tsx new file mode 100644 index 00000000..6bb45dfa --- /dev/null +++ b/frontend/src/StateProvider.tsx @@ -0,0 +1,89 @@ +import React, { createContext, useState } from "react" +import { useToast } from "./hooks/use-toast" +import { isSupportedFileType } from "./lib/common" + +// Define the context and its type +export const StateContext = createContext<{ + stagedFiles: File[] + setStagedFiles: React.Dispatch> + handleFileRemove: (index: number) => void + handleFileSelection: (event: React.ChangeEvent) => void + loading: boolean + setLoading: React.Dispatch> + query: string + setQuery: React.Dispatch> +} | null>(null) + +// Provider component +export const StateContextProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const [stagedFiles, setStagedFiles] = useState([]) + const [loading, setLoading] = useState(false) + const [query, setQuery] = useState("") + const { toast } = useToast() + + const handleFileSelection = (event: React.ChangeEvent) => { + const files = event?.target?.files + const validFiles: File[] = [] // Array to hold files that pass validation + + if (files) { + Array.from(files).forEach((file) => { + // File size check: 20 MB limit + const fileSizeInMB = file.size / (1024 * 1024) + if (fileSizeInMB > 20) { + toast({ + title: `File Too Large`, + description: `The file "${file.name}" exceeds the 20MB size limit. Please choose a smaller file.`, + variant: "destructive", + }) + } else if (!isSupportedFileType(file.type)) { + // Check for unsupported file types + toast({ + title: "File Type not supported", + description: `The file "${file.name}" is of type "${file.type}", which is not supported. Please upload a valid file type.`, + variant: "destructive", + }) + } else { + // If valid, add the file to the validFiles array + validFiles.push(file) + } + }) + } + + setStagedFiles((prev) => { + if (prev.length + validFiles.length > 5) { + toast({ + title: "File Limit Exceeded", + description: "You can only select up to 5 files.", + variant: "destructive", + }) + return prev // Keep the current files unchanged + } + return [...prev, ...validFiles] + }) + + event.target.value = "" + } + + function handleFileRemove(index: number) { + setStagedFiles((prev) => prev.filter((_, i) => i !== index)) + } + + return ( + + {children} + + ) +} diff --git a/frontend/src/components/ChatBox.tsx b/frontend/src/components/ChatBox.tsx index ffe35d45..8ba9aaa4 100644 --- a/frontend/src/components/ChatBox.tsx +++ b/frontend/src/components/ChatBox.tsx @@ -1,20 +1,44 @@ -import { ArrowRight, Globe, Paperclip } from "lucide-react" +import { + ArrowRight, + LoaderCircle, + File, + Trash2, + Globe, + Paperclip, +} from "lucide-react" import { useEffect, useRef } from "react" interface ChatBoxProps { query: string setQuery: (query: string) => void handleSend: (messageToSend: string) => void + stagedFiles: File[] + handleFileRemove: (index: number) => void + handleFileSelection: (event: React.ChangeEvent) => void + loading: boolean isStreaming?: boolean } +export const getFileTypeName = (fileType: string): string => { + if (fileType === "application/pdf") { + return "PDF" + } else { + return "" + } +} + export const ChatBox = ({ query, setQuery, handleSend, + stagedFiles, + handleFileRemove, + handleFileSelection, + loading, isStreaming = false, }: ChatBoxProps) => { const inputRef = useRef(null) + const fileInputRef = useRef(null) useEffect(() => { if (inputRef.current) { inputRef.current.focus() @@ -26,26 +50,63 @@ export const ChatBox = ({ }, []) return (
-
-