From c7b213b36557e5df787ead4e8f090f485f08b6e0 Mon Sep 17 00:00:00 2001 From: Cole Goldsmith Date: Wed, 17 Sep 2025 14:33:59 -0500 Subject: [PATCH] replace knowledge table with ag grid --- Makefile | 8 + frontend/components/ui/input.tsx | 2 +- frontend/package-lock.json | 28 + frontend/package.json | 2 + frontend/src/app/knowledge/page.tsx | 479 ++++++++++++------ .../src/components/AgGrid/agGridStyles.css | 21 + .../AgGrid/registerAgGridModules.ts | 33 ++ 7 files changed, 427 insertions(+), 146 deletions(-) create mode 100644 frontend/src/components/AgGrid/agGridStyles.css create mode 100644 frontend/src/components/AgGrid/registerAgGridModules.ts diff --git a/Makefile b/Makefile index fe76467a..e8b08a1b 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,14 @@ infra: @echo " OpenSearch: http://localhost:9200" @echo " Dashboards: http://localhost:5601" +infra-cpu: + @echo "🔧 Starting infrastructure services only..." + docker-compose -f docker-compose-cpu.yml up -d opensearch dashboards langflow + @echo "✅ Infrastructure services started!" + @echo " Langflow: http://localhost:7860" + @echo " OpenSearch: http://localhost:9200" + @echo " Dashboards: http://localhost:5601" + # Container management stop: @echo "🛑 Stopping all containers..." diff --git a/frontend/components/ui/input.tsx b/frontend/components/ui/input.tsx index 5c14e593..3dc0b5f0 100644 --- a/frontend/components/ui/input.tsx +++ b/frontend/components/ui/input.tsx @@ -29,7 +29,7 @@ const Input = React.forwardRef( /> =0.4.0" } }, + "node_modules/ag-charts-types": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-12.2.0.tgz", + "integrity": "sha512-d2qQrQirt9wP36YW5HPuOvXsiajyiFnr1CTsoCbs02bavPDz7Lk2jHp64+waM4YKgXb3GN7gafbBI9Qgk33BmQ==" + }, + "node_modules/ag-grid-community": { + "version": "34.2.0", + "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-34.2.0.tgz", + "integrity": "sha512-peS7THEMYwpIrwLQHmkRxw/TlOnddD/F5A88RqlBxf8j+WqVYRWMOOhU5TqymGcha7z2oZ8IoL9ROl3gvtdEjg==", + "dependencies": { + "ag-charts-types": "12.2.0" + } + }, + "node_modules/ag-grid-react": { + "version": "34.2.0", + "resolved": "https://registry.npmjs.org/ag-grid-react/-/ag-grid-react-34.2.0.tgz", + "integrity": "sha512-dLKFw6hz75S0HLuZvtcwjm+gyiI4gXVzHEu7lWNafWAX0mb8DhogEOP5wbzAlsN6iCfi7bK/cgZImZFjenlqwg==", + "dependencies": { + "ag-grid-community": "34.2.0", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 185e3866..09dac477 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -26,6 +26,8 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/typography": "^0.5.16", "@tanstack/react-query": "^5.86.0", + "ag-grid-community": "^34.2.0", + "ag-grid-react": "^34.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/frontend/src/app/knowledge/page.tsx b/frontend/src/app/knowledge/page.tsx index 88b93671..0861df59 100644 --- a/frontend/src/app/knowledge/page.tsx +++ b/frontend/src/app/knowledge/page.tsx @@ -8,7 +8,14 @@ import { Loader2, Search, } from "lucide-react"; -import { type FormEvent, useCallback, useEffect, useState } from "react"; +import { AgGridReact, CustomCellRendererProps } from "ag-grid-react"; +import { + type FormEvent, + useCallback, + useEffect, + useState, + useRef, +} from "react"; import { SiGoogledrive } from "react-icons/si"; import { TbBrandOnedrive } from "react-icons/tb"; import { KnowledgeDropdown } from "@/components/knowledge-dropdown"; @@ -18,6 +25,9 @@ import { Input } from "@/components/ui/input"; import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context"; import { useTask } from "@/contexts/task-context"; import { type File, useGetSearchQuery } from "../api/queries/useGetSearchQuery"; +import { ColDef } from "ag-grid-community"; +import "@/components/AgGrid/registerAgGridModules"; +import "@/components/AgGrid/agGridStyles.css"; // Function to get the appropriate icon for a connector type function getSourceIcon(connectorType?: string) { @@ -64,11 +74,76 @@ function SearchPage() { } setQuery(queryInputText); }, - [queryInputText, refetchSearch, query], + [queryInputText, refetchSearch, query] ); const fileResults = data as File[]; + const gridRef = useRef(null); + + const [columnDefs] = useState[]>([ + { + field: "filename", + headerName: "Source", + cellRenderer: ({ data, value }: CustomCellRendererProps) => { + return ( +
+ {getSourceIcon(data?.connector_type)} + + {value} + +
+ ); + }, + }, + { + field: "size", + headerName: "Size", + valueFormatter: (params) => + params.value ? `${Math.round(params.value / 1024)} KB` : "-", + }, + { + field: "mimetype", + headerName: "Type", + }, + { + field: "owner", + headerName: "Owner", + valueFormatter: (params) => + params.value || + params.data?.owner_name || + params.data?.owner_email || + "—", + }, + + { + field: "chunkCount", + headerName: "Chunks", + }, + { + field: "avgScore", + headerName: "Avg score", + cellRenderer: ({ value }: CustomCellRendererProps) => { + return ( + + {value.toFixed(2)} + + ); + }, + }, + ]); + + const defaultColDef: ColDef = { + cellStyle: () => ({ + display: "flex", + alignItems: "center", + }), + initialFlex: 1, + minWidth: 100, + resizable: false, + suppressMovable: true, + }; + return (
+
+

Project Knowledge

+ +
{/* Search Input Area */}
@@ -100,7 +179,7 @@ function SearchPage() { /> -
- -
- - {/* Results Area */} -
-
- {fileResults.length === 0 && !isFetching ? ( -
+ {selectedFile ? ( + // Show chunks for selected file + <> +
+ + + Chunks from {selectedFile} + +
+ {fileResults + .filter((file) => file.filename === selectedFile) + .flatMap((file) => file.chunks) + .map((chunk, index) => ( +
+
+
+ + + {chunk.filename} + +
+ + {chunk.score.toFixed(2)} + +
+
+ {chunk.mimetype} • Page {chunk.page} +
+

+ {chunk.text} +

+
+ ))} + + ) : ( + setSelectedFile(params.data?.filename)} + noRowsOverlayComponent={() => ( +

No documents found @@ -128,143 +251,209 @@ function SearchPage() { Try adjusting your search terms

- ) : ( -
- {/* Results Count */} -
-
- {fileResults.length} file - {fileResults.length !== 1 ? "s" : ""} found -
-
- - {/* Results Display */} -
- {selectedFile ? ( - // Show chunks for selected file - <> -
- - - Chunks from {selectedFile} - -
- {fileResults - .filter((file) => file.filename === selectedFile) - .flatMap((file) => file.chunks) - .map((chunk, index) => ( -
-
-
- - - {chunk.filename} - -
- - {chunk.score.toFixed(2)} - -
-
- {chunk.mimetype} • Page {chunk.page} -
-

- {chunk.text} -

-
- ))} - - ) : ( - // Show files table -
- - - - - - - - - - - - - {fileResults.map((file) => ( - setSelectedFile(file.filename)} - > - - - - - - - - ))} - -
- Source - - Type - - Size - - Matching chunks - - Average score - - Owner -
-
- {getSourceIcon(file.connector_type)} - - {file.filename} - -
-
- {file.mimetype} - - {file.size - ? `${Math.round(file.size / 1024)} KB` - : "—"} - - {file.chunkCount} - - - {file.avgScore.toFixed(2)} - - - {file.owner_name || file.owner || "—"} -
-
- )} -
-
)} -
-
+ /> + )}
); + + // return ( + //
+ //
+ // {/* Search Input Area */} + //
+ //
+ // setQueryInputText(e.target.value)} + // placeholder="Search your documents..." + // className="flex-1 bg-muted/20 rounded-lg border border-border/50 px-4 py-3 h-12 focus-visible:ring-1 focus-visible:ring-ring" + // /> + // + //
+ // + //
+ //
+ //
+ + // {/* Results Area */} + //
+ //
+ // {fileResults.length === 0 && !isFetching ? ( + //
+ // + //

+ // No documents found + //

+ //

+ // Try adjusting your search terms + //

+ //
+ // ) : ( + //
+ // {/* Results Count */} + //
+ //
+ // {fileResults.length} file + // {fileResults.length !== 1 ? "s" : ""} found + //
+ //
+ + // {/* Results Display */} + //
+ // {selectedFile ? ( + // // Show chunks for selected file + // <> + //
+ // + // + // Chunks from {selectedFile} + // + //
+ // {fileResults + // .filter((file) => file.filename === selectedFile) + // .flatMap((file) => file.chunks) + // .map((chunk, index) => ( + //
+ //
+ //
+ // + // + // {chunk.filename} + // + //
+ // + // {chunk.score.toFixed(2)} + // + //
+ //
+ // {chunk.mimetype} • Page {chunk.page} + //
+ //

+ // {chunk.text} + //

+ //
+ // ))} + // + // ) : ( + // // Show files table + //
+ // + // + // + // + // + // + // + // + // + // + // + // + // {fileResults.map((file) => ( + // setSelectedFile(file.filename)} + // > + // + // + // + // + // + // + // + // ))} + // + //
+ // Source + // + // Type + // + // Size + // + // Matching chunks + // + // Average score + // + // Owner + //
+ //
+ // {getSourceIcon(file.connector_type)} + // + // {file.filename} + // + //
+ //
+ // {file.mimetype} + // + // {file.size + // ? `${Math.round(file.size / 1024)} KB` + // : "—"} + // + // {file.chunkCount} + // + // + // {file.avgScore.toFixed(2)} + // + // + // {file.owner_name || file.owner || "—"} + //
+ //
+ // )} + //
+ //
+ // )} + //
+ //
+ //
+ //
+ // ); } export default function ProtectedSearchPage() { diff --git a/frontend/src/components/AgGrid/agGridStyles.css b/frontend/src/components/AgGrid/agGridStyles.css new file mode 100644 index 00000000..b595e18c --- /dev/null +++ b/frontend/src/components/AgGrid/agGridStyles.css @@ -0,0 +1,21 @@ +body { + --ag-text-color: hsl(var(--muted-foreground)); + --ag-background-color: hsl(var(--background)); + --ag-header-background-color: hsl(var(--background)); + --ag-header-text-color: hsl(var(--muted-foreground)); + --ag-header-column-resize-handle-color: hsl(var(--border)); + --ag-header-row-border: hsl(var(--border)); + --ag-header-font-weight: var(--font-medium); + --ag-row-border: undefined; + --ag-row-hover-color: hsl(var(--muted)); + --ag-wrapper-border: none; + --ag-font-family: var(--font-sans); + + .ag-header { + border-bottom: 1px solid hsl(var(--border)); + margin-bottom: 0.5rem; + } + .ag-row { + cursor: pointer; + } +} diff --git a/frontend/src/components/AgGrid/registerAgGridModules.ts b/frontend/src/components/AgGrid/registerAgGridModules.ts new file mode 100644 index 00000000..da2c5280 --- /dev/null +++ b/frontend/src/components/AgGrid/registerAgGridModules.ts @@ -0,0 +1,33 @@ +import { + ModuleRegistry, + ValidationModule, + ColumnAutoSizeModule, + ColumnApiModule, + PaginationModule, + CellStyleModule, + QuickFilterModule, + ClientSideRowModelModule, + TextFilterModule, + DateFilterModule, + EventApiModule, + GridStateModule, + } from 'ag-grid-community'; + + // Importing necessary modules from ag-grid-community + // https://www.ag-grid.com/javascript-data-grid/modules/#selecting-modules + + ModuleRegistry.registerModules([ + ColumnAutoSizeModule, + ColumnApiModule, + PaginationModule, + CellStyleModule, + QuickFilterModule, + ClientSideRowModelModule, + TextFilterModule, + DateFilterModule, + EventApiModule, + GridStateModule, + // The ValidationModule adds helpful console warnings/errors that can help identify bad configuration during development. + ...(process.env.NODE_ENV !== 'production' ? [ValidationModule] : []), + ]); + \ No newline at end of file