Refactor table editor filter state to store as URL params

This commit is contained in:
Joshen Lim committed 2022-06-27 16:59:12 +08:00
1 parent d960037708
commit de6231198d
13 files changed
+257 -315

No files matched your search

+21 -13
View File
@@ -1,4 +1,5 @@
import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react'
import { isEqual } from 'lodash'
import { createPortal } from 'react-dom'
import { useMonaco } from '@monaco-editor/react'
import { DndProvider } from 'react-dnd'
@@ -19,10 +20,12 @@ import { cleanupProps, initTable, saveStorageDebounced } from './SupabaseGrid.ut
import { useUrlState } from 'hooks'
/**
* Supabase Grid.
*
* React component to render database table.
* Supabase Grid: React component to render database table.
*/
// [JOSHEN TODO] Updating of rows when filters updating feels very choppy
// Rows do not get updated immediately for some reason despite a short debounce
export const SupabaseGrid = forwardRef<SupabaseGridRef, SupabaseGridProps>((props, ref) => {
const monaco = useMonaco()
const _props = cleanupProps(props)
@@ -60,8 +63,9 @@ const SupabaseGridLayout = forwardRef<SupabaseGridRef, SupabaseGridProps>((props
const { editable, storageRef, gridProps, headerActions } = props
const dispatch = useDispatch()
const state = useTrackedState()
const gridRef = useRef<DataGridHandle>(null)
const [mounted, setMount] = useState(false)
const [mounted, setMounted] = useState(false)
const [{ sort: sorts, filter: filters }, setParams] = useUrlState({
arrayKeys: ['sort', 'filter'],
@@ -83,19 +87,23 @@ const SupabaseGridLayout = forwardRef<SupabaseGridRef, SupabaseGridProps>((props
}))
useEffect(() => {
if (!mounted) setMount(true)
if (!mounted) setMounted(true)
}, [])
// [Joshen] This where we refetch the data is say a sort or filter was updated
useEffect(() => {
if (state.refreshPageFlag == REFRESH_PAGE_IMMEDIATELY) {
console.log('Refresh page')
fetchPage(state, dispatch)
} else if (state.refreshPageFlag != 0) {
refreshPageDebounced(state, dispatch)
fetchPage(state, dispatch, sorts as string[], filters as string[])
} else if (state.refreshPageFlag !== 0) {
refreshPageDebounced(state, dispatch, sorts as string[], filters as string[])
}
}, [state.refreshPageFlag])
useEffect(() => {
if (mounted) {
dispatch({ type: 'UPDATE_FILTERS_SORTS', payload: {} })
}
}, [JSON.stringify(filters)])
useEffect(() => {
if (state.isInitialComplete && storageRef && state.table) {
saveStorageDebounced(state, storageRef)
@@ -104,14 +112,14 @@ const SupabaseGridLayout = forwardRef<SupabaseGridRef, SupabaseGridProps>((props
state.table,
state.isInitialComplete,
state.gridColumns,
state.sorts,
state.filters,
state.sorts, // [JOSHEN TODO] To update accordingly
// state.filters, // [JOSHEN TODO] To update accordingly
storageRef,
])
useEffect(() => {
if (state.totalRows === TOTAL_ROWS_RESET) {
fetchCount(state, dispatch)
fetchCount(state, dispatch, filters as string[])
}
}, [state.totalRows])
+49 -38
View File
@@ -3,9 +3,10 @@ import { uuidv4 } from 'lib/helpers'
import { STORAGE_KEY_PREFIX } from './constants'
import { IMetaService } from './services/meta'
import { InitialStateType } from './store/reducers'
import { Dictionary, SupabaseGridProps, SupaColumn, SupaTable } from './types'
import { Dictionary, Sort, SupabaseGridProps, SupaColumn, SupaTable } from './types'
import { getGridColumns } from './utils/gridColumns'
import { FilterOperatorOptions } from './components/header/filter'
import { Filter } from 'components/grid/types'
export function defaultErrorHandler(error: any) {
console.log('Supabase grid error: ', error)
@@ -31,6 +32,41 @@ export function cleanupProps(props: SupabaseGridProps) {
}
}
// [JOSHEN TODO] Write tests for this
export function formatSortURLParams(sort?: string[]) {
return (
Array.isArray(sort)
? sort
.map((s) => {
const [column, order] = s.split(':')
// Reject any possible malformed sort param
if (!column || !order) return undefined
else return { column, ascending: order === 'asc' }
})
.filter((s) => s !== undefined)
: []
) as Sort[]
}
// [JOSHEN TODO] Write tests for this
export function formatFilterURLParams(filter?: string[]): Filter[] {
return (
Array.isArray(filter)
? filter
.map((f) => {
const [column, operatorAbbrev, value] = f.split(':')
const operator = FilterOperatorOptions.find(
(option) => option.abbrev === operatorAbbrev
)
// Reject any possible malformed filter param
if (!column || !operatorAbbrev || !operator) return undefined
else return { id: uuidv4(), column, operator: operator.value, value: value || '' }
})
.filter((f) => f !== undefined)
: []
) as Filter[]
}
export function initTable(
props: SupabaseGridProps,
state: InitialStateType,
@@ -45,43 +81,17 @@ export function initTable(
onAddColumn: props.editable ? props.onAddColumn : undefined,
})
// [JOSHEN WE LEFT OFF HERE TO FIGURE OUT HOW TO REPLACE EXISTING FILTER AND SORT]
const sorts = Array.isArray(sort)
? sort
.map((s) => {
const [column, order] = s.split(':')
// Reject any possible malformed sort param
if (!column || !order) return undefined
else return { column, ascending: order === 'asc' }
})
.filter((s) => s !== undefined)
: []
const savedState = props.storageRef
? onLoadStorage(props.storageRef, table.name, table.schema)
: undefined
const filters = Array.isArray(filter)
? filter
.map((f) => {
const [column, operatorAbbrev, value] = f.split(':')
const operator = FilterOperatorOptions.find(
(option) => option.abbrev === operatorAbbrev
)
// Reject any possible malformed filter param
if (!column || !operatorAbbrev || !operator || !value) return undefined
else return { id: uuidv4(), column, operator: operator.value, value }
})
.filter((f) => f !== undefined)
: []
let savedState
if (props.storageRef) {
savedState = onLoadStorage(props.storageRef, table.name, table.schema)
}
console.log('initTable', savedState, {
sort,
sorts,
filter,
filters,
})
// [JOSHEN TODO] Temp force update savedState to use URL param
// Load sort and filters via URL param only if given, otherwise get
// from saved state in local storage
const sorts = formatSortURLParams(sort)
const filters = formatFilterURLParams(filter)
if (sorts.length > 0) savedState.sorts = sorts
if (filters.length > 0) savedState.filters = filters
dispatch({
type: 'INIT_TABLE',
@@ -227,13 +237,14 @@ export function onLoadStorage(storageRef: string, tableName: string, schema?: st
export const saveStorageDebounced = AwesomeDebouncePromise(saveStorage, 500)
// [JOSHEN TODO] Need to fix this
function saveStorage(state: InitialStateType, storageRef: string) {
if (!state.table) return
const config = {
gridColumns: state.gridColumns,
sorts: state.sorts,
filters: state.filters,
// filters: state.filters,
}
const storageKey = getStorageKey(STORAGE_KEY_PREFIX, storageRef)
const savedStr = localStorage.getItem(storageKey)
@@ -1,19 +0,0 @@
import AwesomeDebouncePromise from 'awesome-debounce-promise';
import { Filter } from '../../../types';
const updateFilterValue = (
payload: {
filterIdx: number;
value: Filter;
},
dispatch: (value: unknown) => void
) => {
dispatch({
type: 'UPDATE_FILTER',
payload: payload,
});
};
export const updateFilterValueDebounced = AwesomeDebouncePromise(
updateFilterValue,
550
);
@@ -1,14 +1,16 @@
import { FC } from 'react'
import { Button, IconPlus, IconFilter, Popover } from '@supabase/ui'
import { uuidv4 } from 'lib/helpers'
import { useDispatch, useTrackedState } from '../../../store'
import { useUrlState } from 'hooks'
import FilterRow from './FilterRow'
import { useTrackedState } from 'components/grid/store'
import { formatFilterURLParams } from 'components/grid/SupabaseGrid.utils'
const FilterPopover: FC = () => {
const state = useTrackedState()
const [{ filter: filters }]: any = useUrlState({ arrayKeys: ['filter'] })
const btnText =
state.filters.length > 0
? `Filtered by ${state.filters.length} rule${state.filters.length > 1 ? 's' : ''}`
filters.length > 0
? `Filtered by ${filters.length} rule${filters.length > 1 ? 's' : ''}`
: 'Filter'
return (
@@ -31,27 +33,32 @@ export default FilterPopover
const Filter: FC = () => {
const state = useTrackedState()
const dispatch = useDispatch()
const [{ filter: filters }, setParams] = useUrlState({ arrayKeys: ['filter'] })
const formattedFilters = formatFilterURLParams(filters as string[])
function onAddFilter() {
dispatch({
type: 'ADD_FILTER',
payload: {
id: uuidv4(),
column: state.table?.columns[0].name,
operator: '=',
value: '',
},
setParams((prevParams) => {
const existingFilters = (prevParams?.filter ?? []) as string[]
const column = state.table?.columns[0].name
return {
...prevParams,
filter: existingFilters.concat([`${column}:eq:`]),
}
})
}
return (
<div className="space-y-2 py-2">
<div className="space-y-2">
{state.filters.map((filter, index) => (
<FilterRow key={`filter-${filter.id}`} filterIdx={index} now={Date.now()} />
{formattedFilters.map((filter, index) => (
<FilterRow
key={`filter-${(filters as string[])[index]}-${[index]}`}
filter={filter}
filterIdx={index}
/>
))}
{state.filters.length == 0 && (
{formattedFilters.length == 0 && (
<div className="space-y-1 px-3">
<h5 className="text-scale-1100 text-sm">No filters applied to this view</h5>
<p className="text-scale-900 text-xs">Add a column below to filter the view</p>
@@ -1,23 +1,24 @@
import { FC, memo, useState, useEffect, ChangeEvent } from 'react'
import { debounce } from 'lodash'
import { FC, memo, useState, useEffect, ChangeEvent, useCallback } from 'react'
import { Button, Input, IconChevronDown, IconX } from '@supabase/ui'
import { DropdownControl } from '../../common'
import { useDispatch, useTrackedState } from '../../../store'
import { FilterOperatorOptions } from './Filter.constants'
import { updateFilterValueDebounced } from './Filter.utils'
/**
* use `now` to trigger re-render as filterIdx won't change value
* if not filterText state will not updated on delete filter
*/
import { useUrlState } from 'hooks'
import { Filter } from 'components/grid/types'
import { DropdownControl } from 'components/grid/components/common'
import { useTrackedState } from 'components/grid/store'
import { FilterOperatorOptions } from './Filter.constants'
type FilterRowProps = {
filter: Filter
filterIdx: number
now: number
}
const FilterRow: FC<FilterRowProps> = ({ filterIdx }) => {
// [JOSHEN TODO] Area of improvement: Input field loses focus after the debounce (because of useUrlState?)
// [JOSHEN TODO] Area of improvement: Pretty sure we can refactor for DRY for updates on each column, operator and value
const FilterRow: FC<FilterRowProps> = ({ filter, filterIdx }) => {
const state = useTrackedState()
const dispatch = useDispatch()
const filter = state.filters[filterIdx]
const [_, setParams] = useUrlState()
const column = state.table?.columns.find((x) => x.name === filter.column)
const columnOptions =
state.table?.columns?.map((x) => {
@@ -26,43 +27,87 @@ const FilterRow: FC<FilterRowProps> = ({ filterIdx }) => {
const [filterValue, setFilterValue] = useState(filter.value)
useEffect(() => {
const filter = state.filters[filterIdx]
setFilterValue(filter.value)
}, [filterIdx])
function onRemoveFilter() {
setParams((prevParams) => {
const existingFilters = (prevParams?.filter ?? []) as string[]
const updatedFilters = existingFilters.filter((filter: string, idx: number) => {
if (idx !== filterIdx) return filter
})
return {
...prevParams,
filter: updatedFilters,
}
})
}
function onColumnChange(column: string | number) {
dispatch({
type: 'UPDATE_FILTER',
payload: { filterIdx, value: { ...filter, column } },
setParams((prevParams) => {
const existingFilters = (prevParams?.filter ?? []) as string[]
const updatedFilters = existingFilters.map((filter: string, idx: number) => {
if (idx === filterIdx) {
const [_, operator, value] = filter.split(':')
return `${column}:${operator}:${value}`
} else {
return filter
}
})
return {
...prevParams,
filter: updatedFilters,
}
})
}
function onOperatorChange(operator: string | number) {
dispatch({
type: 'UPDATE_FILTER',
payload: { filterIdx, value: { ...filter, operator } },
setParams((prevParams) => {
const existingFilters = (prevParams?.filter ?? []) as string[]
const updatedFilters = existingFilters.map((filter: string, idx: number) => {
if (idx === filterIdx) {
const [column, _, value] = filter.split(':')
const selectedOperator = FilterOperatorOptions.find((option) => option.value === operator)
return `${column}:${selectedOperator?.abbrev}:${value}`
} else {
return filter
}
})
return {
...prevParams,
filter: updatedFilters,
}
})
}
function onFilterChange(event: ChangeEvent<HTMLInputElement>) {
function onValueChange(event: ChangeEvent<HTMLInputElement>) {
const value = event.target.value
setFilterValue(value)
updateFilterValueDebounced(
{
filterIdx,
value: { ...filter, value: value },
},
dispatch
)
}
function onRemoveFilter() {
dispatch({
type: 'REMOVE_FILTER',
payload: { index: filterIdx },
debounceHandler({
filterIdx,
value: { ...filter, value: value },
})
}
const updateFilterValue = (payload: { filterIdx: number; value: Filter }) => {
setParams((prevParams) => {
const existingFilters = (prevParams?.filter ?? []) as string[]
const updatedFilters = existingFilters.map((filter: string, idx: number) => {
if (idx === filterIdx) {
const [column, operator] = filter.split(':')
return `${column}:${operator}:${payload.value.value}`
} else {
return filter
}
})
return {
...prevParams,
filter: updatedFilters,
}
})
}
const debounceHandler = useCallback(debounce(updateFilterValue, 600), [])
return (
<div className="sb-grid-filter-row px-3">
<DropdownControl align="start" options={columnOptions} onSelect={onColumnChange}>
@@ -97,7 +142,7 @@ const FilterRow: FC<FilterRowProps> = ({ filterIdx }) => {
className="w-full"
placeholder="Enter a value"
value={filterValue}
onChange={onFilterChange}
onChange={onValueChange}
/>
<Button
icon={<IconX strokeWidth={1.5} size={14} />}
+12 -15
View File
@@ -1,37 +1,34 @@
import { useReducer } from 'react';
import { createContainer } from 'react-tracked';
import { useReducer } from 'react'
import { createContainer } from 'react-tracked'
import {
BaseReducer,
CallbackReducer,
ColumnReducer,
FilterReducer,
RowReducer,
SortReducer,
initialState,
InitialStateType,
} from './reducers';
} from './reducers'
const combineReducers = (...reducers: Function[]) => (
state: any = initialState,
action: any
): any => {
for (let i = 0; i < reducers.length; i++) state = reducers[i](state, action);
return state;
};
const combineReducers =
(...reducers: Function[]) =>
(state: any = initialState, action: any): any => {
for (let i = 0; i < reducers.length; i++) state = reducers[i](state, action)
return state
}
const storeReducers = combineReducers(
BaseReducer,
CallbackReducer,
ColumnReducer,
FilterReducer,
RowReducer,
SortReducer
);
)
const useValue = () => useReducer(storeReducers, initialState);
const useValue = () => useReducer(storeReducers, initialState)
export const {
Provider: StoreProvider,
useTrackedState,
useUpdate: useDispatch,
} = createContainer<InitialStateType, React.Dispatch<any>, unknown>(useValue);
} = createContainer<InitialStateType, React.Dispatch<any>, unknown>(useValue)
+42 -31
View File
@@ -1,16 +1,16 @@
import { CalculatedColumn } from '@supabase/react-data-grid';
import { GridProps, SavedState, SupaTable } from '../../types';
import { REFRESH_PAGE_IMMEDIATELY } from '../../constants';
import { IRowService, SqlRowService } from '../../services/row';
import { IMetaService, SqlMetaService } from '../../services/meta';
import { CalculatedColumn } from '@supabase/react-data-grid'
import { GridProps, SavedState, SupaTable } from '../../types'
import { REFRESH_PAGE_IMMEDIATELY, TOTAL_ROWS_RESET } from '../../constants'
import { IRowService, SqlRowService } from '../../services/row'
import { IMetaService, SqlMetaService } from '../../services/meta'
export interface BaseInitialState {
table: SupaTable | null;
metaService: IMetaService | null;
rowService: IRowService | null;
refreshPageFlag: number;
isInitialComplete: boolean;
editable: boolean;
table: SupaTable | null
metaService: IMetaService | null
rowService: IRowService | null
refreshPageFlag: number
isInitialComplete: boolean
editable: boolean
}
export const baseInitialState: BaseInitialState = {
@@ -20,29 +20,33 @@ export const baseInitialState: BaseInitialState = {
refreshPageFlag: 0,
isInitialComplete: false,
editable: false,
};
}
export type INIT_ACTIONTYPE =
| {
type: 'INIT_CLIENT';
type: 'INIT_CLIENT'
payload: {
onSqlQuery: (query: string) => Promise<{ data?: any; error?: any }>;
};
onSqlQuery: (query: string) => Promise<{ data?: any; error?: any }>
}
}
| {
type: 'INIT_TABLE';
type: 'INIT_TABLE'
payload: {
table: SupaTable;
gridColumns: CalculatedColumn<any, any>[];
gridProps?: GridProps;
savedState?: SavedState;
editable?: boolean;
onSqlQuery: (query: string) => Promise<{ data?: any; error?: any }>;
onError: (error: any) => void;
};
};
table: SupaTable
gridColumns: CalculatedColumn<any, any>[]
gridProps?: GridProps
savedState?: SavedState
editable?: boolean
onSqlQuery: (query: string) => Promise<{ data?: any; error?: any }>
onError: (error: any) => void
}
}
| {
type: 'UPDATE_FILTERS_SORTS'
payload: {}
}
type BASE_ACTIONTYPE = INIT_ACTIONTYPE;
type BASE_ACTIONTYPE = INIT_ACTIONTYPE
const BaseReducer = (state: BaseInitialState, action: BASE_ACTIONTYPE) => {
switch (action.type) {
@@ -50,7 +54,7 @@ const BaseReducer = (state: BaseInitialState, action: BASE_ACTIONTYPE) => {
return {
...state,
metaService: new SqlMetaService(action.payload.onSqlQuery),
};
}
}
case 'INIT_TABLE': {
return {
@@ -64,11 +68,18 @@ const BaseReducer = (state: BaseInitialState, action: BASE_ACTIONTYPE) => {
refreshPageFlag: REFRESH_PAGE_IMMEDIATELY,
isInitialComplete: true,
editable: action.payload.editable || false,
};
}
}
case 'UPDATE_FILTERS_SORTS': {
const newState: any = { ...state }
newState.page = 1
newState.refreshPageFlag = Date.now()
newState.totalRows = TOTAL_ROWS_RESET
return newState
}
default:
return state;
return state
}
};
}
export default BaseReducer;
export default BaseReducer
@@ -1,110 +0,0 @@
import update from 'immutability-helper'
import { TOTAL_ROWS_RESET } from '../../constants'
import { Filter } from '../../types'
import { getInitialFilters } from '../../utils'
import { INIT_ACTIONTYPE } from './base'
export interface FilterInitialState {
filters: Filter[]
}
export const filterInitialState: FilterInitialState = { filters: [] }
type FILTER_ACTIONTYPE =
| INIT_ACTIONTYPE
| {
type: 'SET_FILTERS'
payload: Filter[]
}
| {
type: 'ADD_FILTER'
payload: Filter
}
| {
type: 'REMOVE_FILTER'
payload: { index: number }
}
| {
type: 'UPDATE_FILTER'
payload: {
filterIdx: number
value: Filter
}
}
const FilterReducer = (state: FilterInitialState, action: FILTER_ACTIONTYPE) => {
switch (action.type) {
case 'INIT_TABLE': {
console.log('Filter Reducer INIT_TABLE', {
savedState: action.payload.savedState,
filters: getInitialFilters(action.payload.table, action.payload.savedState),
})
return {
...state,
filters: getInitialFilters(action.payload.table, action.payload.savedState),
}
}
case 'SET_FILTERS':
return {
...state,
filters: action.payload,
}
case 'ADD_FILTER': {
const isValid = isValidFilter(action.payload)
const newState: any = {
...state,
filters: update(state.filters, { $push: [action.payload] }),
}
if (isValid) {
newState.page = 1
newState.refreshPageFlag = Date.now()
newState.totalRows = TOTAL_ROWS_RESET
}
return newState
}
case 'REMOVE_FILTER': {
const removeIdx = action.payload.index
const removeFilter = state.filters[removeIdx]
const isValid = isValidFilter(removeFilter)
const newState: any = {
...state,
filters: update(state.filters, {
$splice: [[removeIdx, 1]],
}),
}
if (isValid) {
newState.page = 1
newState.refreshPageFlag = Date.now()
newState.totalRows = TOTAL_ROWS_RESET
}
return newState
}
case 'UPDATE_FILTER': {
const updatedFilter = state.filters[action.payload.filterIdx]
const previousIsValid = isValidFilter(updatedFilter)
const afterIsValid = isValidFilter(action.payload.value)
const newState: any = {
...state,
filters: update(state.filters, {
[action.payload.filterIdx]: { $set: action.payload.value },
}),
}
if (previousIsValid || afterIsValid) {
newState.page = 1
newState.refreshPageFlag = Date.now()
newState.totalRows = TOTAL_ROWS_RESET
}
return newState
}
default:
return state
}
}
export default FilterReducer
function isValidFilter(filter: Filter) {
return filter && filter.column && filter.column != '' && filter.value && filter.value != ''
}
+7 -26
View File
@@ -1,24 +1,13 @@
import BaseReducer, { BaseInitialState, baseInitialState } from './base';
import CallbackReducer, {
CallbackInitialState,
callbackInitialState,
} from './callback';
import ColumnReducer, {
ColumnInitialState,
columnInitialState,
} from './column';
import RowReducer, { RowInitialState, rowInitialState } from './row';
import SortReducer, { SortInitialState, sortInitialState } from './sort';
import FilterReducer, {
FilterInitialState,
filterInitialState,
} from './filter';
import BaseReducer, { BaseInitialState, baseInitialState } from './base'
import CallbackReducer, { CallbackInitialState, callbackInitialState } from './callback'
import ColumnReducer, { ColumnInitialState, columnInitialState } from './column'
import RowReducer, { RowInitialState, rowInitialState } from './row'
import SortReducer, { SortInitialState, sortInitialState } from './sort'
export interface InitialStateType
extends BaseInitialState,
CallbackInitialState,
ColumnInitialState,
FilterInitialState,
RowInitialState,
SortInitialState {}
@@ -26,16 +15,8 @@ export const initialState = {
...baseInitialState,
...callbackInitialState,
...columnInitialState,
...filterInitialState,
...rowInitialState,
...sortInitialState,
};
}
export {
BaseReducer,
CallbackReducer,
ColumnReducer,
FilterReducer,
RowReducer,
SortReducer,
};
export { BaseReducer, CallbackReducer, ColumnReducer, RowReducer, SortReducer }
+1 -1
View File
@@ -33,8 +33,8 @@ export interface Filter {
export interface SavedState {
filters: Filter[]
gridColumns: CalculatedColumn<any, any>[]
sorts: Sort[]
gridColumns: CalculatedColumn<any, any>[]
}
export interface DragItem {
+1
View File
@@ -1,6 +1,7 @@
import { uuidv4 } from 'lib/helpers'
import { Filter, SavedState, SupaTable } from '../types'
// [JOSHEN TODO] Deprecated but double check
export function getInitialFilters(table: SupaTable, savedState?: SavedState): Filter[] {
if (savedState?.filters) {
// verify column still exists
+19 -9
View File
@@ -1,9 +1,15 @@
import AwesomeDebouncePromise from 'awesome-debounce-promise'
import { InitialStateType } from '../store/reducers'
import { formatSortURLParams, formatFilterURLParams } from '../SupabaseGrid.utils'
export async function fetchCount(state: InitialStateType, dispatch: (value: unknown) => void) {
export async function fetchCount(
state: InitialStateType,
dispatch: (value: unknown) => void,
filter?: string[] // Come directly from URL param
) {
if (!state.rowService) return
const { data, error } = await state.rowService.count(state.filters)
const filters = formatFilterURLParams(filter)
const { data, error } = await state.rowService.count(filters)
if (error) {
// TODO: handle fetch rows count error
} else {
@@ -14,18 +20,22 @@ export async function fetchCount(state: InitialStateType, dispatch: (value: unkn
}
}
export async function fetchPage(state: InitialStateType, dispatch: (value: unknown) => void) {
export async function fetchPage(
state: InitialStateType,
dispatch: (value: unknown) => void,
sort?: string[], // Come directly from URL param
filter?: string[] // Come directly from URL param
) {
if (!state.rowService) return
dispatch({ type: 'SET_IS_LOADING', payload: { isLoading: true } })
console.log('Fetch page', {
filters: state.filters,
sorts: state.sorts,
})
const sorts = formatSortURLParams(sort)
const filters = formatFilterURLParams(filter)
const { data, error } = await state.rowService.fetchPage(
state.page,
state.rowsPerPage,
state.filters,
state.sorts
filters,
sorts
)
if (error) {
// TODO: handle fetch rows data error
+1 -1
View File
@@ -1,7 +1,7 @@
import { useCallback } from 'react'
import { useRouter } from 'next/router'
type UrlStateParams = {
export type UrlStateParams = {
[k: string]: string | string[] | undefined
}