mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-27 11:02:16 +01:00
chore: Add state filter to UI query; default to open
This filter behaves in the same way as the `createdBy`/`requestedApprovalBy` filters.
This commit is contained in:
parent
47c2bb7376
commit
07572c8137
@ -1,105 +0,0 @@
|
|||||||
import { Box, Chip, styled, type ChipProps } from '@mui/material';
|
|
||||||
import { useState, type FC } from 'react';
|
|
||||||
|
|
||||||
const makeStyledChip = (ariaControlTarget: string) =>
|
|
||||||
styled(({ ...props }: ChipProps) => (
|
|
||||||
<Chip variant='outlined' aria-controls={ariaControlTarget} {...props} />
|
|
||||||
))<ChipProps>(({ theme, label }) => ({
|
|
||||||
padding: theme.spacing(0.5),
|
|
||||||
fontSize: theme.typography.body2.fontSize,
|
|
||||||
height: 'auto',
|
|
||||||
'&[aria-current="true"]': {
|
|
||||||
backgroundColor: theme.palette.secondary.light,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
borderColor: theme.palette.primary.main,
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
},
|
|
||||||
':focus-visible': {
|
|
||||||
outline: `1px solid ${theme.palette.primary.main}`,
|
|
||||||
borderColor: theme.palette.primary.main,
|
|
||||||
},
|
|
||||||
|
|
||||||
borderRadius: 0,
|
|
||||||
'&:first-of-type': {
|
|
||||||
borderTopLeftRadius: theme.shape.borderRadius,
|
|
||||||
borderBottomLeftRadius: theme.shape.borderRadius,
|
|
||||||
},
|
|
||||||
'&:last-of-type': {
|
|
||||||
borderTopRightRadius: theme.shape.borderRadius,
|
|
||||||
borderBottomRightRadius: theme.shape.borderRadius,
|
|
||||||
},
|
|
||||||
|
|
||||||
'& .MuiChip-label': {
|
|
||||||
position: 'relative',
|
|
||||||
textAlign: 'center',
|
|
||||||
'&::before': {
|
|
||||||
content: `'${label}'`,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
visibility: 'hidden',
|
|
||||||
height: 0,
|
|
||||||
display: 'block',
|
|
||||||
overflow: 'hidden',
|
|
||||||
userSelect: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export type ChangeRequestQuickFilter = 'Created' | 'Approval Requested';
|
|
||||||
|
|
||||||
interface IChangeRequestFiltersProps {
|
|
||||||
ariaControlTarget: string;
|
|
||||||
initialSelection?: ChangeRequestQuickFilter;
|
|
||||||
onSelectionChange: (selection: ChangeRequestQuickFilter) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Wrapper = styled(Box)(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
padding: theme.spacing(1.5, 3, 0, 3),
|
|
||||||
minHeight: theme.spacing(7),
|
|
||||||
gap: theme.spacing(2),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledContainer = styled(Box)({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ChangeRequestFilters: FC<IChangeRequestFiltersProps> = ({
|
|
||||||
onSelectionChange,
|
|
||||||
initialSelection,
|
|
||||||
ariaControlTarget,
|
|
||||||
}) => {
|
|
||||||
const [selected, setSelected] = useState<ChangeRequestQuickFilter>(
|
|
||||||
initialSelection || 'Created',
|
|
||||||
);
|
|
||||||
const handleSelectionChange = (value: ChangeRequestQuickFilter) => () => {
|
|
||||||
if (value === selected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setSelected(value);
|
|
||||||
onSelectionChange(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledChip = makeStyledChip(ariaControlTarget);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Wrapper>
|
|
||||||
<StyledContainer>
|
|
||||||
<StyledChip
|
|
||||||
label={'Created'}
|
|
||||||
aria-current={selected === 'Created'}
|
|
||||||
onClick={handleSelectionChange('Created')}
|
|
||||||
title={'Show change requests created by you'}
|
|
||||||
/>
|
|
||||||
<StyledChip
|
|
||||||
label={'Approval Requested'}
|
|
||||||
aria-current={selected === 'Approval Requested'}
|
|
||||||
onClick={handleSelectionChange('Approval Requested')}
|
|
||||||
title={'Show change requests requesting your approval'}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
|
||||||
</Wrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
import { Box, Chip, styled, type ChipProps } from '@mui/material';
|
||||||
|
|
||||||
|
export const makeStyledChip = (ariaControlTarget: string) =>
|
||||||
|
styled(({ ...props }: ChipProps) => (
|
||||||
|
<Chip variant='outlined' aria-controls={ariaControlTarget} {...props} />
|
||||||
|
))<ChipProps>(({ theme, label }) => ({
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
fontSize: theme.typography.body2.fontSize,
|
||||||
|
height: 'auto',
|
||||||
|
'&[aria-current="true"]': {
|
||||||
|
backgroundColor: theme.palette.secondary.light,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
borderColor: theme.palette.primary.main,
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
':focus-visible': {
|
||||||
|
outline: `1px solid ${theme.palette.primary.main}`,
|
||||||
|
borderColor: theme.palette.primary.main,
|
||||||
|
},
|
||||||
|
|
||||||
|
borderRadius: 0,
|
||||||
|
'&:first-of-type': {
|
||||||
|
borderTopLeftRadius: theme.shape.borderRadius,
|
||||||
|
borderBottomLeftRadius: theme.shape.borderRadius,
|
||||||
|
},
|
||||||
|
'&:last-of-type': {
|
||||||
|
borderTopRightRadius: theme.shape.borderRadius,
|
||||||
|
borderBottomRightRadius: theme.shape.borderRadius,
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .MuiChip-label': {
|
||||||
|
position: 'relative',
|
||||||
|
textAlign: 'center',
|
||||||
|
'&::before': {
|
||||||
|
content: `'${label}'`,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
visibility: 'hidden',
|
||||||
|
height: 0,
|
||||||
|
display: 'block',
|
||||||
|
overflow: 'hidden',
|
||||||
|
userSelect: 'none',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const Wrapper = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
flexFlow: 'row wrap',
|
||||||
|
padding: theme.spacing(1.5, 3, 0, 3),
|
||||||
|
minHeight: theme.spacing(7),
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const StyledContainer = styled(Box)({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import type { TableState } from '../ChangeRequests.types';
|
||||||
|
import { makeStyledChip, Wrapper } from './ChangeRequestFilters.styles';
|
||||||
|
import { UserFilterChips } from './UserFilterChips.tsx';
|
||||||
|
import { StateFilterChips } from './StateFilterChips.tsx';
|
||||||
|
import type { ChangeRequestFiltersProps } from './ChangeRequestFilters.types.ts';
|
||||||
|
|
||||||
|
export const ChangeRequestFilters: FC<ChangeRequestFiltersProps> = ({
|
||||||
|
tableState,
|
||||||
|
setTableState,
|
||||||
|
userId,
|
||||||
|
ariaControlTarget,
|
||||||
|
}) => {
|
||||||
|
const updateTableState = (update: Partial<TableState>) => {
|
||||||
|
setTableState({ ...tableState, ...update, offset: 0 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledChip = makeStyledChip(ariaControlTarget);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<UserFilterChips
|
||||||
|
tableState={tableState}
|
||||||
|
setTableState={updateTableState}
|
||||||
|
userId={userId}
|
||||||
|
StyledChip={StyledChip}
|
||||||
|
/>
|
||||||
|
<StateFilterChips
|
||||||
|
tableState={tableState}
|
||||||
|
setTableState={updateTableState}
|
||||||
|
StyledChip={StyledChip}
|
||||||
|
/>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import type { TableState } from '../ChangeRequests.types';
|
||||||
|
import type { makeStyledChip } from './ChangeRequestFilters.styles';
|
||||||
|
|
||||||
|
export type ChangeRequestFiltersProps = {
|
||||||
|
tableState: Readonly<TableState>;
|
||||||
|
setTableState: (update: Partial<TableState>) => void;
|
||||||
|
userId: number;
|
||||||
|
ariaControlTarget: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FilterChipsProps = {
|
||||||
|
tableState: ChangeRequestFiltersProps['tableState'];
|
||||||
|
setTableState: ChangeRequestFiltersProps['setTableState'];
|
||||||
|
StyledChip: ReturnType<typeof makeStyledChip>;
|
||||||
|
};
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import { StyledContainer } from './ChangeRequestFilters.styles';
|
||||||
|
import type { FilterChipsProps } from './ChangeRequestFilters.types';
|
||||||
|
|
||||||
|
type StateFilterType = 'open' | 'closed';
|
||||||
|
|
||||||
|
const getStateFilter = (
|
||||||
|
stateValue: string | undefined,
|
||||||
|
): StateFilterType | undefined => {
|
||||||
|
if (stateValue === 'open') {
|
||||||
|
return 'open';
|
||||||
|
}
|
||||||
|
if (stateValue === 'closed') {
|
||||||
|
return 'closed';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StateFilterChips: FC<FilterChipsProps> = ({
|
||||||
|
tableState,
|
||||||
|
setTableState,
|
||||||
|
StyledChip,
|
||||||
|
}) => {
|
||||||
|
const activeStateFilter = getStateFilter(tableState.state?.values?.[0]);
|
||||||
|
|
||||||
|
const handleStateFilterChange = (filter: StateFilterType) => () => {
|
||||||
|
if (filter === activeStateFilter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTableState({ state: { operator: 'IS' as const, values: [filter] } });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledChip
|
||||||
|
label={'Open'}
|
||||||
|
aria-current={activeStateFilter === 'open'}
|
||||||
|
onClick={handleStateFilterChange('open')}
|
||||||
|
title={'Show open change requests'}
|
||||||
|
/>
|
||||||
|
<StyledChip
|
||||||
|
label={'Closed'}
|
||||||
|
aria-current={activeStateFilter === 'closed'}
|
||||||
|
onClick={handleStateFilterChange('closed')}
|
||||||
|
title={'Show closed change requests'}
|
||||||
|
/>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
import type { FC } from 'react';
|
||||||
|
import type { TableState } from '../ChangeRequests.types';
|
||||||
|
import { StyledContainer } from './ChangeRequestFilters.styles';
|
||||||
|
import type { FilterChipsProps } from './ChangeRequestFilters.types';
|
||||||
|
|
||||||
|
type UserFilterType = 'created' | 'approval requested';
|
||||||
|
|
||||||
|
type UserFilterChipsProps = FilterChipsProps & { userId: number };
|
||||||
|
|
||||||
|
const getUserFilter = (
|
||||||
|
tableState: TableState,
|
||||||
|
userId: string,
|
||||||
|
): UserFilterType | undefined => {
|
||||||
|
if (
|
||||||
|
!tableState.requestedApproverId &&
|
||||||
|
tableState.createdBy?.values.length === 1 &&
|
||||||
|
tableState.createdBy.values[0] === userId
|
||||||
|
) {
|
||||||
|
return 'created';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!tableState.createdBy &&
|
||||||
|
tableState.requestedApproverId?.values.length === 1 &&
|
||||||
|
tableState.requestedApproverId.values[0] === userId
|
||||||
|
) {
|
||||||
|
return 'approval requested';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const UserFilterChips: FC<UserFilterChipsProps> = ({
|
||||||
|
tableState,
|
||||||
|
setTableState,
|
||||||
|
userId,
|
||||||
|
StyledChip,
|
||||||
|
}) => {
|
||||||
|
const userIdString = userId.toString();
|
||||||
|
|
||||||
|
const activeUserFilter: UserFilterType | undefined = getUserFilter(
|
||||||
|
tableState,
|
||||||
|
userIdString,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleUserFilterChange = (filter: UserFilterType) => () => {
|
||||||
|
if (filter === activeUserFilter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [targetProperty, otherProperty] =
|
||||||
|
filter === 'created'
|
||||||
|
? (['createdBy', 'requestedApproverId'] as const)
|
||||||
|
: (['requestedApproverId', 'createdBy'] as const);
|
||||||
|
|
||||||
|
setTableState({
|
||||||
|
[targetProperty]: {
|
||||||
|
operator: 'IS' as const,
|
||||||
|
values: [userIdString],
|
||||||
|
},
|
||||||
|
[otherProperty]:
|
||||||
|
tableState[otherProperty]?.values.length === 1 &&
|
||||||
|
tableState[otherProperty]?.values[0] === userIdString
|
||||||
|
? null
|
||||||
|
: tableState[otherProperty],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<StyledChip
|
||||||
|
label={'Created'}
|
||||||
|
aria-current={activeUserFilter === 'created'}
|
||||||
|
onClick={handleUserFilterChange('created')}
|
||||||
|
title={'Show change requests created by you'}
|
||||||
|
/>
|
||||||
|
<StyledChip
|
||||||
|
label={'Approval Requested'}
|
||||||
|
aria-current={activeUserFilter === 'approval requested'}
|
||||||
|
onClick={handleUserFilterChange('approval requested')}
|
||||||
|
title={'Show change requests requesting your approval'}
|
||||||
|
/>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -14,25 +14,16 @@ import { useUiFlag } from 'hooks/useUiFlag.js';
|
|||||||
import { withTableState } from 'utils/withTableState';
|
import { withTableState } from 'utils/withTableState';
|
||||||
import {
|
import {
|
||||||
useChangeRequestSearch,
|
useChangeRequestSearch,
|
||||||
DEFAULT_PAGE_LIMIT,
|
|
||||||
type SearchChangeRequestsInput,
|
type SearchChangeRequestsInput,
|
||||||
} from 'hooks/api/getters/useChangeRequestSearch/useChangeRequestSearch';
|
} from 'hooks/api/getters/useChangeRequestSearch/useChangeRequestSearch';
|
||||||
import type { ChangeRequestSearchItemSchema } from 'openapi';
|
import type { ChangeRequestSearchItemSchema } from 'openapi';
|
||||||
import {
|
import { useQueryParams, encodeQueryParams } from 'use-query-params';
|
||||||
NumberParam,
|
|
||||||
StringParam,
|
|
||||||
withDefault,
|
|
||||||
useQueryParams,
|
|
||||||
encodeQueryParams,
|
|
||||||
} from 'use-query-params';
|
|
||||||
import useLoading from 'hooks/useLoading';
|
import useLoading from 'hooks/useLoading';
|
||||||
import { styles as themeStyles } from 'component/common';
|
import { styles as themeStyles } from 'component/common';
|
||||||
import { FilterItemParam } from 'utils/serializeQueryParams';
|
import { ChangeRequestFilters } from './ChangeRequestFilters/ChangeRequestFilters.js';
|
||||||
import {
|
|
||||||
ChangeRequestFilters,
|
|
||||||
type ChangeRequestQuickFilter,
|
|
||||||
} from './ChangeRequestFilters.js';
|
|
||||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser.js';
|
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser.js';
|
||||||
|
import type { IUser } from 'interfaces/user.js';
|
||||||
|
import { stateConfig } from './ChangeRequests.types.js';
|
||||||
|
|
||||||
const columnHelper = createColumnHelper<ChangeRequestSearchItemSchema>();
|
const columnHelper = createColumnHelper<ChangeRequestSearchItemSchema>();
|
||||||
|
|
||||||
@ -49,46 +40,33 @@ const StyledPaginatedTable = styled(
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ChangeRequestsInner = () => {
|
const defaultTableState = (user: IUser) => ({
|
||||||
const { user } = useAuthUser();
|
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
|
||||||
const shouldApplyDefaults =
|
|
||||||
user &&
|
|
||||||
!urlParams.has('createdBy') &&
|
|
||||||
!urlParams.has('requestedApproverId');
|
|
||||||
const initialFilter = urlParams.has('requestedApproverId')
|
|
||||||
? 'Approval Requested'
|
|
||||||
: 'Created';
|
|
||||||
|
|
||||||
const stateConfig = {
|
|
||||||
offset: withDefault(NumberParam, 0),
|
|
||||||
limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT),
|
|
||||||
sortBy: withDefault(StringParam, 'createdAt'),
|
|
||||||
sortOrder: withDefault(StringParam, 'desc'),
|
|
||||||
createdBy: FilterItemParam,
|
|
||||||
requestedApproverId: FilterItemParam,
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialState = shouldApplyDefaults
|
|
||||||
? {
|
|
||||||
createdBy: {
|
createdBy: {
|
||||||
operator: 'IS' as const,
|
operator: 'IS' as const,
|
||||||
values: [user.id.toString()],
|
values: [user.id.toString()],
|
||||||
},
|
},
|
||||||
}
|
state: {
|
||||||
: {};
|
operator: 'IS' as const,
|
||||||
|
values: ['open'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ChangeRequestsInner = ({ user }: { user: IUser }) => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const shouldApplyDefaults = !urlParams.toString();
|
||||||
|
|
||||||
|
const initialState = shouldApplyDefaults ? defaultTableState(user) : {};
|
||||||
|
|
||||||
const [tableState, setTableState] = useQueryParams(stateConfig, {
|
const [tableState, setTableState] = useQueryParams(stateConfig, {
|
||||||
updateType: 'replaceIn',
|
updateType: 'replaceIn',
|
||||||
});
|
});
|
||||||
|
|
||||||
const effectiveTableState = useMemo(
|
const effectiveTableState = shouldApplyDefaults
|
||||||
() => ({
|
? {
|
||||||
...tableState,
|
...tableState,
|
||||||
...initialState,
|
...initialState,
|
||||||
}),
|
}
|
||||||
[initialState, tableState],
|
: tableState;
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
changeRequests: data,
|
changeRequests: data,
|
||||||
@ -172,30 +150,6 @@ const ChangeRequestsInner = () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const tableId = useId();
|
const tableId = useId();
|
||||||
const handleQuickFilterChange = (filter: ChangeRequestQuickFilter) => {
|
|
||||||
if (!user) {
|
|
||||||
// todo (globalChangeRequestList): handle this somehow? Or just ignore.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const [targetProperty, otherProperty] =
|
|
||||||
filter === 'Created'
|
|
||||||
? ['createdBy', 'requestedApproverId']
|
|
||||||
: ['requestedApproverId', 'createdBy'];
|
|
||||||
|
|
||||||
// todo (globalChangeRequestList): extract and test the logic for wiping out createdby/requestedapproverid
|
|
||||||
setTableState((state) => ({
|
|
||||||
[targetProperty]: {
|
|
||||||
operator: 'IS',
|
|
||||||
values: [user.id.toString()],
|
|
||||||
},
|
|
||||||
[otherProperty]:
|
|
||||||
state[otherProperty]?.values.length === 1 &&
|
|
||||||
state[otherProperty].values[0] === user.id.toString()
|
|
||||||
? null
|
|
||||||
: state[otherProperty],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const bodyLoadingRef = useLoading(loading);
|
const bodyLoadingRef = useLoading(loading);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -205,8 +159,9 @@ const ChangeRequestsInner = () => {
|
|||||||
>
|
>
|
||||||
<ChangeRequestFilters
|
<ChangeRequestFilters
|
||||||
ariaControlTarget={tableId}
|
ariaControlTarget={tableId}
|
||||||
initialSelection={initialFilter}
|
tableState={effectiveTableState}
|
||||||
onSelectionChange={handleQuickFilterChange}
|
setTableState={setTableState}
|
||||||
|
userId={user.id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -231,6 +186,7 @@ const ChangeRequestsInner = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const ChangeRequests = () => {
|
export const ChangeRequests = () => {
|
||||||
|
const { user } = useAuthUser();
|
||||||
if (!useUiFlag('globalChangeRequestList')) {
|
if (!useUiFlag('globalChangeRequestList')) {
|
||||||
return (
|
return (
|
||||||
<PageContent header={<PageHeader title='Change requests' />}>
|
<PageContent header={<PageHeader title='Change requests' />}>
|
||||||
@ -239,5 +195,16 @@ export const ChangeRequests = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ChangeRequestsInner />;
|
if (!user) {
|
||||||
|
return (
|
||||||
|
<PageContent header={<PageHeader title='Change requests' />}>
|
||||||
|
<p>
|
||||||
|
Failed to get your user information. Please refresh. If the
|
||||||
|
problem persists, get in touch.
|
||||||
|
</p>
|
||||||
|
</PageContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ChangeRequestsInner user={user} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { DEFAULT_PAGE_LIMIT } from 'hooks/api/getters/useChangeRequestSearch/useChangeRequestSearch';
|
||||||
|
import {
|
||||||
|
NumberParam,
|
||||||
|
StringParam,
|
||||||
|
withDefault,
|
||||||
|
type DecodedValueMap,
|
||||||
|
} from 'use-query-params';
|
||||||
|
import { FilterItemParam } from 'utils/serializeQueryParams';
|
||||||
|
|
||||||
|
export const stateConfig = {
|
||||||
|
offset: withDefault(NumberParam, 0),
|
||||||
|
limit: withDefault(NumberParam, DEFAULT_PAGE_LIMIT),
|
||||||
|
sortBy: withDefault(StringParam, 'createdAt'),
|
||||||
|
sortOrder: withDefault(StringParam, 'desc'),
|
||||||
|
createdBy: FilterItemParam,
|
||||||
|
requestedApproverId: FilterItemParam,
|
||||||
|
state: FilterItemParam,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TableState = DecodedValueMap<typeof stateConfig>;
|
||||||
@ -1303,6 +1303,7 @@ export * from './searchChangeRequests401.js';
|
|||||||
export * from './searchChangeRequests403.js';
|
export * from './searchChangeRequests403.js';
|
||||||
export * from './searchChangeRequests404.js';
|
export * from './searchChangeRequests404.js';
|
||||||
export * from './searchChangeRequestsParams.js';
|
export * from './searchChangeRequestsParams.js';
|
||||||
|
export * from './searchChangeRequestsState.js';
|
||||||
export * from './searchEventsParams.js';
|
export * from './searchEventsParams.js';
|
||||||
export * from './searchFeatures401.js';
|
export * from './searchFeatures401.js';
|
||||||
export * from './searchFeatures403.js';
|
export * from './searchFeatures403.js';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user