1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-12 01:17:04 +02:00

feat: add creation buttons to application features and strategies (#6387)

![image](https://github.com/Unleash/unleash/assets/964450/2c8215af-3ab3-43e6-bdeb-1ae36aad3aa3)
This commit is contained in:
Jaanus Sellin 2024-02-29 12:25:42 +02:00 committed by GitHub
parent 68095916e8
commit a958797a8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 106 additions and 11 deletions

View File

@ -1,12 +1,20 @@
import { Box, styled } from '@mui/material'; import { Box, styled } from '@mui/material';
import { ConditionallyRender } from '../../common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { WarningAmberRounded } from '@mui/icons-material'; import { WarningAmberRounded } from '@mui/icons-material';
import { ApplicationOverviewIssuesSchema } from 'openapi'; import { ApplicationOverviewIssuesSchema } from 'openapi';
import { Link } from 'react-router-dom';
import {
CREATE_FEATURE,
CREATE_STRATEGY,
} from 'component/providers/AccessProvider/permissions';
import { useContext } from 'react';
import AccessContext from 'contexts/AccessContext';
const WarningContainer = styled(Box)(({ theme }) => ({ const WarningContainer = styled(Box)(({ theme }) => ({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignSelf: 'stretch', alignSelf: 'stretch',
fontSize: theme.fontSizes.smallBody,
})); }));
const WarningHeader = styled(Box)(({ theme }) => ({ const WarningHeader = styled(Box)(({ theme }) => ({
@ -21,11 +29,7 @@ const WarningHeader = styled(Box)(({ theme }) => ({
color: theme.palette.warning.main, color: theme.palette.warning.main,
})); }));
const SmallText = styled(Box)(({ theme }) => ({ const WarningHeaderText = styled(Box)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
}));
const WarningHeaderText = styled(SmallText)(({ theme }) => ({
color: theme.palette.warning.dark, color: theme.palette.warning.dark,
fontWeight: theme.fontWeight.bold, fontWeight: theme.fontWeight.bold,
})); }));
@ -36,7 +40,6 @@ const StyledList = styled('ul')(({ theme }) => ({
const StyledListElement = styled('li')(({ theme }) => ({ const StyledListElement = styled('li')(({ theme }) => ({
fontWeight: theme.fontWeight.bold, fontWeight: theme.fontWeight.bold,
fontSize: theme.fontSizes.smallBody,
})); }));
const IssueContainer = styled(Box)(({ theme }) => ({ const IssueContainer = styled(Box)(({ theme }) => ({
@ -62,6 +65,20 @@ const IssueTextContainer = styled(Box)(({ theme }) => ({
border: `1px solid ${theme.palette.divider}`, border: `1px solid ${theme.palette.divider}`,
})); }));
const IssueRowContainer = styled(Box)(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
gap: theme.spacing(1.5),
alignItems: 'center',
}));
const StyledLink = styled(Link)(() => ({
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
textDecoration: 'underline',
}));
export interface IApplicationIssuesProps { export interface IApplicationIssuesProps {
issues: ApplicationOverviewIssuesSchema[]; issues: ApplicationOverviewIssuesSchema[];
} }
@ -85,6 +102,7 @@ const resolveIssueText = (issue: ApplicationOverviewIssuesSchema) => {
}; };
export const ApplicationIssues = ({ issues }: IApplicationIssuesProps) => { export const ApplicationIssues = ({ issues }: IApplicationIssuesProps) => {
const { hasAccess } = useContext(AccessContext);
return ( return (
<ConditionallyRender <ConditionallyRender
condition={issues.length > 0} condition={issues.length > 0}
@ -100,12 +118,44 @@ export const ApplicationIssues = ({ issues }: IApplicationIssuesProps) => {
<IssueContainer> <IssueContainer>
{issues.map((issue) => ( {issues.map((issue) => (
<IssueTextContainer key={issue.type}> <IssueTextContainer key={issue.type}>
<SmallText>{resolveIssueText(issue)}</SmallText> {resolveIssueText(issue)}
<StyledList> <StyledList>
{issue.items.map((item) => ( {issue.items.map((item) => (
<StyledListElement key={item}> <IssueRowContainer key={item}>
{item} <StyledListElement>
</StyledListElement> {item}
</StyledListElement>
<ConditionallyRender
condition={
issue.type ===
'missingFeatures' &&
hasAccess(CREATE_FEATURE)
}
show={
<StyledLink
key={item}
to={`/projects/default/create-toggle?name=${item}`}
>
Create feature flag
</StyledLink>
}
/>
<ConditionallyRender
condition={
issue.type ===
'missingStrategies' &&
hasAccess(CREATE_STRATEGY)
}
show={
<StyledLink
key={item}
to={`/strategies/create`}
>
Create strategy type
</StyledLink>
}
/>
</IssueRowContainer>
))} ))}
</StyledList> </StyledList>
</IssueTextContainer> </IssueTextContainer>

View File

@ -72,3 +72,48 @@ test('Display application overview without environments', async () => {
await screen.findByText('my-app'); await screen.findByText('my-app');
await screen.findByText('No data available.'); await screen.findByText('No data available.');
}); });
test('Display application with issues', async () => {
setupApi({
environments: [
{
name: 'development',
instanceCount: 999,
lastSeen: new Date().toISOString(),
sdks: ['unleash-client-node:5.5.0-beta.0'],
},
],
issues: [
{
type: 'missingFeatures',
items: ['feature1'],
},
{
type: 'missingStrategies',
items: ['strategy1'],
},
],
featureCount: 1,
projects: ['default'],
});
render(
<Routes>
<Route
path={'/applications/:name'}
element={<ApplicationOverview />}
/>
</Routes>,
{
route: '/applications/my-app',
},
);
await screen.findByText(
'We detected 1 feature flag defined in the SDK that does not exist in Unleash',
);
await screen.findByText(
'We detected 1 strategy type defined in the SDK that does not exist in Unleash',
);
await screen.findByText('feature1');
await screen.findByText('strategy1');
});