mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
[wip] add data to ui (#8710)
Hooks up the project status lifecycle data to the UI. Adds some minor refactoring as part of that effort. ## Other files There's been some small changes to `frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleStageIcon.tsx` and `frontend/src/hooks/useLoading.ts` as well to accommodate their usage here and to remove unused stuff. The inline comments mention the same thing but for posterity (especially after this is merged), the comments are: For `frontend/src/component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleStageIcon.tsx`: > The icon only needs the name to pick. https://github.com/Unleash/unleash/pull/7049 deliberately changed the logic so that the completed stage gets the same icon regardless of its status. As such, to make the icon easier to use other places (such as in the lifecycle widget), we'll only require the name. For `frontend/src/hooks/useLoading.ts`: > There's no reason we should only be able to put refs on divs, as far as I'm aware. TS was complaining that that a `ul` couldn't hold a div reference, so I gave it a type parameter that defaults to the old version.
This commit is contained in:
parent
24a30e5ec3
commit
3bc9fe9a9a
@ -6,18 +6,16 @@ import { ReactComponent as CompletedStageIcon } from 'assets/icons/stage-complet
|
||||
import { ReactComponent as ArchivedStageIcon } from 'assets/icons/stage-archived.svg';
|
||||
import type { LifecycleStage } from './LifecycleStage';
|
||||
|
||||
export const FeatureLifecycleStageIcon: FC<{ stage: LifecycleStage }> = ({
|
||||
stage,
|
||||
}) => {
|
||||
export const FeatureLifecycleStageIcon: FC<{
|
||||
stage: Pick<LifecycleStage, 'name'>;
|
||||
}> = ({ stage }) => {
|
||||
if (stage.name === 'archived') {
|
||||
return <ArchivedStageIcon />;
|
||||
} else if (stage.name === 'pre-live') {
|
||||
return <PreLiveStageIcon />;
|
||||
} else if (stage.name === 'live') {
|
||||
return <LiveStageIcon />;
|
||||
} else if (stage.name === 'completed' && stage.status === 'kept') {
|
||||
return <CompletedStageIcon />;
|
||||
} else if (stage.name === 'completed' && stage.status === 'discarded') {
|
||||
} else if (stage.name === 'completed') {
|
||||
return <CompletedStageIcon />;
|
||||
} else {
|
||||
return <InitialStageIcon />;
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { styled } from '@mui/material';
|
||||
import { FeatureLifecycleStageIcon } from 'component/feature/FeatureView/FeatureOverview/FeatureLifecycle/FeatureLifecycleStageIcon';
|
||||
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import type { FC } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const LifecycleBox = styled('li')(({ theme }) => ({
|
||||
@ -41,10 +44,6 @@ const Stats = styled('dl')(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const NegativeStat = styled('span')(({ theme }) => ({
|
||||
color: theme.palette.warning.contrastText,
|
||||
}));
|
||||
|
||||
const NoData = styled('span')({
|
||||
fontWeight: 'normal',
|
||||
});
|
||||
@ -53,123 +52,128 @@ const LinkNoUnderline = styled(Link)({
|
||||
textDecoration: 'none',
|
||||
});
|
||||
|
||||
const AverageDaysStat: FC<{ averageDays?: number | null }> = ({
|
||||
averageDays,
|
||||
}) => {
|
||||
const Content = () => {
|
||||
if (averageDays === null || averageDays === undefined) {
|
||||
return <NoData>No data</NoData>;
|
||||
}
|
||||
|
||||
return `${averageDays} days`;
|
||||
};
|
||||
return (
|
||||
<Stats>
|
||||
<dt>Avg. time in stage</dt>
|
||||
<dd data-loading-project-lifecycle-summary>
|
||||
<Content />
|
||||
</dd>
|
||||
</Stats>
|
||||
);
|
||||
};
|
||||
|
||||
export const ProjectLifecycleSummary = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { data, loading } = useProjectStatus(projectId);
|
||||
|
||||
const loadingRef = useLoading<HTMLUListElement>(
|
||||
loading,
|
||||
'[data-loading-project-lifecycle-summary=true]',
|
||||
);
|
||||
return (
|
||||
<Wrapper>
|
||||
<Wrapper ref={loadingRef}>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber>15</BigNumber>
|
||||
<BigNumber data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.initial.currentFlags ?? 0}
|
||||
</BigNumber>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{
|
||||
name: 'initial',
|
||||
enteredStageAt: '',
|
||||
}}
|
||||
stage={{ name: 'initial' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>flags in initial</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>Avg. time in stage</dt>
|
||||
<dd>
|
||||
<NegativeStat>21 days</NegativeStat>
|
||||
</dd>
|
||||
</Stats>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.initial.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber>3</BigNumber>
|
||||
<BigNumber data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.preLive.currentFlags ?? 0}
|
||||
</BigNumber>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{
|
||||
name: 'pre-live',
|
||||
enteredStageAt: '',
|
||||
environments: [],
|
||||
}}
|
||||
stage={{ name: 'pre-live' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>flags in pre-live</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>Avg. time in stage</dt>
|
||||
<dd>18 days</dd>
|
||||
</Stats>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.preLive.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber>2</BigNumber>
|
||||
<BigNumber data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.live.currentFlags ?? 0}
|
||||
</BigNumber>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{
|
||||
name: 'live',
|
||||
enteredStageAt: '',
|
||||
environments: [],
|
||||
}}
|
||||
stage={{ name: 'live' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>flags in live</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>Avg. time in stage</dt>
|
||||
<dd>10 days</dd>
|
||||
</Stats>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.live.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber>6</BigNumber>
|
||||
<BigNumber data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.completed.currentFlags ?? 0}
|
||||
</BigNumber>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{
|
||||
name: 'completed',
|
||||
enteredStageAt: '',
|
||||
environments: [],
|
||||
status: 'kept',
|
||||
}}
|
||||
stage={{ name: 'completed' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>
|
||||
<LinkNoUnderline
|
||||
to={`/projects/${projectId}/placeholder`}
|
||||
>
|
||||
flags
|
||||
</LinkNoUnderline>{' '}
|
||||
in cleanup
|
||||
</span>
|
||||
<span>flags in cleanup</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>Avg. time in stage</dt>
|
||||
<dd>
|
||||
<NoData>No data</NoData>
|
||||
</dd>
|
||||
</Stats>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.completed.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber>15</BigNumber>
|
||||
<BigNumber data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.archived.currentFlags ?? 0}
|
||||
</BigNumber>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{
|
||||
name: 'archived',
|
||||
enteredStageAt: '',
|
||||
}}
|
||||
stage={{ name: 'archived' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>flags in archived</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>This month</dt>
|
||||
<dd>3 flags archived</dd>
|
||||
<dd data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
|
||||
flags archived
|
||||
</dd>
|
||||
</Stats>
|
||||
</LifecycleBox>
|
||||
</Wrapper>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { createRef, useLayoutEffect } from 'react';
|
||||
|
||||
type refElement = HTMLDivElement;
|
||||
|
||||
const useLoading = (loading: boolean, selector = '[data-loading=true]') => {
|
||||
const ref = createRef<refElement>();
|
||||
const useLoading = <T extends HTMLElement = HTMLDivElement>(
|
||||
loading: boolean,
|
||||
selector = '[data-loading=true]',
|
||||
) => {
|
||||
const ref = createRef<T>();
|
||||
useLayoutEffect(() => {
|
||||
if (ref.current) {
|
||||
const elements = ref.current.querySelectorAll(selector);
|
||||
|
Loading…
Reference in New Issue
Block a user