Skip to content

Commit

Permalink
Merge pull request #1182 from Chia-Network/refactor/table-column-sort
Browse files Browse the repository at this point in the history
feat: add table column sort
  • Loading branch information
wwills2 authored Apr 2, 2024
2 parents a6c1a13 + 6a6862f commit 76bbc66
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 57 deletions.
7 changes: 6 additions & 1 deletion src/renderer/api/cadt/v1/projects/projects.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface GetProjectsParams {
page: number;
orgUid?: string;
search?: string;
order?: string;
}

interface GetProjectsResponse {
Expand All @@ -18,7 +19,7 @@ interface GetProjectsResponse {
const projectsApi = cadtApi.injectEndpoints({
endpoints: (builder) => ({
getProjects: builder.query<GetProjectsResponse, GetProjectsParams>({
query: ({ page, orgUid, search }: GetProjectsParams) => {
query: ({ page, orgUid, search, order }: GetProjectsParams) => {
// Initialize the params object with page and limit
const params: GetProjectsParams & {limit: number} = { page, limit: 10 };

Expand All @@ -30,6 +31,10 @@ const projectsApi = cadtApi.injectEndpoints({
params.search = search.replace(/[^a-zA-Z0-9 _.-]+/, '');
}

if (order) {
params.order = order;
}

return {
url: `${host}/v1/projects`,
params, // Use the constructed params object
Expand Down
7 changes: 6 additions & 1 deletion src/renderer/api/cadt/v1/units/units.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface GetUnitsParams {
page: number;
orgUid?: string;
search?: string;
order?: string;
}

interface GetUnitsResponse {
Expand All @@ -17,7 +18,7 @@ interface GetUnitsResponse {
const unitsApi = cadtApi.injectEndpoints({
endpoints: (builder) => ({
getUnits: builder.query<GetUnitsResponse, GetUnitsParams>({
query: ({ page, orgUid, search }: GetUnitsParams) => {
query: ({ page, orgUid, search, order }: GetUnitsParams) => {
// Initialize the params object with page and limit
const params: GetUnitsParams & {limit: number} = { page, limit: 10 };

Expand All @@ -29,6 +30,10 @@ const unitsApi = cadtApi.injectEndpoints({
params.search = search.replace(/[^a-zA-Z0-9 _.-]+/, '');
}

if (order) {
params.order = order;
}

return {
url: `${host}/v1/units`,
params, // Use the constructed params object
Expand Down
10 changes: 9 additions & 1 deletion src/renderer/components/blocks/tables/ProjectsListTable.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React, { useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
import { DebouncedFunc }from 'lodash'
import { DebouncedFunc } from 'lodash';
import { DataTable, PageCounter, Pagination } from '@/components';

interface TableProps {
data: any[];
isLoading: boolean;
currentPage: number;
onPageChange: DebouncedFunc<(page: any) => void>;
setOrder?: (sort: string) => void;
order?: string;
totalPages: number;
totalCount: number;
}
Expand All @@ -17,9 +19,13 @@ const ProjectsListTable: React.FC<TableProps> = ({
isLoading,
currentPage,
onPageChange,
setOrder,
order,
totalPages,
totalCount,
}) => {


const columns = useMemo(
() => [
{
Expand Down Expand Up @@ -74,6 +80,8 @@ const ProjectsListTable: React.FC<TableProps> = ({
<div className="relative">
<DataTable
columns={columns}
onChangeOrder={setOrder}
order={order}
data={data}
primaryKey="warehouseProjectId"
isLoading={isLoading}
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/components/blocks/tables/UnitsListTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ interface TableProps {
isLoading: boolean;
currentPage: number;
onPageChange: DebouncedFunc<(page: any) => void>;
setOrder?: (sort: string) => void;
order?: string;
totalPages: number;
totalCount: number;
}
Expand All @@ -17,6 +19,8 @@ const UnitsListTable: React.FC<TableProps> = ({
isLoading,
currentPage,
onPageChange,
setOrder,
order,
totalPages,
totalCount,
}) => {
Expand Down Expand Up @@ -75,6 +79,8 @@ const UnitsListTable: React.FC<TableProps> = ({
<DataTable
columns={columns}
data={data}
onChangeOrder={setOrder}
order={order}
primaryKey="warehouseUnitId"
isLoading={isLoading}
footer={
Expand Down
112 changes: 66 additions & 46 deletions src/renderer/components/layout/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FormattedMessage } from 'react-intl';
import { Tooltip } from '@/components';
import SimpleBar from 'simplebar-react';
import 'simplebar/dist/simplebar.min.css';
import { AiOutlineSortAscending, AiOutlineSortDescending } from 'react-icons/ai';

interface Column {
key: string;
Expand All @@ -20,19 +21,21 @@ interface DataTableProps {
data: any[];
isLoading?: boolean;
onRowClick?: (row: any) => void;
onChangeOrder?: (column: string) => void;
order?: string;
footer?: JSX.Element | null;
}

const DataTable: React.FC<DataTableProps> =
({
columns,
primaryKey = 'id',
data,
isLoading = false,
onRowClick = noop,
footer = null,
}) => {

const DataTable: React.FC<DataTableProps> = ({
columns,
primaryKey = 'id',
data,
isLoading = false,
onRowClick = noop,
onChangeOrder,
order,
footer = null,
}) => {
if (isLoading) {
return null;
}
Expand Down Expand Up @@ -80,44 +83,61 @@ const DataTable: React.FC<DataTableProps> =
style={{ height: data.length > 5 ? 'calc(100vh - 265px)' : 'auto' }}
>
<thead className="bg-gray-50 dark:bg-gray-700 sticky top-0 z-10">
<tr>
{columns.map((column) => (
<th
key={column.key}
className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
>
{column.renderHeader ? column.renderHeader(column) : column.title}
</th>
))}
</tr>
</thead>
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
{data?.length > 0 &&
data.map((row, index) => (
<tr
key={row[primaryKey]}
onClick={() => onRowClick(row)}
className={
index % 2 === 0
? 'bg-gray-50 dark:bg-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700'
: 'bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700'
}
>
{columns.map((column) => (
<td key={`${column.key}-${row[primaryKey]}`} className="px-6 py-4 whitespace-normal">
<div className="text-gray-600 dark:text-white">
{column.render ? (
column.render(row)
) : (
<div className="truncate" style={{ maxWidth: '300px' }}>
<Tooltip content={row[column.key]}>{row[column.key]}</Tooltip>
<tr>
{columns.map((column) => {
const isActive = order?.startsWith(column.key);
return (
<th
key={column.key}
style={{ cursor: onChangeOrder ? 'pointer' : 'default' }}
className={`px-6 py-3 text-left text-xs font-medium uppercase tracking-wider ${
isActive
? 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-white'
: 'text-gray-500 dark:text-gray-400'
}`}
onClick={() => onChangeOrder && onChangeOrder(column.key)}
>
<div className="flex items-center justify-between">
<span>{column.renderHeader ? column.renderHeader(column) : column.title}</span>
{order?.includes(column.key) && (
<div style={{ width: '24px', height: '24px', display: 'inline-block' }}>
{order.includes('ASC') && <AiOutlineSortAscending className="w-6 h-6" />}
{order.includes('DESC') && <AiOutlineSortDescending className="w-6 h-6" />}
</div>
)}
</div>
</td>
))}
</tr>
))}
</th>
);
})}
</tr>
</thead>
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
{data?.length > 0 &&
data.map((row, index) => (
<tr
key={row[primaryKey]}
onClick={() => onRowClick(row)}
className={
index % 2 === 0
? 'bg-gray-50 dark:bg-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700'
: 'bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700'
}
>
{columns.map((column) => (
<td key={`${column.key}-${row[primaryKey]}`} className="px-6 py-4 whitespace-normal">
<div className="text-gray-600 dark:text-white">
{column.render ? (
column.render(row)
) : (
<div className="truncate" style={{ maxWidth: '300px' }}>
<Tooltip content={row[column.key]}>{row[column.key]}</Tooltip>
</div>
)}
</div>
</td>
))}
</tr>
))}
</tbody>
</table>
</SimpleBar>
Expand All @@ -135,4 +155,4 @@ const DataTable: React.FC<DataTableProps> =
);
};

export { DataTable };
export { DataTable };
3 changes: 2 additions & 1 deletion src/renderer/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './useUrlHash';
export * from './useQueryParamState';
export * from './useQueryParamState';
export * from './useColumnOrder';
40 changes: 40 additions & 0 deletions src/renderer/hooks/useColumnOrder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useCallback } from 'react';

/**
* A custom hook to manage table column sorting order logic.
* It provides a function to update the order based on user interaction.
* The order cycles through 'ASC', 'DESC', and no order ('') when the same column is clicked.
* Clicking a new column sets the order to 'ASC'.
*
* @param {string} order The current sorting order.
* @param {Function} setOrder The function to update the sorting order.
* @returns {Function} The function to handle updating the order based on column clicks.
*/
function useColumnOrderHandler(order, setOrder) {
const handleSetOrder = useCallback((column) => {
const currentColumn = order.split(':')[0];
const currentDirection = order.split(':')[1];

if (currentColumn === column) {
// Cycle through 'ASC', 'DESC', and no order ('')
switch (currentDirection) {
case 'ASC':
setOrder(`${column}:DESC`);
break;
case 'DESC':
setOrder('');
break;
default:
setOrder(`${column}:ASC`);
break;
}
} else {
// Default to ascending order for a new column
setOrder(`${column}:ASC`);
}
}, [order, setOrder]);

return handleSetOrder;
}

export { useColumnOrderHandler };
2 changes: 1 addition & 1 deletion src/renderer/hooks/useQueryParamState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const useQueryParamState: QueryParamState<string> = (name, defaultValue = '') =>
const location = useLocation();

const setQueryStringParameter = useCallback(
(value: string) => {
(value?: string) => {
const params = new URLSearchParams(window.location.search || window.location.hash.replace(/#.*\?/, ""));

if (_.isNil(value) || value === '') {
Expand Down
9 changes: 7 additions & 2 deletions src/renderer/pages/ProjectsList/ProjectsList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react';
import { useGetProjectsQuery } from '@/api';
import { useQueryParamState } from '@/hooks';
import { useQueryParamState, useColumnOrderHandler } from '@/hooks';
import { debounce } from 'lodash';
import {
OrganizationSelector,
Expand All @@ -15,13 +15,15 @@ const ProjectsList: React.FC = () => {
const [currentPage, setCurrentPage] = useQueryParamState('page', '1');
const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined);
const [search, setSearch] = useQueryParamState('search', undefined);
const [order, setOrder] = useQueryParamState('order', undefined);
const handleSetOrder = useColumnOrderHandler(order, setOrder);

const {
data: projectsData,
isLoading: projectsLoading,
isFetching: projectsFetching,
error: projectsError,
} = useGetProjectsQuery({ page: Number(currentPage), orgUid, search });
} = useGetProjectsQuery({ page: Number(currentPage), orgUid, search, order });

const handlePageChange = useCallback(
debounce((page) => setCurrentPage(page), 800),
Expand All @@ -42,6 +44,7 @@ const ProjectsList: React.FC = () => {
[setSearch, debounce],
);


if (projectsLoading) {
return <SkeletonTable />;
}
Expand Down Expand Up @@ -70,6 +73,8 @@ const ProjectsList: React.FC = () => {
isLoading={projectsLoading}
currentPage={Number(currentPage)}
onPageChange={handlePageChange}
setOrder={handleSetOrder}
order={order}
totalPages={projectsData.pageCount}
totalCount={projectsData.pageCount * 10}
/>
Expand Down
8 changes: 6 additions & 2 deletions src/renderer/pages/UnitsList/UnitsList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback } from 'react';
import { useGetUnitsQuery } from '@/api';
import { useQueryParamState } from '@/hooks';
import { useQueryParamState, useColumnOrderHandler } from '@/hooks';
import { debounce } from 'lodash';
import {
OrganizationSelector,
Expand All @@ -15,13 +15,15 @@ const UnitsList: React.FC = () => {
const [currentPage, setCurrentPage] = useQueryParamState('page', '1');
const [orgUid, setOrgUid] = useQueryParamState('orgUid', undefined);
const [search, setSearch] = useQueryParamState('search', undefined);
const [order, setOrder] = useQueryParamState('order', undefined);
const handleSetOrder = useColumnOrderHandler(order, setOrder);

const {
data: unitsData,
isLoading: unitsLoading,
isFetching: unitsFetching,
error: unitsError,
} = useGetUnitsQuery({ page: Number(currentPage), orgUid, search });
} = useGetUnitsQuery({ page: Number(currentPage), orgUid, search, order });

const handlePageChange = useCallback(
debounce((page) => setCurrentPage(page), 800),
Expand Down Expand Up @@ -70,6 +72,8 @@ const UnitsList: React.FC = () => {
isLoading={unitsLoading}
currentPage={Number(currentPage)}
onPageChange={handlePageChange}
setOrder={handleSetOrder}
order={order}
totalPages={unitsData.pageCount}
totalCount={unitsData.pageCount * 10}
/>
Expand Down
Loading

0 comments on commit 76bbc66

Please sign in to comment.