diff --git a/dashboard/public/locales/en.json b/dashboard/public/locales/en.json index 13efaf0..5f4ef8a 100644 --- a/dashboard/public/locales/en.json +++ b/dashboard/public/locales/en.json @@ -365,6 +365,7 @@ "desc": "This action cannot be undone." }, "table": { + "no-result-found": "No results found.", "row-per-page": "Rows per page", "no-result": "No result", "selected": "Selected", diff --git a/dashboard/src/components/data-table/index.ts b/dashboard/src/components/data-table/index.ts deleted file mode 100644 index 4f04331..0000000 --- a/dashboard/src/components/data-table/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './table' -export * from './pagination' -export * from './column-header' -export * from './table-view-option' -export * from './actions-cell' diff --git a/dashboard/src/components/data-table/pagination.tsx b/dashboard/src/components/data-table/pagination.tsx deleted file mode 100644 index 55095e4..0000000 --- a/dashboard/src/components/data-table/pagination.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { - ChevronLeftIcon, - ChevronRightIcon, - DoubleArrowLeftIcon, - DoubleArrowRightIcon, -} from "@radix-ui/react-icons" -import { Table } from "@tanstack/react-table" -import { cn } from "@marzneshin/utils" -import { - Button, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@marzneshin/components" -import { useTranslation } from "react-i18next" - -interface DataTablePaginationProps { - table: Table - disableSelected?: boolean -} - -export function DataTablePagination({ - table, disableSelected -}: DataTablePaginationProps) { - const { t } = useTranslation(); - return ( -
- {disableSelected || ( -
- {table.getFilteredSelectedRowModel().rows.length} {"/"} - {table.getFilteredRowModel().rows.length} {t('table.selected')} -
- )} -
-
-

{t('table.row-per-page')}

- -
-
- Page {table.getState().pagination.pageIndex + 1} of{" "} - {table.getPageCount()} -
-
- - - - -
-
-
- ) -} diff --git a/dashboard/src/components/index.ts b/dashboard/src/components/index.ts index 168a40b..88b82e4 100644 --- a/dashboard/src/components/index.ts +++ b/dashboard/src/components/index.ts @@ -2,7 +2,6 @@ export * from "./ui"; export * from "./settings-dialog"; export * from "./header"; export * from "./sidebar"; -export * from "./data-table"; export * from "./delete-confirmation-dialog"; export * from "./nav-dir"; export * from "./header"; diff --git a/dashboard/src/features/admins/components/tables/admins/columns.tsx b/dashboard/src/features/admins/components/tables/admins/columns.tsx index db79218..b206d2f 100644 --- a/dashboard/src/features/admins/components/tables/admins/columns.tsx +++ b/dashboard/src/features/admins/components/tables/admins/columns.tsx @@ -4,17 +4,19 @@ import { AdminEnabledPill, AdminPermissionPill, } from "@marzneshin/features/admins"; -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header"; -import i18n from "@marzneshin/features/i18n"; import { DataTableActionsCell, + DataTableColumnHeader +} from "@marzneshin/features/entity-table" +import i18n from "@marzneshin/features/i18n"; +import { NoPropogationButton, } from "@marzneshin/components"; import { type ColumnActions } from "@marzneshin/features/entity-table"; -export const columns = (actions: ColumnActions): ColumnDef[] => [ +export const columns = (actions: ColumnActions): ColumnDef[] => [ { accessorKey: "username", header: ({ column }) => ( @@ -44,12 +46,10 @@ export const columns = (actions: ColumnActions): ColumnDef }, { id: "actions", - cell: ({ row }) => { - return ( - - - - ); - }, + cell: ({ row }) => ( + + + + ), } ]; diff --git a/dashboard/src/features/admins/components/tables/admins/index.tsx b/dashboard/src/features/admins/components/tables/admins/index.tsx index a5650f3..c1787d5 100644 --- a/dashboard/src/features/admins/components/tables/admins/index.tsx +++ b/dashboard/src/features/admins/components/tables/admins/index.tsx @@ -1,4 +1,5 @@ import type { FC } from "react"; +import { ColumnDef } from "@tanstack/react-table"; import { fetchAdmins, AdminType, AdminsQueryFetchKey } from "@marzneshin/features/admins"; import { columns as columnsFn } from "./columns"; import { EntityTable } from "@marzneshin/features/entity-table"; @@ -32,9 +33,8 @@ export const AdminsTable: FC = () => { return ( []} + primaryFilter="username" entityKey={AdminsQueryFetchKey} onCreate={() => navigate({ to: "/admins/create" })} onOpen={onOpen} diff --git a/dashboard/src/features/admins/services/admins.query.ts b/dashboard/src/features/admins/services/admins.query.ts index eedc965..51d23c2 100644 --- a/dashboard/src/features/admins/services/admins.query.ts +++ b/dashboard/src/features/admins/services/admins.query.ts @@ -8,11 +8,16 @@ import { } from "@marzneshin/features/entity-table"; export async function fetchAdmins({ queryKey }: EntityQueryKeyType): FetchEntityReturn { + const pagination = queryKey[1]; + const primaryFilter = queryKey[2]; + const filters = queryKey[4].filters; return fetch(`/admins`, { query: { - page: queryKey[1], - size: queryKey[2], - username: queryKey[3], + ...pagination, + ...filters, + username: primaryFilter, + descending: queryKey[3].desc, + order_by: queryKey[3].sortBy, } }).then((result) => { return { @@ -22,13 +27,13 @@ export async function fetchAdmins({ queryKey }: EntityQueryKeyType): FetchEntity }); } -export const AdminsQueryFetchKey = "users"; +export const AdminsQueryFetchKey = "admins"; export const useAdminsQuery = ({ - page, size, search = "" + page, size, sortBy = "created_at", desc = false, filters = {} }: UseEntityQueryProps) => { return useQuery({ - queryKey: [AdminsQueryFetchKey, page, size, search], + queryKey: [AdminsQueryFetchKey, { page, size }, filters?.username ?? "", { sortBy, desc }, { filters }], queryFn: fetchAdmins, initialData: { entity: [], pageCount: 0 } }) diff --git a/dashboard/src/components/data-table/actions-cell.tsx b/dashboard/src/features/entity-table/components/actions-cell.tsx similarity index 100% rename from dashboard/src/components/data-table/actions-cell.tsx rename to dashboard/src/features/entity-table/components/actions-cell.tsx diff --git a/dashboard/src/features/entity-table/components/column-header-combo-filter.tsx b/dashboard/src/features/entity-table/components/column-header-combo-filter.tsx new file mode 100644 index 0000000..43d2ec6 --- /dev/null +++ b/dashboard/src/features/entity-table/components/column-header-combo-filter.tsx @@ -0,0 +1,124 @@ +import * as React from "react" +import { type Column } from "@tanstack/react-table"; +import { useEntityTableContext } from "../contexts"; +import { useScreenBreakpoint } from "@marzneshin/hooks"; +import { useTranslation } from "react-i18next"; +import { + Button, + Drawer, + Command, + Popover, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + DrawerContent, + DrawerTrigger, + PopoverContent, + PopoverTrigger, +} from "@marzneshin/components" +import { FilterX } from "lucide-react"; + +interface DataTableColumnHeaderFilterOptionProps + extends React.HTMLAttributes { + title: string + options: string[] + column: Column +} + +export function DataTableColumnHeaderFilterOption( + { title, column, options }: DataTableColumnHeaderFilterOptionProps +) { + const [open, setOpen] = React.useState(false) + const isDesktop = useScreenBreakpoint("md"); + const [selectedOption, setSelectedOption] = React.useState( + null + ) + const { filters } = useEntityTableContext(); + + function handleClearingFilter() { + filters.setColumnsFilter({ ...filters.columnsFilter, [column.id]: undefined }); + setSelectedOption(null); + } + + if (isDesktop) { + return ( + + + + } + + + + + + + ) + } + + return ( + + + + + +
+ +
+
+
+ ) +} + +interface ComboFilterOptionListProps { + setOpen: (open: boolean) => void + setSelectedOption: (option: string | null) => void + columnName: string + options: string[] +} + +function ComboFilterOptionList({ + setOpen, + setSelectedOption, + options, + columnName, +}: ComboFilterOptionListProps) { + const { filters } = useEntityTableContext(); + const { t } = useTranslation(); + return ( + + + + {t("table.no-result-found")} + + {options.map((option) => ( + { + setSelectedOption(value) + setOpen(false) + filters.setColumnsFilter({ ...filters.columnsFilter, [columnName]: value }) + }} + > + {option} + + ))} + + + + ) +} diff --git a/dashboard/src/components/data-table/column-header.tsx b/dashboard/src/features/entity-table/components/column-header.tsx similarity index 100% rename from dashboard/src/components/data-table/column-header.tsx rename to dashboard/src/features/entity-table/components/column-header.tsx diff --git a/dashboard/src/components/data-table/table.tsx b/dashboard/src/features/entity-table/components/data-table.tsx similarity index 98% rename from dashboard/src/components/data-table/table.tsx rename to dashboard/src/features/entity-table/components/data-table.tsx index e168fb7..092baa2 100644 --- a/dashboard/src/components/data-table/table.tsx +++ b/dashboard/src/features/entity-table/components/data-table.tsx @@ -25,7 +25,7 @@ import { } from "@marzneshin/components" import { useTranslation } from "react-i18next" -import { DataTablePagination } from "./pagination" +import { DataTablePagination } from "./table-pagination" import { useState } from "react" import { DataTableViewOptions } from "./table-view-option" diff --git a/dashboard/src/features/entity-table/components/index.ts b/dashboard/src/features/entity-table/components/index.ts index d5dad63..c88318c 100644 --- a/dashboard/src/features/entity-table/components/index.ts +++ b/dashboard/src/features/entity-table/components/index.ts @@ -1,6 +1,11 @@ -export * from "./table"; +export * from './table' +export * from './data-table' +export * from './table-pagination' +export * from './column-header' +export * from "./column-header-combo-filter"; +export * from './table-view-option' +export * from './actions-cell' export * from "./table-filtering"; -export * from "./table-pagination"; export * from "./sidebar-entity-card"; export * from "./sidebar-entity-popover"; export * from "./sidebar-entity-selection"; diff --git a/dashboard/src/features/entity-table/components/sidebar-entity-card.tsx b/dashboard/src/features/entity-table/components/sidebar-entity-card.tsx index 9fbba78..bc23a6a 100644 --- a/dashboard/src/features/entity-table/components/sidebar-entity-card.tsx +++ b/dashboard/src/features/entity-table/components/sidebar-entity-card.tsx @@ -20,7 +20,7 @@ export const SidebarEntityCard: FC & SidebarE header: Header, content: Content, entity, checked = false }) => { return ( - +
diff --git a/dashboard/src/features/entity-table/components/sidebar-entity-popover.tsx b/dashboard/src/features/entity-table/components/sidebar-entity-popover.tsx index 14733b0..81c36a2 100644 --- a/dashboard/src/features/entity-table/components/sidebar-entity-popover.tsx +++ b/dashboard/src/features/entity-table/components/sidebar-entity-popover.tsx @@ -19,6 +19,7 @@ export const SidebarEntityTablePopover: FC = ({ )} - +
- +
diff --git a/dashboard/src/features/entity-table/hooks/index.ts b/dashboard/src/features/entity-table/hooks/index.ts index 1662c55..be48a3c 100644 --- a/dashboard/src/features/entity-table/hooks/index.ts +++ b/dashboard/src/features/entity-table/hooks/index.ts @@ -1,3 +1,4 @@ +export * from './use-primary-filtering' export * from './use-filtering' export * from './use-pagination' export * from './use-dialog' diff --git a/dashboard/src/features/entity-table/hooks/use-entity-query.ts b/dashboard/src/features/entity-table/hooks/use-entity-query.ts index 6a0d0b0..19bc0fc 100644 --- a/dashboard/src/features/entity-table/hooks/use-entity-query.ts +++ b/dashboard/src/features/entity-table/hooks/use-entity-query.ts @@ -1,33 +1,40 @@ -import { z } from "zod"; -const UseEntityQueryPropsSchema = z.object({ - page: z.number().gte(1), - size: z.number().gte(1).lte(100), - search: z.string().optional() -}) +interface PaginatedEntityQueryProps { + page: number; + size: number; +} + +export type FilteredEntityType = { + [key: string]: string | undefined; +} -export interface SortableEntitySortQueryProps { +export interface SortedEntityQueryProps { sortBy: string desc: boolean } -export type UseEntityQueryProps = z.infer +interface FiltersEntityQueryProps { + filters: FilteredEntityType; +} + +export type UseEntityQueryProps = Partial & PaginatedEntityQueryProps & Partial + +export type EntityName = string; +export type EntityId = number | string | undefined; +export type PrimaryFilter = string; export type QueryKey = - [string, number, number, string] -export type SortableQueryKey = - [string, number, number, string, string, boolean] + [EntityName, PaginatedEntityQueryProps, PrimaryFilter, SortedEntityQueryProps, FiltersEntityQueryProps] + export type SidebarQueryKey = - [string, number | string | undefined, string, number, number, string] -export type SortableSidebarQueryKey = - [string, number | string | undefined, string, number, number, string, string, boolean] + [EntityName, EntityId, EntityName, PaginatedEntityQueryProps, PrimaryFilter, SortedEntityQueryProps, FiltersEntityQueryProps] export interface QueryKeyType { queryKey: QT } -export type EntityQueryKeyType = QueryKeyType -export type EntitySidebarQueryKeyType = QueryKeyType +export type EntityQueryKeyType = QueryKeyType +export type EntitySidebarQueryKeyType = QueryKeyType interface FetchEntityResult { pageCount: number diff --git a/dashboard/src/features/entity-table/hooks/use-filtering.ts b/dashboard/src/features/entity-table/hooks/use-filtering.ts index 15041e8..82ad53a 100644 --- a/dashboard/src/features/entity-table/hooks/use-filtering.ts +++ b/dashboard/src/features/entity-table/hooks/use-filtering.ts @@ -1,16 +1,12 @@ import { OnChangeFn } from "@tanstack/react-table" import { useState } from "react" -export interface UseFilteringType { - column: string +export interface UseFiltersReturn { + columnsFilter: Record + setColumnsFilter: OnChangeFn> } -export interface UseFilteringReturn extends UseFilteringType { - columnFilters: string - setColumnFilters: OnChangeFn -} - -export const useFiltering = ({ column }: UseFilteringType): UseFilteringReturn => { - const [columnFilters, setColumnFilters] = useState("") - return { setColumnFilters, columnFilters, column } +export const useFilters = (): UseFiltersReturn => { + const [columnsFilter, setColumnsFilter] = useState>({}) + return { columnsFilter, setColumnsFilter } } diff --git a/dashboard/src/features/entity-table/hooks/use-primary-filtering.ts b/dashboard/src/features/entity-table/hooks/use-primary-filtering.ts new file mode 100644 index 0000000..5a8396d --- /dev/null +++ b/dashboard/src/features/entity-table/hooks/use-primary-filtering.ts @@ -0,0 +1,16 @@ +import { OnChangeFn } from "@tanstack/react-table" +import { useState } from "react" + +export interface UsePrimaryFilterType { + column: string +} + +export interface UsePrimaryFilterReturn extends UsePrimaryFilterType { + columnFilters: string + setColumnFilters: OnChangeFn +} + +export const usePrimaryFiltering = ({ column }: UsePrimaryFilterType): UsePrimaryFilterReturn => { + const [columnFilters, setColumnFilters] = useState("") + return { setColumnFilters, columnFilters, column } +} diff --git a/dashboard/src/features/entity-table/hooks/use-sidebar-entity-table.ts b/dashboard/src/features/entity-table/hooks/use-sidebar-entity-table.ts index fca7712..8f429fb 100644 --- a/dashboard/src/features/entity-table/hooks/use-sidebar-entity-table.ts +++ b/dashboard/src/features/entity-table/hooks/use-sidebar-entity-table.ts @@ -2,12 +2,12 @@ import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useQuery } from "@tanstack/react-query"; import { - useFiltering, + usePrimaryFiltering, usePagination, useEntityTable, useVisibility, useSorting, - type SortableSidebarQueryKey, + useFilters, type EntitySidebarQueryKeyType, type FetchEntityReturn, type SidebarQueryKey, @@ -42,7 +42,6 @@ export const useSidebarEntityTable = ({ filteredColumn, rowSelection, entityKey, - manualSorting = false, onCreate, onEdit, onOpen, @@ -54,35 +53,32 @@ export const useSidebarEntityTable = ({ secondaryEntityKey, }: UseSidebarEntityTableParams) => { const { t } = useTranslation(); - const filtering = useFiltering({ column: filteredColumn }); + const primaryFilter = usePrimaryFiltering({ column: filteredColumn }); + const filters = useFilters(); const sorting = useSorting(); const visibility = useVisibility(); const desktop = useScreenBreakpoint("md"); const { onPaginationChange, pageIndex, pageSize } = usePagination(); - const sortedQuery: SortableSidebarQueryKey = [ - entityKey, - sidebarEntityId, - secondaryEntityKey, - pageIndex, - pageSize, - filtering.columnFilters, - sorting.sorting[0]?.id ? sorting.sorting[0].id : "created_at", - sorting.sorting[0]?.desc, - ]; - const query: SidebarQueryKey = [ entityKey, sidebarEntityId, secondaryEntityKey, - pageIndex, - pageSize, - filtering.columnFilters, + { + page: pageIndex, + size: pageSize, + }, + primaryFilter.columnFilters, + { + sortBy: sorting.sorting[0]?.id ? sorting.sorting[0].id : "created_at", + desc: sorting.sorting[0]?.desc + }, + { filters: filters.columnsFilter } ]; const { data, isFetching } = useQuery({ queryFn: fetchEntity, - queryKey: manualSorting ? sortedQuery : query, + queryKey: query, initialData: { entity: [], pageCount: 1 }, }); @@ -99,13 +95,8 @@ export const useSidebarEntityTable = ({ }); const entityTableContextValue = useMemo( - () => ({ - table, - data: data.entity, - filtering, - isLoading: isFetching, - }), - [table, data.entity, filtering, isFetching], + () => ({ table, data: data.entity, primaryFilter, filters, isLoading: isFetching }), + [table, data.entity, filters, primaryFilter, isFetching], ); const sidebarEntityTableContextValue = useMemo( diff --git a/dashboard/src/features/entity-table/index.ts b/dashboard/src/features/entity-table/index.ts index 5f7b69d..945c520 100644 --- a/dashboard/src/features/entity-table/index.ts +++ b/dashboard/src/features/entity-table/index.ts @@ -1,4 +1,5 @@ export * from "./entity-table"; +export * from "./components"; export * from "./sidebar-entity-table"; export * from "./hooks"; export * from "./types"; diff --git a/dashboard/src/features/entity-table/sidebar-entity-table.tsx b/dashboard/src/features/entity-table/sidebar-entity-table.tsx index d691c94..e3cf6bc 100644 --- a/dashboard/src/features/entity-table/sidebar-entity-table.tsx +++ b/dashboard/src/features/entity-table/sidebar-entity-table.tsx @@ -1,9 +1,11 @@ -import { Button, DataTableViewOptions, ResizableHandle, ResizablePanel, ResizablePanelGroup, HStack } from "@marzneshin/components"; +import { Button, ResizableHandle, ResizablePanel, ResizablePanelGroup, HStack } from "@marzneshin/components"; +import { Table } from "@tanstack/react-table" +import { DataTableViewOptions } from "./components"; import { Server } from "lucide-react"; import { EntityTableContext, SidebarEntityTableContext } from "./contexts"; import { SidebarEntityCardSectionsProps, - TableFiltering, + TableSearch, DataTablePagination, EntityDataTable, SidebarEntityTablePopover, @@ -36,6 +38,13 @@ interface SidebarEntityTableProps { onDelete: (entity: T) => void; } +function MainTable({ table, columns, props }: { table: Table, columns: any, props: SidebarEntityTableProps }) { + return
+ + +
; +} + export function SidebarEntityTable(props: SidebarEntityTableProps) { const { t } = useTranslation(); const { @@ -69,24 +78,26 @@ export function SidebarEntityTable(props: SidebarEntityTableProps) { )} - +
- - - - - - -
- - -
-
-
+ {desktop ? ( + + + + + + + + + + ) : ( + + )}
); } + diff --git a/dashboard/src/features/entity-table/stories/entity-table.stories.tsx b/dashboard/src/features/entity-table/stories/entity-table.stories.tsx index a7acccd..5301b65 100644 --- a/dashboard/src/features/entity-table/stories/entity-table.stories.tsx +++ b/dashboard/src/features/entity-table/stories/entity-table.stories.tsx @@ -1,7 +1,7 @@ import { EntityDataTable, DataTablePagination, - TableFiltering, + TableSearch, } from "../components"; import { EntityTable } from "../entity-table"; import React from "react"; @@ -11,7 +11,7 @@ import { QueryClientProvider, QueryClient } from "@tanstack/react-query"; import { columnsFnBasicEntityTable } from "./columns-fns"; import { fetchEntity, fetchEntityLoading } from "./fetch-entities"; -const TableFilteringComponent = TableFiltering as React.FC; +const TableSearchComponent = TableSearch as React.FC; const DataTablePaginationComponent = DataTablePagination as React.FC; const EntityDataTableComponent = EntityDataTable as React.FC; @@ -27,7 +27,7 @@ const meta: Meta = { )], tags: ["autodocs"], subcomponents: { - TableFilteringComponent, + TableSearchComponent, DataTablePaginationComponent, EntityDataTableComponent } @@ -46,7 +46,7 @@ export const BasicEntityTable: Story = { args: { fetchEntity: fetchEntity, columns: columnsFnBasicEntityTable({ onEdit, onDelete, onOpen }), - filteredColumn: "name", + primaryFilter: "name", entityKey: "entities", onCreate: () => alert("Create entity"), onOpen: onOpen, @@ -57,7 +57,7 @@ export const LoadingEntityTable: Story = { args: { fetchEntity: fetchEntityLoading, columns: columnsFnBasicEntityTable({ onEdit, onDelete, onOpen }), - filteredColumn: "name", + primaryFilter: "name", entityKey: "entities", onCreate: () => alert("Create entity"), onOpen: onOpen, diff --git a/dashboard/src/features/hosts/services/hosts.query.ts b/dashboard/src/features/hosts/services/hosts.query.ts index bb9245b..07c3008 100644 --- a/dashboard/src/features/hosts/services/hosts.query.ts +++ b/dashboard/src/features/hosts/services/hosts.query.ts @@ -8,10 +8,16 @@ import { } from "@marzneshin/features/entity-table/hooks"; export async function fetchHosts({ queryKey }: EntitySidebarQueryKeyType): FetchEntityReturn { + const pagination = queryKey[3]; + const primaryFilter = queryKey[4]; + const filters = queryKey[6].filters; return fetch(queryKey[1] !== undefined ? `/inbounds/${queryKey[1]}/hosts` : `/inbounds/hosts`, { query: { - page: queryKey[3], - size: queryKey[4], + ...pagination, + ...filters, + remark: primaryFilter, + descending: queryKey[5].desc, + order_by: queryKey[5].sortBy, } }).then((result) => { return { @@ -24,10 +30,10 @@ export async function fetchHosts({ queryKey }: EntitySidebarQueryKeyType): Fetch export const HostsQueryFetchKey = "hosts"; export const useHostsQuery = ({ - inboundId, page, size, search = "" + page, size, sortBy = "created_at", desc = false, filters = {}, inboundId }: UseEntityQueryProps & { inboundId?: number }) => { return useQuery({ - queryKey: ["inbounds", inboundId, HostsQueryFetchKey, page, size, search], + queryKey: ["inbounds", inboundId, HostsQueryFetchKey, { page, size }, filters?.username ?? "", { sortBy, desc }, { filters }], queryFn: fetchHosts, initialData: { entity: [], pageCount: 0 } }) diff --git a/dashboard/src/features/hosts/tables/inbound-hosts/columns.tsx b/dashboard/src/features/hosts/tables/inbound-hosts/columns.tsx index ba3dcb6..706086f 100644 --- a/dashboard/src/features/hosts/tables/inbound-hosts/columns.tsx +++ b/dashboard/src/features/hosts/tables/inbound-hosts/columns.tsx @@ -1,12 +1,14 @@ import { ColumnDef } from "@tanstack/react-table" import { HostType } from "@marzneshin/features/hosts" -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header" +import { + DataTableActionsCell, + DataTableColumnHeader +} from "@marzneshin/features/entity-table" import i18n from "@marzneshin/features/i18n" import { type ColumnActions } from "@marzneshin/features/entity-table"; import { - DataTableActionsCell, NoPropogationButton, } from "@marzneshin/components" diff --git a/dashboard/src/features/nodes/dialogs/settings/node-backend-setting.tsx b/dashboard/src/features/nodes/dialogs/settings/node-backend-setting.tsx index c773f2f..46895db 100644 --- a/dashboard/src/features/nodes/dialogs/settings/node-backend-setting.tsx +++ b/dashboard/src/features/nodes/dialogs/settings/node-backend-setting.tsx @@ -31,7 +31,7 @@ export const NodeBackendSetting: FC<{ node: NodeType; backend: string }> = ({ )} {backendStats === null ? ( - {t("unkown")} + {t("unknown")} ) : backendStats.running ? ( diff --git a/dashboard/src/features/nodes/services/nodes.query.ts b/dashboard/src/features/nodes/services/nodes.query.ts index c752589..6602fbd 100644 --- a/dashboard/src/features/nodes/services/nodes.query.ts +++ b/dashboard/src/features/nodes/services/nodes.query.ts @@ -9,9 +9,18 @@ import type { export async function fetchNodes({ queryKey, }: EntityQueryKeyType): Promise<{ entity: NodeType[]; pageCount: number }> { - return fetch( - `/nodes?page=${queryKey[1]}&size=${queryKey[2]}&name=${queryKey[3]}`, - ).then((result) => { + const pagination = queryKey[1]; + const primaryFilter = queryKey[2]; + const filters = queryKey[4].filters; + return fetch(`/nodes`, { + query: { + ...pagination, + ...filters, + name: primaryFilter, + descending: queryKey[3].desc, + order_by: queryKey[3].sortBy, + } + }).then((result) => { return { entity: result.items, pageCount: result.pages, @@ -21,9 +30,11 @@ export async function fetchNodes({ export const NodesQueryFetchKey = "nodes"; -export const useNodesQuery = ({ page, size, search }: UseEntityQueryProps) => { +export const useNodesQuery = ({ + page, size, sortBy = "created_at", desc = false, filters = {} +}: UseEntityQueryProps) => { return useQuery({ - queryKey: [NodesQueryFetchKey, page, size, search ? search : ""], + queryKey: [NodesQueryFetchKey, { page, size }, filters?.username ?? "", { sortBy, desc }, { filters }], queryFn: fetchNodes, initialData: { entity: [], pageCount: 0 }, }); diff --git a/dashboard/src/features/nodes/tables/columns.tsx b/dashboard/src/features/nodes/tables/columns.tsx index d68b99c..bd5fa9f 100644 --- a/dashboard/src/features/nodes/tables/columns.tsx +++ b/dashboard/src/features/nodes/tables/columns.tsx @@ -1,12 +1,14 @@ import { ColumnDef } from "@tanstack/react-table" import { NodesStatusBadge, NodeType, NodesStatus } from "@marzneshin/features/nodes" -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header" +import { + DataTableActionsCell, + DataTableColumnHeader +} from "@marzneshin/features/entity-table" import i18n from "@marzneshin/features/i18n" import { type ColumnActions } from "@marzneshin/features/entity-table"; import { - DataTableActionsCell, NoPropogationButton, } from "@marzneshin/components" diff --git a/dashboard/src/features/nodes/tables/table.tsx b/dashboard/src/features/nodes/tables/table.tsx index fb620c8..831a8f8 100644 --- a/dashboard/src/features/nodes/tables/table.tsx +++ b/dashboard/src/features/nodes/tables/table.tsx @@ -37,7 +37,7 @@ export const NodesTable: FC = () => { navigate({ to: "/nodes/create" })} onOpen={onOpen} diff --git a/dashboard/src/features/services/components/tables/inbounds/columns.tsx b/dashboard/src/features/services/components/tables/inbounds/columns.tsx index c42d859..c832a78 100644 --- a/dashboard/src/features/services/components/tables/inbounds/columns.tsx +++ b/dashboard/src/features/services/components/tables/inbounds/columns.tsx @@ -1,7 +1,9 @@ import { ColumnDef } from "@tanstack/react-table" import { InboundType } from "@marzneshin/features/inbounds" -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header" +import { + DataTableColumnHeader +} from "@marzneshin/features/entity-table" import i18n from "@marzneshin/features/i18n" import { Badge, Checkbox } from "@marzneshin/components" diff --git a/dashboard/src/features/services/components/tables/inbounds/index.tsx b/dashboard/src/features/services/components/tables/inbounds/index.tsx index 9b7362a..1e8aeec 100644 --- a/dashboard/src/features/services/components/tables/inbounds/index.tsx +++ b/dashboard/src/features/services/components/tables/inbounds/index.tsx @@ -1,6 +1,7 @@ import { type FC, useCallback, useEffect, useState } from "react"; import { useInboundsQuery } from "@marzneshin/features/inbounds"; -import { Button, DataTable } from "@marzneshin/components"; +import { Button } from "@marzneshin/components"; +import { DataTable } from "@marzneshin/features/entity-table"; import { columns } from "./columns"; import { type ServiceType, useServicesUpdateMutation } from "@marzneshin/features/services"; import { useTranslation } from "react-i18next"; diff --git a/dashboard/src/features/services/components/tables/services/columns.tsx b/dashboard/src/features/services/components/tables/services/columns.tsx index fdd7eb5..d16f9a1 100644 --- a/dashboard/src/features/services/components/tables/services/columns.tsx +++ b/dashboard/src/features/services/components/tables/services/columns.tsx @@ -1,9 +1,11 @@ import { ColumnDef } from "@tanstack/react-table"; import { ServiceType } from "@marzneshin/features/services"; -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header"; -import i18n from "@marzneshin/features/i18n"; import { DataTableActionsCell, + DataTableColumnHeader +} from "@marzneshin/features/entity-table" +import i18n from "@marzneshin/features/i18n"; +import { NoPropogationButton, } from "@marzneshin/components"; import { ColumnActions } from "@marzneshin/features/entity-table"; diff --git a/dashboard/src/features/services/components/tables/services/index.tsx b/dashboard/src/features/services/components/tables/services/index.tsx index f3c9b4e..a61073e 100644 --- a/dashboard/src/features/services/components/tables/services/index.tsx +++ b/dashboard/src/features/services/components/tables/services/index.tsx @@ -1,46 +1,46 @@ import type { FC } from "react"; import { - ServicesQueryFetchKey, - fetchServices, - ServiceType + ServicesQueryFetchKey, + fetchServices, + ServiceType } from "@marzneshin/features/services"; import { columns as columnsFn } from "./columns"; import { EntityTable } from "@marzneshin/features/entity-table"; import { useNavigate } from "@tanstack/react-router"; export const ServicesTable: FC = () => { - const navigate = useNavigate({ from: "/services" }); - const onEdit = (entity: ServiceType) => { - navigate({ - to: "/services/$serviceId/edit", - params: { serviceId: String(entity.id) }, - }) - } + const navigate = useNavigate({ from: "/services" }); + const onEdit = (entity: ServiceType) => { + navigate({ + to: "/services/$serviceId/edit", + params: { serviceId: String(entity.id) }, + }) + } - const onDelete = (entity: ServiceType) => { - navigate({ - to: "/services/$serviceId/delete", - params: { serviceId: String(entity.id) }, - }) - } + const onDelete = (entity: ServiceType) => { + navigate({ + to: "/services/$serviceId/delete", + params: { serviceId: String(entity.id) }, + }) + } - const onOpen = (entity: ServiceType) => { - navigate({ - to: "/services/$serviceId", - params: { serviceId: String(entity.id) }, - }) - } + const onOpen = (entity: ServiceType) => { + navigate({ + to: "/services/$serviceId", + params: { serviceId: String(entity.id) }, + }) + } - const columns = columnsFn({ onEdit, onDelete, onOpen }); + const columns = columnsFn({ onEdit, onDelete, onOpen }); - return ( - navigate({ to: "/services/create" })} - onOpen={onOpen} - /> - ); + return ( + navigate({ to: "/services/create" })} + onOpen={onOpen} + /> + ); }; diff --git a/dashboard/src/features/services/components/tables/users/columns.tsx b/dashboard/src/features/services/components/tables/users/columns.tsx index 4d251e3..a404ac7 100644 --- a/dashboard/src/features/services/components/tables/users/columns.tsx +++ b/dashboard/src/features/services/components/tables/users/columns.tsx @@ -1,5 +1,5 @@ import { ColumnDef } from "@tanstack/react-table" -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header" +import { DataTableColumnHeader } from "@marzneshin/features/entity-table" import i18n from "@marzneshin/features/i18n" import { UserType, diff --git a/dashboard/src/features/services/components/tables/users/index.tsx b/dashboard/src/features/services/components/tables/users/index.tsx index 3518a7e..486e2f5 100644 --- a/dashboard/src/features/services/components/tables/users/index.tsx +++ b/dashboard/src/features/services/components/tables/users/index.tsx @@ -1,6 +1,6 @@ import { FC } from "react"; -import { DataTable } from "@marzneshin/components"; +import { DataTable } from "@marzneshin/features/entity-table"; import { columns } from "./columns"; import { useUsersServiceQuery, type ServiceType } from "@marzneshin/features/services"; diff --git a/dashboard/src/features/services/services/services.query.ts b/dashboard/src/features/services/services/services.query.ts index 088258f..cb16de2 100644 --- a/dashboard/src/features/services/services/services.query.ts +++ b/dashboard/src/features/services/services/services.query.ts @@ -8,10 +8,16 @@ import { import { useQuery } from "@tanstack/react-query"; export async function fetchServices({ queryKey }: EntityQueryKeyType): FetchEntityReturn { + const pagination = queryKey[1]; + const primaryFilter = queryKey[2]; + const filters = queryKey[4].filters; return fetch(`/services`, { query: { - page: queryKey[1], - size: queryKey[2] + ...pagination, + ...filters, + name: primaryFilter, + descending: queryKey[3].desc, + order_by: queryKey[3].sortBy, } }).then((result) => { const services: ServiceType[] = result.items; @@ -24,9 +30,11 @@ export async function fetchServices({ queryKey }: EntityQueryKeyType): FetchEnti export const ServicesQueryFetchKey = "services"; -export const useServicesQuery = ({ page, size, search = "" }: UseEntityQueryProps) => { +export const useServicesQuery = ({ + page, size, sortBy = "created_at", desc = false, filters = {} +}: UseEntityQueryProps) => { return useQuery({ - queryKey: [ServicesQueryFetchKey, page, size, search], + queryKey: [ServicesQueryFetchKey, { page, size }, filters?.username ?? "", { sortBy, desc }, { filters }], queryFn: fetchServices, initialData: { entity: [], pageCount: 0 } }) diff --git a/dashboard/src/features/users/components/tables/services/columns.tsx b/dashboard/src/features/users/components/tables/services/columns.tsx index 4c0dc5e..693e266 100644 --- a/dashboard/src/features/users/components/tables/services/columns.tsx +++ b/dashboard/src/features/users/components/tables/services/columns.tsx @@ -1,5 +1,7 @@ import { ColumnDef } from "@tanstack/react-table" -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header" +import { + DataTableColumnHeader +} from "@marzneshin/features/entity-table" import i18n from "@marzneshin/features/i18n" import { Checkbox } from "@marzneshin/components" import { ServiceType } from "@marzneshin/features/services" diff --git a/dashboard/src/features/users/components/tables/services/index.tsx b/dashboard/src/features/users/components/tables/services/index.tsx index d75bb1f..b2326bd 100644 --- a/dashboard/src/features/users/components/tables/services/index.tsx +++ b/dashboard/src/features/users/components/tables/services/index.tsx @@ -1,6 +1,6 @@ import { type FC, useCallback, useEffect, useState } from "react"; -import { } from "@marzneshin/features/inbounds"; -import { Button, DataTable } from "@marzneshin/components"; +import { Button } from "@marzneshin/components"; +import { DataTable } from "@marzneshin/features/entity-table"; import { columns } from "./columns"; import { type UserType, diff --git a/dashboard/src/features/users/components/tables/users/columns.tsx b/dashboard/src/features/users/components/tables/users/columns.tsx index 711f61d..2cb2bad 100644 --- a/dashboard/src/features/users/components/tables/users/columns.tsx +++ b/dashboard/src/features/users/components/tables/users/columns.tsx @@ -6,17 +6,22 @@ import { UserExpireStrategyPill, UserExpirationValue } from "@marzneshin/features/users"; -import { DataTableColumnHeader } from "@marzneshin/components/data-table/column-header"; +import { useAdminsQuery } from "@marzneshin/features/admins"; import i18n from "@marzneshin/features/i18n"; import { CopyToClipboardButton, - DataTableActionsCell, buttonVariants, NoPropogationButton, } from "@marzneshin/components"; import { LinkIcon } from "lucide-react"; import { getSubscriptionLink } from "@marzneshin/utils"; -import type { ColumnActions, ColumnDefWithSudoRole } from "@marzneshin/features/entity-table"; +import { + DataTableColumnHeader, + DataTableColumnHeaderFilterOption, + DataTableActionsCell, + type ColumnActions, type ColumnDefWithSudoRole +} from "@marzneshin/features/entity-table"; +import { type Column } from "@tanstack/react-table"; export const columns = (actions: ColumnActions): ColumnDefWithSudoRole[] => [ { @@ -45,12 +50,7 @@ export const columns = (actions: ColumnActions): ColumnDefWithSudoRole accessorKey: "owner_username", enableSorting: false, sudoVisibleOnly: true, - header: ({ column }) => ( - - ), + header: ({ column }) => , }, { accessorKey: "used_traffic", @@ -104,3 +104,15 @@ export const columns = (actions: ColumnActions): ColumnDefWithSudoRole ) } ]; + +function AdminsColumnsHeaderOptionFilter({ column }: { column: Column }) { + const { data } = useAdminsQuery({ page: 1, size: 100, filters: {} }); + return ( + admin.username)} + /> + ); +} + diff --git a/dashboard/src/features/users/components/tables/users/index.tsx b/dashboard/src/features/users/components/tables/users/index.tsx index badeacb..60f6f6e 100644 --- a/dashboard/src/features/users/components/tables/users/index.tsx +++ b/dashboard/src/features/users/components/tables/users/index.tsx @@ -36,9 +36,8 @@ export const UsersTable: FC = () => { return ( navigate({ to: "/users/create" })} onOpen={onOpen} diff --git a/dashboard/src/features/users/services/users.query.ts b/dashboard/src/features/users/services/users.query.ts index 28f1d1e..8cd8d4e 100644 --- a/dashboard/src/features/users/services/users.query.ts +++ b/dashboard/src/features/users/services/users.query.ts @@ -4,20 +4,22 @@ import { UserType } from "../types"; import { FetchEntityReturn, UseEntityQueryProps, - SortableEntitySortQueryProps, EntityQueryKeyType } from "@marzneshin/features/entity-table"; export type SortUserBy = "username" | "used_traffic" | "data_limit" | "expire_date" | "created_at" export async function fetchUsers({ queryKey }: EntityQueryKeyType): FetchEntityReturn { + const pagination = queryKey[1]; + const primaryFilter = queryKey[2]; + const filters = queryKey[4].filters; return fetch(`/users`, { query: { - page: queryKey[1], - size: queryKey[2], - username: queryKey[3], - descending: queryKey[5], - order_by: queryKey[4] + ...pagination, + ...filters, + username: primaryFilter, + descending: queryKey[3].desc, + order_by: queryKey[3].sortBy, } }).then((result) => { return { @@ -30,10 +32,10 @@ export async function fetchUsers({ queryKey }: EntityQueryKeyType): FetchEntityR export const UsersQueryFetchKey = "users"; export const useUsersQuery = ({ - page, size, search = "", sortBy = "created_at", desc = false -}: UseEntityQueryProps & SortableEntitySortQueryProps) => { + page, size, sortBy = "created_at", desc = false, filters = {} +}: UseEntityQueryProps) => { return useQuery({ - queryKey: [UsersQueryFetchKey, page, size, search, sortBy, desc], + queryKey: [UsersQueryFetchKey, { page, size }, filters?.username ?? "", { sortBy, desc }, { filters }], queryFn: fetchUsers, initialData: { entity: [], pageCount: 0 } })