Merge branch 'main' into feat/knowledge-page-sweep
This commit is contained in:
commit
87a9fb8440
2 changed files with 119 additions and 114 deletions
|
|
@ -29,6 +29,7 @@ export interface ChunkResult {
|
||||||
owner_email?: string;
|
owner_email?: string;
|
||||||
file_size?: number;
|
file_size?: number;
|
||||||
connector_type?: string;
|
connector_type?: string;
|
||||||
|
index?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface File {
|
export interface File {
|
||||||
|
|
@ -55,7 +56,7 @@ export interface File {
|
||||||
export const useGetSearchQuery = (
|
export const useGetSearchQuery = (
|
||||||
query: string,
|
query: string,
|
||||||
queryData?: ParsedQueryData | null,
|
queryData?: ParsedQueryData | null,
|
||||||
options?: Omit<UseQueryOptions, "queryKey" | "queryFn">,
|
options?: Omit<UseQueryOptions, "queryKey" | "queryFn">
|
||||||
) => {
|
) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
|
@ -184,7 +185,7 @@ export const useGetSearchQuery = (
|
||||||
queryFn: getFiles,
|
queryFn: getFiles,
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
queryClient,
|
queryClient
|
||||||
);
|
);
|
||||||
|
|
||||||
return queryResult;
|
return queryResult;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ArrowLeft, Check, Copy, Loader2, Search } from "lucide-react";
|
import { ArrowLeft, Check, Copy, Loader2, Search, X } from "lucide-react";
|
||||||
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
|
import { Suspense, useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { ProtectedRoute } from "@/components/protected-route";
|
import { ProtectedRoute } from "@/components/protected-route";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
|
||||||
|
import { useTask } from "@/contexts/task-context";
|
||||||
import {
|
import {
|
||||||
type ChunkResult,
|
type ChunkResult,
|
||||||
type File,
|
type File,
|
||||||
useGetSearchQuery,
|
useGetSearchQuery,
|
||||||
} from "../../api/queries/useGetSearchQuery";
|
} from "../../api/queries/useGetSearchQuery";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
// import { Label } from "@/components/ui/label";
|
||||||
|
// import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { KnowledgeSearchInput } from "@/components/knowledge-search-input";
|
import { KnowledgeSearchInput } from "@/components/knowledge-search-input";
|
||||||
|
|
||||||
const getFileTypeLabel = (mimetype: string) => {
|
const getFileTypeLabel = (mimetype: string) => {
|
||||||
|
|
@ -25,11 +27,12 @@ function ChunksPageContent() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const { parsedFilterData, queryOverride } = useKnowledgeFilter();
|
const { parsedFilterData, queryOverride } = useKnowledgeFilter();
|
||||||
|
|
||||||
const filename = searchParams.get("filename");
|
const filename = searchParams.get("filename");
|
||||||
const [chunks, setChunks] = useState<ChunkResult[]>([]);
|
const [chunks, setChunks] = useState<ChunkResult[]>([]);
|
||||||
|
const [chunksFilteredByQuery, setChunksFilteredByQuery] = useState<
|
||||||
const [selectedChunks, setSelectedChunks] = useState<Set<number>>(new Set());
|
ChunkResult[]
|
||||||
|
>([]);
|
||||||
|
// const [selectedChunks, setSelectedChunks] = useState<Set<number>>(new Set());
|
||||||
const [activeCopiedChunkIndex, setActiveCopiedChunkIndex] = useState<
|
const [activeCopiedChunkIndex, setActiveCopiedChunkIndex] = useState<
|
||||||
number | null
|
number | null
|
||||||
>(null);
|
>(null);
|
||||||
|
|
@ -50,18 +53,6 @@ function ChunksPageContent() {
|
||||||
parsedFilterData
|
parsedFilterData
|
||||||
);
|
);
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (queryInputText === "") {
|
|
||||||
// setChunksFilteredByQuery(chunks);
|
|
||||||
// } else {
|
|
||||||
// setChunksFilteredByQuery(
|
|
||||||
// chunks.filter((chunk) =>
|
|
||||||
// chunk.text.toLowerCase().includes(queryInputText.toLowerCase())
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }, [queryInputText, chunks]);
|
|
||||||
|
|
||||||
const handleCopy = useCallback((text: string, index: number) => {
|
const handleCopy = useCallback((text: string, index: number) => {
|
||||||
// Trim whitespace and remove new lines/tabs for cleaner copy
|
// Trim whitespace and remove new lines/tabs for cleaner copy
|
||||||
navigator.clipboard.writeText(text.trim().replace(/[\n\r\t]/gm, ""));
|
navigator.clipboard.writeText(text.trim().replace(/[\n\r\t]/gm, ""));
|
||||||
|
|
@ -80,7 +71,9 @@ function ChunksPageContent() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setChunks(fileData?.chunks || []);
|
setChunks(
|
||||||
|
fileData?.chunks?.map((chunk, i) => ({ ...chunk, index: i + 1 })) || []
|
||||||
|
);
|
||||||
}, [data, filename]);
|
}, [data, filename]);
|
||||||
|
|
||||||
// Set selected state for all checkboxes when selectAll changes
|
// Set selected state for all checkboxes when selectAll changes
|
||||||
|
|
@ -96,20 +89,20 @@ function ChunksPageContent() {
|
||||||
router.push("/knowledge");
|
router.push("/knowledge");
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|
||||||
const handleChunkCardCheckboxChange = useCallback(
|
// const handleChunkCardCheckboxChange = useCallback(
|
||||||
(index: number) => {
|
// (index: number) => {
|
||||||
setSelectedChunks((prevSelected) => {
|
// setSelectedChunks((prevSelected) => {
|
||||||
const newSelected = new Set(prevSelected);
|
// const newSelected = new Set(prevSelected);
|
||||||
if (newSelected.has(index)) {
|
// if (newSelected.has(index)) {
|
||||||
newSelected.delete(index);
|
// newSelected.delete(index);
|
||||||
} else {
|
// } else {
|
||||||
newSelected.add(index);
|
// newSelected.add(index);
|
||||||
}
|
// }
|
||||||
return newSelected;
|
// return newSelected;
|
||||||
});
|
// });
|
||||||
},
|
// },
|
||||||
[setSelectedChunks]
|
// [setSelectedChunks]
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -126,16 +119,16 @@ function ChunksPageContent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex">
|
<div className="flex flex-col h-full">
|
||||||
<div className="flex-1 flex flex-col min-h-0">
|
<div className="flex flex-col h-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex flex-col mb-6 gap-6">
|
<div className="flex flex-col mb-6">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex items-center gap-3 mb-6">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={handleBack}
|
onClick={handleBack}
|
||||||
size="sm"
|
size="sm"
|
||||||
className="max-h-8 max-w-8 -m-2 mr-1"
|
className="max-w-8 max-h-8 -m-2"
|
||||||
>
|
>
|
||||||
<ArrowLeft size={24} />
|
<ArrowLeft size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -144,12 +137,28 @@ function ChunksPageContent() {
|
||||||
{filename.replace(/\.[^/.]+$/, "")}
|
{filename.replace(/\.[^/.]+$/, "")}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
{/* Search input */}
|
<div className="flex flex-1">
|
||||||
<KnowledgeSearchInput />
|
<KnowledgeSearchInput />
|
||||||
|
{/* <div className="flex items-center pl-4 gap-2">
|
||||||
|
<Checkbox
|
||||||
|
id="selectAllChunks"
|
||||||
|
checked={selectAll}
|
||||||
|
onCheckedChange={(handleSelectAll) =>
|
||||||
|
setSelectAll(!!handleSelectAll)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
htmlFor="selectAllChunks"
|
||||||
|
className="font-medium text-muted-foreground whitespace-nowrap cursor-pointer"
|
||||||
|
>
|
||||||
|
Select all
|
||||||
|
</Label>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Area - matches knowledge page structure */}
|
{/* Content Area - matches knowledge page structure */}
|
||||||
<div className="flex-1 pr-6">
|
<div className="flex-1 overflow-auto pr-6">
|
||||||
{isFetching ? (
|
{isFetching ? (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
|
|
@ -162,48 +171,31 @@ function ChunksPageContent() {
|
||||||
) : chunks.length === 0 ? (
|
) : chunks.length === 0 ? (
|
||||||
<div className="flex items-center justify-center h-64">
|
<div className="flex items-center justify-center h-64">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Search className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
|
<p className="text-xl font-semibold mb-2">No knowledge</p>
|
||||||
<p className="text-lg text-muted-foreground">No chunks found</p>
|
<p className="text-sm text-secondary-foreground">
|
||||||
<p className="text-sm text-muted-foreground/70 mt-2">
|
Clear the knowledge filter or return to the knowledge page
|
||||||
This file may not have been indexed yet
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4 pb-6">
|
<div className="space-y-4 pb-6">
|
||||||
{/* TODO - add chunk selection when sync and delete are ready */}
|
{chunksFilteredByQuery.map((chunk, index) => (
|
||||||
{/* <div className="flex items-center pl-4 gap-2">
|
|
||||||
<Checkbox
|
|
||||||
id="selectAllChunks"
|
|
||||||
checked={selectAll}
|
|
||||||
onCheckedChange={(handleSelectAll) =>
|
|
||||||
setSelectAll(!!handleSelectAll)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Label
|
|
||||||
htmlFor="selectAllChunks"
|
|
||||||
className="font-medium text-muted-foreground whitespace-nowrap cursor-pointer"
|
|
||||||
>
|
|
||||||
Select all
|
|
||||||
</Label>
|
|
||||||
</div> */}
|
|
||||||
{chunks.map((chunk, index) => (
|
|
||||||
<div
|
<div
|
||||||
key={chunk.filename + index}
|
key={chunk.filename + index}
|
||||||
className="bg-muted rounded-lg p-4 border border-border/50"
|
className="bg-muted rounded-lg p-4 border border-border/50"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div>
|
{/* <div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={selectedChunks.has(index)}
|
checked={selectedChunks.has(index)}
|
||||||
onCheckedChange={() =>
|
onCheckedChange={() =>
|
||||||
handleChunkCardCheckboxChange(index)
|
handleChunkCardCheckboxChange(index)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> */}
|
||||||
<span className="text-sm font-bold">
|
<span className="text-sm font-bold">
|
||||||
Chunk {chunk.page}
|
Chunk {chunk.index}
|
||||||
</span>
|
</span>
|
||||||
<span className="bg-background p-1 rounded text-xs text-muted-foreground/70">
|
<span className="bg-background p-1 rounded text-xs text-muted-foreground/70">
|
||||||
{chunk.text.length} chars
|
{chunk.text.length} chars
|
||||||
|
|
@ -223,6 +215,10 @@ function ChunksPageContent() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<span className="bg-background p-1 rounded text-xs text-muted-foreground/70">
|
||||||
|
{chunk.score.toFixed(2)} score
|
||||||
|
</span>
|
||||||
|
|
||||||
{/* TODO: Update to use active toggle */}
|
{/* TODO: Update to use active toggle */}
|
||||||
{/* <span className="px-2 py-1 text-green-500">
|
{/* <span className="px-2 py-1 text-green-500">
|
||||||
<Switch
|
<Switch
|
||||||
|
|
@ -232,7 +228,7 @@ function ChunksPageContent() {
|
||||||
Active
|
Active
|
||||||
</span> */}
|
</span> */}
|
||||||
</div>
|
</div>
|
||||||
<blockquote className="text-sm text-muted-foreground leading-relaxed border-l-2 border-input ml-1.5 pl-4">
|
<blockquote className="text-sm text-muted-foreground leading-relaxed ml-1.5">
|
||||||
{chunk.text}
|
{chunk.text}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -242,24 +238,29 @@ function ChunksPageContent() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Right panel - Summary (TODO), Technical details, */}
|
{/* Right panel - Summary (TODO), Technical details, */}
|
||||||
<div className="w-[320px] py-20 px-2">
|
{chunks.length > 0 && (
|
||||||
<div className="mb-8">
|
<div className="w-[320px] py-20 px-2">
|
||||||
<h2 className="text-xl font-semibold mt-3 mb-4">Technical details</h2>
|
<div className="mb-8">
|
||||||
<dl>
|
<h2 className="text-xl font-semibold mt-3 mb-4">
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
Technical details
|
||||||
<dt className="text-sm/6 text-muted-foreground">Total chunks</dt>
|
</h2>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dl>
|
||||||
{chunks.length}
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
</dd>
|
<dt className="text-sm/6 text-muted-foreground">
|
||||||
</div>
|
Total chunks
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
</dt>
|
||||||
<dt className="text-sm/6 text-muted-foreground">Avg length</dt>
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
{chunks.length}
|
||||||
{averageChunkLength.toFixed(0)} chars
|
</dd>
|
||||||
</dd>
|
</div>
|
||||||
</div>
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
{/* TODO: Uncomment after data is available */}
|
<dt className="text-sm/6 text-muted-foreground">Avg length</dt>
|
||||||
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
|
{averageChunkLength.toFixed(0)} chars
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
{/* TODO: Uncomment after data is available */}
|
||||||
|
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Process time</dt>
|
<dt className="text-sm/6 text-muted-foreground">Process time</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
</dd>
|
</dd>
|
||||||
|
|
@ -269,51 +270,54 @@ function ChunksPageContent() {
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
</dd>
|
</dd>
|
||||||
</div> */}
|
</div> */}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h2 className="text-xl font-semibold mt-2 mb-3">Original document</h2>
|
<h2 className="text-xl font-semibold mt-2 mb-3">
|
||||||
<dl>
|
Original document
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
</h2>
|
||||||
|
<dl>
|
||||||
|
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Name</dt>
|
<dt className="text-sm/6 text-muted-foreground">Name</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
{fileData?.filename}
|
{fileData?.filename}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div> */}
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Type</dt>
|
<dt className="text-sm/6 text-muted-foreground">Type</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
{fileData ? getFileTypeLabel(fileData.mimetype) : "Unknown"}
|
{fileData ? getFileTypeLabel(fileData.mimetype) : "Unknown"}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Size</dt>
|
<dt className="text-sm/6 text-muted-foreground">Size</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
{fileData?.size
|
{fileData?.size
|
||||||
? `${Math.round(fileData.size / 1024)} KB`
|
? `${Math.round(fileData.size / 1024)} KB`
|
||||||
: "Unknown"}
|
: "Unknown"}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Uploaded</dt>
|
<dt className="text-sm/6 text-muted-foreground">Uploaded</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
N/A
|
N/A
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div> */}
|
||||||
{/* TODO: Uncomment after data is available */}
|
{/* TODO: Uncomment after data is available */}
|
||||||
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Source</dt>
|
<dt className="text-sm/6 text-muted-foreground">Source</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0"></dd>
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0"></dd>
|
||||||
</div> */}
|
</div> */}
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
{/* <div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0 mb-2.5">
|
||||||
<dt className="text-sm/6 text-muted-foreground">Updated</dt>
|
<dt className="text-sm/6 text-muted-foreground">Updated</dt>
|
||||||
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
<dd className="mt-1 text-sm/6 text-gray-100 sm:col-span-2 sm:mt-0">
|
||||||
N/A
|
N/A
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div> */}
|
||||||
</dl>
|
</dl>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue