2024-03-14 10:33:30 +01:00
import { useState , VFC } from 'react' ;
2024-03-04 12:56:17 +01:00
import { Box , styled } from '@mui/material' ;
import { ArrayParam , withDefault } from 'use-query-params' ;
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender' ;
import { usePersistentTableState } from 'hooks/usePersistentTableState' ;
2024-03-01 13:28:57 +01:00
import {
allOption ,
2024-03-04 12:56:17 +01:00
ProjectSelect ,
} from 'component/common/ProjectSelect/ProjectSelect' ;
import { useExecutiveDashboard } from 'hooks/api/getters/useExecutiveSummary/useExecutiveSummary' ;
import { useFilteredFlagsSummary } from './hooks/useFilteredFlagsSummary' ;
import { useFilteredTrends } from './hooks/useFilteredTrends' ;
import { Widget } from './components/Widget/Widget' ;
import { DashboardHeader } from './components/DashboardHeader/DashboardHeader' ;
import { UserStats } from './componentsStat/UserStats/UserStats' ;
import { FlagStats } from './componentsStat/FlagStats/FlagStats' ;
import { HealthStats } from './componentsStat/HealthStats/HealthStats' ;
import { UsersChart } from './componentsChart/UsersChart/UsersChart' ;
import { FlagsChart } from './componentsChart/FlagsChart/FlagsChart' ;
import { FlagsProjectChart } from './componentsChart/FlagsProjectChart/FlagsProjectChart' ;
import { ProjectHealthChart } from './componentsChart/ProjectHealthChart/ProjectHealthChart' ;
import { MetricsSummaryChart } from './componentsChart/MetricsSummaryChart/MetricsSummaryChart' ;
import { UsersPerProjectChart } from './componentsChart/UsersPerProjectChart/UsersPerProjectChart' ;
2024-03-07 10:00:18 +01:00
import { UpdatesPerEnvironmentTypeChart } from './componentsChart/UpdatesPerEnvironmentTypeChart/UpdatesPerEnvironmentTypeChart' ;
2024-03-15 09:37:02 +01:00
import { TimeToProduction } from './componentsStat/TimeToProduction/TimeToProduction' ;
import { TimeToProductionChart } from './componentsChart/TimeToProductionChart/TimeToProductionChart' ;
import { useGroupedProjectTrends } from './hooks/useGroupedProjectTrends' ;
import { useAvgTimeToProduction } from './hooks/useAvgTimeToProduction' ;
2024-01-22 11:07:38 +01:00
const StyledGrid = styled ( Box ) ( ( { theme } ) = > ( {
display : 'grid' ,
2024-03-04 12:56:17 +01:00
gridTemplateColumns : ` repeat(2, 1fr) ` ,
2024-01-30 09:00:06 +01:00
gridAutoRows : 'auto' ,
2024-01-22 11:07:38 +01:00
gap : theme.spacing ( 2 ) ,
2024-03-04 12:56:17 +01:00
paddingBottom : theme.spacing ( 2 ) ,
[ theme . breakpoints . up ( 'md' ) ] : {
gridTemplateColumns : ` 300px 1fr ` ,
} ,
2024-01-22 11:07:38 +01:00
} ) ) ;
2024-01-18 12:32:25 +01:00
2024-03-04 12:56:17 +01:00
const ChartWidget = styled ( Widget ) ( ( { theme } ) = > ( {
[ theme . breakpoints . down ( 'md' ) ] : {
gridColumnStart : 'span 2' ,
order : 2 ,
2024-03-01 13:28:57 +01:00
} ,
} ) ) ;
2024-03-14 10:33:30 +01:00
const StickyWrapper = styled ( Box ) < { scrolled? : boolean } > (
( { theme , scrolled } ) = > ( {
position : 'sticky' ,
top : 0 ,
zIndex : 1000 ,
padding : scrolled ? theme . spacing ( 2 , 0 ) : theme . spacing ( 0 , 0 , 2 ) ,
background : theme.palette.background.application ,
transition : 'padding 0.3s ease' ,
} ) ,
) ;
2024-01-18 12:32:25 +01:00
export const ExecutiveDashboard : VFC = ( ) = > {
2024-03-14 10:33:30 +01:00
const [ scrolled , setScrolled ] = useState ( false ) ;
2024-01-26 09:03:12 +01:00
const { executiveDashboardData , loading , error } = useExecutiveDashboard ( ) ;
2024-03-04 12:56:17 +01:00
const stateConfig = {
projects : withDefault ( ArrayParam , [ allOption . id ] ) ,
} ;
2024-03-06 12:19:27 +01:00
const [ state , setState ] = usePersistentTableState ( 'insights' , stateConfig ) ;
2024-03-04 12:56:17 +01:00
const setProjects = ( projects : string [ ] ) = > {
setState ( { projects } ) ;
} ;
const projects = state . projects
? ( state . projects . filter ( Boolean ) as string [ ] )
: [ ] ;
const showAllProjects = projects [ 0 ] === allOption . id ;
const projectsData = useFilteredTrends (
executiveDashboardData . projectFlagTrends ,
projects ,
) ;
2024-03-15 09:37:02 +01:00
const groupedProjectsData = useGroupedProjectTrends ( projectsData ) ;
2024-03-04 12:56:17 +01:00
const metricsData = useFilteredTrends (
executiveDashboardData . metricsSummaryTrends ,
projects ,
) ;
2024-03-15 09:37:02 +01:00
const groupedMetricsData = useGroupedProjectTrends ( metricsData ) ;
2024-03-07 10:00:18 +01:00
const { users , environmentTypeTrends } = executiveDashboardData ;
2024-02-19 15:28:03 +01:00
2024-03-04 12:56:17 +01:00
const summary = useFilteredFlagsSummary ( projectsData ) ;
2024-03-15 09:37:02 +01:00
const avgDaysToProduction = useAvgTimeToProduction ( groupedProjectsData ) ;
2024-03-04 12:56:17 +01:00
const isOneProjectSelected = projects . length === 1 ;
2024-01-30 10:07:16 +01:00
2024-03-14 10:33:30 +01:00
const handleScroll = ( ) = > {
if ( ! scrolled && window . scrollY > 0 ) {
setScrolled ( true ) ;
} else if ( scrolled && window . scrollY === 0 ) {
setScrolled ( false ) ;
}
} ;
if ( typeof window !== 'undefined' ) {
window . addEventListener ( 'scroll' , handleScroll ) ;
}
2024-01-22 11:07:38 +01:00
return (
< >
2024-03-14 10:33:30 +01:00
< StickyWrapper scrolled = { scrolled } >
2024-03-04 12:56:17 +01:00
< DashboardHeader
actions = {
< ProjectSelect
selectedProjects = { projects }
onChange = { setProjects }
dataTestId = { 'DASHBOARD_PROJECT_SELECT' }
2024-03-06 12:19:27 +01:00
sx = { { flex : 1 , maxWidth : '360px' , width : '100%' } }
2024-03-04 12:56:17 +01:00
/ >
}
/ >
2024-03-14 10:33:30 +01:00
< / StickyWrapper >
2024-03-04 12:56:17 +01:00
< StyledGrid >
< ConditionallyRender
condition = { showAllProjects }
show = {
< Widget title = 'Total users' >
< UserStats
count = { users . total }
active = { users . active }
inactive = { users . inactive }
/ >
< / Widget >
}
elseShow = {
< Widget
title = {
isOneProjectSelected
? 'Users in project'
: 'Users per project on average'
}
>
< UserStats count = { summary . averageUsers } / >
< / Widget >
}
/ >
< ConditionallyRender
condition = { showAllProjects }
show = {
< ChartWidget title = 'Users' >
< UsersChart
userTrends = { executiveDashboardData . userTrends }
isLoading = { loading }
/ >
< / ChartWidget >
}
elseShow = {
< ChartWidget title = 'Users per project' >
< UsersPerProjectChart
2024-03-15 09:37:02 +01:00
projectFlagTrends = { groupedProjectsData }
2024-03-04 12:56:17 +01:00
/ >
< / ChartWidget >
}
/ >
2024-01-30 17:02:13 +01:00
< Widget
title = 'Total flags'
2024-03-04 12:56:17 +01:00
tooltip = 'Active flags (not archived) that currently exist across selected projects.'
2024-01-30 17:02:13 +01:00
>
< FlagStats
2024-03-04 12:56:17 +01:00
count = { summary . total }
flagsPerUser = {
showAllProjects
? ( summary . total / users . total ) . toFixed ( 2 )
: ''
}
2024-01-30 17:02:13 +01:00
/ >
< / Widget >
2024-03-04 12:56:17 +01:00
< ConditionallyRender
condition = { showAllProjects }
show = {
< ChartWidget title = 'Number of flags' >
< FlagsChart
flagTrends = { executiveDashboardData . flagTrends }
isLoading = { loading }
/ >
< / ChartWidget >
}
elseShow = {
< ChartWidget title = 'Flags per project' >
< FlagsProjectChart
2024-03-15 09:37:02 +01:00
projectFlagTrends = { groupedProjectsData }
2024-03-04 12:56:17 +01:00
/ >
< / ChartWidget >
}
2024-03-01 13:28:57 +01:00
/ >
2024-03-06 12:19:27 +01:00
< Widget
title = 'Average health'
tooltip = 'Average health is a percentage of flags that are not stale nor potencially stale.'
>
2024-02-23 09:05:59 +01:00
< HealthStats
2024-03-04 12:56:17 +01:00
value = { summary . averageHealth }
healthy = { summary . active }
stale = { summary . stale }
potentiallyStale = { summary . potentiallyStale }
2024-01-31 13:19:28 +01:00
/ >
< / Widget >
2024-03-04 12:56:17 +01:00
< ChartWidget
title = {
showAllProjects ? 'Healthy flags' : 'Health per project'
}
2024-02-23 14:17:58 +01:00
>
2024-03-04 12:56:17 +01:00
< ProjectHealthChart
2024-03-15 09:37:02 +01:00
projectFlagTrends = { groupedProjectsData }
2024-03-04 12:56:17 +01:00
isAggregate = { showAllProjects }
2024-02-23 14:17:58 +01:00
/ >
2024-03-04 12:56:17 +01:00
< / ChartWidget >
2024-03-15 09:37:02 +01:00
< Widget
title = 'Average time to production'
tooltip = 'How long did it take on average from a feature toggle was created until it was enabled in an environment of type production. This is calculated only from feature toggles with the type of "release". '
>
< TimeToProduction daysToProduction = { avgDaysToProduction } / >
2024-02-14 13:32:12 +01:00
< / Widget >
2024-03-15 09:37:02 +01:00
< ChartWidget
title = 'Time to production'
tooltip = 'How the average time to production changes over time'
>
< TimeToProductionChart
projectFlagTrends = { groupedProjectsData }
/ >
< / ChartWidget >
2024-01-22 11:07:38 +01:00
< / StyledGrid >
2024-03-06 12:19:27 +01:00
< Widget
title = 'Metrics'
tooltip = 'Summary of all flag evaluations reported by SDKs.'
>
2024-03-15 09:37:02 +01:00
< MetricsSummaryChart
metricsSummaryTrends = { groupedMetricsData }
/ >
2024-03-04 12:56:17 +01:00
< / Widget >
2024-03-07 10:00:18 +01:00
< Widget
title = 'Updates per environment type'
tooltip = 'Summary of all configuration updates per environment type'
sx = { { mt : ( theme ) = > theme . spacing ( 2 ) } }
>
< UpdatesPerEnvironmentTypeChart
environmentTypeTrends = { environmentTypeTrends }
isLoading = { loading }
/ >
< / Widget >
2024-01-22 11:07:38 +01:00
< / >
) ;
2024-01-18 12:32:25 +01:00
} ;