mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
feat: display change details (#2327)
* feat: display change details * refactor: reorganize components * feat: display deleted strategy name if present * feat: UI tweaks * fix: types * refactor: remove unnecessary checks for types
This commit is contained in:
parent
4b281d9513
commit
065833e5d1
@ -2,15 +2,11 @@ import { VFC } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { ChangeRequestFeatureToggleChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { ToggleStatusChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange';
|
||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import useToast from 'hooks/useToast';
|
||||
import type {
|
||||
IChangeRequest,
|
||||
IChangeRequestAddStrategy,
|
||||
} from '../changeRequest.types';
|
||||
import type { IChangeRequest } from '../changeRequest.types';
|
||||
import {
|
||||
StrategyAddedChange,
|
||||
StrategyDeletedChange,
|
||||
@ -20,7 +16,7 @@ import {
|
||||
formatStrategyName,
|
||||
GetFeatureStrategyIcon,
|
||||
} from 'utils/strategyNames';
|
||||
import { IChangeRequestEnabled } from '../changeRequest.types';
|
||||
import { hasNameField } from '../changeRequest.types';
|
||||
|
||||
interface IChangeRequestProps {
|
||||
changeRequest: IChangeRequest;
|
||||
@ -62,46 +58,60 @@ export const ChangeRequest: VFC<IChangeRequestProps> = ({
|
||||
onNavigate={onNavigate}
|
||||
>
|
||||
{featureToggleChange.changes.map(change => (
|
||||
<Box key={objectId(change)}>
|
||||
<ConditionallyRender
|
||||
condition={change.action === 'updateEnabled'}
|
||||
show={
|
||||
<ToggleStatusChange
|
||||
enabled={
|
||||
(change as IChangeRequestEnabled)
|
||||
?.payload?.enabled
|
||||
}
|
||||
onDiscard={onDiscard(change.id!)}
|
||||
<Box
|
||||
key={objectId(change)}
|
||||
sx={theme => ({
|
||||
padding: 2,
|
||||
borderTop: '1px solid',
|
||||
borderColor: theme =>
|
||||
theme.palette.dividerAlternative,
|
||||
})}
|
||||
>
|
||||
{change.action === 'updateEnabled' && (
|
||||
<ToggleStatusChange
|
||||
enabled={change.payload.enabled}
|
||||
onDiscard={onDiscard(change.id)}
|
||||
/>
|
||||
)}
|
||||
{change.action === 'addStrategy' && (
|
||||
<StrategyAddedChange
|
||||
onDiscard={onDiscard(change.id)}
|
||||
>
|
||||
<GetFeatureStrategyIcon
|
||||
strategyName={change.payload.name}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={change.action === 'addStrategy'}
|
||||
show={
|
||||
<StrategyAddedChange>
|
||||
<GetFeatureStrategyIcon
|
||||
strategyName={
|
||||
(
|
||||
change as IChangeRequestAddStrategy
|
||||
)?.payload.name!
|
||||
}
|
||||
/>
|
||||
{formatStrategyName(
|
||||
(
|
||||
change as IChangeRequestAddStrategy
|
||||
)?.payload.name!
|
||||
)}
|
||||
</StrategyAddedChange>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={change.action === 'deleteStrategy'}
|
||||
show={<StrategyDeletedChange />}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={change.action === 'updateStrategy'}
|
||||
show={<StrategyEditedChange />}
|
||||
/>
|
||||
|
||||
{formatStrategyName(change.payload.name)}
|
||||
</StrategyAddedChange>
|
||||
)}
|
||||
{change.action === 'deleteStrategy' && (
|
||||
<StrategyDeletedChange
|
||||
onDiscard={onDiscard(change.id)}
|
||||
>
|
||||
{hasNameField(change.payload) && (
|
||||
<>
|
||||
<GetFeatureStrategyIcon
|
||||
strategyName={
|
||||
change.payload.name
|
||||
}
|
||||
/>
|
||||
{formatStrategyName(
|
||||
change.payload.name
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</StrategyDeletedChange>
|
||||
)}
|
||||
{change.action === 'updateStrategy' && (
|
||||
<StrategyEditedChange
|
||||
onDiscard={onDiscard(change.id)}
|
||||
>
|
||||
<GetFeatureStrategyIcon
|
||||
strategyName={change.payload.name}
|
||||
/>
|
||||
{formatStrategyName(change.payload.name)}
|
||||
</StrategyEditedChange>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
</ChangeRequestFeatureToggleChange>
|
||||
|
@ -26,7 +26,7 @@ export const ChangeRequestFeatureToggleChange: FC<
|
||||
<Box
|
||||
sx={theme => ({
|
||||
backgroundColor: theme.palette.tableHeaderBackground,
|
||||
p: 2,
|
||||
padding: theme.spacing(3),
|
||||
})}
|
||||
>
|
||||
<Box sx={{ display: 'flex', gap: 1 }}>
|
||||
@ -42,7 +42,7 @@ export const ChangeRequestFeatureToggleChange: FC<
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ p: 2 }}>{children}</Box>
|
||||
<Box>{children}</Box>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
@ -1,27 +1,74 @@
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Box, Link, styled, Typography } from '@mui/material';
|
||||
import { FC } from 'react';
|
||||
|
||||
export const StrategyAddedChange: FC = ({ children }) => {
|
||||
interface IStrategyChangeProps {
|
||||
onDiscard: () => void;
|
||||
}
|
||||
|
||||
export const ChangeItemWrapper = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
padding: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const ChangeItemInfo: FC = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const Discard: FC<IStrategyChangeProps> = ({ onDiscard }) => (
|
||||
<Box>
|
||||
<Link onClick={onDiscard}>Discard</Link>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export const StrategyAddedChange: FC<IStrategyChangeProps> = ({
|
||||
children,
|
||||
onDiscard,
|
||||
}) => {
|
||||
return (
|
||||
<Box sx={{ p: 1, display: 'flex', gap: 1 }}>
|
||||
<Typography sx={theme => ({ color: theme.palette.success.main })}>
|
||||
+ Strategy Added:
|
||||
</Typography>
|
||||
{children}
|
||||
</Box>
|
||||
<ChangeItemWrapper>
|
||||
<ChangeItemInfo>
|
||||
<Typography
|
||||
sx={theme => ({ color: theme.palette.success.main })}
|
||||
>
|
||||
+ Adding strategy:
|
||||
</Typography>
|
||||
{children}
|
||||
</ChangeItemInfo>
|
||||
<Discard onDiscard={onDiscard} />
|
||||
</ChangeItemWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const StrategyEditedChange: FC = () => {
|
||||
return <Box sx={{ p: 1 }}>Strategy Edited</Box>;
|
||||
};
|
||||
|
||||
export const StrategyDeletedChange: FC = () => {
|
||||
export const StrategyEditedChange: FC<IStrategyChangeProps> = ({
|
||||
children,
|
||||
onDiscard,
|
||||
}) => {
|
||||
return (
|
||||
<Box sx={{ p: 1 }}>
|
||||
<Typography sx={theme => ({ color: theme.palette.error.main })}>
|
||||
- Strategy Deleted
|
||||
</Typography>
|
||||
</Box>
|
||||
<ChangeItemWrapper>
|
||||
<ChangeItemInfo>
|
||||
<Typography>Editing strategy:</Typography>
|
||||
{children}
|
||||
</ChangeItemInfo>
|
||||
<Discard onDiscard={onDiscard} />
|
||||
</ChangeItemWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const StrategyDeletedChange: FC<IStrategyChangeProps> = ({
|
||||
onDiscard,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<ChangeItemWrapper>
|
||||
<ChangeItemInfo>
|
||||
<Typography sx={theme => ({ color: theme.palette.error.main })}>
|
||||
- Deleting strategy
|
||||
</Typography>
|
||||
{children}
|
||||
</ChangeItemInfo>
|
||||
<Discard onDiscard={onDiscard} />
|
||||
</ChangeItemWrapper>
|
||||
);
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { VFC } from 'react';
|
||||
import { Link, Box } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import { ChangeItemWrapper } from './StrategyChange';
|
||||
|
||||
interface IPlaygroundResultsTable {
|
||||
enabled: boolean;
|
||||
@ -13,7 +14,7 @@ export const ToggleStatusChange: VFC<IPlaygroundResultsTable> = ({
|
||||
onDiscard,
|
||||
}) => {
|
||||
return (
|
||||
<Box sx={{ p: 1, display: 'flex', justifyContent: 'space-between' }}>
|
||||
<ChangeItemWrapper>
|
||||
<Box>
|
||||
New status:{' '}
|
||||
<Badge color={enabled ? 'success' : 'error'}>
|
||||
@ -28,6 +29,6 @@ export const ToggleStatusChange: VFC<IPlaygroundResultsTable> = ({
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</ChangeItemWrapper>
|
||||
);
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
styled,
|
||||
Tooltip,
|
||||
Divider,
|
||||
IconButton,
|
||||
} from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||
@ -17,6 +18,7 @@ import { ChangeRequest } from '../ChangeRequest/ChangeRequest';
|
||||
import { useChangeRequestOpen } from 'hooks/api/getters/useChangeRequestOpen/useChangeRequestOpen';
|
||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||
import { ChangeRequestStatusBadge } from '../ChangeRequestStatusBadge/ChangeRequestStatusBadge';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
interface IChangeRequestSidebarProps {
|
||||
open: boolean;
|
||||
@ -112,16 +114,28 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
||||
header={
|
||||
<PageHeader
|
||||
secondary
|
||||
actions={
|
||||
<IconButton onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
}
|
||||
titleElement={
|
||||
<>
|
||||
Review your changes
|
||||
<Tooltip
|
||||
title="You can review your changes from this page.
|
||||
Needs a text to explain the process."
|
||||
arrow
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<StyledHelpOutline />
|
||||
</Tooltip>
|
||||
Review your changes
|
||||
<Tooltip
|
||||
title="You can review your changes from this page.
|
||||
Needs a text to explain the process."
|
||||
arrow
|
||||
>
|
||||
<StyledHelpOutline />
|
||||
</Tooltip>
|
||||
</Box>
|
||||
<StyledHeaderHint>
|
||||
Make sure you are sending the right changes
|
||||
to be reviewed
|
||||
@ -228,7 +242,6 @@ export const ChangeRequestSidebar: VFC<IChangeRequestSidebarProps> = ({
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
<BackButton onClick={onClose}>Close</BackButton>
|
||||
</StyledPageContent>
|
||||
</SidebarModal>
|
||||
);
|
||||
|
@ -18,7 +18,7 @@ export interface IChangeRequestFeature {
|
||||
}
|
||||
|
||||
export interface IChangeRequestBase {
|
||||
id?: number;
|
||||
id: number;
|
||||
action: ChangeRequestAction;
|
||||
payload: ChangeRequestPayload;
|
||||
conflict?: string;
|
||||
@ -83,3 +83,6 @@ export type ChangeRequestAction =
|
||||
| 'addStrategy'
|
||||
| 'updateStrategy'
|
||||
| 'deleteStrategy';
|
||||
|
||||
export const hasNameField = (payload: unknown): payload is { name: string } =>
|
||||
typeof payload === 'object' && payload !== null && 'name' in payload;
|
||||
|
@ -15,6 +15,7 @@ import PermissionIconButton from 'component/common/PermissionIconButton/Permissi
|
||||
import { Delete } from '@mui/icons-material';
|
||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||
import { useChangeRequestOpen } from 'hooks/api/getters/useChangeRequestOpen/useChangeRequestOpen';
|
||||
|
||||
interface IFeatureStrategyRemoveProps {
|
||||
projectId: string;
|
||||
@ -130,6 +131,7 @@ const useOnSuggestRemove = ({
|
||||
strategyId,
|
||||
}: IRemoveProps) => {
|
||||
const { addChangeRequest } = useChangeRequestApi();
|
||||
const { refetch: refetchChangeRequests } = useChangeRequestOpen(projectId);
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const onSuggestRemove = async (event: React.FormEvent) => {
|
||||
try {
|
||||
@ -145,6 +147,7 @@ const useOnSuggestRemove = ({
|
||||
title: 'Changes added to the draft!',
|
||||
type: 'success',
|
||||
});
|
||||
await refetchChangeRequests();
|
||||
} catch (error: unknown) {
|
||||
setToastApiError(formatUnknownError(error));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user