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 (#10858)
Add an open/closed filter to the global change requests table. To accomodate this, the PR: - refactors the previous `ChangeRequestFilters` into its own directory with additional files for each of the filter groups. - updates the change request filters to work based on the table state instead of its own external state (this was fine with only one param, but would've gotten too complicated with two). <img width="1108" height="442" alt="image" src="https://github.com/user-attachments/assets/9cda0cbc-8524-45ce-b201-260e9581a346" /> <img width="1101" height="381" alt="image" src="https://github.com/user-attachments/assets/ab77b17f-5449-4987-9d08-341e856cb7ac" />
This commit is contained in:
parent
fd4fa815a9
commit
1932d21336
@ -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,67 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
|
||||||
|
'&:not(&[aria-current="true"])': {
|
||||||
|
'&:not(&:first-of-type)': {
|
||||||
|
borderLeftWidth: 0,
|
||||||
|
},
|
||||||
|
'&:not(&:last-of-type)': {
|
||||||
|
borderRightWidth: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .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();
|
createdBy: {
|
||||||
|
operator: 'IS' as const,
|
||||||
|
values: [user.id.toString()],
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
operator: 'IS' as const,
|
||||||
|
values: ['open'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ChangeRequestsInner = ({ user }: { user: IUser }) => {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const shouldApplyDefaults =
|
const shouldApplyDefaults = !urlParams.toString();
|
||||||
user &&
|
|
||||||
!urlParams.has('createdBy') &&
|
|
||||||
!urlParams.has('requestedApproverId');
|
|
||||||
const initialFilter = urlParams.has('requestedApproverId')
|
|
||||||
? 'Approval Requested'
|
|
||||||
: 'Created';
|
|
||||||
|
|
||||||
const stateConfig = {
|
const initialState = shouldApplyDefaults ? defaultTableState(user) : {};
|
||||||
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: {
|
|
||||||
operator: 'IS' as const,
|
|
||||||
values: [user.id.toString()],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
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