Improve rendering of the file objects and sort files by updated_date

This commit is contained in:
sabaimran
2025-01-10 18:18:15 -08:00
parent 454a752071
commit f2c6ce2435
2 changed files with 131 additions and 95 deletions

View File

@@ -54,6 +54,7 @@ import {
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogDescription,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
@@ -213,9 +214,6 @@ const UploadFiles: React.FC<{
onClose: () => void; onClose: () => void;
setUploadedFiles: (files: string[]) => void; setUploadedFiles: (files: string[]) => void;
}> = ({ onClose, setUploadedFiles }) => { }> = ({ onClose, setUploadedFiles }) => {
const [syncedFiles, setSyncedFiles] = useState<string[]>([]);
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
const [searchQuery, setSearchQuery] = useState("");
const [isDragAndDropping, setIsDragAndDropping] = useState(false); const [isDragAndDropping, setIsDragAndDropping] = useState(false);
const [warning, setWarning] = useState<string | null>(null); const [warning, setWarning] = useState<string | null>(null);
@@ -241,10 +239,6 @@ const UploadFiles: React.FC<{
} }
}, [uploading]); }, [uploading]);
const filteredFiles = syncedFiles.filter((file) =>
file.toLowerCase().includes(searchQuery.toLowerCase()),
);
function handleDragOver(event: React.DragEvent<HTMLDivElement>) { function handleDragOver(event: React.DragEvent<HTMLDivElement>) {
event.preventDefault(); event.preventDefault();
setIsDragAndDropping(true); setIsDragAndDropping(true);
@@ -281,47 +275,94 @@ const UploadFiles: React.FC<{
} }
return ( return (
<div <Dialog>
className={`flex flex-col h-full`} <DialogTrigger>
onDragOver={handleDragOver} <Button variant={"secondary"} className="mt-4">
onDragLeave={handleDragLeave} Add Documents
onDrop={handleDragAndDropFiles} </Button>
onClick={openFileInput} </DialogTrigger>
> <DialogContent>
<input <DialogHeader>
type="file" <DialogTitle>Build Knowledge Base</DialogTitle>
multiple <DialogDescription>
ref={fileInputRef} Adding files to your Khoj knowledge base allows your AI to search through
style={{ display: "none" }} your own documents. This helps you get personalized answers, grounded in
onChange={handleFileChange} your own data.
/> </DialogDescription>
<div className="flex-none p-4"> </DialogHeader>
{uploading && ( <div
<Progress className={`flex flex-col h-full`}
indicatorColor="bg-slate-500" onDragOver={handleDragOver}
className="w-full h-2 rounded-full" onDragLeave={handleDragLeave}
value={progressValue} onDrop={handleDragAndDropFiles}
onClick={openFileInput}
>
<input
type="file"
multiple
ref={fileInputRef}
style={{ display: "none" }}
onChange={handleFileChange}
/> />
)} <div className="flex-none p-4">
</div> {uploading && (
<div <Progress
className={`flex-none p-4 bg-secondary border-b ${isDragAndDropping ? "animate-pulse" : ""} rounded-lg`} indicatorColor="bg-slate-500"
> className="w-full h-2 rounded-full"
<div className="flex items-center justify-center w-full h-32 border-2 border-dashed border-gray-300 rounded-lg"> value={progressValue}
{isDragAndDropping ? ( />
<div className="flex items-center justify-center w-full h-full"> )}
<Waveform className="h-6 w-6 mr-2" /> </div>
<span>Drop files to upload</span> {warning && (
</div> <div className="flex-none p-4 border-b rounded-lg">
) : ( <div className="flex items-center gap-2">
<div className="flex items-center justify-center w-full h-full"> <Lightbulb className="h-6 w-6" />
<Plus className="h-6 w-6 mr-2" /> <span>{warning}</span>
<span>Drag and drop files here</span> </div>
<Button
onClick={() => setWarning(null)}
className="mt-2"
variant={"ghost"}
>
Dismiss
</Button>
</div> </div>
)} )}
{error && (
<div className="flex-none p-4 border-b rounded-lg">
<div className="flex items-center gap-2">
<Lightbulb className="h-6 w-6" />
<span>{error}</span>
</div>
<Button
onClick={() => setError(null)}
className="mt-2"
variant={"ghost"}
>
Dismiss
</Button>
</div>
)}
<div
className={`flex-none p-4 bg-secondary border-b ${isDragAndDropping ? "animate-pulse" : ""} rounded-lg`}
>
<div className="flex items-center justify-center w-full h-32 border-2 border-dashed border-gray-300 rounded-lg">
{isDragAndDropping ? (
<div className="flex items-center justify-center w-full h-full">
<Waveform className="h-6 w-6 mr-2" />
<span>Drop files to upload</span>
</div>
) : (
<div className="flex items-center justify-center w-full h-full">
<Plus className="h-6 w-6 mr-2" />
<span>Drag and drop files here</span>
</div>
)}
</div>
</div>
</div> </div>
</div> </DialogContent>
</div> </Dialog>
); );
}; };
@@ -598,12 +639,51 @@ export default function Search() {
> >
<CardHeader className="p-2"> <CardHeader className="p-2">
<CardTitle <CardTitle
className="flex items-center gap-2" className="flex items-center gap-2 justify-between"
title={file.file_name} title={file.file_name}
> >
<div className="text-sm font-medium truncate hover:text-clip hover:whitespace-normal"> <Dialog>
{file.file_name.split("/").pop()} <DialogTrigger asChild>
</div> <div
className="text-sm font-medium truncate hover:text-clip hover:whitespace-normal cursor-pointer"
onClick={() => {
setSelectedFileFullText(
null,
);
setSelectedFile(
file.file_name,
);
}}
>
{file.file_name
.split("/")
.pop()}
</div>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
{file.file_name
.split("/")
.pop()}
</DialogTitle>
</DialogHeader>
<ScrollArea className="h-[50vh]">
<p className="whitespace-pre-wrap break-words text-sm font-normal">
{!selectedFileFullText && (
<InlineLoading
className="mt-4"
message={
"Loading"
}
iconClassName="h-5 w-5"
/>
)}
{selectedFileFullText}
</p>
</ScrollArea>
</DialogContent>
</Dialog>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger> <DropdownMenuTrigger>
<Button variant={"ghost"}> <Button variant={"ghost"}>
@@ -656,57 +736,13 @@ export default function Search() {
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem className="p-0">
<Dialog>
<DialogTrigger>
<Button
variant={
"ghost"
}
className="flex items-center gap-2 p-1 text-sm"
onClick={() => {
setSelectedFileFullText(
null,
);
setSelectedFile(
file.file_name,
);
}}
>
<ArrowsOutSimple className="h-4 w-4" />
<span className="text-xs">
View Full
Text
</span>
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
{file.file_name
.split(
"/",
)
.pop()}
</DialogTitle>
</DialogHeader>
<ScrollArea className="h-[50vh]">
<p className="whitespace-pre-wrap break-words text-sm font-normal">
{
selectedFileFullText
}
</p>
</ScrollArea>
</DialogContent>
</Dialog>
</DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="p-2"> <CardContent className="p-2">
<ScrollArea className="h-24"> <ScrollArea className="h-24 bg-background rounded-lg">
<p className="whitespace-pre-wrap break-words text-sm font-normal text-muted-foreground p-2 rounded-lg bg-background"> <p className="whitespace-pre-wrap break-words text-sm font-normal text-muted-foreground p-2 h-full">
{file.raw_text.slice(0, 100)}... {file.raw_text.slice(0, 100)}...
</p> </p>
</ScrollArea> </ScrollArea>

View File

@@ -1474,7 +1474,7 @@ class FileObjectAdapters:
@staticmethod @staticmethod
@arequire_valid_user @arequire_valid_user
async def aget_all_file_objects(user: KhojUser): async def aget_all_file_objects(user: KhojUser):
return await sync_to_async(list)(FileObject.objects.filter(user=user)) return await sync_to_async(list)(FileObject.objects.filter(user=user).order_by("-updated_at"))
@staticmethod @staticmethod
@arequire_valid_user @arequire_valid_user