Merge pull request #182 from langflow-ai/101-design-sweep-polish-chunk-page
101 design sweep polish chunk page
This commit is contained in:
commit
0d22345769
2 changed files with 124 additions and 90 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,6 +1,6 @@
|
||||||
"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";
|
||||||
|
|
@ -13,9 +13,9 @@ import {
|
||||||
type File,
|
type File,
|
||||||
useGetSearchQuery,
|
useGetSearchQuery,
|
||||||
} from "../../api/queries/useGetSearchQuery";
|
} from "../../api/queries/useGetSearchQuery";
|
||||||
import { Label } from "@/components/ui/label";
|
// import { Label } from "@/components/ui/label";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
// import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Input } from "@/components/ui/input";
|
import { filterAccentClasses } from "@/components/knowledge-filter-panel";
|
||||||
|
|
||||||
const getFileTypeLabel = (mimetype: string) => {
|
const getFileTypeLabel = (mimetype: string) => {
|
||||||
if (mimetype === "application/pdf") return "PDF";
|
if (mimetype === "application/pdf") return "PDF";
|
||||||
|
|
@ -27,9 +27,10 @@ const getFileTypeLabel = (mimetype: string) => {
|
||||||
function ChunksPageContent() {
|
function ChunksPageContent() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
const { selectedFilter, setSelectedFilter, parsedFilterData, isPanelOpen } =
|
||||||
|
useKnowledgeFilter();
|
||||||
const { isMenuOpen } = useTask();
|
const { isMenuOpen } = useTask();
|
||||||
const { totalTopOffset } = useLayout();
|
const { totalTopOffset } = useLayout();
|
||||||
const { parsedFilterData, isPanelOpen } = useKnowledgeFilter();
|
|
||||||
|
|
||||||
const filename = searchParams.get("filename");
|
const filename = searchParams.get("filename");
|
||||||
const [chunks, setChunks] = useState<ChunkResult[]>([]);
|
const [chunks, setChunks] = useState<ChunkResult[]>([]);
|
||||||
|
|
@ -87,7 +88,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
|
||||||
|
|
@ -103,20 +106,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 (
|
||||||
|
|
@ -161,19 +164,38 @@ function ChunksPageContent() {
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-start mt-2">
|
<div className="flex flex-col items-start mt-2">
|
||||||
<div className="flex-1 flex items-center gap-2 w-full max-w-[616px] mb-8">
|
<div className="flex-1 flex items-center gap-2 w-full max-w-[640px]">
|
||||||
<Input
|
<div className="primary-input min-h-10 !flex items-center flex-nowrap focus-within:border-foreground transition-colors !p-[0.3rem]">
|
||||||
name="search-query"
|
{selectedFilter?.name && (
|
||||||
icon={!queryInputText.length ? <Search size={18} /> : null}
|
<div
|
||||||
id="search-query"
|
className={`flex items-center gap-1 h-full px-1.5 py-0.5 mr-1 rounded max-w-[25%] ${
|
||||||
type="text"
|
filterAccentClasses[parsedFilterData?.color || "zinc"]
|
||||||
defaultValue={parsedFilterData?.query}
|
}`}
|
||||||
value={queryInputText}
|
>
|
||||||
onChange={(e) => setQueryInputText(e.target.value)}
|
<span className="truncate">{selectedFilter?.name}</span>
|
||||||
placeholder="Search chunks..."
|
<X
|
||||||
/>
|
aria-label="Remove filter"
|
||||||
|
className="h-4 w-4 flex-shrink-0 cursor-pointer"
|
||||||
|
onClick={() => setSelectedFilter(null)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Search
|
||||||
|
className="h-4 w-4 ml-1 flex-shrink-0 text-placeholder-foreground"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
className="bg-transparent w-full h-full ml-2 focus:outline-none focus-visible:outline-none font-mono placeholder:font-mono"
|
||||||
|
name="search-query"
|
||||||
|
id="search-query"
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter your search query..."
|
||||||
|
onChange={(e) => setQueryInputText(e.target.value)}
|
||||||
|
value={queryInputText}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center pl-4 gap-2">
|
{/* <div className="flex items-center pl-4 gap-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="selectAllChunks"
|
id="selectAllChunks"
|
||||||
checked={selectAll}
|
checked={selectAll}
|
||||||
|
|
@ -187,7 +209,7 @@ function ChunksPageContent() {
|
||||||
>
|
>
|
||||||
Select all
|
Select all
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -205,10 +227,9 @@ 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>
|
||||||
|
|
@ -221,16 +242,16 @@ function ChunksPageContent() {
|
||||||
>
|
>
|
||||||
<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
|
||||||
|
|
@ -250,6 +271,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
|
||||||
|
|
@ -259,7 +284,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>
|
||||||
|
|
@ -269,24 +294,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>
|
||||||
|
|
@ -296,51 +326,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