Skip to content

Commit

Permalink
feat: server sorting
Browse files Browse the repository at this point in the history
  • Loading branch information
casperiv0 committed Oct 21, 2023
1 parent 62ba07d commit aa32ce3
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 13 deletions.
10 changes: 6 additions & 4 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@types/express": "^4.17.19",
"@types/glob": "^8.1.0",
"@types/jsonwebtoken": "9.0.3",
"@types/lodash.set": "^4.3.8",
"@types/multer": "^1.4.8",
"@types/node": "^20.8.6",
"@types/qrcode": "^1.5.2",
Expand All @@ -36,9 +37,6 @@
"vitest": "^0.34.6"
},
"dependencies": {
"multer": "^1.4.5-lts.1",
"ts-node": "^10.9.1",
"tslib": "^2.6.2",
"@discordjs/rest": "^2.0.1",
"@paralleldrive/cuid2": "^2.2.2",
"@prisma/client": "^5.4.2",
Expand Down Expand Up @@ -78,15 +76,19 @@
"glob": "^10.3.10",
"is-ip": "3.1.0",
"jsonwebtoken": "9.0.2",
"lodash.set": "^4.3.2",
"multer": "^1.4.5-lts.1",
"nanoid": "^3.3.4",
"nodemon": "^3.0.1",
"otplib": "^12.0.1",
"prisma": "^5.4.2",
"puppeteer": "^21.3.8",
"qrcode": "^1.5.3",
"sharp": "^0.32.6",
"reflect-metadata": "^0.1.13",
"sharp": "^0.32.6",
"socket.io": "^4.7.2",
"ts-node": "^10.9.1",
"tslib": "^2.6.2",
"undici": "^5.26.3",
"use-intl": "^2.20.2",
"zod": "^3.22.4"
Expand Down
14 changes: 13 additions & 1 deletion apps/api/src/controllers/admin/values/values-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { validateSchema } from "lib/data/validate-schema";
import { createSearchWhereObject } from "lib/values/create-where-object";
import generateBlurPlaceholder from "lib/images/generate-image-blur-data";
import { AuditLogActionType, createAuditLogEntry } from "@snailycad/audit-logger/server";
import set from "lodash.set";

export const GET_VALUES: Partial<Record<ValueType, ValuesSelect>> = {
QUALIFICATION: {
Expand Down Expand Up @@ -74,6 +75,7 @@ export class ValuesController {
@QueryParams("skip", Number) skip = 0,
@QueryParams("query", String) query = "",
@QueryParams("includeAll", Boolean) includeAll = true,
@QueryParams("sorting") sorting: string = "",
): Promise<APITypes.GetValuesData | APITypes.GetValuesPenalCodesData> {
// allow more paths in one request
let paths =
Expand All @@ -83,6 +85,16 @@ export class ValuesController {
paths = validValuePaths.filter((v) => v !== "penal_code_group");
}

const orderBy = sorting.split(",").reduce((obj, cv) => {
const [key, sortOrder] = cv.split(":") as [string, "asc" | "desc"];

return set(obj, key, sortOrder);
}, {});

console.log({
orderBy,
});

const values = await Promise.all(
paths.map(async (path) => {
const type = getTypeFromPath(path) as ValueType;
Expand Down Expand Up @@ -114,7 +126,7 @@ export class ValuesController {
...(type === "ADDRESS" ? {} : { _count: true }),
value: true,
},
orderBy: { value: { position: "asc" } },
orderBy: sorting ? orderBy : { value: { position: "asc" } },
take: includeAll ? undefined : 35,
skip: includeAll ? undefined : skip,
}),
Expand Down
27 changes: 24 additions & 3 deletions apps/client/src/components/shared/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,28 @@ export function Table<TData extends _RowData>({
cols.unshift(createTableDragDropColumn(tableState.dragDrop));
}

if (tableState.sorting.useServerSorting) {
const disabledSorting = cols.map((col) => {
const accessorKey = (col as { accessorKey: string }).accessorKey;

return {
...col,
enableSorting: Boolean(tableState.sorting.sortingSchema?.[accessorKey]),
};
});
return disabledSorting;
}

return cols;
}, [columns, tableActionsAlignment, features?.dragAndDrop, tableState.dragDrop?.disabledIndices]); // eslint-disable-line
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
columns,
tableActionsAlignment,
tableState.sorting.sortingSchema,
tableState.sorting.useServerSorting,
features?.dragAndDrop,
tableState.dragDrop?.disabledIndices,
]);

const table = useReactTable({
data,
Expand All @@ -83,18 +103,19 @@ export function Table<TData extends _RowData>({
enableRowSelection: true,
enableSorting: true,
manualPagination: true,
manualSorting: tableState.sorting.useServerSorting,

getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),

onSortingChange: tableState.setSorting,
onSortingChange: tableState.sorting.setSorting,
onRowSelectionChange: tableState.setRowSelection,
onPaginationChange: tableState.setPagination,
onColumnVisibilityChange: tableState.setColumnVisibility,

state: {
sorting: tableState.sorting,
sorting: tableState.sorting.sorting,
rowSelection: tableState.rowSelection,
pagination: tableState.pagination,
columnVisibility: tableState.columnVisibility,
Expand Down
32 changes: 30 additions & 2 deletions apps/client/src/hooks/shared/table/use-async-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useFetch from "lib/useFetch";
import { useDebounce } from "react-use";
import { useQuery, type QueryFunctionContext } from "@tanstack/react-query";
import { useList } from "./use-list";
import type { SortingState } from "@tanstack/react-table";

interface FetchOptions {
pageSize?: number;
Expand All @@ -16,6 +17,7 @@ interface FetchOptions {

interface Options<T> {
search?: string;
sortingSchema?: Record<string, string>;

disabled?: boolean;
totalCount?: number;
Expand All @@ -35,6 +37,7 @@ export function useAsyncTable<T>(options: Options<T>) {
});
const { state: loadingState, execute } = useFetch();

const [sorting, setSorting] = React.useState<SortingState>([]);
const [debouncedSearch, setDebouncedSearch] = React.useState(options.search);
const [filters, setFilters] = React.useState<Record<string, any> | null>(null);
const [paginationOptions, setPagination] = React.useState({
Expand All @@ -47,7 +50,13 @@ export function useAsyncTable<T>(options: Options<T>) {
enabled: !options.disabled,
initialData: options.initialData ?? undefined,
queryFn: fetchData,
queryKey: [paginationOptions.pageIndex, debouncedSearch, filters, options.fetchOptions.path],
queryKey: [
paginationOptions.pageIndex,
debouncedSearch,
sorting,
filters,
options.fetchOptions.path,
],
refetchOnMount: options.fetchOptions.refetchOnMount,
refetchOnWindowFocus: options.fetchOptions.refetchOnWindowFocus,
});
Expand All @@ -68,13 +77,25 @@ export function useAsyncTable<T>(options: Options<T>) {
}, [options.initialData]); // eslint-disable-line react-hooks/exhaustive-deps

async function fetchData(context: QueryFunctionContext<any>) {
const [pageIndex, search, _filters] = context.queryKey;
const [pageIndex, search, _sorting, _filters] = context.queryKey;
const path = options.fetchOptions.path;
const skip = Number(pageIndex * paginationOptions.pageSize) || 0;
const filters = _filters || {};
const sorting = [];

const searchParams = new URLSearchParams();

for (const sort of _sorting as SortingState) {
const key = options.sortingSchema?.[sort.id];
if (!key) continue;

sorting.push(`${key}:${sort.desc ? "desc" : "asc"}`);
}

if (sorting.length > 0) {
searchParams.append("sorting", sorting.join(","));
}

filters.query = search;
filters.skip = skip;

Expand Down Expand Up @@ -123,8 +144,15 @@ export function useAsyncTable<T>(options: Options<T>) {
...paginationOptions,
} as const;

const sortingState = {
sorting,
setSorting,
sortingSchema: options.sortingSchema,
} as const;

return {
...list,
sorting: sortingState,
noItemsAvailable: !isInitialLoading && !error && list.items.length <= 0,
isInitialLoading,
filters,
Expand Down
14 changes: 11 additions & 3 deletions apps/client/src/hooks/shared/table/use-table-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface TableStateOptions {
onListChange(list: any[]): void;
disabledIndices?: number[];
};
sorting?: Partial<ReturnType<typeof useAsyncTable>["sorting"]>;
pagination?: Partial<ReturnType<typeof useAsyncTable>["pagination"]>;
defaultHiddenColumns?: string[];
tableId?: string;
Expand All @@ -17,6 +18,7 @@ export function useTableState({
pagination,
dragDrop,
tableId,
sorting,
defaultHiddenColumns,
}: TableStateOptions = {}) {
const isMounted = useMounted();
Expand Down Expand Up @@ -51,7 +53,7 @@ export function useTableState({
}
}, [columnVisibility, tableId]);

const [sorting, setSorting] = React.useState<SortingState>([]);
const [regularSorting, setRegularSorting] = React.useState<SortingState>([]);
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});

const _pagination = {
Expand All @@ -63,10 +65,16 @@ export function useTableState({
error: pagination?.error,
};

const _sorting = {
sorting: sorting?.sorting ?? regularSorting,
setSorting: sorting?.setSorting ?? setRegularSorting,
useServerSorting: Boolean(sorting?.sorting),
sortingSchema: sorting?.sortingSchema,
};

return {
tableId,
sorting,
setSorting,
sorting: _sorting,
rowSelection,
setRowSelection,
pagination: _pagination,
Expand Down
7 changes: 7 additions & 0 deletions apps/client/src/pages/admin/values/[path].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ export default function ValuePath({ pathValues: { totalCount, type, values: data
const [search, setSearch] = React.useState("");
const asyncTable = useAsyncTable({
search,
sortingSchema: {
value: "value.value",
gameHash: "hash",
isDisabled: "value.isDisabled",
createdAt: "value.createdAt",
},
fetchOptions: {
onResponse(json: GetValuesData) {
const [forType] = json;
Expand All @@ -112,6 +118,7 @@ export default function ValuePath({ pathValues: { totalCount, type, values: data
const extraTableHeaders = useTableHeadersOfType(type);
const extraTableData = useTableDataOfType(type);
const tableState = useTableState({
sorting: asyncTable.sorting,
pagination: asyncTable.pagination,
dragDrop: { onListChange: setList },
});
Expand Down
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit aa32ce3

Please sign in to comment.