import { isEmpty , isUndefined } from 'lodash' ; import moment from 'moment' ; import { useEffect , useState } from 'react' ; import ReactApexChart from 'react-apexcharts' ; import semver from 'semver' ; import { PackageViewsStats , RepositoryKind } from '../../types' ; import { getSeriesDataPerPkgVersionViews , sumViewsPerVersions } from '../../utils/viewsStats' ; import Loading from '../common/Loading' ; import styles from './PackageViewsStats.module.css' ; interface Props { stats ? : PackageViewsStats ; version ? : string ; repoKind : RepositoryKind ; title : JSX . Element ; } interface Series { name : string ; data : any [ ] ; } const MAX_VISIBLE_VERSIONS = 3 ; const sortPkgVersions = ( availableVersions ) : string [ ] => { if ( availableVersions ) { const validVersions = availableVersions . filter ( ( version ) => semver . valid ( version ) ) ; const invalidVersions = availableVersions . filter ( ( version ) => ! semver . valid ( version ) ) ; try { const sortedValidVersions = validVersions . sort ( ( a , b ) => ( semver . lt ( a , b ) ? 1 : - 1 ) ) ; return [ ... sortedValidVersions , ... invalidVersions ] ; } catch { return availableVersions ; } } return [ ] ; } ; const getMostRecentVersions = ( stats ) : string [ ] => { const sortedVersions = sortPkgVersions ( Object . keys ( stats ) ) ; return sortedVersions . slice ( 0 , MAX_VISIBLE_VERSIONS ) ; } ; const prepareChartsSeries = ( repoKind , stats , version ? ) : Series [ ] => { if ( isEmpty ( stats ) ) return [ ] ; let series = [ ] ; let visibleVersions = [ ] ; if ( version ) { if ( ! isUndefined ( stats [ version ] ) ) { visibleVersions = [ version ] ; } else { return [ ] ; } } else { visibleVersions = repoKind === RepositoryKind . Container ? [ ] : getMostRecentVersions ( stats ) ; } visibleVersions . forEach ( ( version ) => { series . push ( { name : version , data : getSeriesDataPerPkgVersionViews ( stats , version ) , } ) ; } ) ; const statsVersions = Object . keys ( stats ) ; if ( statsVersions . length > visibleVersions . length && isUndefined ( version ) ) { series . push ( { name : repoKind === RepositoryKind . Container ? 'All tags' : 'Other' , data : sumViewsPerVersions ( stats , visibleVersions ) , } ) ; } return series ; } ; const PackagesViewsStats = ( props ) => { const [ series , setSeries ] = useState < any [ ] > ( [ ] ) ; const getStackedChartConfig = ( ) : ApexCharts . ApexOptions => { return { chart : { id : 'pkg-views' , type : 'bar' , stacked : true , redrawOnWindowResize : true , redrawOnParentResize : false , zoom : { enabled : false , } , fontFamily : "'Lato', Roboto, 'Helvetica Neue', Arial, sans-serif !default" , toolbar : { show : false , } , } , grid : { borderColor : 'var(--border-md)' } , dataLabels : { enabled : false , } , plotOptions : { bar : { } , } , tooltip : { x : { formatter : ( val , opts ? ) : string => { return moment ( val ) . format ( 'DD MMM YY' ) ; } , } , y : { formatter : function ( value ) { return value . toFixed ( 0 ) ; } , } , } , states : { hover : { filter : { type : 'none' , value : 0 , } , } , active : { filter : { type : 'none' , value : 0 , } , } , } , legend : { showForSingleSeries : true , position : 'bottom' , horizontalAlign : 'center' , itemMargin : { horizontal : 10 , } , offsetY : 20 , } , xaxis : { type : 'datetime' , labels : { style : { colors : 'var(--color-font)' , fontSize : '11px' , } , } , } , yaxis : { labels : { formatter : function ( value ) { return value === 0 || value >= 1 ? value . toFixed ( 0 ) . toString ( ) : '' ; } , minWidth : 25 , style : { colors : 'var(--color-font)' , fontSize : '11px' , } , } , } , colors : [ '#33a1fd' , '#ffc43d' , '#ff8e01' , '#994477' ] , } ; } ; useEffect ( ( ) => { if ( ! isUndefined ( props . stats ) ) { setSeries ( prepareChartsSeries ( props . repoKind , props . stats , props . version ) ) ; } } , [ props . version , props . stats ] ) ; if ( series . length === 0 && ! isUndefined ( props . stats ) ) return null ; return ( < div className = "mb-5 pb-3" > { props . title } < div className = { `card ${ styles . chartWrapper }` } > { isUndefined ( props . stats ) ? ( < Loading />) : (<ReactApexChart options={getStackedChartConfig()} series={series} type="bar" height="300" width="100%"/ > ) } < /div> < /div>); } ; export default PackagesViewsStats ;