1
0
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:
Mateusz Kwasniewski 2022-11-04 12:52:47 +01:00 committed by GitHub
parent 4b281d9513
commit 065833e5d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 153 additions and 76 deletions

View File

@ -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>

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);

View File

@ -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;

View File

@ -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));
}