Merge pull request #2190 from automatisch/AUT-1347
feat: persist pagination and search value on flows page
This commit is contained in:
@@ -7,7 +7,7 @@ import FormControl from '@mui/material/FormControl';
|
|||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
|
||||||
export default function SearchInput({ onChange }) {
|
export default function SearchInput({ onChange, defaultValue = '' }) {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
return (
|
return (
|
||||||
<FormControl variant="outlined" fullWidth>
|
<FormControl variant="outlined" fullWidth>
|
||||||
@@ -16,6 +16,7 @@ export default function SearchInput({ onChange }) {
|
|||||||
</InputLabel>
|
</InputLabel>
|
||||||
|
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
|
defaultValue={defaultValue}
|
||||||
id="search-input"
|
id="search-input"
|
||||||
type="text"
|
type="text"
|
||||||
size="medium"
|
size="medium"
|
||||||
@@ -34,4 +35,5 @@ export default function SearchInput({ onChange }) {
|
|||||||
|
|
||||||
SearchInput.propTypes = {
|
SearchInput.propTypes = {
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
defaultValue: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link, useSearchParams } from 'react-router-dom';
|
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
@@ -23,13 +23,18 @@ import useLazyFlows from 'hooks/useLazyFlows';
|
|||||||
|
|
||||||
export default function Flows() {
|
export default function Flows() {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
||||||
const [flowName, setFlowName] = React.useState('');
|
const flowName = searchParams.get('flowName') || '';
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(true);
|
||||||
const currentUserAbility = useCurrentUserAbility();
|
const currentUserAbility = useCurrentUserAbility();
|
||||||
|
|
||||||
const { data, mutate: fetchFlows } = useLazyFlows(
|
const {
|
||||||
|
data,
|
||||||
|
mutate: fetchFlows,
|
||||||
|
isSuccess,
|
||||||
|
} = useLazyFlows(
|
||||||
{ flowName, page },
|
{ flowName, page },
|
||||||
{
|
{
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
@@ -38,6 +43,36 @@ export default function Flows() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const flows = data?.data || [];
|
||||||
|
const pageInfo = data?.meta;
|
||||||
|
const hasFlows = flows?.length;
|
||||||
|
const navigateToLastPage = isSuccess && !hasFlows && page > 1;
|
||||||
|
|
||||||
|
const onSearchChange = React.useCallback((event) => {
|
||||||
|
setSearchParams({ flowName: event.target.value });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getPathWithSearchParams = (page, flowName) => {
|
||||||
|
const searchParams = new URLSearchParams();
|
||||||
|
|
||||||
|
if (page > 1) {
|
||||||
|
searchParams.set('page', page);
|
||||||
|
}
|
||||||
|
if (flowName) {
|
||||||
|
searchParams.set('flowName', flowName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { search: searchParams.toString() };
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDuplicateFlow = () => {
|
||||||
|
if (pageInfo?.currentPage > 1) {
|
||||||
|
navigate(getPathWithSearchParams(1, flowName));
|
||||||
|
} else {
|
||||||
|
fetchFlows();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const fetchData = React.useMemo(
|
const fetchData = React.useMemo(
|
||||||
() => debounce(fetchFlows, 300),
|
() => debounce(fetchFlows, 300),
|
||||||
[fetchFlows],
|
[fetchFlows],
|
||||||
@@ -54,21 +89,14 @@ export default function Flows() {
|
|||||||
}, [fetchData, flowName, page]);
|
}, [fetchData, flowName, page]);
|
||||||
|
|
||||||
React.useEffect(
|
React.useEffect(
|
||||||
function resetPageOnSearch() {
|
function redirectToLastPage() {
|
||||||
// reset search params which only consists of `page`
|
if (navigateToLastPage) {
|
||||||
setSearchParams({});
|
navigate(getPathWithSearchParams(pageInfo.totalPages, flowName));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[flowName],
|
[navigateToLastPage],
|
||||||
);
|
);
|
||||||
|
|
||||||
const flows = data?.data || [];
|
|
||||||
const pageInfo = data?.meta;
|
|
||||||
const hasFlows = flows?.length;
|
|
||||||
|
|
||||||
const onSearchChange = React.useCallback((event) => {
|
|
||||||
setFlowName(event.target.value);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ py: 3 }}>
|
<Box sx={{ py: 3 }}>
|
||||||
<Container>
|
<Container>
|
||||||
@@ -78,7 +106,7 @@ export default function Flows() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} sm="auto" order={{ xs: 2, sm: 1 }}>
|
<Grid item xs={12} sm="auto" order={{ xs: 2, sm: 1 }}>
|
||||||
<SearchInput onChange={onSearchChange} />
|
<SearchInput onChange={onSearchChange} defaultValue={flowName} />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
@@ -111,7 +139,7 @@ export default function Flows() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
||||||
{isLoading && (
|
{(isLoading || navigateToLastPage) && (
|
||||||
<CircularProgress sx={{ display: 'block', margin: '20px auto' }} />
|
<CircularProgress sx={{ display: 'block', margin: '20px auto' }} />
|
||||||
)}
|
)}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
@@ -119,11 +147,11 @@ export default function Flows() {
|
|||||||
<FlowRow
|
<FlowRow
|
||||||
key={flow.id}
|
key={flow.id}
|
||||||
flow={flow}
|
flow={flow}
|
||||||
onDuplicateFlow={fetchFlows}
|
onDuplicateFlow={onDuplicateFlow}
|
||||||
onDeleteFlow={fetchFlows}
|
onDeleteFlow={fetchFlows}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{!isLoading && !hasFlows && (
|
{!isLoading && !navigateToLastPage && !hasFlows && (
|
||||||
<NoResultFound
|
<NoResultFound
|
||||||
text={formatMessage('flows.noFlows')}
|
text={formatMessage('flows.noFlows')}
|
||||||
{...(currentUserAbility.can('create', 'Flow') && {
|
{...(currentUserAbility.can('create', 'Flow') && {
|
||||||
@@ -131,18 +159,18 @@ export default function Flows() {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!isLoading && pageInfo && pageInfo.totalPages > 1 && (
|
{!isLoading &&
|
||||||
|
!navigateToLastPage &&
|
||||||
|
pageInfo &&
|
||||||
|
pageInfo.totalPages > 1 && (
|
||||||
<Pagination
|
<Pagination
|
||||||
sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}
|
sx={{ display: 'flex', justifyContent: 'center', mt: 3 }}
|
||||||
page={pageInfo?.currentPage}
|
page={pageInfo?.currentPage}
|
||||||
count={pageInfo?.totalPages}
|
count={pageInfo?.totalPages}
|
||||||
onChange={(event, page) =>
|
|
||||||
setSearchParams({ page: page.toString() })
|
|
||||||
}
|
|
||||||
renderItem={(item) => (
|
renderItem={(item) => (
|
||||||
<PaginationItem
|
<PaginationItem
|
||||||
component={Link}
|
component={Link}
|
||||||
to={`${item.page === 1 ? '' : `?page=${item.page}`}`}
|
to={getPathWithSearchParams(item.page, flowName)}
|
||||||
{...item}
|
{...item}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
Reference in New Issue
Block a user