Preserve filters and sorts while switching between tables (and load from URL)

[GCU-47]: https://hasurahq.atlassian.net/browse/GCU-47?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7522
GitOrigin-RevId: 92ade94f2fbd6013986b28b5a97ba5683a4fdd2e
This commit is contained in:
Luca Restagno 2023-01-17 20:08:39 +01:00 committed by hasura-bot
parent 611bd0363f
commit 664db29bcc
11 changed files with 829 additions and 50 deletions

View File

@ -3,6 +3,7 @@ import { ReactQueryDecorator } from '@/storybook/decorators/react-query';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { userEvent, waitFor, within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { action } from '@storybook/addon-actions';
import { BrowseRows } from './BrowseRows';
import { handlers } from './__mocks__/handlers.mock';
@ -20,6 +21,7 @@ export const Basic: ComponentStory<typeof BrowseRows> = () => {
table={['Album']}
dataSourceName="sqlite_test"
primaryKeys={[]}
onUpdateOptions={action('onUpdateOptions')}
/>
);
};
@ -30,6 +32,7 @@ export const BasicDisplayTest: ComponentStory<typeof BrowseRows> = () => {
table={['Album']}
dataSourceName="sqlite_test"
primaryKeys={[]}
onUpdateOptions={action('onUpdateOptions')}
/>
);
};

View File

@ -1,7 +1,8 @@
import { getTableDisplayName } from '@/features/DatabaseRelationships';
import { Table } from '@/features/hasura-metadata-types';
import produce from 'immer';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { setWhereAndSortToUrl } from './BrowseRows.utils';
import {
DataGrid,
DataGridOptions,
@ -9,12 +10,13 @@ import {
} from './components/DataGrid/DataGrid';
import { TableTabView } from './components/DataGrid/parts/TableTabView';
interface BrowseRowsProps {
type BrowseRowsProps = {
dataSourceName: string;
table: Table;
options?: DataGridOptions;
primaryKeys: string[];
}
onUpdateOptions: (options: DataGridOptions) => void;
};
type TabState = { name: string; details: DataGridProps; parentValue: string };
type OpenNewRelationshipTabProps = {
@ -94,9 +96,13 @@ const onTabClose = (
}
};
export const BrowseRows = (props: BrowseRowsProps) => {
const { dataSourceName, table, options, primaryKeys } = props;
export const BrowseRows = ({
dataSourceName,
table,
options,
primaryKeys,
onUpdateOptions,
}: BrowseRowsProps) => {
const defaultTabState = {
name: getTableDisplayName(table),
details: { dataSourceName, table, options, primaryKeys: [] as string[] },
@ -117,6 +123,21 @@ export const BrowseRows = (props: BrowseRowsProps) => {
const defaultActiveTab = getTableDisplayName(table);
const [activeTab, setActiveTab] = useState(defaultActiveTab);
useEffect(() => {
setOriginalTableOptions(options);
const currentTab = openTabs[0];
setOpenTabs([
{
...currentTab,
details: {
...currentTab.details,
options,
},
},
]);
}, [options]);
/**
* when relationships are open, disable sorting and searching through all the views
*/
@ -150,43 +171,51 @@ export const BrowseRows = (props: BrowseRowsProps) => {
setActiveTab(`${openTab.name}.${relationshipName}`);
};
const onUpdateOptionsGenerator =
(index: number) =>
(_options: DataGridOptions): void => {
setWhereAndSortToUrl(_options);
setOriginalTableOptions(_options);
setOpenTabs(_openTabs =>
produce(_openTabs, draft => {
draft[index].details.options = _options;
})
);
onUpdateOptions(_options);
};
const tableTabItems = openTabs.map((openTab, index) => {
const innerOnUpdateOptions = onUpdateOptionsGenerator(index);
return {
value: openTab.name,
label: openTab.name,
parentValue: openTab.parentValue,
content: (
<DataGrid
key={JSON.stringify(openTab)}
table={openTab.details.table}
dataSourceName={openTab.details.dataSourceName}
options={openTab.details.options}
activeRelationships={openTab.details.activeRelationships}
onRelationshipOpen={data => openNewRelationshipTab({ data, openTab })}
onRelationshipClose={relationshipName =>
setActiveTab(`${openTab.name}.${relationshipName}`)
}
disableRunQuery={disableRunQuery}
updateOptions={innerOnUpdateOptions}
primaryKeys={primaryKeys}
/>
),
};
});
return (
<div>
<TableTabView
items={openTabs.map((openTab, index) => ({
value: openTab.name,
label: openTab.name,
parentValue: openTab.parentValue,
content: (
<DataGrid
key={JSON.stringify(openTab)}
table={openTab.details.table}
dataSourceName={openTab.details.dataSourceName}
options={openTab.details.options}
activeRelationships={openTab.details.activeRelationships}
onRelationshipOpen={data =>
openNewRelationshipTab({ data, openTab })
}
onRelationshipClose={relationshipName =>
setActiveTab(`${openTab.name}.${relationshipName}`)
}
disableRunQuery={disableRunQuery}
updateOptions={_options => {
if (index === 0) {
// Save a copy of the parent filters before opening
setOriginalTableOptions(_options);
}
setOpenTabs(_openTabs =>
produce(_openTabs, draft => {
draft[index].details.options = _options;
})
);
}}
primaryKeys={primaryKeys}
/>
),
}))}
items={tableTabItems}
activeTab={activeTab}
onTabClick={value => {
setActiveTab(value);

View File

@ -0,0 +1,18 @@
import { DataGridOptions } from './components/DataGrid/DataGrid';
import { applyWhereAndSortConditionsToQueryString } from './components/DataGrid/DataGrid.utils';
export const setWhereAndSortToUrl = (options: DataGridOptions) => {
const searchQueryString = applyWhereAndSortConditionsToQueryString({
options,
search: window.location.search,
});
if (window.history.pushState) {
const {
location: { protocol, host, pathname },
} = window;
const newUrl = `${protocol}//${host}${pathname}?${searchQueryString}`;
window.history.pushState({ path: newUrl }, '', newUrl);
}
};

View File

@ -2,6 +2,7 @@ import { Table } from '@/features/hasura-metadata-types';
import React from 'react';
import { BrowseRows } from '../../BrowseRows';
import { useTableColumns } from '../../hooks';
import { useInitialWhereAndOrderBy } from './hooks/useInitialWhereAndOrderBy';
interface BrowseRowsContainerProps {
table: Table;
@ -22,12 +23,20 @@ export const BrowseRowsContainer = ({
.map(column => column.graphQLProperties?.name)
.filter(columnName => columnName !== undefined) as string[];
const { options, onUpdateOptions } = useInitialWhereAndOrderBy({
columns: tableColumns?.columns,
table,
dataSourceName,
});
return (
<div className="p-2">
<BrowseRows
table={table}
dataSourceName={dataSourceName}
primaryKeys={primaryKeys}
options={options}
onUpdateOptions={onUpdateOptions}
/>
</div>
);

View File

@ -0,0 +1,130 @@
import { TableColumn } from '@/features/DataSource';
import { Table } from '@/features/hasura-metadata-types';
import { getLSItem, setLSItem } from '@/utils';
import { useEffect, useState } from 'react';
import { DataGridOptions } from '../../DataGrid/DataGrid';
import { convertUrlToDataGridOptions } from '../../DataGrid/DataGrid.utils';
type GetUniqueTableKeyProps = {
table: Table;
dataSourceName: string;
};
const getUniqueTableKey = ({
table,
dataSourceName,
}: GetUniqueTableKeyProps) => {
if (Array.isArray(table)) {
return `${dataSourceName}.${table.join('-')}.query`;
}
return `${dataSourceName}.${table}.query`;
};
type WhereAndOrderBy = Pick<DataGridOptions, 'where' | 'order_by'>;
const getWhereAndOrderByFromLocalStorage = ({
table,
dataSourceName,
}: GetUniqueTableKeyProps): WhereAndOrderBy | undefined => {
const localStorageKey = getUniqueTableKey({ table, dataSourceName });
const localUserQueryString = localStorageKey
? getLSItem(localStorageKey)
: '';
if (localUserQueryString) {
return JSON.parse(localUserQueryString) as WhereAndOrderBy;
}
return undefined;
};
type SetWhereAndOrderByToLocalStorage = {
whereAndOrderBy: WhereAndOrderBy;
} & GetUniqueTableKeyProps;
const setWhereAndOrderByToLocalStorage = ({
table,
dataSourceName,
whereAndOrderBy,
}: SetWhereAndOrderByToLocalStorage) => {
const localStorageKey = getUniqueTableKey({ table, dataSourceName });
setLSItem(localStorageKey, JSON.stringify(whereAndOrderBy));
};
type UseInitialWhereAndOrderByProps = {
columns: TableColumn[] | undefined;
table: Table;
dataSourceName: string;
};
export const useInitialWhereAndOrderBy = ({
columns,
table,
dataSourceName,
}: UseInitialWhereAndOrderByProps) => {
const [options, setOptions] = useState<DataGridOptions | undefined>(
undefined
);
const localStorageWhereAndOrderBy = getWhereAndOrderByFromLocalStorage({
table,
dataSourceName,
});
const [initialWhereAndOrderBy] = useState(localStorageWhereAndOrderBy);
const [initialUrlSearchParams] = useState(window.location.search);
useEffect(() => {
if (columns) {
if (initialUrlSearchParams) {
const newOptions = convertUrlToDataGridOptions(
initialUrlSearchParams,
columns
);
const hasWhere = newOptions.where && newOptions.where?.length > 0;
const hasOrderBy =
newOptions.order_by && newOptions.order_by?.length > 0;
if (hasWhere || hasOrderBy) {
setWhereAndOrderByToLocalStorage({
table,
dataSourceName,
whereAndOrderBy: {
where: newOptions?.where || [],
order_by: newOptions?.order_by || [],
},
});
setOptions(newOptions);
return;
}
}
if (initialWhereAndOrderBy) {
const newOptions: DataGridOptions = {
where: initialWhereAndOrderBy.where,
order_by: initialWhereAndOrderBy.order_by,
};
setOptions(newOptions);
}
}
}, [columns]);
const onUpdateOptions = (_options: DataGridOptions) => {
const whereAndOrderBy: WhereAndOrderBy = {
where: _options?.where || [],
order_by: _options?.order_by || [],
};
setWhereAndOrderByToLocalStorage({
table,
dataSourceName,
whereAndOrderBy,
});
};
return {
options,
onUpdateOptions,
};
};

View File

@ -107,6 +107,12 @@ export const DataGrid = (props: DataGridProps) => {
setSorting(DEFAULT_SORT_CLAUSES);
setWhereClauses(DEFAULT_WHERE_CLAUSES);
setOrderClauses(DEFAULT_ORDER_BY_CLAUSES);
updateOptions?.({
limit: pageSize,
offset: pageIndex * pageSize,
where: DEFAULT_WHERE_CLAUSES,
order_by: DEFAULT_ORDER_BY_CLAUSES,
});
};
/**
@ -309,10 +315,24 @@ export const DataGrid = (props: DataGridProps) => {
whereClauses,
supportedOperators: tableColumnQueryResult?.supportedOperators ?? [],
removeWhereClause: id => {
const newWhereClauses = whereClauses.filter((_, i) => i !== id);
setWhereClauses(whereClauses.filter((_, i) => i !== id));
updateOptions?.({
limit: pageSize,
offset: pageIndex * pageSize,
where: newWhereClauses,
order_by: orderByClauses,
});
},
removeOrderByClause: id => {
setOrderClauses(orderByClauses.filter((_, i) => i !== id));
const newOrderByClauses = orderByClauses.filter((_, i) => i !== id);
setOrderClauses(newOrderByClauses);
updateOptions?.({
limit: pageSize,
offset: pageIndex * pageSize,
where: whereClauses,
order_by: newOrderByClauses,
});
},
onExportRows,
onExportSelectedRows,

View File

@ -1,7 +1,18 @@
import { TableRow, WhereClause } from '../../../../features/DataSource';
import {
TableColumn,
TableRow,
WhereClause,
} from '../../../../features/DataSource';
import { DataGridOptions } from './DataGrid';
import {
adaptSelectedRowIdsToWhereClause,
AdaptSelectedRowIdsToWhereClauseArgs,
mapWhereAndSortConditions,
FilterConditions,
replaceFiltersInUrl,
applyWhereAndSortConditionsToQueryString,
convertUrlToDataGridOptions,
convertValueToGraphQL,
} from './DataGrid.utils';
describe('adaptSelectedRowIdsToWhereClause', () => {
@ -50,3 +61,396 @@ describe('adaptSelectedRowIdsToWhereClause', () => {
).toEqual(expected);
});
});
describe('mapWhereAndSortConditions', () => {
describe('when where and sort conditions are defined', () => {
it('returns the query string', () => {
const options: DataGridOptions = {
limit: 0,
offset: 0,
where: [
{
AlbumId: {
_gte: 2,
},
},
{
Title: {
_like: '%foo%',
},
},
],
order_by: [
{
column: 'AlbumId',
type: 'desc',
},
{
column: 'Title',
type: 'asc',
},
],
};
expect(mapWhereAndSortConditions(options)).toEqual([
{
filter: 'AlbumId;_gte;2',
},
{
filter: 'Title;_like;%foo%',
},
{
sort: 'AlbumId;desc',
},
{
sort: 'Title;asc',
},
]);
});
});
describe('when only where conditions are defined', () => {
it('returns the query string', () => {
const options: DataGridOptions = {
limit: 0,
offset: 0,
where: [
{
AlbumId: {
_gte: 2,
},
},
{
Title: {
_like: '%foo%',
},
},
],
};
expect(mapWhereAndSortConditions(options)).toEqual([
{
filter: 'AlbumId;_gte;2',
},
{
filter: 'Title;_like;%foo%',
},
]);
});
});
describe('when only sort conditions are defined', () => {
it('returns the query string', () => {
const options: DataGridOptions = {
limit: 0,
offset: 0,
order_by: [
{
column: 'AlbumId',
type: 'desc',
},
{
column: 'Title',
type: 'asc',
},
],
};
expect(mapWhereAndSortConditions(options)).toEqual([
{
sort: 'AlbumId;desc',
},
{
sort: 'Title;asc',
},
]);
});
});
});
describe('replaceFiltersInUrl', () => {
describe('when filter and sort conditions are provided', () => {
it('returns the query string', () => {
const filterConditions: FilterConditions = [
{ filter: 'AlbumId;_gte;1' },
{ filter: 'Title;_like;%foo%' },
{ sort: 'AlbumId;asc' },
];
expect(
replaceFiltersInUrl('?database=Chinook&table=Album', filterConditions)
).toBe(
'database=Chinook&table=Album&filter=AlbumId%3B_gte%3B1&filter=Title%3B_like%3B%25foo%25&sort=AlbumId%3Basc'
);
});
});
});
describe('applyWhereAndSortConditionsToQueryString', () => {
it('returns the query string', () => {
const options: DataGridOptions = {
limit: 0,
offset: 0,
where: [
{
AlbumId: {
_gte: 2,
},
},
{
Title: {
_like: '%foo%',
},
},
],
order_by: [
{
column: 'AlbumId',
type: 'desc',
},
{
column: 'Title',
type: 'asc',
},
],
};
const search = '?database=Chinook&table=%5B%22Album%22%5D';
expect(
applyWhereAndSortConditionsToQueryString({
options,
search,
})
).toBe(
'database=Chinook&table=%5B%22Album%22%5D&filter=AlbumId%3B_gte%3B2&filter=Title%3B_like%3B%25foo%25&sort=AlbumId%3Bdesc&sort=Title%3Basc'
);
});
});
describe('convertUrlToDataGridOptions', () => {
describe('when filters and sort are defined', () => {
it('returns the options', () => {
const search =
'database=Chinook&table=Album&filter=AlbumId%3B_gte%3B1&filter=Title%3B_like%3B%25foo%25&sort=AlbumId%3Basc';
const expected: DataGridOptions = {
where: [
{
AlbumId: {
_gte: '1',
},
},
{
Title: {
_like: '%foo%',
},
},
],
order_by: [
{
column: 'AlbumId',
type: 'asc',
},
],
};
expect(convertUrlToDataGridOptions(search)).toEqual(expected);
});
});
describe('when filters are defined', () => {
it('returns the options', () => {
const search =
'database=Chinook&table=Album&filter=AlbumId%3B_gte%3B1&filter=Title%3B_like%3B%25foo%25';
const expected: DataGridOptions = {
where: [
{
AlbumId: {
_gte: '1',
},
},
{
Title: {
_like: '%foo%',
},
},
],
order_by: [],
};
expect(convertUrlToDataGridOptions(search)).toEqual(expected);
});
});
describe('when sort are defined', () => {
it('returns the options', () => {
const search =
'database=Chinook&table=Album&sort=AlbumId%3Basc&sort=Title%3Bdesc';
const expected: DataGridOptions = {
where: [],
order_by: [
{
column: 'AlbumId',
type: 'asc',
},
{
column: 'Title',
type: 'desc',
},
],
};
expect(convertUrlToDataGridOptions(search)).toEqual(expected);
});
});
describe('when filters and sort are not defined', () => {
it('returns the options', () => {
const search = 'database=Chinook&table=Album';
const expected: DataGridOptions = {
where: [],
order_by: [],
};
expect(convertUrlToDataGridOptions(search)).toEqual(expected);
});
});
describe('when table columns are provided', () => {
it('returns the options', () => {
const search =
'database=Chinook&table=Album&filter=AlbumId%3B_gte%3B1&filter=Title%3B_like%3B%25foo%25&sort=AlbumId%3Basc';
const expected: DataGridOptions = {
where: [
{
AlbumId: {
_gte: 1,
},
},
{
Title: {
_like: '%foo%',
},
},
],
order_by: [
{
column: 'AlbumId',
type: 'asc',
},
],
};
const tableColumns: TableColumn[] = [
{
name: 'AlbumId',
dataType: 'number',
graphQLProperties: { name: 'AlbumId', scalarType: 'decimal' },
},
{
name: 'Title',
dataType: 'string',
graphQLProperties: { name: 'Title', scalarType: 'String' },
},
];
expect(convertUrlToDataGridOptions(search, tableColumns)).toEqual(
expected
);
});
});
});
describe('convertValueToGraphQL', () => {
it('converts decimal', () => {
const value = '1';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'number',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'decimal',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe(1);
});
it('converts float', () => {
const value = '1';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'number',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'float',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe(1);
});
it('converts boolean', () => {
const value = 'true';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'bool',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'boolean',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe(true);
});
it('converts string', () => {
const value = 'aString';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'string',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'string',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe('aString');
});
it('converts array of strings', () => {
const value = '[1, 2, 3, 4]';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'string',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'string',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe('["1","2","3","4"]');
});
it('converts array of int', () => {
const value = '[1, 2, 3, 4]';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'number',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'int',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe('[1,2,3,4]');
});
it('converts array of float', () => {
const value = '[1.1, 2.2, 3.3, 4.4]';
const tableColumn: TableColumn = {
name: 'AlbumId',
dataType: 'number',
graphQLProperties: {
name: 'AlbumId',
scalarType: 'float',
},
};
expect(convertValueToGraphQL(value, tableColumn)).toBe('[1.1,2.2,3.3,4.4]');
});
});

View File

@ -1,4 +1,5 @@
import { TableRow, WhereClause } from '@/features/DataSource';
import { TableRow, WhereClause, TableColumn } from '@/features/DataSource';
import { DataGridOptions } from './DataGrid';
export type AdaptSelectedRowIdsToWhereClauseArgs = {
rowsId: Record<number, boolean>;
@ -39,3 +40,167 @@ export const adaptSelectedRowIdsToWhereClause = ({
return whereClause;
};
export type FilterConditions = ({ filter: string } | { sort: string })[];
export const mapWhereAndSortConditions = (
options: DataGridOptions
): FilterConditions => {
const { where = [], order_by = [] } = options;
const whereQueryString = where.map(whereCondition => {
const columnName = Object.keys(whereCondition)[0];
const operator = Object.keys(whereCondition[columnName])[0];
const value = whereCondition[columnName][operator];
const filterQueryString = `${columnName};${operator};${value}`;
return { filter: filterQueryString };
});
const orderQueryString = order_by.map(orderCondition => {
const columnName = orderCondition.column;
const sortOrder = orderCondition.type;
const sortQueryString = `${columnName};${sortOrder}`;
return { sort: sortQueryString };
});
return [...whereQueryString, ...orderQueryString];
};
export const replaceFiltersInUrl = (
currentSearch: string,
newFilterConditions: FilterConditions
): string => {
const searchParams = new URLSearchParams(currentSearch);
searchParams.delete('filter');
searchParams.delete('sort');
newFilterConditions.forEach(newFilterCondition => {
if ('filter' in newFilterCondition) {
searchParams.append('filter', newFilterCondition.filter);
}
if ('sort' in newFilterCondition) {
searchParams.append('sort', newFilterCondition.sort);
}
});
return searchParams.toString();
};
type Args = {
options: DataGridOptions;
search: string;
};
export const applyWhereAndSortConditionsToQueryString = ({
options,
search,
}: Args) => {
const whereAndSortMap = mapWhereAndSortConditions(options);
const searchQueryString = replaceFiltersInUrl(search, whereAndSortMap);
return searchQueryString;
};
export const convertValueToGraphQL = (
value: string,
column: TableColumn
): number | string | boolean => {
const scalarType = column.graphQLProperties?.scalarType || column.dataType;
if (value.includes('[')) {
const values = value.replace('[', '').replace(']', '').split(',');
if (scalarType === 'decimal' || scalarType === 'float') {
return JSON.stringify(values.map(_value => parseFloat(_value)));
}
if (scalarType === 'int') {
return JSON.stringify(values.map(_value => parseInt(_value, 10)));
}
if (scalarType === 'string') {
return JSON.stringify(values.map(_value => _value.trim().toString()));
}
}
if (scalarType === 'decimal' || scalarType === 'float') {
return parseFloat(value);
}
if (scalarType === 'int') {
return parseInt(value, 10);
}
if (scalarType === 'boolean') {
return value === 'true';
}
return value;
};
export const convertUrlToDataGridOptions = (
search: string,
columns: TableColumn[] | undefined = []
): DataGridOptions => {
const searchParams = new URLSearchParams(search);
const baseOption: DataGridOptions = {
where: [],
order_by: [],
};
const searchParamsArray: [string, string][] = Array.from(
searchParams.entries()
);
return searchParamsArray.reduce<DataGridOptions>((acc, value) => {
const key = value[0];
if (key === 'database' || key === 'table') {
return acc;
}
if (key === 'filter') {
const where = acc?.where || [];
const [columnName, operator, filterValue] = value[1].split(';');
const column = columns.find(_column => _column.name === columnName);
const convertedValue = column
? convertValueToGraphQL(filterValue, column)
: filterValue;
return {
...acc,
where: [
...where,
{
[columnName]: {
[operator]: convertedValue,
},
},
],
};
}
if (key === 'sort') {
const order_by = acc?.order_by || [];
const [columnName, orderType] = value[1].split(';');
if (orderType === 'asc' || orderType === 'desc') {
return {
...acc,
order_by: [
...order_by,
{
column: columnName,
type: orderType,
},
],
};
}
}
return acc;
}, baseOption);
};

View File

@ -12,6 +12,7 @@ import {
FaFilter,
FaRegTimesCircle,
FaSearch,
FaSortAmountDownAlt,
FaSortAmountUpAlt,
FaTimes,
} from 'react-icons/fa';
@ -95,7 +96,11 @@ const DisplayOrderByClauses = ({
<Badge color="yellow" key={id}>
<div className={`gap-3 ${twFlexCenter}`}>
<span className={`min-h-3 ${twFlexCenter}`}>
<FaSortAmountUpAlt />
{orderByClause.type === 'desc' ? (
<FaSortAmountDownAlt />
) : (
<FaSortAmountUpAlt />
)}
</span>
<span className={twFlexCenter}>
{orderByClause.column} ({orderByClause.type})

View File

@ -1,6 +1,6 @@
import React from 'react';
import { DropdownMenu } from '@/new-components/DropdownMenu';
import { FaEllipsisV } from 'react-icons/fa';
import { RiMore2Fill } from 'react-icons/ri';
export const RowOptionsButton: React.VFC<{
row: Record<string, any>;
@ -19,7 +19,7 @@ export const RowOptionsButton: React.VFC<{
>
<div className="flex items-start">
<div className="mx-2 my-1 cursor-pointer group-hover:opacity-100">
<FaEllipsisV />
<RiMore2Fill size="14px" />
</div>
</div>
</DropdownMenu>

View File

@ -25,8 +25,6 @@ export const fetchRows = async ({
table,
});
console.log('>>>', columns, tableColumns);
const result = await DataSource(httpClient).getTableRows({
dataSourceName,
table,
@ -34,8 +32,6 @@ export const fetchRows = async ({
options,
});
console.log('>>>', result);
return result;
};