mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-24 17:51:14 +02:00
Merge branch 'main' into 1-4036
This commit is contained in:
commit
9474e6ebbe
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
[](https://github.com/Unleash/unleash/actions/workflows/build.yaml) [](https://github.com/Unleash/unleash/actions/workflows/build_coverage.yaml) [](https://hub.docker.com/r/unleashorg/unleash-server) [](https://github.com/Unleash/unleash/blob/main/LICENSE) [](https://slack.unleash.run)
|
[](https://github.com/Unleash/unleash/actions/workflows/build.yaml) [](https://github.com/Unleash/unleash/actions/workflows/build_coverage.yaml) [](https://hub.docker.com/r/unleashorg/unleash-server) [](https://github.com/Unleash/unleash/blob/main/LICENSE) [](https://slack.unleash.run)
|
||||||
|
|
||||||
[Experience Unleash Live Demo →](https://www.getunleash.io/interactive-demo)
|
[Try Unleash Cloud for free →](https://www.getunleash.io/plans/enterprise-payg?utm_source=readme&utm_medium=oss&utm_content=top-cta)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -93,9 +93,9 @@ The above sections show you how to get up and running quickly and easily. When y
|
|||||||
|
|
||||||
## Online demo
|
## Online demo
|
||||||
|
|
||||||
Try out [the Unleash online demo](https://www.getunleash.io/interactive-demo).
|
Try out [the Unleash online demo](https://www.getunleash.io/interactive-demo?utm_source=readme&utm_medium=oss&utm_content=demo-section-cta).
|
||||||
|
|
||||||
[](https://www.getunleash.io/interactive-demo)
|
[](https://www.getunleash.io/interactive-demo?utm_source=readme&utm_medium=oss&utm_content=demo-section-cta)
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { calculateRatio } from './calculate-ratio.ts';
|
import { calculateRatio } from './calculate-ratio.ts';
|
||||||
|
|
||||||
test('A ratio of anything to 0 is 100', () => {
|
test('A ratio of anything to 0 is N/A', () => {
|
||||||
expect(calculateRatio(0, 0)).toBe(100);
|
expect(calculateRatio(0, 0)).toBe('N/A');
|
||||||
expect(calculateRatio(5, 0)).toBe(100);
|
expect(calculateRatio(5, 0)).toBe('N/A');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Normal ratios work as expected', () => {
|
test('Normal ratios work as expected', () => {
|
||||||
expect(calculateRatio(0, 1)).toBe(0);
|
expect(calculateRatio(0, 1)).toBe('0%');
|
||||||
expect(calculateRatio(1, 1)).toBe(100);
|
expect(calculateRatio(1, 1)).toBe('100%');
|
||||||
expect(calculateRatio(1, 2)).toBe(50);
|
expect(calculateRatio(1, 2)).toBe('50%');
|
||||||
expect(calculateRatio(5, 2)).toBe(250);
|
expect(calculateRatio(5, 2)).toBe('250%');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Numbers are rounded to the nearest integer', () => {
|
test('Numbers are rounded to the nearest integer', () => {
|
||||||
expect(calculateRatio(5, 9)).toBe(56);
|
expect(calculateRatio(5, 9)).toBe('56%');
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
export const calculateRatio = (
|
export const calculateRatio = (
|
||||||
antecedent: number,
|
antecedent: number,
|
||||||
consequent: number,
|
consequent: number,
|
||||||
): number => {
|
): string => {
|
||||||
const rawRatio = Math.round((antecedent / consequent) * 100);
|
if (consequent === 0) {
|
||||||
|
return 'N/A';
|
||||||
if (Number.isNaN(rawRatio) || rawRatio === Number.POSITIVE_INFINITY) {
|
|
||||||
return 100;
|
|
||||||
}
|
}
|
||||||
|
const ratio = Math.round((antecedent / consequent) * 100);
|
||||||
|
|
||||||
return rawRatio;
|
return `${ratio}%`;
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,7 @@ export const CreationArchiveRatioTooltip: FC<
|
|||||||
<ChartTooltipContainer tooltip={tooltip}>
|
<ChartTooltipContainer tooltip={tooltip}>
|
||||||
<StyledTooltipItemContainer elevation={3}>
|
<StyledTooltipItemContainer elevation={3}>
|
||||||
<Typography variant='body2' component='span' fontWeight='bold'>
|
<Typography variant='body2' component='span' fontWeight='bold'>
|
||||||
Ratio {ratio}%
|
Ratio {ratio}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<DataList>
|
<DataList>
|
||||||
|
@ -90,7 +90,7 @@ export const CreationArchiveStats: FC<CreationArchiveStatsProps> = ({
|
|||||||
<StyledRatioContainer>
|
<StyledRatioContainer>
|
||||||
<StyledPercentageRow>
|
<StyledPercentageRow>
|
||||||
<StyledRatioTypography>
|
<StyledRatioTypography>
|
||||||
{isLoading ? '...' : `${currentRatio}%`}
|
{isLoading ? '...' : currentRatio}
|
||||||
</StyledRatioTypography>
|
</StyledRatioTypography>
|
||||||
<HelpIcon tooltip='Ratio of archived flags to created flags'>
|
<HelpIcon tooltip='Ratio of archived flags to created flags'>
|
||||||
<StyledInfoIcon />
|
<StyledInfoIcon />
|
||||||
|
@ -113,7 +113,7 @@ export const PerformanceInsights: FC = () => {
|
|||||||
{isLifecycleGraphsEnabled && isEnterprise() ? (
|
{isLifecycleGraphsEnabled && isEnterprise() ? (
|
||||||
<StyledWidget>
|
<StyledWidget>
|
||||||
<StyledWidgetStats width={275}>
|
<StyledWidgetStats width={275}>
|
||||||
<WidgetTitle title='Flags created vs archived' />
|
<WidgetTitle title='Flags archived vs flags created' />
|
||||||
<CreationArchiveStats
|
<CreationArchiveStats
|
||||||
groupedCreationArchiveData={
|
groupedCreationArchiveData={
|
||||||
groupedCreationArchiveData
|
groupedCreationArchiveData
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
import Add from '@mui/icons-material/Add';
|
||||||
|
import { Box, styled } from '@mui/material';
|
||||||
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
|
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||||
|
import type { UnknownFlag } from './hooks/useUnknownFlags.js';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import useProjects from 'hooks/api/getters/useProjects/useProjects.js';
|
||||||
|
import { DEFAULT_PROJECT_ID } from 'hooks/api/getters/useDefaultProject/useDefaultProjectId.js';
|
||||||
|
import AccessContext from 'contexts/AccessContext.js';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
const StyledBox = styled(Box)(() => ({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IUnknownFlagsActionsCellProps {
|
||||||
|
unknownFlag: UnknownFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UnknownFlagsActionsCell = ({
|
||||||
|
unknownFlag,
|
||||||
|
}: IUnknownFlagsActionsCellProps) => {
|
||||||
|
const { projects } = useProjects();
|
||||||
|
const { hasAccess } = useContext(AccessContext);
|
||||||
|
|
||||||
|
let project =
|
||||||
|
projects.find(({ id }) => id === DEFAULT_PROJECT_ID) || projects[0];
|
||||||
|
if (!hasAccess(CREATE_FEATURE, project?.id)) {
|
||||||
|
for (const proj of projects) {
|
||||||
|
if (hasAccess(CREATE_FEATURE, proj.id)) {
|
||||||
|
project = proj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledBox>
|
||||||
|
<PermissionIconButton
|
||||||
|
component={Link}
|
||||||
|
data-loading
|
||||||
|
to={`/projects/${project?.id}?create=true&name=${unknownFlag.name}`}
|
||||||
|
permission={CREATE_FEATURE}
|
||||||
|
projectId={project?.id}
|
||||||
|
tooltipProps={{
|
||||||
|
title: 'Create feature flag',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Add />
|
||||||
|
</PermissionIconButton>
|
||||||
|
</StyledBox>
|
||||||
|
);
|
||||||
|
};
|
@ -18,6 +18,7 @@ import { useUiFlag } from 'hooks/useUiFlag.js';
|
|||||||
import NotFound from 'component/common/NotFound/NotFound.js';
|
import NotFound from 'component/common/NotFound/NotFound.js';
|
||||||
import { UnknownFlagsSeenInUnleashCell } from './UnknownFlagsSeenInUnleashCell.js';
|
import { UnknownFlagsSeenInUnleashCell } from './UnknownFlagsSeenInUnleashCell.js';
|
||||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon.js';
|
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon.js';
|
||||||
|
import { UnknownFlagsActionsCell } from './UnknownFlagsActionsCell.js';
|
||||||
|
|
||||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||||
marginBottom: theme.spacing(3),
|
marginBottom: theme.spacing(3),
|
||||||
@ -104,6 +105,17 @@ export const UnknownFlagsTable = () => {
|
|||||||
),
|
),
|
||||||
width: 150,
|
width: 150,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Header: 'Actions',
|
||||||
|
align: 'center',
|
||||||
|
Cell: ({
|
||||||
|
row: { original: unknownFlag },
|
||||||
|
}: {
|
||||||
|
row: { original: UnknownFlag };
|
||||||
|
}) => <UnknownFlagsActionsCell unknownFlag={unknownFlag} />,
|
||||||
|
width: 100,
|
||||||
|
disableSortBy: true,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
@ -221,10 +221,9 @@ const theme = {
|
|||||||
*/
|
*/
|
||||||
charts: {
|
charts: {
|
||||||
A1: '#6C65E5',
|
A1: '#6C65E5',
|
||||||
A2: '#8C87EB',
|
A2: '#9D98EE',
|
||||||
A3: '#ADA9F1',
|
A3: '#CECCF6',
|
||||||
A4: '#CECCF6',
|
A4: '#F1F0FC',
|
||||||
A5: '#F1F0FC',
|
|
||||||
B1: '#1791AE',
|
B1: '#1791AE',
|
||||||
C1: '#DF416E',
|
C1: '#DF416E',
|
||||||
D1: '#D76500',
|
D1: '#D76500',
|
||||||
|
@ -277,10 +277,9 @@ const theme = {
|
|||||||
*/
|
*/
|
||||||
charts: {
|
charts: {
|
||||||
A1: '#6C65E5',
|
A1: '#6C65E5',
|
||||||
A2: '#8C87EB',
|
A2: '#9D98EE',
|
||||||
A3: '#ADA9F1',
|
A3: '#CECCF6',
|
||||||
A4: '#CECCF6',
|
A4: '#F1F0FC',
|
||||||
A5: '#F1F0FC',
|
|
||||||
B1: '#1791AE',
|
B1: '#1791AE',
|
||||||
C1: '#DF416E',
|
C1: '#DF416E',
|
||||||
D1: '#D76500',
|
D1: '#D76500',
|
||||||
|
@ -142,7 +142,6 @@ declare module '@mui/material/styles' {
|
|||||||
A2: string;
|
A2: string;
|
||||||
A3: string;
|
A3: string;
|
||||||
A4: string;
|
A4: string;
|
||||||
A5: string;
|
|
||||||
B1: string;
|
B1: string;
|
||||||
C1: string;
|
C1: string;
|
||||||
D1: string;
|
D1: string;
|
||||||
|
Loading…
Reference in New Issue
Block a user