Skip to content

Commit

Permalink
added search
Browse files Browse the repository at this point in the history
  • Loading branch information
vwh committed Jul 31, 2024
1 parent 8ba80c6 commit 5eaaaef
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 61 deletions.
8 changes: 4 additions & 4 deletions src/components/page-select.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import useSQLiteStore from "@/store/useSQLiteStore";
import { Button } from "./ui/button";
import { ChevronRightIcon, ChevronLeftIcon } from "lucide-react";

interface PageSelectProps {
page: number;
setPage: React.Dispatch<React.SetStateAction<number>>;
rowsPerPage: number;
rowCount: number;
}

export default function PageSelect({
page,
setPage,
rowsPerPage,
rowCount
rowsPerPage
}: PageSelectProps) {
const totalPages = Math.ceil(rowCount / rowsPerPage);
const { totalRows } = useSQLiteStore();
const totalPages = Math.ceil(totalRows / rowsPerPage);
const currentPage = Math.floor(page / rowsPerPage) + 1;

const nextPage = () => {
Expand Down
84 changes: 60 additions & 24 deletions src/components/table-data.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import useSQLiteStore from "@/store/useSQLiteStore";
import React, { useMemo } from "react";
import React, { useMemo, useEffect, useState } from "react";

import type { TableInfo, TableRow } from "@/types";
import { dateFormats } from "@/lib/date-format";

import { Input } from "./ui/input";
import { HoverCard, HoverCardContent, HoverCardTrigger } from "./ui/hover-card";
import {
Table,
Expand All @@ -13,6 +14,7 @@ import {
TableHeader,
TableRow as TTableRow
} from "./ui/table";
import ErrorMessage from "./error";

import {
KeyRoundIcon,
Expand Down Expand Up @@ -47,24 +49,28 @@ const ColumnIcon: React.FC<{ columnSchema: ColumnSchema }> = React.memo(
}
);

const TableHeadCell: React.FC<{ col: string; columnSchema: ColumnSchema }> =
React.memo(({ col, columnSchema }) => (
<TableHead>
<HoverCard>
<HoverCardTrigger asChild>
<span className="cursor-pointer hover:underline">
<div className="flex gap-1">
{col}
<ColumnIcon columnSchema={columnSchema} />
</div>
</span>
</HoverCardTrigger>
<HoverCardContent side="bottom" align="start">
{columnSchema?.type || "Unknown"}
</HoverCardContent>
</HoverCard>
</TableHead>
));
const TableHeadCell: React.FC<{
col: string;
columnSchema: ColumnSchema;
children?: React.ReactNode;
}> = React.memo(({ col, columnSchema, children }) => (
<TableHead className="py-2">
<HoverCard>
<HoverCardTrigger asChild>
<span className="cursor-pointer hover:underline">
<div className="flex gap-1">
{col}
<ColumnIcon columnSchema={columnSchema} />
</div>
{children}
</span>
</HoverCardTrigger>
<HoverCardContent side="bottom" align="start">
{columnSchema?.type || "Unknown"}
</HoverCardContent>
</HoverCard>
</TableHead>
));

const TableBodyCell: React.FC<{ value: any; dataType?: string }> = React.memo(
({ value, dataType }) => {
Expand All @@ -87,13 +93,36 @@ const TableBodyCell: React.FC<{ value: any; dataType?: string }> = React.memo(
return <TableCell dataType={dataType}>{renderCellContent()}</TableCell>;
}
);
function TableHeadFilter({ col }: { col: string }) {
const { appendToFilters, selectedTable } = useSQLiteStore();
const [inputValue, setInputValue] = useState("");

const onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value);
appendToFilters(col, e.target.value);
};

useEffect(() => {
setInputValue("");
}, [selectedTable]);

return (
<Input
value={inputValue}
onChange={onInputChange}
className="w-full"
placeholder="Filter"
/>
);
}

export default function DBTableComponent({
data,
columns,
tableName,
tableSchemas
}: DBTableComponentProps) {
console.log("DEBUG: columns", columns, typeof columns);
const tableHead = useMemo(
() => (
<TableHeader>
Expand All @@ -103,7 +132,9 @@ export default function DBTableComponent({
key={index}
col={col}
columnSchema={tableSchemas[tableName][col]}
/>
>
<TableHeadFilter col={col} />
</TableHeadCell>
))}
</TTableRow>
</TableHeader>
Expand Down Expand Up @@ -131,9 +162,14 @@ export default function DBTableComponent({
);

return (
<Table>
{tableHead}
{tableBody}
</Table>
<>
<Table>
{tableHead}
{data.length > 0 && tableBody}
</Table>
{data.length === 0 && (
<ErrorMessage>{tableName} return no data</ErrorMessage>
)}
</>
);
}
64 changes: 37 additions & 27 deletions src/components/table.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useCallback, useEffect } from "react";
import { useMemo, useCallback, useEffect, useState } from "react";
import useSQLiteStore from "@/store/useSQLiteStore";
import { useQueryData } from "@/hooks/useQueryData";
import { usePagination } from "@/hooks/usePagination";
Expand Down Expand Up @@ -33,7 +33,9 @@ export default function DBTable() {
customQuery,
setCustomQuery,
expandPage,
setExpandPage
setExpandPage,
filters,
setFilters
} = useSQLiteStore();

const { page, setPage, rowsPerPage } = usePagination(rowPerPageOrAuto);
Expand All @@ -43,18 +45,22 @@ export default function DBTable() {
[tables, selectedTable]
);

const rowCount = useMemo(
() => tables[parseInt(selectedTable)]?.count || 0,
[tables, selectedTable]
);

const { data, columns, isQueryLoading, handleCustomQuery } = useQueryData(
tableName,
rowsPerPage,
page,
isCustomQuery
);

// State to keep a persistent copy of columns
const [savedColumns, setSavedColumns] = useState<string[]>([]);

useEffect(() => {
if (columns.length > 0) {
setSavedColumns(columns);
}
}, [tableName, columns]);

const handleResetQuery = useCallback(() => {
setQueryError(null);
setCustomQuery("");
Expand All @@ -68,6 +74,7 @@ export default function DBTable() {

useEffect(() => {
setPage(0);
setFilters({});
}, [selectedTable]);

const renderQueryInput = useMemo(
Expand Down Expand Up @@ -113,20 +120,28 @@ export default function DBTable() {

const renderTableContent = useMemo(() => {
if (isQueryLoading) return <Loading>Loading {tableName}</Loading>;
if (data.length > 0) {
return (
<div className="rounded border">
<DBTableComponent
data={data}
columns={columns}
tableName={tableName}
tableSchemas={tableSchemas}
/>
</div>
);
}
return <ErrorMessage>{`Table ${tableName} is empty`}</ErrorMessage>;
}, [isQueryLoading, data, columns, tableName, tableSchemas]);

return (
<div className="rounded border">
<DBTableComponent
data={data}
columns={savedColumns.length > 0 ? savedColumns : columns}
tableName={tableName}
tableSchemas={tableSchemas}
/>
</div>
);

return <ErrorMessage>Table {tableName} is empty</ErrorMessage>;
}, [
isQueryLoading,
data,
columns,
tableName,
tableSchemas,
filters,
savedColumns
]);

return (
<div className="flex flex-col gap-3 pb-8">
Expand Down Expand Up @@ -157,12 +172,7 @@ export default function DBTable() {
</section>
{renderTableContent}
{!isCustomQuery && (
<PageSelect
page={page}
setPage={setPage}
rowsPerPage={rowsPerPage}
rowCount={rowCount}
/>
<PageSelect page={page} setPage={setPage} rowsPerPage={rowsPerPage} />
)}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const TableHead = React.forwardRef<
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
"h-12 min-w-[150px] px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
Expand Down
40 changes: 36 additions & 4 deletions src/hooks/useQueryData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback } from "react";
import useSQLiteStore from "@/store/useSQLiteStore";

import { mapQueryResults } from "@/lib/sqlite";
import type { QueryExecResult } from "sql.js";
import type { TableRow } from "@/types";
Expand All @@ -17,7 +18,10 @@ export function useQueryData(
query,
unShiftToQueryHestory,
customQuery,
setCustomQuery
setCustomQuery,
filters,
totalRows,
setTotalRows
} = useSQLiteStore();

const [data, setData] = useState<TableRow[]>([]);
Expand Down Expand Up @@ -45,7 +49,32 @@ export function useQueryData(
)
.join(", ");

const queryString = `SELECT ${columnSelects} FROM "${tableName}" LIMIT ${rowsPerPage} OFFSET ${page};`;
// Construct the count query string
let countQueryString = `SELECT COUNT(*) as count FROM "${tableName}"`;
if (Object.keys(filters).length > 0) {
const filterQuery = Object.entries(filters)
.map(([key, value]) => {
return `LOWER(${key}) LIKE LOWER('%${value}%')`;
})
.join(" AND ");
countQueryString = `SELECT COUNT(*) as count FROM "${tableName}" WHERE ${filterQuery}`;
}

// Execute the count query
const countResult: QueryExecResult[] = query(countQueryString);
const totalRows = countResult[0].values[0][0] as number;
setTotalRows(totalRows);

// Construct the data query string
let queryString = `SELECT ${columnSelects} FROM "${tableName}" LIMIT ${rowsPerPage} OFFSET ${page};`;
if (Object.keys(filters).length > 0) {
const filterQuery = Object.entries(filters)
.map(([key, value]) => {
return `LOWER(${key}) LIKE LOWER('%${value}%')`;
})
.join(" AND ");
queryString = `SELECT ${columnSelects} FROM "${tableName}" WHERE ${filterQuery} LIMIT ${rowsPerPage} OFFSET ${page};`;
}

const tableResult: QueryExecResult[] = query(queryString);
const { data, columns } = mapQueryResults(tableResult);
Expand All @@ -70,7 +99,9 @@ export function useQueryData(
setQueryError,
query,
setCustomQuery,
unShiftToQueryHestory
unShiftToQueryHestory,
filters,
setTotalRows
]);

const handleCustomQuery = useCallback(() => {
Expand Down Expand Up @@ -101,6 +132,7 @@ export function useQueryData(
customQuery,
setCustomQuery,
isQueryLoading,
handleCustomQuery
handleCustomQuery,
totalRows
};
}
21 changes: 20 additions & 1 deletion src/store/useSQLiteStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ interface SQLiteState {
setExpandPage: (value: boolean) => void;
dateFormatValue: string;
setDateFormatValue: (value: string) => void;
filters: {
[columnName: string]: string;
};
setFilters: (value: { [columnName: string]: string }) => void;
appendToFilters: (columnName: string, value: string) => void;

totalRows: number;
setTotalRows: (value: number) => void;
}

const initializeStore = create<SQLiteState>((set, get) => ({
Expand Down Expand Up @@ -106,7 +114,18 @@ const initializeStore = create<SQLiteState>((set, get) => ({
setExpandPage: (value: boolean) => set({ expandPage: value }),

dateFormatValue: "formatDateFormatted",
setDateFormatValue: (value: string) => set({ dateFormatValue: value })
setDateFormatValue: (value: string) => set({ dateFormatValue: value }),

filters: {},
setFilters: (value: { [columnName: string]: string }) =>
set({ filters: value }),
appendToFilters: (columnName: string, value: string) =>
set((state) => ({
filters: { ...state.filters, [columnName]: value }
})),

totalRows: 0,
setTotalRows: (value: number) => set({ totalRows: value })
}));

export default initializeStore;

0 comments on commit 5eaaaef

Please sign in to comment.