mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 05:39:12 +00:00
Improve rendering of the file objects and sort files by updated_date
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user