mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
Add close button, fix layout, refactor (#8755)
This PR consists of cleanup for the project status modal as well as adding the missing button. I've added inline comments to explain the different steps. It now looks like this: 
This commit is contained in:
parent
395a4b6be3
commit
b4e2d5d270
@ -1,3 +1,3 @@
|
|||||||
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M14.125 0C15.7917 0 17.1875 0.616667 18.3125 1.85C19.4375 3.08333 20 4.55 20 6.25C20 6.55 19.9833 6.84583 19.95 7.1375C19.9167 7.42917 19.8583 7.71667 19.775 8H13.525L11.825 5.45C11.7417 5.31667 11.625 5.20833 11.475 5.125C11.325 5.04167 11.1667 5 11 5C10.7833 5 10.5875 5.06667 10.4125 5.2C10.2375 5.33333 10.1167 5.5 10.05 5.7L8.7 9.75L7.825 8.45C7.74167 8.31667 7.625 8.20833 7.475 8.125C7.325 8.04167 7.16667 8 7 8H0.225C0.141667 7.71667 0.0833333 7.42917 0.05 7.1375C0.0166667 6.84583 0 6.55833 0 6.275C0 4.55833 0.558333 3.08333 1.675 1.85C2.79167 0.616667 4.18333 0 5.85 0C6.65 0 7.40417 0.158333 8.1125 0.475C8.82083 0.791667 9.45 1.23333 10 1.8C10.5333 1.23333 11.1542 0.791667 11.8625 0.475C12.5708 0.158333 13.325 0 14.125 0ZM10 18C9.7 18 9.4125 17.9458 9.1375 17.8375C8.8625 17.7292 8.61667 17.5667 8.4 17.35L1.7 10.625C1.6 10.525 1.50833 10.425 1.425 10.325C1.34167 10.225 1.25833 10.1167 1.175 10H6.45L8.15 12.55C8.23333 12.6833 8.35 12.7917 8.5 12.875C8.65 12.9583 8.80833 13 8.975 13C9.19167 13 9.39167 12.9333 9.575 12.8C9.75833 12.6667 9.88333 12.5 9.95 12.3L11.3 8.25L12.15 9.55C12.25 9.68333 12.375 9.79167 12.525 9.875C12.675 9.95833 12.8333 10 13 10H18.8L18.55 10.3L18.3 10.6L11.575 17.35C11.3583 17.5667 11.1167 17.7292 10.85 17.8375C10.5833 17.9458 10.3 18 10 18Z" fill="#000"/>
|
<path d="M14.125 0C15.7917 0 17.1875 0.616667 18.3125 1.85C19.4375 3.08333 20 4.55 20 6.25C20 6.55 19.9833 6.84583 19.95 7.1375C19.9167 7.42917 19.8583 7.71667 19.775 8H13.525L11.825 5.45C11.7417 5.31667 11.625 5.20833 11.475 5.125C11.325 5.04167 11.1667 5 11 5C10.7833 5 10.5875 5.06667 10.4125 5.2C10.2375 5.33333 10.1167 5.5 10.05 5.7L8.7 9.75L7.825 8.45C7.74167 8.31667 7.625 8.20833 7.475 8.125C7.325 8.04167 7.16667 8 7 8H0.225C0.141667 7.71667 0.0833333 7.42917 0.05 7.1375C0.0166667 6.84583 0 6.55833 0 6.275C0 4.55833 0.558333 3.08333 1.675 1.85C2.79167 0.616667 4.18333 0 5.85 0C6.65 0 7.40417 0.158333 8.1125 0.475C8.82083 0.791667 9.45 1.23333 10 1.8C10.5333 1.23333 11.1542 0.791667 11.8625 0.475C12.5708 0.158333 13.325 0 14.125 0ZM10 18C9.7 18 9.4125 17.9458 9.1375 17.8375C8.8625 17.7292 8.61667 17.5667 8.4 17.35L1.7 10.625C1.6 10.525 1.50833 10.425 1.425 10.325C1.34167 10.225 1.25833 10.1167 1.175 10H6.45L8.15 12.55C8.23333 12.6833 8.35 12.7917 8.5 12.875C8.65 12.9583 8.80833 13 8.975 13C9.19167 13 9.39167 12.9333 9.575 12.8C9.75833 12.6667 9.88333 12.5 9.95 12.3L11.3 8.25L12.15 9.55C12.25 9.68333 12.375 9.79167 12.525 9.875C12.675 9.95833 12.8333 10 13 10H18.8L18.55 10.3L18.3 10.6L11.575 17.35C11.3583 17.5667 11.1167 17.7292 10.85 17.8375C10.5833 17.9458 10.3 18 10 18Z" />
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -7,16 +7,9 @@ import { styled, Tooltip } from '@mui/material';
|
|||||||
const StyledContainer = styled('div')(({ theme }) => ({
|
const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing(2),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const TitleContainer = styled('h4')({
|
|
||||||
margin: 0,
|
|
||||||
width: '100%',
|
|
||||||
});
|
|
||||||
|
|
||||||
type Output = { date: string; count: number; level: number };
|
type Output = { date: string; count: number; level: number };
|
||||||
|
|
||||||
const ensureFullYearData = (data: Output[]): Output[] => {
|
const ensureFullYearData = (data: Output[]): Output[] => {
|
||||||
@ -93,7 +86,6 @@ export const ProjectActivity = () => {
|
|||||||
<>
|
<>
|
||||||
{data.activityCountByDate.length > 0 ? (
|
{data.activityCountByDate.length > 0 ? (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<TitleContainer>Activity in project</TitleContainer>
|
|
||||||
<ActivityCalendar
|
<ActivityCalendar
|
||||||
theme={explicitTheme}
|
theme={explicitTheme}
|
||||||
data={fullData}
|
data={fullData}
|
||||||
|
@ -3,14 +3,7 @@ import { Link } from 'react-router-dom';
|
|||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
|
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
import { HealthGridTile } from './ProjectHealthGrid.styles';
|
||||||
const HealthContainer = styled('div')(({ theme }) => ({
|
|
||||||
backgroundColor: theme.palette.envAccordion.expanded,
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
|
||||||
minWidth: '300px',
|
|
||||||
gridArea: 'health',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const TextContainer = styled('div')(({ theme }) => ({
|
const TextContainer = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -63,7 +56,7 @@ export const ProjectHealth = () => {
|
|||||||
: theme.palette.success.border;
|
: theme.palette.success.border;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HealthContainer>
|
<HealthGridTile>
|
||||||
<ChartRow>
|
<ChartRow>
|
||||||
<SVGWrapper>
|
<SVGWrapper>
|
||||||
<StyledSVG viewBox='0 0 100 100'>
|
<StyledSVG viewBox='0 0 100 100'>
|
||||||
@ -111,6 +104,6 @@ export const ProjectHealth = () => {
|
|||||||
)}
|
)}
|
||||||
</TextContainer>
|
</TextContainer>
|
||||||
</ChartRow>
|
</ChartRow>
|
||||||
</HealthContainer>
|
</HealthGridTile>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
import { styled } from '@mui/material';
|
||||||
|
|
||||||
|
export const HealthGridTile = styled('article')(({ theme }) => ({
|
||||||
|
backgroundColor: theme.palette.envAccordion.expanded,
|
||||||
|
padding: theme.spacing(3),
|
||||||
|
borderRadius: theme.shape.borderRadiusExtraLarge,
|
||||||
|
}));
|
@ -0,0 +1,57 @@
|
|||||||
|
import { ProjectHealth } from './ProjectHealth';
|
||||||
|
import { styled } from '@mui/material';
|
||||||
|
import { StaleFlags } from './StaleFlags';
|
||||||
|
import { ProjectResources } from './ProjectResources';
|
||||||
|
|
||||||
|
const onNarrowGrid = (css: object) => ({
|
||||||
|
'@container (max-width: 650px)': css,
|
||||||
|
'@supports not (container-type: inline-size)': {
|
||||||
|
'@media (max-width: 712px)': css,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const HealthContainer = styled('div')({
|
||||||
|
containerType: 'inline-size',
|
||||||
|
});
|
||||||
|
|
||||||
|
const HealthGrid = styled('div')(({ theme }) => ({
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateAreas: `
|
||||||
|
"health resources"
|
||||||
|
"stale resources"
|
||||||
|
`,
|
||||||
|
gridTemplateColumns: 'repeat(2, minmax(300px, 1fr))',
|
||||||
|
gap: theme.spacing(1, 2),
|
||||||
|
...onNarrowGrid({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Tile = styled('div', {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'gridArea',
|
||||||
|
})<{ gridArea: string }>(({ theme, gridArea }) => ({
|
||||||
|
gridArea,
|
||||||
|
'&>*': {
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const ProjectHealthGrid = () => {
|
||||||
|
return (
|
||||||
|
<HealthContainer>
|
||||||
|
<HealthGrid>
|
||||||
|
<Tile gridArea='health'>
|
||||||
|
<ProjectHealth />
|
||||||
|
</Tile>
|
||||||
|
<Tile gridArea='stale'>
|
||||||
|
<StaleFlags />
|
||||||
|
</Tile>
|
||||||
|
<Tile gridArea='resources'>
|
||||||
|
<ProjectResources />
|
||||||
|
</Tile>
|
||||||
|
</HealthGrid>
|
||||||
|
</HealthContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -6,26 +6,10 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
|||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
|
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
|
||||||
import type { ProjectStatusSchemaLifecycleSummary } from 'openapi';
|
import type { ProjectStatusSchemaLifecycleSummary } from 'openapi';
|
||||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
|
||||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||||
import { lifecycleMessages } from './LifecycleMessages';
|
import { lifecycleMessages } from './LifecycleMessages';
|
||||||
import InfoIcon from '@mui/icons-material/Info';
|
import InfoIcon from '@mui/icons-material/Info';
|
||||||
|
|
||||||
const LifecycleRow = styled('div')(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
flexFlow: 'column',
|
|
||||||
gap: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const HeaderRow = styled('div')(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
flex: 'auto',
|
|
||||||
'& > *': {
|
|
||||||
marginBlock: 0,
|
|
||||||
fontWeight: 'normal',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const LifecycleBoxContent = styled('div')(({ theme }) => ({
|
const LifecycleBoxContent = styled('div')(({ theme }) => ({
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
gap: theme.spacing(4),
|
gap: theme.spacing(4),
|
||||||
@ -87,6 +71,7 @@ const LifecycleList = styled('ul')(({ theme }) => ({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
flex: 'auto',
|
flex: 'auto',
|
||||||
|
margin: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const Counter = styled('span')({
|
const Counter = styled('span')({
|
||||||
@ -146,44 +131,6 @@ const BigNumber: FC<{ value?: number }> = ({ value }) => {
|
|||||||
</BigText>
|
</BigText>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TooltipContent = styled('div')(({ theme }) => ({
|
|
||||||
padding: theme.spacing(0.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const TooltipText = styled('p')(({ theme }) => ({
|
|
||||||
fontSize: theme.typography.body1.fontSize,
|
|
||||||
'& + p': {
|
|
||||||
marginTop: theme.spacing(1),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const LifecycleTooltip: FC = () => {
|
|
||||||
return (
|
|
||||||
<HelpIcon
|
|
||||||
htmlTooltip
|
|
||||||
htmlTooltipMaxWidth='550px'
|
|
||||||
tooltip={
|
|
||||||
<TooltipContent>
|
|
||||||
<TooltipText>
|
|
||||||
Based on usage metrics and interactions with Unleash,
|
|
||||||
feature flags can go through five distinct lifecycle
|
|
||||||
stages. These stages mirror the typical software
|
|
||||||
development process and allow you to identify
|
|
||||||
bottlenecks at any stage of the lifecycle.
|
|
||||||
</TooltipText>
|
|
||||||
|
|
||||||
<TooltipText>
|
|
||||||
<a href='https://docs.getunleash.io/reference/feature-toggles#feature-flag-lifecycle'>
|
|
||||||
Read more in our documentation
|
|
||||||
</a>
|
|
||||||
</TooltipText>
|
|
||||||
</TooltipContent>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProjectLifecycleSummary = () => {
|
export const ProjectLifecycleSummary = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const { data, loading } = useProjectStatus(projectId);
|
const { data, loading } = useProjectStatus(projectId);
|
||||||
@ -200,119 +147,103 @@ export const ProjectLifecycleSummary = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<LifecycleRow>
|
<LifecycleList ref={loadingRef}>
|
||||||
<HeaderRow>
|
<LifecycleBox tooltipText={lifecycleMessages.initial}>
|
||||||
<h4>Flag lifecycle</h4>
|
<p>
|
||||||
<LifecycleTooltip />
|
<Counter>
|
||||||
</HeaderRow>
|
<BigNumber
|
||||||
|
value={data?.lifecycleSummary.initial.currentFlags}
|
||||||
|
/>
|
||||||
|
|
||||||
<LifecycleList ref={loadingRef}>
|
<FeatureLifecycleStageIcon
|
||||||
<LifecycleBox tooltipText={lifecycleMessages.initial}>
|
aria-hidden='true'
|
||||||
<p>
|
stage={{ name: 'initial' }}
|
||||||
<Counter>
|
/>
|
||||||
<BigNumber
|
</Counter>
|
||||||
value={
|
<span>{flagWord('initial')} in initial</span>
|
||||||
data?.lifecycleSummary.initial.currentFlags
|
</p>
|
||||||
}
|
<AverageDaysStat
|
||||||
/>
|
averageDays={data?.lifecycleSummary.initial.averageDays}
|
||||||
|
/>
|
||||||
|
</LifecycleBox>
|
||||||
|
<LifecycleBox tooltipText={lifecycleMessages.preLive}>
|
||||||
|
<p>
|
||||||
|
<Counter>
|
||||||
|
<BigNumber
|
||||||
|
value={data?.lifecycleSummary.preLive.currentFlags}
|
||||||
|
/>
|
||||||
|
|
||||||
<FeatureLifecycleStageIcon
|
<FeatureLifecycleStageIcon
|
||||||
aria-hidden='true'
|
aria-hidden='true'
|
||||||
stage={{ name: 'initial' }}
|
stage={{ name: 'pre-live' }}
|
||||||
/>
|
/>
|
||||||
</Counter>
|
</Counter>
|
||||||
<span>{flagWord('initial')} in initial</span>
|
<span>{flagWord('preLive')} in pre-live</span>
|
||||||
</p>
|
</p>
|
||||||
<AverageDaysStat
|
<AverageDaysStat
|
||||||
averageDays={data?.lifecycleSummary.initial.averageDays}
|
averageDays={data?.lifecycleSummary.preLive.averageDays}
|
||||||
/>
|
/>
|
||||||
</LifecycleBox>
|
</LifecycleBox>
|
||||||
<LifecycleBox tooltipText={lifecycleMessages.preLive}>
|
<LifecycleBox tooltipText={lifecycleMessages.live}>
|
||||||
<p>
|
<p>
|
||||||
<Counter>
|
<Counter>
|
||||||
<BigNumber
|
<BigNumber
|
||||||
value={
|
value={data?.lifecycleSummary.live.currentFlags}
|
||||||
data?.lifecycleSummary.preLive.currentFlags
|
/>
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureLifecycleStageIcon
|
<FeatureLifecycleStageIcon
|
||||||
aria-hidden='true'
|
aria-hidden='true'
|
||||||
stage={{ name: 'pre-live' }}
|
stage={{ name: 'live' }}
|
||||||
/>
|
/>
|
||||||
</Counter>
|
</Counter>
|
||||||
<span>{flagWord('preLive')} in pre-live</span>
|
<span>{flagWord('live')} in live</span>
|
||||||
</p>
|
</p>
|
||||||
<AverageDaysStat
|
<AverageDaysStat
|
||||||
averageDays={data?.lifecycleSummary.preLive.averageDays}
|
averageDays={data?.lifecycleSummary.live.averageDays}
|
||||||
/>
|
/>
|
||||||
</LifecycleBox>
|
</LifecycleBox>
|
||||||
<LifecycleBox tooltipText={lifecycleMessages.live}>
|
<LifecycleBox tooltipText={lifecycleMessages.completed}>
|
||||||
<p>
|
<p>
|
||||||
<Counter>
|
<Counter>
|
||||||
<BigNumber
|
<BigNumber
|
||||||
value={data?.lifecycleSummary.live.currentFlags}
|
value={
|
||||||
/>
|
data?.lifecycleSummary.completed.currentFlags
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<FeatureLifecycleStageIcon
|
<FeatureLifecycleStageIcon
|
||||||
aria-hidden='true'
|
aria-hidden='true'
|
||||||
stage={{ name: 'live' }}
|
stage={{ name: 'completed' }}
|
||||||
/>
|
/>
|
||||||
</Counter>
|
</Counter>
|
||||||
<span>{flagWord('live')} in live</span>
|
<span>{flagWord('completed')} in cleanup</span>
|
||||||
</p>
|
</p>
|
||||||
<AverageDaysStat
|
<AverageDaysStat
|
||||||
averageDays={data?.lifecycleSummary.live.averageDays}
|
averageDays={data?.lifecycleSummary.completed.averageDays}
|
||||||
/>
|
/>
|
||||||
</LifecycleBox>
|
</LifecycleBox>
|
||||||
<LifecycleBox tooltipText={lifecycleMessages.completed}>
|
<LifecycleBox tooltipText={lifecycleMessages.archived}>
|
||||||
<p>
|
<p>
|
||||||
<Counter>
|
<Counter>
|
||||||
<BigNumber
|
<BigNumber
|
||||||
value={
|
value={data?.lifecycleSummary.archived.currentFlags}
|
||||||
data?.lifecycleSummary.completed
|
/>
|
||||||
.currentFlags
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureLifecycleStageIcon
|
<FeatureLifecycleStageIcon
|
||||||
aria-hidden='true'
|
aria-hidden='true'
|
||||||
stage={{ name: 'completed' }}
|
stage={{ name: 'archived' }}
|
||||||
/>
|
/>
|
||||||
</Counter>
|
</Counter>
|
||||||
<span>{flagWord('completed')} in cleanup</span>
|
<span>{flagWord('archived')} in archived</span>
|
||||||
</p>
|
</p>
|
||||||
<AverageDaysStat
|
<Stats>
|
||||||
averageDays={
|
<dt>This month</dt>
|
||||||
data?.lifecycleSummary.completed.averageDays
|
<dd data-loading-project-lifecycle-summary>
|
||||||
}
|
{data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
|
||||||
/>
|
flags archived
|
||||||
</LifecycleBox>
|
</dd>
|
||||||
<LifecycleBox tooltipText={lifecycleMessages.archived}>
|
</Stats>
|
||||||
<p>
|
</LifecycleBox>
|
||||||
<Counter>
|
</LifecycleList>
|
||||||
<BigNumber
|
|
||||||
value={
|
|
||||||
data?.lifecycleSummary.archived.currentFlags
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FeatureLifecycleStageIcon
|
|
||||||
aria-hidden='true'
|
|
||||||
stage={{ name: 'archived' }}
|
|
||||||
/>
|
|
||||||
</Counter>
|
|
||||||
<span>{flagWord('archived')} in archived</span>
|
|
||||||
</p>
|
|
||||||
<Stats>
|
|
||||||
<dt>This month</dt>
|
|
||||||
<dd data-loading-project-lifecycle-summary>
|
|
||||||
{data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
|
|
||||||
flags archived
|
|
||||||
</dd>
|
|
||||||
</Stats>
|
|
||||||
</LifecycleBox>
|
|
||||||
</LifecycleList>
|
|
||||||
</LifecycleRow>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,14 +8,7 @@ import SegmentsIcon from '@mui/icons-material/DonutLarge';
|
|||||||
import ConnectedIcon from '@mui/icons-material/Cable';
|
import ConnectedIcon from '@mui/icons-material/Cable';
|
||||||
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
|
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
|
||||||
import useLoading from 'hooks/useLoading';
|
import useLoading from 'hooks/useLoading';
|
||||||
|
import { HealthGridTile } from './ProjectHealthGrid.styles';
|
||||||
const Wrapper = styled('article')(({ theme }) => ({
|
|
||||||
backgroundColor: theme.palette.envAccordion.expanded,
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
|
||||||
minWidth: '300px',
|
|
||||||
gridArea: 'resources',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const ProjectResourcesInner = styled('div')(({ theme }) => ({
|
const ProjectResourcesInner = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -115,7 +108,7 @@ export const ProjectResources = () => {
|
|||||||
const loadingRef = useLoading(loading, '[data-loading-resources=true]');
|
const loadingRef = useLoading(loading, '[data-loading-resources=true]');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper ref={loadingRef}>
|
<HealthGridTile ref={loadingRef}>
|
||||||
<ProjectResourcesInner>
|
<ProjectResourcesInner>
|
||||||
<Typography variant='h4' sx={{ margin: 0 }}>
|
<Typography variant='h4' sx={{ margin: 0 }}>
|
||||||
Project resources
|
Project resources
|
||||||
@ -155,6 +148,6 @@ export const ProjectResources = () => {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
</ResourceList>
|
</ResourceList>
|
||||||
</ProjectResourcesInner>
|
</ProjectResourcesInner>
|
||||||
</Wrapper>
|
</HealthGridTile>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,20 +1,28 @@
|
|||||||
import { styled } from '@mui/material';
|
import { Button, styled } from '@mui/material';
|
||||||
import { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
import { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal';
|
||||||
import { ProjectResources } from './ProjectResources';
|
import { ReactComponent as ProjectStatusSvg } from 'assets/icons/projectStatus.svg';
|
||||||
import { ProjectActivity } from './ProjectActivity';
|
import { ProjectActivity } from './ProjectActivity';
|
||||||
import { ProjectHealth } from './ProjectHealth';
|
|
||||||
import { ProjectLifecycleSummary } from './ProjectLifecycleSummary';
|
import { ProjectLifecycleSummary } from './ProjectLifecycleSummary';
|
||||||
import { StaleFlags } from './StaleFlags';
|
import type { FC } from 'react';
|
||||||
|
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||||
|
import { ProjectHealthGrid } from './ProjectHealthGrid';
|
||||||
|
|
||||||
const ModalContentContainer = styled('section')(({ theme }) => ({
|
const ModalContentContainer = styled('section')(({ theme }) => ({
|
||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
maxWidth: 1100,
|
maxWidth: 1100,
|
||||||
width: '95vw',
|
width: '95vw',
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
padding: theme.spacing(4),
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexFlow: 'column',
|
flexFlow: 'column',
|
||||||
gap: theme.spacing(4),
|
gap: theme.spacing(4),
|
||||||
|
paddingInline: theme.spacing(4),
|
||||||
|
paddingBlock: theme.spacing(10),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const WidgetContainer = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(9),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -22,47 +30,112 @@ type Props = {
|
|||||||
close: () => void;
|
close: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNarrowGrid = (css: object) => ({
|
const LifecycleHeaderRow = styled('div')(({ theme }) => ({
|
||||||
'@container (max-width: 650px)': css,
|
display: 'flex',
|
||||||
'@supports not (container-type: inline-size)': {
|
alignItems: 'end',
|
||||||
'@media (max-width: 712px)': css,
|
}));
|
||||||
|
|
||||||
|
const HeaderRow = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(1.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledProjectStatusSvg = styled(ProjectStatusSvg)(({ theme }) => ({
|
||||||
|
fill: theme.palette.primary.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ModalHeader = styled('h3')(({ theme }) => ({
|
||||||
|
fontSize: theme.typography.h2.fontSize,
|
||||||
|
margin: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const RowHeader = styled('h4')(({ theme }) => ({
|
||||||
|
margin: 0,
|
||||||
|
fontWeight: 'normal',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Row = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TooltipContent = styled('div')(({ theme }) => ({
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TooltipText = styled('p')(({ theme }) => ({
|
||||||
|
fontSize: theme.typography.body1.fontSize,
|
||||||
|
'& + p': {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
|
|
||||||
const HealthContainer = styled('div')({
|
const LifecycleTooltip: FC = () => {
|
||||||
containerType: 'inline-size',
|
return (
|
||||||
});
|
<HelpIcon
|
||||||
|
htmlTooltip
|
||||||
|
htmlTooltipMaxWidth='550px'
|
||||||
|
tooltip={
|
||||||
|
<TooltipContent>
|
||||||
|
<TooltipText>
|
||||||
|
Based on usage metrics and interactions with Unleash,
|
||||||
|
feature flags can go through five distinct lifecycle
|
||||||
|
stages. These stages mirror the typical software
|
||||||
|
development process and allow you to identify
|
||||||
|
bottlenecks at any stage of the lifecycle.
|
||||||
|
</TooltipText>
|
||||||
|
|
||||||
const HealthGrid = styled('div')(({ theme }) => ({
|
<TooltipText>
|
||||||
display: 'grid',
|
<a href='https://docs.getunleash.io/reference/feature-toggles#feature-flag-lifecycle'>
|
||||||
gridTemplateAreas: `
|
Read more in our documentation
|
||||||
"health resources"
|
</a>
|
||||||
"stale resources"
|
</TooltipText>
|
||||||
`,
|
</TooltipContent>
|
||||||
gridTemplateColumns: '1fr 1fr',
|
}
|
||||||
gap: theme.spacing(1, 2),
|
/>
|
||||||
...onNarrowGrid({
|
);
|
||||||
display: 'flex',
|
};
|
||||||
flexDirection: 'column',
|
|
||||||
gap: theme.spacing(1),
|
const CloseRow = styled('div')(({ theme }) => ({
|
||||||
}),
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
marginBlockStart: 'auto',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ProjectStatusModal = ({ open, close }: Props) => {
|
export const ProjectStatusModal = ({ open, close }: Props) => {
|
||||||
return (
|
return (
|
||||||
<DynamicSidebarModal open={open} onClose={close} label='Project status'>
|
<DynamicSidebarModal open={open} onClose={close} label='Project status'>
|
||||||
<ModalContentContainer>
|
<ModalContentContainer>
|
||||||
<HealthContainer>
|
<HeaderRow>
|
||||||
<HealthGrid>
|
<StyledProjectStatusSvg aria-hidden='true' />
|
||||||
<ProjectHealth />
|
<ModalHeader>Project status</ModalHeader>
|
||||||
<StaleFlags />
|
</HeaderRow>
|
||||||
<ProjectResources />
|
<WidgetContainer>
|
||||||
</HealthGrid>
|
<Row>
|
||||||
</HealthContainer>
|
<RowHeader>Health</RowHeader>
|
||||||
|
<ProjectHealthGrid />
|
||||||
|
</Row>
|
||||||
|
|
||||||
<ProjectActivity />
|
<Row>
|
||||||
|
<RowHeader>Activity in project</RowHeader>
|
||||||
|
<ProjectActivity />
|
||||||
|
</Row>
|
||||||
|
|
||||||
<ProjectLifecycleSummary />
|
<Row>
|
||||||
|
<LifecycleHeaderRow>
|
||||||
|
<RowHeader>Flag lifecycle</RowHeader>
|
||||||
|
<LifecycleTooltip />
|
||||||
|
</LifecycleHeaderRow>
|
||||||
|
<ProjectLifecycleSummary />
|
||||||
|
</Row>
|
||||||
|
</WidgetContainer>
|
||||||
|
<CloseRow>
|
||||||
|
<Button variant='outlined' onClick={close}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</CloseRow>
|
||||||
</ModalContentContainer>
|
</ModalContentContainer>
|
||||||
</DynamicSidebarModal>
|
</DynamicSidebarModal>
|
||||||
);
|
);
|
||||||
|
@ -4,13 +4,9 @@ import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/Pretti
|
|||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { HealthGridTile } from './ProjectHealthGrid.styles';
|
||||||
|
|
||||||
const Wrapper = styled('article')(({ theme }) => ({
|
const Wrapper = styled(HealthGridTile)(({ theme }) => ({
|
||||||
backgroundColor: theme.palette.envAccordion.expanded,
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
|
||||||
minWidth: '300px',
|
|
||||||
gridArea: 'stale',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
|
Loading…
Reference in New Issue
Block a user