mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: parent and child info in feature overview header (#4901)
This commit is contained in:
		
							parent
							
								
									3a6e38a7bd
								
							
						
					
					
						commit
						40dfb927e9
					
				
							
								
								
									
										4
									
								
								frontend/src/assets/icons/link-child.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								frontend/src/assets/icons/link-child.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
 | 
			
		||||
<path d="M1 12a5 5 0 0 1 5-5h4a5 5 0 0 1 4.584 7H14a1.993 1.993 0 0 1-1.375-.547A3 3 0 0 0 10 9H6a3 3 0 1 0 0 6h2.803a6.03 6.03 0 0 0 1.822 1.961A5.033 5.033 0 0 1 10 17H6a5 5 0 0 1-5-5Z" fill="#202021"/>
 | 
			
		||||
<path d="M9 12c0-.711.148-1.387.416-2H10c.532 0 1.016.208 1.375.547A3 3 0 0 0 14 15h4a3 3 0 1 0 0-6h-2.803a6.03 6.03 0 0 0-1.822-1.961C13.58 7.013 13.788 7 14 7h4a5 5 0 0 1 0 10h-4a5 5 0 0 1-5-5Z" fill="#202021"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 499 B  | 
							
								
								
									
										3
									
								
								frontend/src/assets/icons/link-parent.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								frontend/src/assets/icons/link-parent.svg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
 | 
			
		||||
    <path fill-rule="evenodd" clip-rule="evenodd" d="M1 8a5 5 0 0 1 5-5h4a5 5 0 0 1 4.584 7H14a1.993 1.993 0 0 1-1.375-.547A3 3 0 0 0 10 5H6a3 3 0 0 0 0 6h2.803a6.03 6.03 0 0 0 1.822 1.961A5.033 5.033 0 0 1 10 13H8v3a1 1 0 0 0 1 1h1.17A3.001 3.001 0 0 1 13 15h7a3 3 0 1 1 0 6h-7a3.001 3.001 0 0 1-2.83-2H9a3 3 0 0 1-3-3v-3a5 5 0 0 1-5-5Zm8 0c0-.711.148-1.388.416-2H10c.532 0 1.016.208 1.375.547A3 3 0 0 0 14 11h4a3 3 0 1 0 0-6h-2.803a6.03 6.03 0 0 0-1.822-1.961C13.58 3.013 13.788 3 14 3h4a5 5 0 0 1 0 10h-4a5 5 0 0 1-5-5Z" fill="#202021"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 621 B  | 
@ -179,6 +179,8 @@ const feature = ({ name, enabled }: { name: string; enabled: boolean }) =>
 | 
			
		||||
        lastSeenAt: null,
 | 
			
		||||
        type: 'release',
 | 
			
		||||
        archived: false,
 | 
			
		||||
        dependencies: [],
 | 
			
		||||
        children: [],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
const otherRequests = (feature: string) => {
 | 
			
		||||
 | 
			
		||||
@ -170,6 +170,8 @@ const featureEnvironments = (
 | 
			
		||||
        lastSeenAt: null,
 | 
			
		||||
        type: 'release',
 | 
			
		||||
        archived: false,
 | 
			
		||||
        children: [],
 | 
			
		||||
        dependencies: [],
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,24 @@
 | 
			
		||||
import { StyledLink } from './StyledRow';
 | 
			
		||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
 | 
			
		||||
import { FC } from 'react';
 | 
			
		||||
 | 
			
		||||
export const ChildrenTooltip: FC<{
 | 
			
		||||
    childFeatures: string[];
 | 
			
		||||
    project: string;
 | 
			
		||||
}> = ({ childFeatures, project }) => (
 | 
			
		||||
    <TooltipLink
 | 
			
		||||
        tooltip={
 | 
			
		||||
            <>
 | 
			
		||||
                {childFeatures.map(child => (
 | 
			
		||||
                    <StyledLink to={`/projects/${project}/features/${child}`}>
 | 
			
		||||
                        <div>{child}</div>
 | 
			
		||||
                    </StyledLink>
 | 
			
		||||
                ))}
 | 
			
		||||
            </>
 | 
			
		||||
        }
 | 
			
		||||
    >
 | 
			
		||||
        {childFeatures.length === 1
 | 
			
		||||
            ? '1 feature'
 | 
			
		||||
            : `${childFeatures.length} features`}
 | 
			
		||||
    </TooltipLink>
 | 
			
		||||
);
 | 
			
		||||
@ -9,6 +9,7 @@ import { FlexRow, StyledDetail, StyledLabel, StyledLink } from './StyledRow';
 | 
			
		||||
import { DependencyActions } from './DependencyActions';
 | 
			
		||||
import { useDependentFeaturesApi } from 'hooks/api/actions/useDependentFeaturesApi/useDependentFeaturesApi';
 | 
			
		||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
 | 
			
		||||
import { ChildrenTooltip } from './ChildrenTooltip';
 | 
			
		||||
 | 
			
		||||
export const DependencyRow: FC<{ feature: IFeatureToggle }> = ({ feature }) => {
 | 
			
		||||
    const { removeDependencies } = useDependentFeaturesApi(feature.project);
 | 
			
		||||
@ -71,23 +72,10 @@ export const DependencyRow: FC<{ feature: IFeatureToggle }> = ({ feature }) => {
 | 
			
		||||
                    <FlexRow>
 | 
			
		||||
                        <StyledDetail>
 | 
			
		||||
                            <StyledLabel>Children:</StyledLabel>
 | 
			
		||||
                            <TooltipLink
 | 
			
		||||
                                tooltip={
 | 
			
		||||
                                    <>
 | 
			
		||||
                                        {feature.children.map(child => (
 | 
			
		||||
                                            <StyledLink
 | 
			
		||||
                                                to={`/projects/${feature.project}/features/${child}`}
 | 
			
		||||
                                            >
 | 
			
		||||
                                                <div>{child}</div>
 | 
			
		||||
                                            </StyledLink>
 | 
			
		||||
                                        ))}
 | 
			
		||||
                                    </>
 | 
			
		||||
                                }
 | 
			
		||||
                            >
 | 
			
		||||
                                {feature.children.length === 1
 | 
			
		||||
                                    ? '1 feature'
 | 
			
		||||
                                    : `${feature.children.length} features`}
 | 
			
		||||
                            </TooltipLink>
 | 
			
		||||
                            <ChildrenTooltip
 | 
			
		||||
                                childFeatures={feature.children}
 | 
			
		||||
                                project={feature.project}
 | 
			
		||||
                            />
 | 
			
		||||
                        </StyledDetail>
 | 
			
		||||
                    </FlexRow>
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { useState } from 'react';
 | 
			
		||||
import { styled, Tab, Tabs, useMediaQuery } from '@mui/material';
 | 
			
		||||
import { styled, Tab, Tabs, useMediaQuery, Box, Card } from '@mui/material';
 | 
			
		||||
import { Archive, FileCopy, Label, WatchLater } from '@mui/icons-material';
 | 
			
		||||
import {
 | 
			
		||||
    Link,
 | 
			
		||||
@ -31,6 +31,11 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
			
		||||
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
 | 
			
		||||
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
 | 
			
		||||
import { FavoriteIconButton } from 'component/common/FavoriteIconButton/FavoriteIconButton';
 | 
			
		||||
import { ReactComponent as ChildLinkIcon } from 'assets/icons/link-child.svg';
 | 
			
		||||
import { ReactComponent as ParentLinkIcon } from 'assets/icons/link-parent.svg';
 | 
			
		||||
import { TooltipLink } from '../../common/TooltipLink/TooltipLink';
 | 
			
		||||
import { ChildrenTooltip } from './FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelDetails/ChildrenTooltip';
 | 
			
		||||
import { useUiFlag } from '../../../hooks/useUiFlag';
 | 
			
		||||
 | 
			
		||||
const StyledHeader = styled('div')(({ theme }) => ({
 | 
			
		||||
    backgroundColor: theme.palette.background.paper,
 | 
			
		||||
@ -53,6 +58,28 @@ const StyledToggleInfoContainer = styled('div')({
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const StyledDependency = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    marginTop: theme.spacing(1),
 | 
			
		||||
    fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    padding: theme.spacing(0.75, 1.5),
 | 
			
		||||
    backgroundColor: theme.palette.background.elevation2,
 | 
			
		||||
    borderRadius: `${theme.shape.borderRadiusMedium}px`,
 | 
			
		||||
    width: 'max-content',
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyleChildLinkIcon = styled(ChildLinkIcon)(({ theme }) => ({
 | 
			
		||||
    width: theme.fontSizes.smallBody,
 | 
			
		||||
    height: theme.fontSizes.smallBody,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledParentLinkIcon = styled(ParentLinkIcon)(({ theme }) => ({
 | 
			
		||||
    width: theme.fontSizes.smallBody,
 | 
			
		||||
    height: theme.fontSizes.smallBody,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledFeatureViewHeader = styled('h1')(({ theme }) => ({
 | 
			
		||||
    fontSize: theme.fontSizes.mainHeader,
 | 
			
		||||
    fontWeight: 'normal',
 | 
			
		||||
@ -86,12 +113,21 @@ const StyledTabButton = styled(Tab)(({ theme }) => ({
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const StyledLink = styled(Link)(({ theme }) => ({
 | 
			
		||||
    maxWidth: '100%',
 | 
			
		||||
    textDecoration: 'none',
 | 
			
		||||
    '&:hover, &:focus': {
 | 
			
		||||
        textDecoration: 'underline',
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const FeatureView = () => {
 | 
			
		||||
    const projectId = useRequiredPathParam('projectId');
 | 
			
		||||
    const featureId = useRequiredPathParam('featureId');
 | 
			
		||||
    const { refetch: projectRefetch } = useProject(projectId);
 | 
			
		||||
    const { favorite, unfavorite } = useFavoriteFeaturesApi();
 | 
			
		||||
    const { refetchFeature } = useFeature(projectId, featureId);
 | 
			
		||||
    const dependentFeatures = useUiFlag('dependentFeatures');
 | 
			
		||||
 | 
			
		||||
    const [openTagDialog, setOpenTagDialog] = useState(false);
 | 
			
		||||
    const [showDelDialog, setShowDelDialog] = useState(false);
 | 
			
		||||
@ -157,13 +193,56 @@ export const FeatureView = () => {
 | 
			
		||||
                            onClick={onFavorite}
 | 
			
		||||
                            isFavorite={feature?.favorite}
 | 
			
		||||
                        />
 | 
			
		||||
                        <StyledFeatureViewHeader data-loading>
 | 
			
		||||
                            {feature.name}{' '}
 | 
			
		||||
                        </StyledFeatureViewHeader>
 | 
			
		||||
                        <ConditionallyRender
 | 
			
		||||
                            condition={!smallScreen}
 | 
			
		||||
                            show={<FeatureStatusChip stale={feature?.stale} />}
 | 
			
		||||
                        />
 | 
			
		||||
                        <div>
 | 
			
		||||
                            <StyledToggleInfoContainer>
 | 
			
		||||
                                <StyledFeatureViewHeader data-loading>
 | 
			
		||||
                                    {feature.name}{' '}
 | 
			
		||||
                                </StyledFeatureViewHeader>
 | 
			
		||||
                                <ConditionallyRender
 | 
			
		||||
                                    condition={!smallScreen}
 | 
			
		||||
                                    show={
 | 
			
		||||
                                        <FeatureStatusChip
 | 
			
		||||
                                            stale={feature?.stale}
 | 
			
		||||
                                        />
 | 
			
		||||
                                    }
 | 
			
		||||
                                />
 | 
			
		||||
                            </StyledToggleInfoContainer>
 | 
			
		||||
                            <ConditionallyRender
 | 
			
		||||
                                condition={
 | 
			
		||||
                                    dependentFeatures &&
 | 
			
		||||
                                    feature.dependencies.length > 0
 | 
			
		||||
                                }
 | 
			
		||||
                                show={
 | 
			
		||||
                                    <StyledDependency>
 | 
			
		||||
                                        <StyleChildLinkIcon />{' '}
 | 
			
		||||
                                        <b>Child feature</b>
 | 
			
		||||
                                        <span>{' < '}</span>
 | 
			
		||||
                                        <StyledLink
 | 
			
		||||
                                            to={`/projects/${feature.project}/features/${feature?.dependencies[0]?.feature}`}
 | 
			
		||||
                                        >
 | 
			
		||||
                                            {feature?.dependencies[0]?.feature}
 | 
			
		||||
                                        </StyledLink>
 | 
			
		||||
                                    </StyledDependency>
 | 
			
		||||
                                }
 | 
			
		||||
                            />
 | 
			
		||||
                            <ConditionallyRender
 | 
			
		||||
                                condition={
 | 
			
		||||
                                    dependentFeatures &&
 | 
			
		||||
                                    feature.children.length > 0
 | 
			
		||||
                                }
 | 
			
		||||
                                show={
 | 
			
		||||
                                    <StyledDependency>
 | 
			
		||||
                                        <StyledParentLinkIcon />{' '}
 | 
			
		||||
                                        <b>Parent feature</b>
 | 
			
		||||
                                        <span>{' > '}</span>
 | 
			
		||||
                                        <ChildrenTooltip
 | 
			
		||||
                                            childFeatures={feature.children}
 | 
			
		||||
                                            project={feature.project}
 | 
			
		||||
                                        />
 | 
			
		||||
                                    </StyledDependency>
 | 
			
		||||
                                }
 | 
			
		||||
                            />
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </StyledToggleInfoContainer>
 | 
			
		||||
 | 
			
		||||
                    <StyledToolbarContainer>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user