feat(tables): implement sortable columns across various table components

- Added TableSortProvider to manage sorting state in AdminsTable, CmsTable, CompaniesTable, CustomersTable, TendersTable, and CompanyCategoriesTable.
- Enhanced TableHead component to support sortable columns with visual indicators for sort direction.
- Introduced columnSortKeys and columnDefaultOrder mappings in respective table presenters to define sortable fields and default sorting behavior.
- Updated useCompanyListPresenter, useCmsTablePresenter, useCustomerListPresenter, useTenderListPresenter, and useCompanyCategoriesPresenter to handle sorting logic and state management.
- Improved user experience by allowing dynamic sorting of table data based on user interactions.
This commit is contained in:
AmirReza Jamali
2026-05-31 10:48:08 +03:30
parent 5ef0298840
commit 46ddc018a6
16 changed files with 971 additions and 54 deletions
+27 -9
View File
@@ -22,6 +22,7 @@ import {
TableHead,
TableHeader,
TableRow,
TableSortProvider,
} from "@/components/ui/table";
import TableSkeleton from "@/components/ui/TableSkeleton";
import { _TooltipDefaultParams } from "@/constants/tooltip";
@@ -38,6 +39,11 @@ const CustomersTable = () => {
pathName,
router,
columns,
columnSortKeys,
columnDefaultOrder,
sortBy,
sortOrder,
handleSortChange,
allCustomers,
metadata,
isModalOpen,
@@ -84,18 +90,29 @@ const CustomersTable = () => {
onClearAll={clearAllFilters}
/>
<div ref={tableSectionRef}>
<TableSortProvider
sortKey={sortBy}
sortOrder={sortOrder}
onSortChange={handleSortChange}
>
<Table>
<TableHeader>
<TableRow>
{columns.map((col) => (
<TableHead
colSpan={100}
className="text-start font-extrabold uppercase"
key={col}
>
{col}
</TableHead>
))}
{columns.map((col) => {
const sortKey = columnSortKeys[col];
return (
<TableHead
colSpan={100}
className="text-start font-extrabold uppercase"
key={col}
sortable={Boolean(sortKey)}
sortKey={sortKey}
defaultSortOrder={columnDefaultOrder[col]}
>
{col}
</TableHead>
);
})}
</TableRow>
</TableHeader>
{isPending ? (
@@ -266,6 +283,7 @@ const CustomersTable = () => {
</TableBody>
)}
</Table>
</TableSortProvider>
</div>
<IsVisible condition={shouldShowPagination}>
<Pagination
@@ -1,5 +1,6 @@
"use client";
import { SortDirection } from "@/components/ui/tableSortContext";
import {
useAssignCompany,
useCompanyFullList,
@@ -11,7 +12,7 @@ import { useHybridPagination } from "@/hooks/useHybridPagination";
import { useTableSectionRowAnimations } from "@/hooks/useTableSectionRowAnimations";
import { TAssignCustomerToCompanyCredentials, TCustomer } from "@/lib/api";
import { apiDefaultParams } from "@/constants/Api_Params";
import { deleteEmptyKeys } from "@/utils/shared";
import { buildQueryString, deleteEmptyKeys } from "@/utils/shared";
import { useIsMutating } from "@tanstack/react-query";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -144,6 +145,42 @@ const useCustomerListPresenter = () => {
}
};
/** Column label → BSON `sort_by` field. */
const columnSortKeys: Record<string, string> = {
"full name": "full_name",
email: "email",
"created at": "created_at",
type: "type",
status: "status",
role: "role",
};
/** First-click direction per column. */
const columnDefaultOrder: Record<string, SortDirection> = {
"full name": "asc",
email: "asc",
type: "asc",
role: "asc",
"created at": "desc",
};
const sortBy = (params.sort_by as string) ?? "created_at";
const sortOrder = (params.sort_order as SortDirection) ?? "desc";
const handleSortChange = useCallback(
(key: string, order: SortDirection) => {
const newParams = buildFirstPageParams({
...params,
sort_by: key,
sort_order: order,
});
const cleanedParams = deleteEmptyKeys(newParams);
const queryString = buildQueryString(cleanedParams);
router.push(`${pathName}?${queryString}`);
},
[buildFirstPageParams, params, pathName, router],
);
const customers = data?.data?.customers ?? [];
const shouldShowPagination =
!isPending && (data?.meta?.pages ?? 1) * (data?.meta?.limit ?? 10) > 10;
@@ -153,6 +190,11 @@ const useCustomerListPresenter = () => {
metadata: data?.meta,
currentCustomer,
columns,
columnSortKeys,
columnDefaultOrder,
sortBy,
sortOrder,
handleSortChange,
isPending,
setCurrentCustomer,
selectedCompanies,