diff --git a/dashboard/src/components/entity-table/entity-table-provider.ts b/dashboard/src/components/entity-table/entity-table-provider.ts
@@ -6,6 +6,7 @@ interface EntityTableContextProps<TData> {
table: Table<TData>
data: TData[]
filtering: UseFilteringReturn
+ isLoading: boolean
}
export const EntityTableContext = createContext<EntityTableContextProps<any> | null>(null);
diff --git a/dashboard/src/components/entity-table/hooks/use-entity-query.ts b/dashboard/src/components/entity-table/hooks/use-entity-query.ts
@@ -2,12 +2,13 @@ import { z } from "zod";
const UseEntityQueryPropsSchema = z.object({
page: z.number().gte(1),
- size: z.number().gte(1).lte(100)
+ size: z.number().gte(1).lte(100),
+ search: z.string().optional()
})
export type UseEntityQueryProps = z.infer<typeof UseEntityQueryPropsSchema>
-export type EntityQueryKeyType = { queryKey: [string, number, number] }
+export type EntityQueryKeyType = { queryKey: [string, number, number, string] }
interface FetchEntityResult<T> {
pageCount: number
diff --git a/dashboard/src/components/entity-table/hooks/use-entity-table.ts b/dashboard/src/components/entity-table/hooks/use-entity-table.ts
@@ -1,5 +1,17 @@
-import { ColumnDef, OnChangeFn, PaginationState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
-import { UseFilteringReturn, UseRowSelectionReturn, UseSortingReturn, UseVisibilityReturn } from ".";
+import {
+ ColumnDef,
+ OnChangeFn,
+ PaginationState,
+ getCoreRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable
+} from "@tanstack/react-table";
+import {
+ UseRowSelectionReturn,
+ UseSortingReturn,
+ UseVisibilityReturn
+} from ".";
interface UseEntityTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
@@ -7,7 +19,6 @@ interface UseEntityTableProps<TData, TValue> {
entity: TData[]
pageCount: number
}
- filtering: UseFilteringReturn
sorting: UseSortingReturn
visibility: UseVisibilityReturn
rowSelection?: UseRowSelectionReturn
@@ -20,7 +31,6 @@ export const useEntityTable = <TData, TValue>(
{
columns,
data,
- filtering,
sorting,
visibility,
rowSelection,
@@ -36,16 +46,13 @@ export const useEntityTable = <TData, TValue>(
autoResetPageIndex: false,
onPaginationChange,
onSortingChange: sorting.setSorting,
- onColumnFiltersChange: filtering.setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
- getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: visibility.setColumnVisibility,
onRowSelectionChange: rowSelection && rowSelection.setSelectedRow,
state: {
sorting: sorting.sorting,
- columnFilters: filtering.columnFilters,
columnVisibility: visibility.columnVisibility,
pagination: { pageIndex: pageIndex - 1, pageSize },
rowSelection: rowSelection ? rowSelection.selectedRow : {},
diff --git a/dashboard/src/components/entity-table/hooks/use-filtering.ts b/dashboard/src/components/entity-table/hooks/use-filtering.ts
@@ -1,4 +1,4 @@
-import { ColumnFiltersState, OnChangeFn } from "@tanstack/react-table"
+import { OnChangeFn } from "@tanstack/react-table"
import { useState } from "react"
export interface UseFilteringType {
@@ -6,11 +6,11 @@ export interface UseFilteringType {
}
export interface UseFilteringReturn extends UseFilteringType {
- columnFilters: ColumnFiltersState
- setColumnFilters: OnChangeFn<ColumnFiltersState>
+ columnFilters: string
+ setColumnFilters: OnChangeFn<string>
}
export const useFiltering = ({ column }: UseFilteringType): UseFilteringReturn => {
- const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([])
+ const [columnFilters, setColumnFilters] = useState<string>("")
return { setColumnFilters, columnFilters, column }
}
diff --git a/dashboard/src/components/entity-table/index.tsx b/dashboard/src/components/entity-table/index.tsx
@@ -1,4 +1,4 @@
-import { FC, useState } from "react";
+import { FC, useEffect, useState } from "react";
import { Button, DataTableViewOptions } from "@marzneshin/components";
import { useTranslation } from "react-i18next";
import { useQuery } from "@tanstack/react-query";
@@ -71,16 +71,15 @@ export function EntityTable<T>({
const visibility = useVisibility()
const { onPaginationChange, pageIndex, pageSize } = usePagination();
- const { data } = useQuery({
+ const { data, isLoading } = useQuery({
queryFn: fetchEntity,
- queryKey: [entityKey, pageIndex, pageSize],
+ queryKey: [entityKey, pageIndex, pageSize, filtering.columnFilters],
initialData: { entity: [], pageCount: 1 }
});
const columns = columnsFn({ onEdit, onDelete, onOpen })
const table = useEntityTable({
data,
- filtering,
columns,
pageSize,
pageIndex,
@@ -90,9 +89,11 @@ export function EntityTable<T>({
onPaginationChange
})
+ useEffect(() => table.setPageIndex(1), [filtering.columnFilters, table])
+
// TODO: Move the selectedEntity to context
return (
- <EntityTableContext.Provider value={{ table, data: data.entity, filtering }}>
+ <EntityTableContext.Provider value={{ table, data: data.entity, filtering, isLoading }}>
<SettingsDialog
open={settingsDialogOpen}
onOpenChange={setSettingsDialogOpen}
diff --git a/dashboard/src/components/entity-table/table-filtering.tsx b/dashboard/src/components/entity-table/table-filtering.tsx
@@ -7,15 +7,13 @@ import { useTranslation } from 'react-i18next';
interface TableFilteringProps { }
export const TableFiltering: FC<TableFilteringProps> = () => {
- const { table, filtering } = useEntityTableContext()
+ const { filtering } = useEntityTableContext()
const { t } = useTranslation()
return (
<Input
placeholder={t('table.filter-placeholder', { name: filtering.column })}
- value={(table.getColumn(filtering.column)?.getFilterValue() as string) ?? ""}
- onChange={(event) =>
- table.getColumn(filtering.column)?.setFilterValue(event.target.value)
- }
+ value={filtering.columnFilters}
+ onChange={(e) => filtering.setColumnFilters(e.target.value)}
className="max-w-sm"
/>
);
diff --git a/dashboard/src/components/entity-table/table.tsx b/dashboard/src/components/entity-table/table.tsx
@@ -14,6 +14,7 @@ import {
import { useTranslation } from "react-i18next"
import { useEntityTableContext } from "./entity-table-provider"
+import { LoaderCircle } from "lucide-react"
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
@@ -25,7 +26,7 @@ export function EntityDataTable<TData, TValue>({
onRowClick,
}: DataTableProps<TData, TValue>) {
const { t } = useTranslation();
- const { table } = useEntityTableContext()
+ const { table, isLoading } = useEntityTableContext()
return (
<Table>
<TableHeader>
@@ -47,27 +48,35 @@ export function EntityDataTable<TData, TValue>({
))}
</TableHeader>
<TableBody>
- {table.getRowModel().rows?.length ? (
- table.getRowModel().rows.map((row) => (
- <TableRow
- key={row.id}
- data-state={row.getIsSelected() && "selected"}
- data-testid={"entity-table-row"}
- onClick={() => onRowClick && onRowClick(row.original)}
- >
- {row.getVisibleCells().map((cell) => (
- <TableCell key={cell.id}>
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
- </TableCell>
- ))}
- </TableRow>
- ))
- ) : (
- <TableRow>
+ {isLoading ? (
+ <TableRow className="w-full">
<TableCell colSpan={columns.length} className="h-24 text-center">
- {t('table.no-result')}
+ <LoaderCircle className="w-5 h-5 animate-spin text-primary" />
</TableCell>
</TableRow>
+ ) : (
+ table.getRowModel().rows?.length ? (
+ table.getRowModel().rows.map((row) => (
+ <TableRow
+ key={row.id}
+ data-state={row.getIsSelected() && "selected"}
+ data-testid={"entity-table-row"}
+ onClick={() => onRowClick && onRowClick(row.original)}
+ >
+ {row.getVisibleCells().map((cell) => (
+ <TableCell key={cell.id}>
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+ </TableCell>
+ ))}
+ </TableRow>
+ ))
+ ) : (
+ <TableRow>
+ <TableCell colSpan={columns.length} className="h-24 text-center">
+ {t('table.no-result')}
+ </TableCell>
+ </TableRow>
+ )
)}
</TableBody>
</Table>
diff --git a/dashboard/src/features/nodes/services/nodes.query.ts b/dashboard/src/features/nodes/services/nodes.query.ts
@@ -7,7 +7,7 @@ import {
} from "@marzneshin/components";
export async function fetchNodes({ queryKey }: EntityQueryKeyType): Promise<{ entity: NodeType[], pageCount: number }> {
- return fetch(`/nodes?page=${queryKey[1]}&size=${queryKey[2]}`).then((result) => {
+ return fetch(`/nodes?page=${queryKey[1]}&size=${queryKey[2]}&name=${queryKey[3]}`).then((result) => {
return {
entity: result.items,
pageCount: result.pages,
@@ -18,9 +18,9 @@ export async function fetchNodes({ queryKey }: EntityQueryKeyType): Promise<{ en
export const NodesQueryFetchKey = "nodes";
-export const useNodesQuery = ({ page, size }: UseEntityQueryProps) => {
+export const useNodesQuery = ({ page, size, search }: UseEntityQueryProps) => {
return useQuery({
- queryKey: [NodesQueryFetchKey, page, size],
+ queryKey: [NodesQueryFetchKey, page, size, search ? search : ""],
queryFn: fetchNodes,
initialData: { entity: [], pageCount: 0 },
})
diff --git a/dashboard/src/features/services/services/services.query.ts b/dashboard/src/features/services/services/services.query.ts
@@ -17,7 +17,7 @@ interface ServiceResponse {
}
export async function fetchServices({ queryKey }: EntityQueryKeyType): FetchEntityReturn<ServiceType> {
- return fetch(`/services?page=${queryKey[1]}&size=${queryKey[2]}`).then((result) => {
+ return fetch(`/services?page=${queryKey[1]}&size=${queryKey[2]}&name=${queryKey[3]}`).then((result) => {
const services: ServiceType[] = result.items.map((fetchedService: ServiceResponse) => {
const service: ServiceType = fetchedService;
service.users = fetchedService.user_ids
@@ -33,9 +33,9 @@ export async function fetchServices({ queryKey }: EntityQueryKeyType): FetchEnti
export const ServicesQueryFetchKey = "services";
-export const useServicesQuery = ({ page, size }: UseEntityQueryProps) => {
+export const useServicesQuery = ({ page, size, search }: UseEntityQueryProps) => {
return useQuery({
- queryKey: [ServicesQueryFetchKey, page, size],
+ queryKey: [ServicesQueryFetchKey, page, size, search ? search : ""],
queryFn: fetchServices,
initialData: { entity: [], pageCount: 0 }
})
diff --git a/dashboard/src/features/users/services/users.query.ts b/dashboard/src/features/users/services/users.query.ts
@@ -9,7 +9,7 @@ interface UserResponse extends UserType {
}
export async function fetchUsers({ queryKey }: EntityQueryKeyType): FetchEntityReturn<UserType> {
- return fetch(`/users?page=${queryKey[1]}&size=${queryKey[2]}`).then((result) => {
+ return fetch(`/users?page=${queryKey[1]}&size=${queryKey[2]}&username=${queryKey[3]}`).then((result) => {
const users: UserType[] = result.items.map((fetchedUser: UserResponse) => {
const user: UserType = fetchedUser;
user.services = fetchedUser.service_ids
@@ -24,9 +24,9 @@ export async function fetchUsers({ queryKey }: EntityQueryKeyType): FetchEntityR
export const UsersQueryFetchKey = "users";
-export const useUsersQuery = ({ page, size }: UseEntityQueryProps) => {
+export const useUsersQuery = ({ page, size, search }: UseEntityQueryProps) => {
return useQuery({
- queryKey: [UsersQueryFetchKey, page, size],
+ queryKey: [UsersQueryFetchKey, page, size, search ? search : ""],
queryFn: fetchUsers,
initialData: { entity: [], pageCount: 0 }
})