1
0
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


![image](https://github.com/Unleash/unleash/assets/2625371/e9d851e6-57b5-4deb-b3de-2c0c69fa71dd)

---------

Signed-off-by: andreas-unleash <andreas@getunleash.ai>
Co-authored-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
Tymoteusz Czech 2023-05-16 13:15:59 +02:00 committed by GitHub
parent e075d46f79
commit 0cb6174f75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 284 additions and 269 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,4 +5,5 @@ export interface IDisableEnableStrategyProps {
featureId: string; featureId: string;
environmentId: string; environmentId: string;
strategy: IFeatureStrategy; strategy: IFeatureStrategy;
text?: boolean;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = '') => {

View File

@ -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' }],