mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-18 01:18:23 +02:00
feat: add project flags component (#6070)
This PR adds project flags line chart component
This commit is contained in:
parent
c6a2303026
commit
aae1d0576f
@ -13,6 +13,7 @@ import { useExecutiveDashboard } from 'hooks/api/getters/useExecutiveSummary/use
|
|||||||
import { UserStats } from './UserStats/UserStats';
|
import { UserStats } from './UserStats/UserStats';
|
||||||
import { FlagStats } from './FlagStats/FlagStats';
|
import { FlagStats } from './FlagStats/FlagStats';
|
||||||
import { Widget } from './Widget/Widget';
|
import { Widget } from './Widget/Widget';
|
||||||
|
import { FlagsProjectChart } from './FlagsProjectChart/FlagsProjectChart';
|
||||||
|
|
||||||
const StyledGrid = styled(Box)(({ theme }) => ({
|
const StyledGrid = styled(Box)(({ theme }) => ({
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
@ -107,6 +108,10 @@ export const ExecutiveDashboard: VFC = () => {
|
|||||||
/>
|
/>
|
||||||
</Widget>
|
</Widget>
|
||||||
</StyledGrid>
|
</StyledGrid>
|
||||||
|
|
||||||
|
<FlagsProjectChart
|
||||||
|
projectFlagTrends={executiveDashboardData.projectFlagTrends}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -64,7 +64,11 @@ const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
|
|||||||
const date =
|
const date =
|
||||||
item?.chart?.data?.labels?.[item.dataIndex];
|
item?.chart?.data?.labels?.[item.dataIndex];
|
||||||
return date
|
return date
|
||||||
? formatDateYMD(date, locationSettings.locale)
|
? formatDateYMD(
|
||||||
|
date,
|
||||||
|
locationSettings.locale,
|
||||||
|
'UTC',
|
||||||
|
)
|
||||||
: '';
|
: '';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import { lazy } from 'react';
|
||||||
|
|
||||||
|
export const FlagsProjectChart = lazy(
|
||||||
|
() => import('./FlagsProjectChartComponent'),
|
||||||
|
);
|
@ -0,0 +1,162 @@
|
|||||||
|
import { useMemo, type VFC } from 'react';
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
TimeScale,
|
||||||
|
} from 'chart.js';
|
||||||
|
import { Line } from 'react-chartjs-2';
|
||||||
|
import 'chartjs-adapter-date-fns';
|
||||||
|
import { Paper, Theme, Typography, useTheme } from '@mui/material';
|
||||||
|
import {
|
||||||
|
useLocationSettings,
|
||||||
|
type ILocationSettings,
|
||||||
|
} from 'hooks/useLocationSettings';
|
||||||
|
import { formatDateYMD } from 'utils/formatDate';
|
||||||
|
import {
|
||||||
|
ExecutiveSummarySchema,
|
||||||
|
ExecutiveSummarySchemaProjectFlagTrendsItem,
|
||||||
|
} from 'openapi';
|
||||||
|
|
||||||
|
const getRandomColor = () => {
|
||||||
|
const letters = '0123456789ABCDEF';
|
||||||
|
let color = '#';
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
color += letters[Math.floor(Math.random() * 16)];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createData = (
|
||||||
|
theme: Theme,
|
||||||
|
flagTrends: ExecutiveSummarySchema['projectFlagTrends'] = [],
|
||||||
|
) => {
|
||||||
|
const groupedFlagTrends = flagTrends.reduce<
|
||||||
|
Record<string, ExecutiveSummarySchemaProjectFlagTrendsItem[]>
|
||||||
|
>((groups, item) => {
|
||||||
|
if (!groups[item.project]) {
|
||||||
|
groups[item.project] = [];
|
||||||
|
}
|
||||||
|
groups[item.project].push(item);
|
||||||
|
return groups;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const datasets = Object.entries(groupedFlagTrends).map(
|
||||||
|
([project, trends]) => {
|
||||||
|
const color = getRandomColor();
|
||||||
|
return {
|
||||||
|
label: project,
|
||||||
|
data: trends.map((item) => item.total),
|
||||||
|
borderColor: color,
|
||||||
|
backgroundColor: color,
|
||||||
|
fill: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
labels: flagTrends.map((item) => item.date),
|
||||||
|
datasets,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createOptions = (theme: Theme, locationSettings: ILocationSettings) =>
|
||||||
|
({
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
title: (tooltipItems: any) => {
|
||||||
|
const item = tooltipItems?.[0];
|
||||||
|
const date =
|
||||||
|
item?.chart?.data?.labels?.[item.dataIndex];
|
||||||
|
return date
|
||||||
|
? formatDateYMD(
|
||||||
|
date,
|
||||||
|
locationSettings.locale,
|
||||||
|
'UTC',
|
||||||
|
)
|
||||||
|
: '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
locale: locationSettings.locale,
|
||||||
|
interaction: {
|
||||||
|
intersect: false,
|
||||||
|
axis: 'x',
|
||||||
|
},
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
type: 'linear',
|
||||||
|
grid: {
|
||||||
|
color: theme.palette.divider,
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
},
|
||||||
|
ticks: { color: theme.palette.text.secondary },
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'month',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: theme.palette.divider,
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}) as const;
|
||||||
|
|
||||||
|
interface IFlagsChartComponentProps {
|
||||||
|
projectFlagTrends: ExecutiveSummarySchema['projectFlagTrends'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const FlagsProjectChart: VFC<IFlagsChartComponentProps> = ({
|
||||||
|
projectFlagTrends,
|
||||||
|
}) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
const { locationSettings } = useLocationSettings();
|
||||||
|
const data = useMemo(
|
||||||
|
() => createData(theme, projectFlagTrends),
|
||||||
|
[theme, projectFlagTrends],
|
||||||
|
);
|
||||||
|
const options = createOptions(theme, locationSettings);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper sx={(theme) => ({ padding: theme.spacing(4) })}>
|
||||||
|
<Typography
|
||||||
|
variant='h3'
|
||||||
|
sx={(theme) => ({ marginBottom: theme.spacing(3) })}
|
||||||
|
>
|
||||||
|
Number of flags per project
|
||||||
|
</Typography>
|
||||||
|
<Line options={options} data={data} />
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
TimeScale,
|
||||||
|
Title,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FlagsProjectChart;
|
@ -32,6 +32,7 @@ export const useExecutiveDashboard = (
|
|||||||
flags: { total: 0 },
|
flags: { total: 0 },
|
||||||
userTrends: [],
|
userTrends: [],
|
||||||
flagTrends: [],
|
flagTrends: [],
|
||||||
|
projectFlagTrends: [],
|
||||||
},
|
},
|
||||||
refetchExecutiveDashboard,
|
refetchExecutiveDashboard,
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import type { ExecutiveSummarySchemaFlags } from './executiveSummarySchemaFlags';
|
import type { ExecutiveSummarySchemaFlags } from './executiveSummarySchemaFlags';
|
||||||
import type { ExecutiveSummarySchemaFlagTrendsItem } from './executiveSummarySchemaFlagTrendsItem';
|
import type { ExecutiveSummarySchemaFlagTrendsItem } from './executiveSummarySchemaFlagTrendsItem';
|
||||||
|
import type { ExecutiveSummarySchemaProjectFlagTrendsItem } from './executiveSummarySchemaProjectFlagTrendsItem';
|
||||||
import type { ExecutiveSummarySchemaUsers } from './executiveSummarySchemaUsers';
|
import type { ExecutiveSummarySchemaUsers } from './executiveSummarySchemaUsers';
|
||||||
import type { ExecutiveSummarySchemaUserTrendsItem } from './executiveSummarySchemaUserTrendsItem';
|
import type { ExecutiveSummarySchemaUserTrendsItem } from './executiveSummarySchemaUserTrendsItem';
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ export interface ExecutiveSummarySchema {
|
|||||||
flags: ExecutiveSummarySchemaFlags;
|
flags: ExecutiveSummarySchemaFlags;
|
||||||
/** How number of flags changed over time */
|
/** How number of flags changed over time */
|
||||||
flagTrends: ExecutiveSummarySchemaFlagTrendsItem[];
|
flagTrends: ExecutiveSummarySchemaFlagTrendsItem[];
|
||||||
|
/** How number of flags per project changed over time */
|
||||||
|
projectFlagTrends: ExecutiveSummarySchemaProjectFlagTrendsItem[];
|
||||||
/** High level user count statistics */
|
/** High level user count statistics */
|
||||||
users: ExecutiveSummarySchemaUsers;
|
users: ExecutiveSummarySchemaUsers;
|
||||||
/** How number of users changed over time */
|
/** How number of users changed over time */
|
||||||
|
@ -10,7 +10,7 @@ export type ExecutiveSummarySchemaFlagTrendsItem = {
|
|||||||
/** A UTC date when the stats were captured. Time is the very end of a given day. */
|
/** A UTC date when the stats were captured. Time is the very end of a given day. */
|
||||||
date: string;
|
date: string;
|
||||||
/** The number of time calculated potentially stale flags on a particular day */
|
/** The number of time calculated potentially stale flags on a particular day */
|
||||||
potentiallyStale?: number;
|
potentiallyStale: number;
|
||||||
/** The number of user marked stale flags on a particular day */
|
/** The number of user marked stale flags on a particular day */
|
||||||
stale: number;
|
stale: number;
|
||||||
/** The number of all flags on a particular day */
|
/** The number of all flags on a particular day */
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Generated by Orval
|
||||||
|
* Do not edit manually.
|
||||||
|
* See `gen:api` script in package.json
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type ExecutiveSummarySchemaProjectFlagTrendsItem = {
|
||||||
|
/** The number of active flags on a particular day */
|
||||||
|
active: number;
|
||||||
|
/** A UTC date when the stats were captured. Time is the very end of a given day. */
|
||||||
|
date: string;
|
||||||
|
/** An indicator of the [project's health](https://docs.getunleash.io/reference/technical-debt#health-rating) on a scale from 0 to 100 */
|
||||||
|
health?: number;
|
||||||
|
/** The number of time calculated potentially stale flags on a particular day */
|
||||||
|
potentiallyStale: number;
|
||||||
|
/** Project id of the project the flag trends belong to */
|
||||||
|
project: string;
|
||||||
|
/** The number of user marked stale flags on a particular day */
|
||||||
|
stale: number;
|
||||||
|
/** The average time from when a feature was created to when it was enabled in the "production" environment during the current window */
|
||||||
|
timeToProduction?: number;
|
||||||
|
/** The number of all flags on a particular day */
|
||||||
|
total: number;
|
||||||
|
};
|
@ -497,6 +497,7 @@ export * from './eventsSchemaVersion';
|
|||||||
export * from './executiveSummarySchema';
|
export * from './executiveSummarySchema';
|
||||||
export * from './executiveSummarySchemaFlagTrendsItem';
|
export * from './executiveSummarySchemaFlagTrendsItem';
|
||||||
export * from './executiveSummarySchemaFlags';
|
export * from './executiveSummarySchemaFlags';
|
||||||
|
export * from './executiveSummarySchemaProjectFlagTrendsItem';
|
||||||
export * from './executiveSummarySchemaUserTrendsItem';
|
export * from './executiveSummarySchemaUserTrendsItem';
|
||||||
export * from './executiveSummarySchemaUsers';
|
export * from './executiveSummarySchemaUsers';
|
||||||
export * from './exportFeatures404';
|
export * from './exportFeatures404';
|
||||||
|
Loading…
Reference in New Issue
Block a user