mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
Feat: toggle overview env last seen (#4445)
<!-- Thanks for creating a PR! To make it easier for reviewers and everyone else to understand what your changes relate to, please add some relevant content to the headings below. Feel free to ignore or delete sections that you don't think are relevant. Thank you! ❤️ --> Adds last seen by environment to FeatureOverview - Refactored FeatureEnvironmentSeen component for reusability <!-- Does it close an issue? Multiple? --> Closes # [1-1223](https://linear.app/unleash/issue/1-1223/toggle-overview-add-last-seen-by-environment) ![Screenshot 2023-08-08 at 14 12 29](https://github.com/Unleash/unleash/assets/104830839/cfb29492-863b-491c-8f6f-e1133246dcb3) --------- Signed-off-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
parent
e7ae1ff714
commit
839c36d547
@ -1,108 +1,19 @@
|
||||
import React, { FC, ReactElement, VFC } from 'react';
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||
import { LastSeenTooltip } from './LastSeenTooltip';
|
||||
import React, { VFC } from 'react';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
import { ReactComponent as UsageLine } from 'assets/icons/usage-line.svg';
|
||||
import { ReactComponent as UsageRate } from 'assets/icons/usage-rate.svg';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { useLastSeenColors } from './useLastSeenColors';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
padding: theme.spacing(1.5),
|
||||
}));
|
||||
|
||||
const StyledBox = styled(Box)(({ theme }) => ({
|
||||
width: '28px',
|
||||
height: '28px',
|
||||
background: 'transparent',
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
margin: '0 auto',
|
||||
}));
|
||||
|
||||
const StyledIconWrapper = styled('div')(({ theme }) => ({
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
background: theme.palette.background.paper,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
margin: '0 auto',
|
||||
}));
|
||||
import { FeatureEnvironmentSeen } from 'component/feature/FeatureView/FeatureEnvironmentSeen/FeatureEnvironmentSeen';
|
||||
|
||||
interface IFeatureSeenCellProps {
|
||||
feature: IFeatureToggleListItem;
|
||||
}
|
||||
|
||||
const TooltipContainer: FC<{
|
||||
color?: string;
|
||||
tooltip: ReactElement | string;
|
||||
}> = ({ tooltip, color, children }) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<TooltipResolver
|
||||
variant="custom"
|
||||
titleComponent={tooltip}
|
||||
arrow
|
||||
describeChild
|
||||
>
|
||||
<StyledBox sx={{ '&:hover': { background: color } }}>
|
||||
<StyledIconWrapper style={{ background: color }}>
|
||||
{children}
|
||||
</StyledIconWrapper>
|
||||
</StyledBox>
|
||||
</TooltipResolver>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const FeatureEnvironmentSeenCell: VFC<IFeatureSeenCellProps> = ({
|
||||
feature,
|
||||
}) => {
|
||||
const getColor = useLastSeenColors();
|
||||
const environments = Object.values(feature.environments);
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={Boolean(feature.lastSeenAt)}
|
||||
show={
|
||||
feature.lastSeenAt && (
|
||||
<TimeAgo
|
||||
date={feature.lastSeenAt}
|
||||
title=""
|
||||
live={false}
|
||||
formatter={(value: number, unit: string) => {
|
||||
const [color, textColor] = getColor(unit);
|
||||
return (
|
||||
<TooltipContainer
|
||||
tooltip={
|
||||
<LastSeenTooltip
|
||||
environments={environments}
|
||||
/>
|
||||
}
|
||||
color={color}
|
||||
>
|
||||
<UsageRate stroke={textColor} />
|
||||
</TooltipContainer>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
elseShow={
|
||||
<TooltipContainer tooltip="No usage reported from connected applications">
|
||||
<UsageLine />
|
||||
</TooltipContainer>
|
||||
}
|
||||
<FeatureEnvironmentSeen
|
||||
featureLastSeen={feature.lastSeenAt}
|
||||
environments={environments}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { styled, SxProps, Theme } from '@mui/material';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { IEnvironments } from 'interfaces/featureToggle';
|
||||
import { IEnvironments, IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import React from 'react';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useLastSeenColors } from './useLastSeenColors';
|
||||
import { useLastSeenColors } from 'component/feature/FeatureView/FeatureEnvironmentSeen/useLastSeenColors';
|
||||
|
||||
const StyledDescription = styled(
|
||||
'div',
|
||||
@ -56,7 +56,7 @@ const StyledValue = styled('div', {
|
||||
}));
|
||||
|
||||
interface ILastSeenTooltipProps {
|
||||
environments?: IEnvironments[];
|
||||
environments?: IEnvironments[] | IFeatureEnvironment[];
|
||||
className?: string;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
@ -66,7 +66,7 @@ export const LastSeenTooltip = ({
|
||||
...rest
|
||||
}: ILastSeenTooltipProps) => {
|
||||
const getColor = useLastSeenColors();
|
||||
const [defaultColor] = getColor();
|
||||
const [, defaultTextColor] = getColor();
|
||||
return (
|
||||
<StyledDescription {...rest}>
|
||||
<StyledDescriptionHeader sx={{ mb: 0 }}>
|
||||
@ -107,7 +107,7 @@ export const LastSeenTooltip = ({
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<StyledValue color={defaultColor}>
|
||||
<StyledValue color={defaultTextColor}>
|
||||
no usage
|
||||
</StyledValue>
|
||||
}
|
||||
|
@ -0,0 +1,113 @@
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { LastSeenTooltip } from 'component/common/Table/cells/FeatureSeenCell/LastSeenTooltip';
|
||||
import React, { FC, ReactElement } from 'react';
|
||||
import { IEnvironments, IFeatureEnvironment } from 'interfaces/featureToggle';
|
||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||
import { Box, styled, SxProps } from '@mui/material';
|
||||
import { ReactComponent as UsageLine } from 'assets/icons/usage-line.svg';
|
||||
import { ReactComponent as UsageRate } from 'assets/icons/usage-rate.svg';
|
||||
import { useLastSeenColors } from './useLastSeenColors';
|
||||
|
||||
interface IFeatureEnvironmentSeenProps {
|
||||
featureLastSeen: string | undefined;
|
||||
environments: IEnvironments[] | IFeatureEnvironment[];
|
||||
sx?: SxProps;
|
||||
}
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
padding: theme.spacing(1.5),
|
||||
}));
|
||||
|
||||
const StyledBox = styled(Box)(({ theme }) => ({
|
||||
width: '28px',
|
||||
height: '28px',
|
||||
background: 'transparent',
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
margin: '0 auto',
|
||||
}));
|
||||
|
||||
const StyledIconWrapper = styled('div')(({ theme }) => ({
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
background: theme.palette.background.paper,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
margin: '0 auto',
|
||||
}));
|
||||
|
||||
const TooltipContainer: FC<{
|
||||
color?: string;
|
||||
tooltip: ReactElement | string;
|
||||
sx?: SxProps;
|
||||
}> = ({ sx, tooltip, color, children }) => {
|
||||
return (
|
||||
<StyledContainer sx={sx}>
|
||||
<TooltipResolver
|
||||
variant="custom"
|
||||
titleComponent={tooltip}
|
||||
arrow
|
||||
describeChild
|
||||
>
|
||||
<StyledBox sx={{ '&:hover': { background: color } }}>
|
||||
<StyledIconWrapper style={{ background: color }}>
|
||||
{children}
|
||||
</StyledIconWrapper>
|
||||
</StyledBox>
|
||||
</TooltipResolver>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const FeatureEnvironmentSeen = ({
|
||||
featureLastSeen,
|
||||
environments,
|
||||
sx,
|
||||
}: IFeatureEnvironmentSeenProps) => {
|
||||
const getColor = useLastSeenColors();
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={Boolean(featureLastSeen)}
|
||||
show={
|
||||
featureLastSeen && (
|
||||
<TimeAgo
|
||||
date={featureLastSeen}
|
||||
title=""
|
||||
live={false}
|
||||
formatter={(value: number, unit: string) => {
|
||||
const [color, textColor] = getColor(unit);
|
||||
return (
|
||||
<TooltipContainer
|
||||
sx={sx}
|
||||
tooltip={
|
||||
<LastSeenTooltip
|
||||
environments={environments}
|
||||
/>
|
||||
}
|
||||
color={color}
|
||||
>
|
||||
<UsageRate stroke={textColor} />
|
||||
</TooltipContainer>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
elseShow={
|
||||
<TooltipContainer tooltip="No usage reported from connected applications">
|
||||
<UsageLine />
|
||||
</TooltipContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
@ -3,6 +3,8 @@ import { styled } from '@mui/material';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import { formatDateYMD } from 'utils/formatDate';
|
||||
import { parseISO } from 'date-fns';
|
||||
import { FeatureEnvironmentSeen } from '../../../FeatureEnvironmentSeen/FeatureEnvironmentSeen';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -21,24 +23,49 @@ interface IFeatureOverviewSidePanelDetailsProps {
|
||||
header: React.ReactNode;
|
||||
}
|
||||
|
||||
const FlexRow = styled('div')({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
|
||||
const StyledDetail = styled('div')(({ theme }) => ({
|
||||
justifyContent: 'center',
|
||||
paddingTop: theme.spacing(0.75),
|
||||
}));
|
||||
|
||||
export const FeatureOverviewSidePanelDetails = ({
|
||||
feature,
|
||||
header,
|
||||
}: IFeatureOverviewSidePanelDetailsProps) => {
|
||||
const { locationSettings } = useLocationSettings();
|
||||
const { uiConfig } = useUiConfig();
|
||||
|
||||
const showLastSeenByEnvironment = Boolean(
|
||||
uiConfig.flags.lastSeenByEnvironment
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
{header}
|
||||
<div data-loading>
|
||||
<StyledLabel>Created at:</StyledLabel>
|
||||
<span>
|
||||
{formatDateYMD(
|
||||
parseISO(feature.createdAt),
|
||||
locationSettings.locale
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<FlexRow>
|
||||
<StyledDetail>
|
||||
<StyledLabel>Created at:</StyledLabel>
|
||||
<span>
|
||||
{formatDateYMD(
|
||||
parseISO(feature.createdAt),
|
||||
locationSettings.locale
|
||||
)}
|
||||
</span>
|
||||
</StyledDetail>
|
||||
{showLastSeenByEnvironment && (
|
||||
<FeatureEnvironmentSeen
|
||||
featureLastSeen={feature.lastSeenAt}
|
||||
environments={feature.environments}
|
||||
sx={{ pt: 0 }}
|
||||
/>
|
||||
)}
|
||||
</FlexRow>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user