diff --git a/bun.lockb b/bun.lockb index b7d0b14..89a38a0 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 64b8d02..0cf7ef5 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@t3-oss/env-nextjs": "^0.10.1", "@tanstack/eslint-plugin-query": "^5.43.1", "@tanstack/react-query": "^5.45.0", + "@tanstack/react-table": "^8.19.2", "@vercel/postgres": "^0.8.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", diff --git a/src/app/whishper/page.tsx b/src/app/whishper/page.tsx index a757f36..45e5547 100644 --- a/src/app/whishper/page.tsx +++ b/src/app/whishper/page.tsx @@ -3,7 +3,7 @@ import { SignedIn, SignedOut } from "@clerk/nextjs"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useForm } from "react-hook-form"; -import { array, z } from "zod"; +import { z } from "zod"; import { Button } from "@/components/ui/button"; import { Form, @@ -27,6 +27,23 @@ import LoadingSpinner from "@/components/loading-spinner"; import type { WhishperRecordingsType } from "@/types"; import OpenInternalLink from "@/components/internal-link"; import OpenExternalLInk from "@/components/external-link"; +import AudioViewer from "@/components/audio-viewer"; +import { + type ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { ExternalLink } from "lucide-react"; +import { columns } from "@/types"; const queryClient = new QueryClient(); @@ -39,11 +56,27 @@ async function getWhishperRecordings(query: string) { const data = (await response.json()) as WhishperRecordingsType; const lowerCaseQuery = query.toLowerCase(); - return data.filter( - (item) => - item.fileName.toLowerCase().includes(lowerCaseQuery) || - item.result.text.toLowerCase().includes(lowerCaseQuery), - ); + const filteredAndScored = data + .filter( + (item) => + item.fileName.toLowerCase().includes(lowerCaseQuery) || + item.result.text.toLowerCase().includes(lowerCaseQuery), + ) + .map((item) => { + const fileNameOccurrences = ( + item.fileName.toLowerCase().match(new RegExp(lowerCaseQuery, "g")) ?? [] + ).length; + const textOccurrences = ( + item.result.text.toLowerCase().match(new RegExp(lowerCaseQuery, "g")) ?? + [] + ).length; + const score = fileNameOccurrences + textOccurrences; + return { ...item, score }; + }); + const sortedByScore = filteredAndScored.sort((a, b) => b.score - a.score); + + // Step 4: Return the sorted array without the score + return sortedByScore.map(({ ...item }) => item); } function SearchForm() { @@ -160,22 +193,76 @@ function RecordingsList() { return ( <>

Search Results

- + + data={WhishperRecordingsMap as WhishperRecordingsType[]} + /> ); } +interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; +} + +function DataTable({ + columns, + data, +}: DataTableProps) { + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + return ( +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ ); +} + export default function WhishperPage() { return (
diff --git a/src/components/audio-viewer.tsx b/src/components/audio-viewer.tsx new file mode 100644 index 0000000..25b5022 --- /dev/null +++ b/src/components/audio-viewer.tsx @@ -0,0 +1,8 @@ + + + + +export default function AudioViewer() + + return ; +} diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx new file mode 100644 index 0000000..973c6e5 --- /dev/null +++ b/src/components/ui/table.tsx @@ -0,0 +1,117 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ + +)) +Table.displayName = "Table" + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableHeader.displayName = "TableHeader" + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableBody.displayName = "TableBody" + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0 dark:bg-slate-800/50", + className + )} + {...props} + /> +)) +TableFooter.displayName = "TableFooter" + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableRow.displayName = "TableRow" + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableHead.displayName = "TableHead" + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableCell.displayName = "TableCell" + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableCaption.displayName = "TableCaption" + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/src/types/index.ts b/src/types/index.ts index bd1bb75..3eedd6c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,5 @@ +import type { ColumnDef } from "@tanstack/react-table"; + export type PaperlessSearchType = { total: number; documents: { @@ -104,3 +106,18 @@ export type AdviceAPIType = { advice: string; }; }; + +export const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: "Name", + }, + { + accessorKey: "status", + header: "Status", + }, + { + accessorKey: "link", + header: "Link", + }, +];