mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
Fix/strategy UI improvements (#3766)
https://linear.app/unleash/issue/1-889/ui-adjustments  --------- Signed-off-by: andreas-unleash <andreas@getunleash.ai> Co-authored-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
parent
e075d46f79
commit
0cb6174f75
@ -24,22 +24,34 @@ export const ChangeItemWrapper = styled(Box)({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ChangeItemCreateEditWrapper = styled(Box)(({ theme }) => ({
|
const ChangeItemCreateEditWrapper = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'grid',
|
||||||
justifyContent: 'space-between',
|
gridTemplateColumns: 'auto 40px',
|
||||||
|
gap: theme.spacing(1),
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: theme.spacing(2),
|
marginBottom: theme.spacing(2),
|
||||||
|
width: '100%',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ChangeItemInfo: FC = styled(Box)(({ theme }) => ({
|
const ChangeItemInfo: FC = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '150px auto',
|
||||||
|
gridAutoFlow: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
flexGrow: 1,
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const hasNameField = (payload: unknown): payload is { name: string } =>
|
const hasNameField = (payload: unknown): payload is { name: string } =>
|
||||||
typeof payload === 'object' && payload !== null && 'name' in payload;
|
typeof payload === 'object' && payload !== null && 'name' in payload;
|
||||||
|
|
||||||
const DisabledEnabledState: VFC<{ disabled: boolean }> = ({ disabled }) => {
|
const DisabledEnabledState: VFC<{ show?: boolean; disabled: boolean }> = ({
|
||||||
|
show = true,
|
||||||
|
disabled,
|
||||||
|
}) => {
|
||||||
|
if (!show) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@ -73,18 +85,16 @@ const EditHeader: VFC<{
|
|||||||
}> = ({ wasDisabled = false, willBeDisabled = false }) => {
|
}> = ({ wasDisabled = false, willBeDisabled = false }) => {
|
||||||
if (wasDisabled && willBeDisabled) {
|
if (wasDisabled && willBeDisabled) {
|
||||||
return (
|
return (
|
||||||
<Typography color="action.disabled">
|
<Typography color="action.disabled">Editing strategy:</Typography>
|
||||||
Editing disabled strategy
|
|
||||||
</Typography>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wasDisabled && willBeDisabled) {
|
if (!wasDisabled && willBeDisabled) {
|
||||||
return <Typography color="error.dark">Editing strategy</Typography>;
|
return <Typography color="error.dark">Editing strategy:</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasDisabled && !willBeDisabled) {
|
if (wasDisabled && !willBeDisabled) {
|
||||||
return <Typography color="success.dark">Editing strategy</Typography>;
|
return <Typography color="success.dark">Editing strategy:</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Typography>Editing strategy:</Typography>;
|
return <Typography>Editing strategy:</Typography>;
|
||||||
@ -128,14 +138,14 @@ export const StrategyChange: VFC<{
|
|||||||
currentStrategy={currentStrategy}
|
currentStrategy={currentStrategy}
|
||||||
/>
|
/>
|
||||||
</StrategyTooltipLink>
|
</StrategyTooltipLink>
|
||||||
<ConditionallyRender
|
<div>
|
||||||
condition={Boolean(
|
<DisabledEnabledState
|
||||||
change.payload?.disabled === true
|
disabled
|
||||||
)}
|
show={change.payload?.disabled === true}
|
||||||
show={<DisabledEnabledState disabled={true} />}
|
/>
|
||||||
/>
|
</div>
|
||||||
</ChangeItemInfo>
|
</ChangeItemInfo>
|
||||||
{discard}
|
<div>{discard}</div>
|
||||||
</ChangeItemCreateEditWrapper>
|
</ChangeItemCreateEditWrapper>
|
||||||
<StrategyExecution strategy={change.payload} />
|
<StrategyExecution strategy={change.payload} />
|
||||||
</>
|
</>
|
||||||
@ -144,9 +154,11 @@ export const StrategyChange: VFC<{
|
|||||||
<ChangeItemWrapper>
|
<ChangeItemWrapper>
|
||||||
<ChangeItemInfo>
|
<ChangeItemInfo>
|
||||||
<Typography
|
<Typography
|
||||||
sx={theme => ({ color: theme.palette.error.main })}
|
sx={theme => ({
|
||||||
|
color: theme.palette.error.main,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
- Deleting strategy
|
- Deleting strategy:
|
||||||
</Typography>
|
</Typography>
|
||||||
{hasNameField(change.payload) && (
|
{hasNameField(change.payload) && (
|
||||||
<StrategyTooltipLink change={change}>
|
<StrategyTooltipLink change={change}>
|
||||||
@ -157,7 +169,7 @@ export const StrategyChange: VFC<{
|
|||||||
</StrategyTooltipLink>
|
</StrategyTooltipLink>
|
||||||
)}
|
)}
|
||||||
</ChangeItemInfo>
|
</ChangeItemInfo>
|
||||||
{discard}
|
<div>{discard}</div>
|
||||||
</ChangeItemWrapper>
|
</ChangeItemWrapper>
|
||||||
)}
|
)}
|
||||||
{change.action === 'updateStrategy' && (
|
{change.action === 'updateStrategy' && (
|
||||||
@ -178,9 +190,8 @@ export const StrategyChange: VFC<{
|
|||||||
/>
|
/>
|
||||||
</StrategyTooltipLink>
|
</StrategyTooltipLink>
|
||||||
</ChangeItemInfo>
|
</ChangeItemInfo>
|
||||||
{discard}
|
<div>{discard}</div>
|
||||||
</ChangeItemCreateEditWrapper>
|
</ChangeItemCreateEditWrapper>
|
||||||
<StrategyExecution strategy={change.payload} />
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={
|
condition={
|
||||||
change.payload?.disabled !==
|
change.payload?.disabled !==
|
||||||
@ -190,8 +201,7 @@ export const StrategyChange: VFC<{
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
marginTop: theme => theme.spacing(2),
|
marginTop: theme => theme.spacing(2),
|
||||||
paddingLeft: theme => theme.spacing(3),
|
marginBottom: theme => theme.spacing(2),
|
||||||
paddingRight: theme => theme.spacing(3),
|
|
||||||
...flexRow,
|
...flexRow,
|
||||||
gap: theme => theme.spacing(1),
|
gap: theme => theme.spacing(1),
|
||||||
}}
|
}}
|
||||||
@ -203,6 +213,7 @@ export const StrategyChange: VFC<{
|
|||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<StrategyExecution strategy={change.payload} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -56,53 +56,59 @@ interface IStrategyTooltipLinkProps {
|
|||||||
previousTitle?: string;
|
previousTitle?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyledContainer: FC = styled('div')(({ theme }) => ({
|
||||||
|
display: 'grid',
|
||||||
|
gridAutoFlow: 'column',
|
||||||
|
gridTemplateColumns: 'auto 1fr',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Truncated = styled('div')(() => ({
|
||||||
|
...textTruncated,
|
||||||
|
maxWidth: 500,
|
||||||
|
}));
|
||||||
|
|
||||||
export const StrategyTooltipLink: FC<IStrategyTooltipLinkProps> = ({
|
export const StrategyTooltipLink: FC<IStrategyTooltipLinkProps> = ({
|
||||||
change,
|
change,
|
||||||
previousTitle,
|
previousTitle,
|
||||||
children,
|
children,
|
||||||
}) => (
|
}) => (
|
||||||
<>
|
<StyledContainer>
|
||||||
<GetFeatureStrategyIcon strategyName={change.payload.name} />
|
<GetFeatureStrategyIcon strategyName={change.payload.name} />
|
||||||
<ConditionallyRender
|
<Truncated>
|
||||||
condition={Boolean(
|
<ConditionallyRender
|
||||||
previousTitle && previousTitle !== change.payload.title
|
condition={Boolean(
|
||||||
)}
|
(previousTitle && previousTitle !== change.payload.title) ||
|
||||||
show={
|
true
|
||||||
<>
|
)}
|
||||||
<Typography
|
show={
|
||||||
component="s"
|
<Truncated>
|
||||||
color="action.disabled"
|
<Typography component="s" color="text.secondary">
|
||||||
sx={{
|
{previousTitle}
|
||||||
...textTruncated,
|
PREVIOUS consectetur adipiscing elit, sed do eiusmod
|
||||||
maxWidth: '100px',
|
tempor incididunt ut labore et dolore magna aliqua.
|
||||||
}}
|
</Typography>{' '}
|
||||||
>
|
</Truncated>
|
||||||
{previousTitle}
|
}
|
||||||
</Typography>{' '}
|
/>
|
||||||
</>
|
<Truncated>
|
||||||
}
|
<TooltipLink
|
||||||
/>
|
tooltip={children}
|
||||||
<TooltipLink
|
tooltipProps={{
|
||||||
tooltip={children}
|
maxWidth: 500,
|
||||||
tooltipProps={{
|
maxHeight: 600,
|
||||||
maxWidth: 500,
|
}}
|
||||||
maxHeight: 600,
|
>
|
||||||
}}
|
<Typography component="span">
|
||||||
>
|
{change.payload.title ||
|
||||||
<Typography
|
formatStrategyName(change.payload.name)}
|
||||||
component="span"
|
lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||||
sx={{
|
sed do eiusmod tempor incididunt ut labore et dolore
|
||||||
...textTruncated,
|
magna aliqua.
|
||||||
maxWidth:
|
</Typography>
|
||||||
previousTitle === change.payload.title
|
</TooltipLink>
|
||||||
? '300px'
|
</Truncated>
|
||||||
: '200px',
|
</Truncated>
|
||||||
display: 'block',
|
</StyledContainer>
|
||||||
}}
|
|
||||||
>
|
|
||||||
{change.payload.title ||
|
|
||||||
formatStrategyName(change.payload.name)}
|
|
||||||
</Typography>
|
|
||||||
</TooltipLink>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
|
@ -81,7 +81,7 @@ const StyledHeader = styled('div', {
|
|||||||
fontWeight: theme.typography.fontWeightMedium,
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
paddingLeft: draggable ? theme.spacing(1) : theme.spacing(2),
|
paddingLeft: draggable ? theme.spacing(1) : theme.spacing(2),
|
||||||
color: disabled
|
color: disabled
|
||||||
? theme.palette.action.disabled
|
? theme.palette.text.secondary
|
||||||
: theme.palette.text.primary,
|
: theme.palette.text.primary,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -139,7 +139,7 @@ export const StrategyItemContainer: FC<IStrategyItemContainerProps> = ({
|
|||||||
/>
|
/>
|
||||||
<StyledHeaderContainer>
|
<StyledHeaderContainer>
|
||||||
<StringTruncator
|
<StringTruncator
|
||||||
maxWidth="150"
|
maxWidth="400"
|
||||||
maxLength={15}
|
maxLength={15}
|
||||||
text={formatStrategyName(
|
text={formatStrategyName(
|
||||||
uiConfig?.flags?.strategyImprovements
|
uiConfig?.flags?.strategyImprovements
|
||||||
|
@ -211,16 +211,6 @@ export const FeatureStrategyForm = ({
|
|||||||
/>
|
/>
|
||||||
</FeatureStrategyEnabled>
|
</FeatureStrategyEnabled>
|
||||||
<StyledHr />
|
<StyledHr />
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(uiConfig.flags.SE)}
|
|
||||||
show={
|
|
||||||
<FeatureStrategySegment
|
|
||||||
segments={segments}
|
|
||||||
setSegments={setSegments}
|
|
||||||
projectId={projectId}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(uiConfig?.flags?.strategyImprovements)}
|
condition={Boolean(uiConfig?.flags?.strategyImprovements)}
|
||||||
show={
|
show={
|
||||||
@ -235,6 +225,16 @@ export const FeatureStrategyForm = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(uiConfig.flags.SE)}
|
||||||
|
show={
|
||||||
|
<FeatureStrategySegment
|
||||||
|
segments={segments}
|
||||||
|
setSegments={setSegments}
|
||||||
|
projectId={projectId}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<FeatureStrategyConstraints
|
<FeatureStrategyConstraints
|
||||||
projectId={feature.project}
|
projectId={feature.project}
|
||||||
environmentId={environmentId}
|
environmentId={environmentId}
|
||||||
|
@ -24,6 +24,7 @@ interface IFeatureStrategyRemoveProps {
|
|||||||
strategyId: string;
|
strategyId: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
icon?: boolean;
|
icon?: boolean;
|
||||||
|
text?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFeatureStrategyRemoveDialogueProps {
|
interface IFeatureStrategyRemoveDialogueProps {
|
||||||
@ -163,6 +164,7 @@ export const FeatureStrategyRemove = ({
|
|||||||
strategyId,
|
strategyId,
|
||||||
disabled,
|
disabled,
|
||||||
icon,
|
icon,
|
||||||
|
text,
|
||||||
}: IFeatureStrategyRemoveProps) => {
|
}: IFeatureStrategyRemoveProps) => {
|
||||||
const [openDialogue, setOpenDialogue] = useState(false);
|
const [openDialogue, setOpenDialogue] = useState(false);
|
||||||
|
|
||||||
@ -197,6 +199,18 @@ export const FeatureStrategyRemove = ({
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<Delete />
|
<Delete />
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(text)}
|
||||||
|
show={
|
||||||
|
<Typography
|
||||||
|
variant={'body1'}
|
||||||
|
color={'text.secondary'}
|
||||||
|
sx={{ ml: theme => theme.spacing(1) }}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</PermissionIconButton>
|
</PermissionIconButton>
|
||||||
}
|
}
|
||||||
elseShow={
|
elseShow={
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { VFC, useState } from 'react';
|
import { VFC, useState } from 'react';
|
||||||
import { Alert } from '@mui/material';
|
import { Alert, Typography } from '@mui/material';
|
||||||
import BlockIcon from '@mui/icons-material/Block';
|
import BlockIcon from '@mui/icons-material/Block';
|
||||||
import TrackChangesIcon from '@mui/icons-material/TrackChanges';
|
import TrackChangesIcon from '@mui/icons-material/TrackChanges';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
@ -46,6 +46,18 @@ const DisableStrategy: VFC<IDisableEnableStrategyProps> = ({ ...props }) => {
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<BlockIcon />
|
<BlockIcon />
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(props.text)}
|
||||||
|
show={
|
||||||
|
<Typography
|
||||||
|
variant={'body1'}
|
||||||
|
color={'text.secondary'}
|
||||||
|
sx={{ ml: theme => theme.spacing(1) }}
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</PermissionIconButton>
|
</PermissionIconButton>
|
||||||
<Dialogue
|
<Dialogue
|
||||||
title={
|
title={
|
||||||
@ -111,6 +123,18 @@ const EnableStrategy: VFC<IDisableEnableStrategyProps> = ({ ...props }) => {
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<TrackChangesIcon />
|
<TrackChangesIcon />
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(props.text)}
|
||||||
|
show={
|
||||||
|
<Typography
|
||||||
|
variant={'body1'}
|
||||||
|
color={'text.secondary'}
|
||||||
|
sx={{ ml: theme => theme.spacing(1) }}
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</PermissionIconButton>
|
</PermissionIconButton>
|
||||||
<Dialogue
|
<Dialogue
|
||||||
title={
|
title={
|
||||||
|
@ -5,4 +5,5 @@ export interface IDisableEnableStrategyProps {
|
|||||||
featureId: string;
|
featureId: string;
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
strategy: IFeatureStrategy;
|
strategy: IFeatureStrategy;
|
||||||
|
text?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,134 @@
|
|||||||
|
import React, { SyntheticEvent } from 'react';
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
IconButton,
|
||||||
|
ListItem,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
styled,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||||
|
import { IFeatureStrategy } from '../../../../../../../../../../interfaces/strategy';
|
||||||
|
import { FeatureStrategyRemove } from '../../../../../../../../FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove';
|
||||||
|
import { DisableEnableStrategy } from '../DisableEnableStrategy/DisableEnableStrategy';
|
||||||
|
|
||||||
|
export interface IRemoveStrategyMenuProps {
|
||||||
|
projectId: string;
|
||||||
|
featureId: string;
|
||||||
|
environmentId: string;
|
||||||
|
strategy: IFeatureStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledContainer = styled(ListItem)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
minWidth: 'fit-content',
|
||||||
|
padding: theme.spacing(0, 2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const RemoveStrategyMenu = ({
|
||||||
|
projectId,
|
||||||
|
strategy,
|
||||||
|
featureId,
|
||||||
|
environmentId,
|
||||||
|
}: IRemoveStrategyMenuProps) => {
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = (event: SyntheticEvent) => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip title="More actions">
|
||||||
|
<IconButton
|
||||||
|
onClick={handleClick}
|
||||||
|
size="small"
|
||||||
|
aria-controls={open ? 'actions-menu' : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={open ? 'true' : undefined}
|
||||||
|
>
|
||||||
|
<MoreVertIcon sx={{ width: 32, height: 32 }} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
id="actions-menu"
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
onClick={handleClose}
|
||||||
|
PaperProps={{
|
||||||
|
elevation: 0,
|
||||||
|
sx: {
|
||||||
|
overflow: 'visible',
|
||||||
|
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
|
||||||
|
mt: 1.5,
|
||||||
|
pl: 0.5,
|
||||||
|
minWidth: 'fit-content',
|
||||||
|
justifyContent: 'center',
|
||||||
|
li: {
|
||||||
|
pl: 0,
|
||||||
|
},
|
||||||
|
'&:before': {
|
||||||
|
content: '""',
|
||||||
|
display: 'block',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
right: 14,
|
||||||
|
width: 10,
|
||||||
|
height: 10,
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
transform: 'translateY(-50%) rotate(45deg)',
|
||||||
|
zIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||||
|
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||||
|
>
|
||||||
|
<MenuItem
|
||||||
|
component={() => (
|
||||||
|
<StyledContainer>
|
||||||
|
<DisableEnableStrategy
|
||||||
|
projectId={projectId}
|
||||||
|
featureId={featureId}
|
||||||
|
environmentId={environmentId}
|
||||||
|
strategy={strategy}
|
||||||
|
text
|
||||||
|
/>
|
||||||
|
</StyledContainer>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<MenuItem
|
||||||
|
component={() => (
|
||||||
|
<FeatureStrategyRemove
|
||||||
|
projectId={projectId}
|
||||||
|
featureId={featureId}
|
||||||
|
environmentId={environmentId}
|
||||||
|
strategyId={strategy.id}
|
||||||
|
text
|
||||||
|
icon
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RemoveStrategyMenu;
|
@ -14,6 +14,7 @@ import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMen
|
|||||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||||
import { DisableEnableStrategy } from './DisableEnableStrategy/DisableEnableStrategy';
|
import { DisableEnableStrategy } from './DisableEnableStrategy/DisableEnableStrategy';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
|
import RemoveStrategyMenu from './RemoveStrategyMenu/RemoveStrategyMenu';
|
||||||
|
|
||||||
interface IStrategyItemProps {
|
interface IStrategyItemProps {
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
@ -84,20 +85,22 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
|
|||||||
uiConfig?.flags?.strategyImprovements
|
uiConfig?.flags?.strategyImprovements
|
||||||
)}
|
)}
|
||||||
show={() => (
|
show={() => (
|
||||||
<DisableEnableStrategy
|
<RemoveStrategyMenu
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
featureId={featureId}
|
featureId={featureId}
|
||||||
environmentId={environmentId}
|
environmentId={environmentId}
|
||||||
strategy={strategy}
|
strategy={strategy}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
elseShow={() => (
|
||||||
<FeatureStrategyRemove
|
<FeatureStrategyRemove
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
featureId={featureId}
|
featureId={featureId}
|
||||||
environmentId={environmentId}
|
environmentId={environmentId}
|
||||||
strategyId={strategy.id}
|
strategyId={strategy.id}
|
||||||
icon
|
icon
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import useToast from 'hooks/useToast';
|
|||||||
import { EnvironmentVariantsCopyFrom } from './EnvironmentVariantsCopyFrom/EnvironmentVariantsCopyFrom';
|
import { EnvironmentVariantsCopyFrom } from './EnvironmentVariantsCopyFrom/EnvironmentVariantsCopyFrom';
|
||||||
import { PushVariantsButton } from './PushVariantsButton/PushVariantsButton';
|
import { PushVariantsButton } from './PushVariantsButton/PushVariantsButton';
|
||||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|
||||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||||
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
@ -42,7 +41,6 @@ const StyledButtonContainer = styled('div')(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const FeatureEnvironmentVariants = () => {
|
export const FeatureEnvironmentVariants = () => {
|
||||||
const { uiConfig } = useUiConfig();
|
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
|
@ -3,7 +3,6 @@ import useProject from 'hooks/api/getters/useProject/useProject';
|
|||||||
import useLoading from 'hooks/useLoading';
|
import useLoading from 'hooks/useLoading';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import {
|
import {
|
||||||
StyledColumn,
|
|
||||||
StyledDiv,
|
StyledDiv,
|
||||||
StyledFavoriteIconButton,
|
StyledFavoriteIconButton,
|
||||||
StyledHeader,
|
StyledHeader,
|
||||||
@ -13,8 +12,6 @@ import {
|
|||||||
StyledSeparator,
|
StyledSeparator,
|
||||||
StyledTab,
|
StyledTab,
|
||||||
StyledTabContainer,
|
StyledTabContainer,
|
||||||
StyledText,
|
|
||||||
StyledTitle,
|
|
||||||
StyledTopRow,
|
StyledTopRow,
|
||||||
} from './Project.styles';
|
} from './Project.styles';
|
||||||
import { Tabs } from '@mui/material';
|
import { Tabs } from '@mui/material';
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
import { Box, styled, Typography } from '@mui/material';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
|
|
||||||
import { flexRow } from 'themes/themeStyles';
|
|
||||||
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
|
||||||
import { StyledProjectInfoWidgetContainer } from './ProjectInfo.styles';
|
|
||||||
|
|
||||||
interface ILegacyHealthWidgetProps {
|
|
||||||
projectId: string;
|
|
||||||
health: number;
|
|
||||||
total?: number;
|
|
||||||
stale?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const StyledParagraphEmphasizedText = styled('p')(({ theme }) => ({
|
|
||||||
fontSize: '1.5rem',
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
fontSize: theme.fontSizes.bodySize,
|
|
||||||
marginBottom: theme.spacing(4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledDivPercentageContainer = styled('div')(() => ({
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledLink = styled(Link)(({ theme }) => ({
|
|
||||||
textDecoration: 'none',
|
|
||||||
...flexRow,
|
|
||||||
justifyContent: 'center',
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: theme.spacing(1.5),
|
|
||||||
right: theme.spacing(1.5),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledSpanLinkText = styled('p')(({ theme }) => ({
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledArrowIcon = styled(ArrowForwardIcon)(({ theme }) => ({
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export const LegacyHealthWidget = ({
|
|
||||||
projectId,
|
|
||||||
health,
|
|
||||||
}: ILegacyHealthWidgetProps) => (
|
|
||||||
<StyledProjectInfoWidgetContainer>
|
|
||||||
<StyledDivPercentageContainer>
|
|
||||||
<PercentageCircle percentage={health} />
|
|
||||||
</StyledDivPercentageContainer>
|
|
||||||
<Typography data-loading sx={{ marginTop: theme => theme.spacing(2) }}>
|
|
||||||
Overall health rating
|
|
||||||
</Typography>
|
|
||||||
<Box sx={{ marginBottom: theme => theme.spacing(2.5) }}>
|
|
||||||
<StyledParagraphEmphasizedText data-loading>
|
|
||||||
{health}%
|
|
||||||
</StyledParagraphEmphasizedText>
|
|
||||||
</Box>
|
|
||||||
<StyledLink data-loading to={`/projects/${projectId}/health`}>
|
|
||||||
<StyledSpanLinkText data-loading>view more </StyledSpanLinkText>
|
|
||||||
<StyledArrowIcon data-loading />
|
|
||||||
</StyledLink>
|
|
||||||
</StyledProjectInfoWidgetContainer>
|
|
||||||
);
|
|
@ -1,93 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
|
|
||||||
import { flexRow } from 'themes/themeStyles';
|
|
||||||
import { styled } from '@mui/material';
|
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|
||||||
|
|
||||||
const StyledDivInfoContainer = styled('div')(({ theme }) => ({
|
|
||||||
textAlign: 'center',
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
|
||||||
borderRadius: theme.shape.borderRadiusLarge,
|
|
||||||
width: '100%',
|
|
||||||
padding: theme.spacing(3, 2, 3, 2),
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
...flexRow,
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
position: 'relative',
|
|
||||||
padding: theme.spacing(1.5),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledParagraphSubtitle = styled('p')(({ theme }) => ({
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledParagraphEmphasizedText = styled('p')(({ theme }) => ({
|
|
||||||
fontSize: '1.5rem',
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
fontSize: theme.fontSizes.bodySize,
|
|
||||||
marginBottom: theme.spacing(4),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledSpanLinkText = styled('p')(({ theme }) => ({
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledLink = styled(Link)(({ theme }) => ({
|
|
||||||
textDecoration: 'none',
|
|
||||||
...flexRow,
|
|
||||||
justifyContent: 'center',
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
position: 'absolute',
|
|
||||||
right: theme.spacing(1.5),
|
|
||||||
bottom: theme.spacing(1.5),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledArrowIcon = styled(ArrowForwardIcon)(({ theme }) => ({
|
|
||||||
color: theme.palette.primary.main,
|
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface ILegacyProjectMembersWidgetProps {
|
|
||||||
projectId: string;
|
|
||||||
memberCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
export const LegacyProjectMembersWidget = ({
|
|
||||||
projectId,
|
|
||||||
memberCount,
|
|
||||||
}: ILegacyProjectMembersWidgetProps) => {
|
|
||||||
const { uiConfig } = useUiConfig();
|
|
||||||
|
|
||||||
let link = `/admin/users`;
|
|
||||||
|
|
||||||
if (uiConfig?.versionInfo?.current?.enterprise) {
|
|
||||||
link = `/projects/${projectId}/settings/access`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledDivInfoContainer>
|
|
||||||
<StyledParagraphSubtitle data-loading>
|
|
||||||
Project members
|
|
||||||
</StyledParagraphSubtitle>
|
|
||||||
<StyledParagraphEmphasizedText data-loading>
|
|
||||||
{memberCount}
|
|
||||||
</StyledParagraphEmphasizedText>
|
|
||||||
<StyledLink data-loading to={link}>
|
|
||||||
<StyledSpanLinkText data-loading>view more </StyledSpanLinkText>
|
|
||||||
<StyledArrowIcon data-loading />
|
|
||||||
</StyledLink>
|
|
||||||
</StyledDivInfoContainer>
|
|
||||||
);
|
|
||||||
};
|
|
@ -10,8 +10,6 @@ import { ProjectMembersWidget } from './ProjectMembersWidget';
|
|||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { ChangeRequestsWidget } from './ChangeRequestsWidget';
|
import { ChangeRequestsWidget } from './ChangeRequestsWidget';
|
||||||
import { flexRow } from 'themes/themeStyles';
|
import { flexRow } from 'themes/themeStyles';
|
||||||
import { LegacyHealthWidget } from './LegacyHealthWidget';
|
|
||||||
import { LegacyProjectMembersWidget } from './LegacyProjectMembersWidget';
|
|
||||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||||
|
|
||||||
interface IProjectInfoProps {
|
interface IProjectInfoProps {
|
||||||
@ -48,7 +46,7 @@ const ProjectInfo = ({
|
|||||||
features,
|
features,
|
||||||
stats,
|
stats,
|
||||||
}: IProjectInfoProps) => {
|
}: IProjectInfoProps) => {
|
||||||
const { uiConfig, isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(id);
|
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(id);
|
||||||
|
@ -42,7 +42,6 @@ const ProjectOverview = () => {
|
|||||||
project;
|
project;
|
||||||
usePageTitle(`Project overview – ${projectName}`);
|
usePageTitle(`Project overview – ${projectName}`);
|
||||||
const { setLastViewed } = useLastViewedProject();
|
const { setLastViewed } = useLastViewedProject();
|
||||||
const { uiConfig } = useUiConfig();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLastViewed(projectId);
|
setLastViewed(projectId);
|
||||||
|
@ -3,11 +3,11 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useStrategy } from 'hooks/api/getters/useStrategy/useStrategy';
|
import { useStrategy } from 'hooks/api/getters/useStrategy/useStrategy';
|
||||||
import React, { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
|
||||||
import { IFeatureStrategy, IStrategy } from 'interfaces/strategy';
|
import { IStrategy } from 'interfaces/strategy';
|
||||||
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
||||||
import { ISegment } from 'interfaces/segment';
|
import { ISegment } from 'interfaces/segment';
|
||||||
import { useFormErrors } from 'hooks/useFormErrors';
|
import { useFormErrors } from 'hooks/useFormErrors';
|
||||||
@ -42,7 +42,7 @@ const EditDefaultStrategy = ({ strategy }: EditDefaultStrategyProps) => {
|
|||||||
const { unleashUrl } = uiConfig;
|
const { unleashUrl } = uiConfig;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [previousTitle, setPreviousTitle] = useState<string>('');
|
const [previousTitle] = useState<string>('');
|
||||||
const { trackEvent } = usePlausibleTracker();
|
const { trackEvent } = usePlausibleTracker();
|
||||||
|
|
||||||
const trackTitle = (title: string = '') => {
|
const trackTitle = (title: string = '') => {
|
||||||
|
@ -27,13 +27,11 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { Search } from 'component/common/Search/Search';
|
import { Search } from 'component/common/Search/Search';
|
||||||
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns';
|
||||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
|
||||||
import { useOptionalPathParam } from 'hooks/useOptionalPathParam';
|
import { useOptionalPathParam } from 'hooks/useOptionalPathParam';
|
||||||
|
|
||||||
export const SegmentTable = () => {
|
export const SegmentTable = () => {
|
||||||
const projectId = useOptionalPathParam('projectId');
|
const projectId = useOptionalPathParam('projectId');
|
||||||
const { segments, loading } = useSegments();
|
const { segments, loading } = useSegments();
|
||||||
const { uiConfig } = useUiConfig();
|
|
||||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||||
const [initialState] = useState({
|
const [initialState] = useState({
|
||||||
sortBy: [{ id: 'createdAt' }],
|
sortBy: [{ id: 'createdAt' }],
|
||||||
|
Loading…
Reference in New Issue
Block a user