mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
feat: new flag header (#9302)
Initial spike to add the new design for the flag page header
This commit is contained in:
parent
8dc6fbf149
commit
aafacc68cf
@ -1,18 +1,24 @@
|
|||||||
import { useState } from 'react';
|
import { type PropsWithChildren, useState, type FC } from 'react';
|
||||||
import {
|
import {
|
||||||
IconButton,
|
IconButton,
|
||||||
styled,
|
styled,
|
||||||
Tab,
|
Tab,
|
||||||
Tabs,
|
Tabs,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Typography,
|
||||||
useMediaQuery,
|
useMediaQuery,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import Archive from '@mui/icons-material/Archive';
|
import Archive from '@mui/icons-material/Archive';
|
||||||
|
import ArchiveOutlined from '@mui/icons-material/ArchiveOutlined';
|
||||||
import FileCopy from '@mui/icons-material/FileCopy';
|
import FileCopy from '@mui/icons-material/FileCopy';
|
||||||
|
import FileCopyOutlined from '@mui/icons-material/FileCopyOutlined';
|
||||||
import Label from '@mui/icons-material/Label';
|
import Label from '@mui/icons-material/Label';
|
||||||
import WatchLater from '@mui/icons-material/WatchLater';
|
import WatchLater from '@mui/icons-material/WatchLater';
|
||||||
|
import WatchLaterOutlined from '@mui/icons-material/WatchLaterOutlined';
|
||||||
import LibraryAdd from '@mui/icons-material/LibraryAdd';
|
import LibraryAdd from '@mui/icons-material/LibraryAdd';
|
||||||
|
import LibraryAddOutlined from '@mui/icons-material/LibraryAddOutlined';
|
||||||
import Check from '@mui/icons-material/Check';
|
import Check from '@mui/icons-material/Check';
|
||||||
|
import Star from '@mui/icons-material/Star';
|
||||||
import {
|
import {
|
||||||
Link,
|
Link,
|
||||||
Route,
|
Route,
|
||||||
@ -49,6 +55,46 @@ import useToast from 'hooks/useToast';
|
|||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import type { IFeatureToggle } from 'interfaces/featureToggle';
|
import type { IFeatureToggle } from 'interfaces/featureToggle';
|
||||||
import { Collaborators } from './Collaborators';
|
import { Collaborators } from './Collaborators';
|
||||||
|
import StarBorder from '@mui/icons-material/StarBorder';
|
||||||
|
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||||
|
|
||||||
|
const NewStyledHeader = styled('div')(({ theme }) => ({
|
||||||
|
backgroundColor: 'none',
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const LowerHeaderRow = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexFlow: 'row nowrap',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: theme.spacing(4),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const HeaderActions = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexFlow: 'row nowrap',
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const IconButtonWithTooltip: FC<
|
||||||
|
PropsWithChildren<{
|
||||||
|
onClick: () => void;
|
||||||
|
label: string;
|
||||||
|
}>
|
||||||
|
> = ({ children, label, onClick }) => {
|
||||||
|
return (
|
||||||
|
<TooltipResolver
|
||||||
|
title={label}
|
||||||
|
arrow
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<IconButton aria-label={label} onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</IconButton>
|
||||||
|
</TooltipResolver>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const StyledHeader = styled('div')(({ theme }) => ({
|
const StyledHeader = styled('div')(({ theme }) => ({
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
@ -139,6 +185,7 @@ const useLegacyVariants = (environments: IFeatureToggle['environments']) => {
|
|||||||
export const FeatureView = () => {
|
export const FeatureView = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const featureId = useRequiredPathParam('featureId');
|
const featureId = useRequiredPathParam('featureId');
|
||||||
|
const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
|
||||||
const { favorite, unfavorite } = useFavoriteFeaturesApi();
|
const { favorite, unfavorite } = useFavoriteFeaturesApi();
|
||||||
const { refetchFeature } = useFeature(projectId, featureId);
|
const { refetchFeature } = useFeature(projectId, featureId);
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
@ -231,6 +278,84 @@ export const FeatureView = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref}>
|
<div ref={ref}>
|
||||||
|
{flagOverviewRedesign ? (
|
||||||
|
<NewStyledHeader>
|
||||||
|
<Typography variant='h1'>{feature.name}</Typography>
|
||||||
|
<LowerHeaderRow>
|
||||||
|
<Tabs
|
||||||
|
value={activeTab.path}
|
||||||
|
indicatorColor='primary'
|
||||||
|
textColor='primary'
|
||||||
|
>
|
||||||
|
{tabData.map((tab) => (
|
||||||
|
<StyledTabButton
|
||||||
|
key={tab.title}
|
||||||
|
label={tab.title}
|
||||||
|
value={tab.path}
|
||||||
|
onClick={() => navigate(tab.path)}
|
||||||
|
data-testid={`TAB-${tab.title}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
<HeaderActions>
|
||||||
|
<IconButtonWithTooltip
|
||||||
|
label='Favorite this feature flag'
|
||||||
|
onClick={onFavorite}
|
||||||
|
data-loading
|
||||||
|
>
|
||||||
|
{feature?.favorite ? <Star /> : <StarBorder />}
|
||||||
|
</IconButtonWithTooltip>
|
||||||
|
|
||||||
|
<IconButtonWithTooltip
|
||||||
|
label='Copy flag name'
|
||||||
|
onClick={handleCopyToClipboard}
|
||||||
|
data-loading
|
||||||
|
>
|
||||||
|
{isFeatureNameCopied ? (
|
||||||
|
<Check />
|
||||||
|
) : (
|
||||||
|
<FileCopyOutlined />
|
||||||
|
)}
|
||||||
|
</IconButtonWithTooltip>
|
||||||
|
<PermissionIconButton
|
||||||
|
permission={CREATE_FEATURE}
|
||||||
|
projectId={projectId}
|
||||||
|
data-loading
|
||||||
|
component={Link}
|
||||||
|
to={`/projects/${projectId}/features/${featureId}/copy`}
|
||||||
|
tooltipProps={{
|
||||||
|
title: 'Clone',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LibraryAddOutlined />
|
||||||
|
</PermissionIconButton>
|
||||||
|
|
||||||
|
<PermissionIconButton
|
||||||
|
permission={DELETE_FEATURE}
|
||||||
|
projectId={projectId}
|
||||||
|
tooltipProps={{
|
||||||
|
title: 'Archive feature flag',
|
||||||
|
}}
|
||||||
|
data-loading
|
||||||
|
onClick={() => setShowDelDialog(true)}
|
||||||
|
>
|
||||||
|
<ArchiveOutlined />
|
||||||
|
</PermissionIconButton>
|
||||||
|
<PermissionIconButton
|
||||||
|
onClick={() => setOpenStaleDialog(true)}
|
||||||
|
permission={UPDATE_FEATURE}
|
||||||
|
projectId={projectId}
|
||||||
|
tooltipProps={{
|
||||||
|
title: 'Toggle stale state',
|
||||||
|
}}
|
||||||
|
data-loading
|
||||||
|
>
|
||||||
|
<WatchLaterOutlined />
|
||||||
|
</PermissionIconButton>
|
||||||
|
</HeaderActions>
|
||||||
|
</LowerHeaderRow>
|
||||||
|
</NewStyledHeader>
|
||||||
|
) : (
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<StyledInnerContainer>
|
<StyledInnerContainer>
|
||||||
<StyledFlagInfoContainer>
|
<StyledFlagInfoContainer>
|
||||||
@ -256,7 +381,9 @@ export const FeatureView = () => {
|
|||||||
style={{ marginLeft: 8 }}
|
style={{ marginLeft: 8 }}
|
||||||
>
|
>
|
||||||
{isFeatureNameCopied ? (
|
{isFeatureNameCopied ? (
|
||||||
<Check style={{ fontSize: 16 }} />
|
<Check
|
||||||
|
style={{ fontSize: 16 }}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FileCopy
|
<FileCopy
|
||||||
style={{ fontSize: 16 }}
|
style={{ fontSize: 16 }}
|
||||||
@ -281,7 +408,10 @@ export const FeatureView = () => {
|
|||||||
<StyledLink
|
<StyledLink
|
||||||
to={`/projects/${feature.project}/features/${feature?.dependencies[0]?.feature}`}
|
to={`/projects/${feature.project}/features/${feature?.dependencies[0]?.feature}`}
|
||||||
>
|
>
|
||||||
{feature?.dependencies[0]?.feature}
|
{
|
||||||
|
feature?.dependencies[0]
|
||||||
|
?.feature
|
||||||
|
}
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
</StyledDependency>
|
</StyledDependency>
|
||||||
}
|
}
|
||||||
@ -369,6 +499,7 @@ export const FeatureView = () => {
|
|||||||
/>
|
/>
|
||||||
</StyledTabRow>
|
</StyledTabRow>
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
|
)}
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path='metrics' element={<FeatureMetrics />} />
|
<Route path='metrics' element={<FeatureMetrics />} />
|
||||||
<Route path='logs' element={<FeatureLog />} />
|
<Route path='logs' element={<FeatureLog />} />
|
||||||
|
@ -52,7 +52,7 @@ process.nextTick(async () => {
|
|||||||
releasePlans: false,
|
releasePlans: false,
|
||||||
releasePlanChangeRequests: false,
|
releasePlanChangeRequests: false,
|
||||||
showUserDeviceCount: true,
|
showUserDeviceCount: true,
|
||||||
flagOverviewRedesign: false,
|
flagOverviewRedesign: true,
|
||||||
granularAdminPermissions: true,
|
granularAdminPermissions: true,
|
||||||
deltaApi: true,
|
deltaApi: true,
|
||||||
uniqueSdkTracking: true,
|
uniqueSdkTracking: true,
|
||||||
|
Loading…
Reference in New Issue
Block a user