Skip to content

Commit

Permalink
New insights layout - feature flag (#7598)
Browse files Browse the repository at this point in the history
Preparing insights component for refactoring and enhancements.
  • Loading branch information
Tymek authored Jul 16, 2024
1 parent 7ed1d77 commit 7b2532e
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 5 deletions.
25 changes: 21 additions & 4 deletions frontend/src/component/insights/Insights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { useInsights } from 'hooks/api/getters/useInsights/useInsights';
import { InsightsHeader } from './components/InsightsHeader/InsightsHeader';
import { useInsightsData } from './hooks/useInsightsData';
import { InsightsCharts } from './InsightsCharts';
import { LegacyInsightsCharts } from './LegacyInsightsCharts';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useUiFlag } from 'hooks/useUiFlag';

const StickyWrapper = styled(Box, {
shouldForwardProp: (prop) => prop !== 'scrolled',
Expand Down Expand Up @@ -50,6 +53,8 @@ export const Insights: VFC = () => {
window.addEventListener('scroll', handleScroll);
}

const isInsightsV2Enabled = useUiFlag('insightsV2');

return (
<>
<StickyWrapper scrolled={scrolled}>
Expand All @@ -69,10 +74,22 @@ export const Insights: VFC = () => {
}
/>
</StickyWrapper>
<InsightsCharts
loading={loading}
projects={projects}
{...insightsData}
<ConditionallyRender
condition={isInsightsV2Enabled}
show={
<InsightsCharts
loading={loading}
projects={projects}
{...insightsData}
/>
}
elseShow={
<LegacyInsightsCharts
loading={loading}
projects={projects}
{...insightsData}
/>
}
/>
</>
);
Expand Down
251 changes: 251 additions & 0 deletions frontend/src/component/insights/LegacyInsightsCharts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
import type { VFC } from 'react';
import { Box, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Widget } from './components/Widget/Widget';
import { UserStats } from './componentsStat/UserStats/UserStats';
import { UsersChart } from './componentsChart/UsersChart/UsersChart';
import { UsersPerProjectChart } from './componentsChart/UsersPerProjectChart/UsersPerProjectChart';
import { FlagStats } from './componentsStat/FlagStats/FlagStats';
import { FlagsChart } from './componentsChart/FlagsChart/FlagsChart';
import { FlagsProjectChart } from './componentsChart/FlagsProjectChart/FlagsProjectChart';
import { HealthStats } from './componentsStat/HealthStats/HealthStats';
import { ProjectHealthChart } from './componentsChart/ProjectHealthChart/ProjectHealthChart';
import { TimeToProduction } from './componentsStat/TimeToProduction/TimeToProduction';
import { TimeToProductionChart } from './componentsChart/TimeToProductionChart/TimeToProductionChart';
import { MetricsSummaryChart } from './componentsChart/MetricsSummaryChart/MetricsSummaryChart';
import { UpdatesPerEnvironmentTypeChart } from './componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart';
import type {
InstanceInsightsSchema,
InstanceInsightsSchemaFlags,
InstanceInsightsSchemaUsers,
} from 'openapi';
import type { GroupedDataByProject } from './hooks/useGroupedProjectTrends';
import { allOption } from 'component/common/ProjectSelect/ProjectSelect';
import { chartInfo } from './chart-info';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';

interface IChartsProps {
flags: InstanceInsightsSchema['flags'];
flagTrends: InstanceInsightsSchema['flagTrends'];
projectsData: InstanceInsightsSchema['projectFlagTrends'];
groupedProjectsData: GroupedDataByProject<
InstanceInsightsSchema['projectFlagTrends']
>;
metricsData: InstanceInsightsSchema['metricsSummaryTrends'];
groupedMetricsData: GroupedDataByProject<
InstanceInsightsSchema['metricsSummaryTrends']
>;
users: InstanceInsightsSchema['users'];
userTrends: InstanceInsightsSchema['userTrends'];
environmentTypeTrends: InstanceInsightsSchema['environmentTypeTrends'];
summary: {
total: number;
active: number;
stale: number;
potentiallyStale: number;
averageUsers: number;
averageHealth?: string;
flagsPerUser?: string;
medianTimeToProduction?: number;
};
loading: boolean;
projects: string[];
allMetricsDatapoints: string[];
}

const StyledGrid = styled(Box)(({ theme }) => ({
display: 'grid',
gridTemplateColumns: `repeat(2, 1fr)`,
gridAutoRows: 'auto',
gap: theme.spacing(2),
paddingBottom: theme.spacing(2),
[theme.breakpoints.up('md')]: {
gridTemplateColumns: `300px 1fr`,
},
}));

const ChartWidget = styled(Widget)(({ theme }) => ({
[theme.breakpoints.down('md')]: {
gridColumnStart: 'span 2',
order: 2,
},
}));

export const LegacyInsightsCharts: VFC<IChartsProps> = ({
projects,
flags,
users,
summary,
userTrends,
groupedProjectsData,
flagTrends,
groupedMetricsData,
environmentTypeTrends,
allMetricsDatapoints,
loading,
}) => {
const { isEnterprise } = useUiConfig();
const showAllProjects = projects[0] === allOption.id;
const isOneProjectSelected = projects.length === 1;

function getFlagsPerUser(
flags: InstanceInsightsSchemaFlags,
users: InstanceInsightsSchemaUsers,
) {
const flagsPerUserCalculation = flags.total / users.total;
return Number.isNaN(flagsPerUserCalculation)
? 'N/A'
: flagsPerUserCalculation.toFixed(2);
}

return (
<>
<StyledGrid>
<ConditionallyRender
condition={showAllProjects}
show={
<Widget {...chartInfo.totalUsers}>
<UserStats
count={users.total}
active={users.active}
inactive={users.inactive}
isLoading={loading}
/>
</Widget>
}
elseShow={
<Widget
{...(isOneProjectSelected
? chartInfo.usersInProject
: chartInfo.avgUsersPerProject)}
>
<UserStats
count={summary.averageUsers}
isLoading={loading}
/>
</Widget>
}
/>
<ConditionallyRender
condition={showAllProjects}
show={
<ChartWidget {...chartInfo.users}>
<UsersChart
userTrends={userTrends}
isLoading={loading}
/>
</ChartWidget>
}
elseShow={
<ChartWidget {...chartInfo.usersPerProject}>
<UsersPerProjectChart
projectFlagTrends={groupedProjectsData}
isLoading={loading}
/>
</ChartWidget>
}
/>
<Widget {...chartInfo.totalFlags}>
<FlagStats
count={showAllProjects ? flags.total : summary.total}
flagsPerUser={
showAllProjects ? getFlagsPerUser(flags, users) : ''
}
isLoading={loading}
/>
</Widget>
<ConditionallyRender
condition={showAllProjects}
show={
<ChartWidget {...chartInfo.flags}>
<FlagsChart
flagTrends={flagTrends}
isLoading={loading}
/>
</ChartWidget>
}
elseShow={
<ChartWidget {...chartInfo.flagsPerProject}>
<FlagsProjectChart
projectFlagTrends={groupedProjectsData}
isLoading={loading}
/>
</ChartWidget>
}
/>
<ConditionallyRender
condition={isEnterprise()}
show={
<>
<Widget {...chartInfo.averageHealth}>
<HealthStats
value={summary.averageHealth}
healthy={summary.active}
stale={summary.stale}
potentiallyStale={summary.potentiallyStale}
/>
</Widget>
<ChartWidget
{...(showAllProjects
? chartInfo.overallHealth
: chartInfo.healthPerProject)}
>
<ProjectHealthChart
projectFlagTrends={groupedProjectsData}
isAggregate={showAllProjects}
isLoading={loading}
/>
</ChartWidget>
<Widget {...chartInfo.medianTimeToProduction}>
<TimeToProduction
daysToProduction={
summary.medianTimeToProduction
}
/>
</Widget>
<ChartWidget
{...(showAllProjects
? chartInfo.timeToProduction
: chartInfo.timeToProductionPerProject)}
>
<TimeToProductionChart
projectFlagTrends={groupedProjectsData}
isAggregate={showAllProjects}
isLoading={loading}
/>
</ChartWidget>
</>
}
/>
</StyledGrid>
<ConditionallyRender
condition={isEnterprise()}
show={
<>
<Widget
{...(showAllProjects
? chartInfo.metrics
: chartInfo.metricsPerProject)}
>
<MetricsSummaryChart
metricsSummaryTrends={groupedMetricsData}
allDatapointsSorted={allMetricsDatapoints}
isAggregate={showAllProjects}
isLoading={loading}
/>
</Widget>
<Widget
{...chartInfo.updates}
sx={{ mt: (theme) => theme.spacing(2) }}
>
<UpdatesPerEnvironmentTypeChart
environmentTypeTrends={environmentTypeTrends}
isLoading={loading}
/>
</Widget>
</>
}
/>
</>
);
};
1 change: 1 addition & 0 deletions frontend/src/interfaces/uiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export type UiFlags = {
commandBarUI?: boolean;
flagCreator?: boolean;
resourceLimits?: boolean;
insightsV2?: boolean;
};

export interface IVersionInfo {
Expand Down
1 change: 1 addition & 0 deletions src/lib/__snapshots__/create-config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ exports[`should create default config 1`] = `
"filterInvalidClientMetrics": false,
"flagCreator": false,
"googleAuthEnabled": false,
"insightsV2": false,
"killInsightsUI": false,
"killScheduledChangeRequestCache": false,
"maintenanceMode": false,
Expand Down
7 changes: 6 additions & 1 deletion src/lib/types/experimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ export type IFlagKey =
| 'extendedMetrics'
| 'cleanApiTokenWhenOrphaned'
| 'allowOrphanedWildcardTokens'
| 'removeUnsafeInlineStyleSrc';
| 'removeUnsafeInlineStyleSrc'
| 'insightsV2';

export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;

Expand Down Expand Up @@ -319,6 +320,10 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_REMOVE_UNSAFE_INLINE_STYLE_SRC,
false,
),
insightsV2: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_INSIGHTS_V2,
false,
),
};

export const defaultExperimentalOptions: IExperimentalOptions = {
Expand Down
1 change: 1 addition & 0 deletions src/server-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ process.nextTick(async () => {
flagCreator: true,
resourceLimits: true,
extendedMetrics: true,
insightsV2: true,
},
},
authentication: {
Expand Down

0 comments on commit 7b2532e

Please sign in to comment.