Skip to content

Commit

Permalink
feat: use local storage to store some info about sessions ✨ (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
kareemmahlees authored Sep 16, 2024
1 parent 27def8b commit 6af5db7
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 52 deletions.
1 change: 1 addition & 0 deletions apps/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"react-resizable-panels": "^2.1.1",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"usehooks-ts": "^3.1.0",
"zod": "^3.23.8",
"zustand": "^4.5.5"
},
Expand Down
17 changes: 13 additions & 4 deletions apps/core/src/hooks/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getZodSchemaFromCols } from "@/commands/columns"
import { generateColumnsDefs } from "@/routes/dashboard/_layout/$tableName/-components/columns"
import { useSettings } from "@/settings/manager"
import { useTableState } from "@/state/tableState"
import { TableLocalStorage } from "@/types"
import { rankItem, type RankingInfo } from "@tanstack/match-sorter-utils"
import { useQuery } from "@tanstack/react-query"
import {
Expand All @@ -16,6 +17,7 @@ import {
type SortingState
} from "@tanstack/react-table"
import { useMemo, useRef, useState } from "react"
import { useLocalStorage } from "usehooks-ts"
import { useGetPaginatedRows } from "./row"

export const useGetTableColumns = (tableName: string) => {
Expand Down Expand Up @@ -60,6 +62,7 @@ const fuzzyFilter: FilterFn<any> = (row, columnId, value, addMeta) => {
type SetupReactTableOptions<TData, TValue> = {
columns: ColumnDef<TData, TValue>[]
tableName: string
connectionId: string
}

/**
Expand All @@ -68,10 +71,11 @@ type SetupReactTableOptions<TData, TValue> = {
*/
export const useSetupReactTable = <TData, TValue>({
tableName,
columns
columns,
connectionId
}: SetupReactTableOptions<TData, TValue>) => {
const { defaultData, pagination, setPagination, pageIndex, pageSize } =
useSetupPagination()
useSetupPagination(connectionId)
const { globalFilter, setGlobalFilter } = useTableState()

const { data: rows, isLoading: isRowsLoading } = useGetPaginatedRows(
Expand Down Expand Up @@ -121,10 +125,15 @@ export const useSetupReactTable = <TData, TValue>({
* Sets up the state and memoization for page index & page size
* to be used in paginating the rows.
*/
const useSetupPagination = () => {
const useSetupPagination = (connectionId: string) => {
const settings = useSettings()
const [{ pageIndex: persistedPageIndex }] =
useLocalStorage<TableLocalStorage>(`@tablex/${connectionId}`, {
tableName: "",
pageIndex: 0
})
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageIndex: persistedPageIndex,
pageSize: settings.pageSize
})
const defaultData = useMemo(() => [], [])
Expand Down
20 changes: 19 additions & 1 deletion apps/core/src/routes/connections/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "@/components/ui/dropdown-menu"
import { Separator } from "@/components/ui/separator"
import { unwrapResult } from "@/lib/utils"
import { TableLocalStorage } from "@/types"
import { createFileRoute, redirect, useRouter } from "@tanstack/react-router"
import { MoreHorizontal, Trash } from "lucide-react"
import { Suspense } from "react"
Expand Down Expand Up @@ -54,9 +55,26 @@ function ConnectionsPage() {

if (connectionEstablishment === false) return

const connectionStorageData = localStorage.getItem(
`@tablex/${connectionId}`
)

if (connectionStorageData) {
const parsedConnectionData: TableLocalStorage = JSON.parse(
connectionStorageData
)
return router.navigate({
to: "/dashboard/$tableName",
params: {
tableName: parsedConnectionData.tableName
},
search: { connectionId }
})
}

router.navigate({
to: "/dashboard/land",
search: { connectionName: connectionDetails.connName }
search: { connectionId }
})
}

Expand Down
70 changes: 41 additions & 29 deletions apps/core/src/routes/dashboard/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,29 @@ import { unwrapResult } from "@/lib/utils"
import { cn } from "@tablex/lib/utils"
import { createFileRoute, Link, Outlet, redirect } from "@tanstack/react-router"
import { ArrowLeft, PanelLeftClose, Table } from "lucide-react"
import { useEffect, useRef, useState, type KeyboardEvent } from "react"
import { useEffect, useRef, useState } from "react"
import type { ImperativePanelHandle } from "react-resizable-panels"
import { useDebounceCallback } from "usehooks-ts"
import { z } from "zod"

const dashboardConnectionSchema = z.object({
connectionName: z.string().optional(),
connectionId: z.string().uuid(),
tableName: z.string().optional()
})

export const Route = createFileRoute("/dashboard/_layout")({
validateSearch: dashboardConnectionSchema,
loaderDeps: ({ search: { tableName, connectionName } }) => ({
connectionName,
loaderDeps: ({ search: { tableName, connectionId } }) => ({
connectionId,
tableName
}),
loader: async ({ deps: { connectionName } }) => {
const connName = connectionName || "Temp Connection"
loader: async ({ deps: { connectionId } }) => {
const connectionDetailsResult =
await commands.getConnectionDetails(connectionId)
const connectionDetails = unwrapResult(connectionDetailsResult)

if (!connectionDetails) throw redirect({ to: "/connections" })
const connName = connectionDetails.connName || "Temp Connection"

const tables = unwrapResult(await commands.getTables())
if (!tables)
Expand All @@ -49,9 +55,10 @@ export const Route = createFileRoute("/dashboard/_layout")({
function DashboardLayout() {
const deps = Route.useLoaderDeps()
const data = Route.useLoaderData()
const searchParams = Route.useSearch()
const keybindingsManager = useKeybindings()
const [, setSideBarCollapsed] = useState(false) // NOTE: I don't know why this is needed, but collapsing doesn't work without it.
const [tables, setTables] = useState<string[]>(data!.tables)
const [tables, setTables] = useState<string[]>(data.tables)
const [, setSideBarCollapsed] = useState(false)
const sidebarPanelRef = useRef<ImperativePanelHandle>(null)

useEffect(() => {
Expand All @@ -63,30 +70,31 @@ function DashboardLayout() {
])
}, [keybindingsManager])

let timeout: NodeJS.Timeout
const handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
const searchPattern = e.currentTarget.value
const handleTableSearch = (searchPattern: string) => {
if (searchPattern === "") return setTables(data!.tables)

clearTimeout(timeout)

timeout = setTimeout(() => {
const filteredTables = tables.filter((table) =>
table.includes(searchPattern)
)
setTables(filteredTables)
}, 100)
const filteredTables = tables.filter((table) =>
table.includes(searchPattern)
)
setTables(filteredTables)
}

const debounced = useDebounceCallback(handleTableSearch)

return (
<ResizablePanelGroup className="flex h-full w-full" direction="horizontal">
<ResizablePanelGroup
className="flex h-full w-full"
direction="horizontal"
autoSaveId={"@tablex/layout"}
>
<ResizablePanel
ref={sidebarPanelRef}
defaultSize={14}
onCollapse={() => setSideBarCollapsed(true)}
onExpand={() => setSideBarCollapsed(false)}
onCollapse={() => setSideBarCollapsed(true)}
collapsible
minSize={0}
minSize={12}
collapsedSize={0}
className={cn(
"flex flex-col items-start justify-between bg-zinc-800 p-4 pt-2 transition-all lg:p-6",
sidebarPanelRef.current?.isCollapsed() && "w-0 p-0 lg:w-0 lg:p-0"
Expand Down Expand Up @@ -125,13 +133,13 @@ function DashboardLayout() {
<div className="flex w-full items-center justify-center rounded-sm px-1">
<Input
id="search_input"
onKeyUp={handleKeyUp}
onChange={(e) => debounced(e.currentTarget.value)}
placeholder="Search..."
className="h-6 border-none text-sm transition-all placeholder:text-xs focus-visible:ring-0 focus-visible:ring-offset-0 lg:h-8 lg:w-[170px] lg:placeholder:text-base lg:focus:w-full"
/>
</div>
<div className="mb-4 overflow-y-auto">
<ul className="flex flex-col items-start gap-y-1 overflow-y-auto">
<div className="mb-4 w-full overflow-y-auto">
<ul className="flex w-full flex-col items-start gap-y-1 overflow-y-auto">
{tables.map((table, index) => {
return (
<Link
Expand All @@ -140,16 +148,20 @@ function DashboardLayout() {
tableName: table
}}
search={{
connectionName: deps.connectionName,
connectionId: deps.connectionId,
tableName: table
}}
preload={false}
key={index}
className="flex items-center justify-center gap-x-1 text-sm text-white lg:text-base"
className={cn(
"hover:bg-muted-foreground/30 flex w-full items-center gap-x-1 overflow-x-hidden rounded-md p-1 text-sm text-white lg:text-base",
searchParams.tableName === table &&
"bg-muted-foreground/30"
)}
role="button"
>
<Table size={16} className="fill-amber-600 text-black" />
{table}
<Table className="h-4 w-4 fill-amber-600 text-black" />
<p className="flex items-center justify-center">{table}</p>
</Link>
)
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ import TableActions from "./table-actions"

interface DataTableProps {
columns: ColumnDef<ColumnProps>[]
connectionId: string
}

const DataTable = ({ columns }: DataTableProps) => {
const DataTable = ({ columns, connectionId }: DataTableProps) => {
const { tableName, pkColumn } = useTableState()
const { isRowsLoading, contextMenuRow, setContextMenuRow, table, tableRef } =
useSetupReactTable({ columns, tableName })
useSetupReactTable({ columns, tableName, connectionId })
const queryClient = useQueryClient()
const keybindingsManager = useKeybindings()
const { isOpen, toggleSheet } = useEditRowSheetState()
Expand Down Expand Up @@ -83,14 +84,14 @@ const DataTable = ({ columns }: DataTableProps) => {
const virtualizer = useVirtualizer({
count: table.getRowCount(),
getScrollElement: () => parentRef.current,
estimateSize: () => 50, // I reached to this number by trial and error
estimateSize: () => 34, // I reached to this number by trial and error
overscan: 10,
debug: import.meta.env.DEV
})

return (
<Sheet open={isOpen} onOpenChange={toggleSheet}>
<TableActions table={table} />
<TableActions table={table} connectionId={connectionId} />
{isRowsLoading ? (
<LoadingSpinner />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,21 @@ import {
} from "@/components/ui/dropdown-menu"
import { Input } from "@/components/ui/input"
import { useTableState } from "@/state/tableState"
import { TableLocalStorage } from "@/types"
import { useDebounceCallback, useLocalStorage } from "usehooks-ts"

type TableActionsProps = {
table: Table<any>
connectionId: string
}

const TableActions = ({ table }: TableActionsProps) => {
const TableActions = ({ table, connectionId }: TableActionsProps) => {
const { toggleDialog: toggleSqlEditor } = useSqlEditorState()
const { tableName, setGlobalFilter } = useTableState()
const debounced = useDebounceCallback(
(filter: string) => setGlobalFilter(filter),
500
)
return (
<>
<div className="flex items-end justify-between p-4">
Expand All @@ -39,13 +46,11 @@ const TableActions = ({ table }: TableActionsProps) => {
<Input
className="hidden min-w-[500px] placeholder:text-white/50 lg:block"
placeholder="Type something to filter..."
onChange={(value) =>
setGlobalFilter(String(value.currentTarget.value))
}
onChange={(value) => debounced(String(value.currentTarget.value))}
/>
</div>
<div className="flex flex-col items-end gap-y-1 lg:gap-y-3">
<DataTablePagination table={table} />
<DataTablePagination table={table} connectionId={connectionId} />
<div className="flex items-center gap-x-3">
<DataTableViewOptions table={table} />
<Button
Expand All @@ -69,11 +74,19 @@ export default TableActions

interface DataTablePaginationProps<TData> {
table: Table<TData>
connectionId: string
}

export function DataTablePagination<TData>({
table
table,
connectionId
}: DataTablePaginationProps<TData>) {
const [connectionStorage, setConnectionStorage] =
useLocalStorage<TableLocalStorage>(`@tablex/${connectionId}`, {
tableName: "",
pageIndex: 0
})

return (
<div className="flex items-center justify-between px-2">
<div className="text-muted-foreground hidden text-sm lg:flex">
Expand All @@ -89,7 +102,10 @@ export function DataTablePagination<TData>({
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(0)}
onClick={() => {
table.setPageIndex(0)
setConnectionStorage({ ...connectionStorage, pageIndex: 0 })
}}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
Expand All @@ -98,7 +114,13 @@ export function DataTablePagination<TData>({
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.previousPage()}
onClick={() => {
setConnectionStorage({
...connectionStorage,
pageIndex: table.getState().pagination.pageIndex - 1
})
table.previousPage()
}}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
Expand All @@ -107,7 +129,13 @@ export function DataTablePagination<TData>({
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.nextPage()}
onClick={() => {
setConnectionStorage({
...connectionStorage,
pageIndex: table.getState().pagination.pageIndex + 1
})
table.nextPage()
}}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
Expand All @@ -116,7 +144,13 @@ export function DataTablePagination<TData>({
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
onClick={() => {
table.setPageIndex(table.getPageCount() - 1)
setConnectionStorage({
...connectionStorage,
pageIndex: table.getPageCount() - 1
})
}}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
Expand Down
Loading

0 comments on commit 6af5db7

Please sign in to comment.