import * as React from 'react' ; import StatTitle from './StatTitle' ; import { ResponsiveLine , Serie , Datum } from '@nivo/line' ; import { SpacedRepStat , StatType } from "polar-firebase/src/firebase/om/SpacedRepStats" ; import { Statistics } from "polar-shared/src/util/Statistics" ; import { ISODateTimeString , ISODateTimeStrings } from "polar-shared/src/metadata/ISODateTimeStrings" ; import { RepetitionMode } from "polar-spaced-repetition-api/src/scheduler/S2Plus/S2Plus" ; import { ReviewerStatistics } from "../reviewer/ReviewerStatistics" ; import { Logger } from "polar-shared/src/logger/Logger" ; import { StatBox } from "./StatBox" ; import { LoadingProgress } from "../../../../web/js/ui/LoadingProgress" ; import { minDatapointsReducer , sumDatapointsReducer } from './StatisticsReducers' ; import { useNivoTheme } from "./NivoHooks" ; import useTheme from '@material-ui/core/styles/useTheme' ; const HEIGHT = '300px' ; const log = Logger . create ( ) ; interface ChartProps { readonly type : 'queue' | 'completed' ; readonly lineData : Serie [ ] ; } const Chart = React . memo ( function Chart ( props ) { const nivoTheme = useNivoTheme ( ) ; const theme = useTheme ( ) ; const createTitle = ( ) => { switch ( props . type ) { case "queue" : return "Number of tasks pending (queue length)" ; case "completed" : return "Number of tasks completed" ; } } ; const title = createTitle ( ) ; return ( < StatBox > < > < StatTitle > { title } < /StatTitle> < div style = { { height : HEIGHT , width : '100%' } } > < ResponsiveLine data = { props . lineData } isInteractive = { true } margin = { { top : 10 , right : 10 , bottom : 50 , left : 40 } } colors = { { scheme : 'nivo' } } enableArea = { true } yScale = { { type : 'linear' } } xScale = { { type : 'time' , } } axisBottom = { { format : '%b %d' , tickValues : 5 , } } animate = { true } theme = { nivoTheme } legends = { [ { anchor : 'bottom' , direction : 'row' , justify : false , translateX : 0 , translateY : 50 , itemWidth : 100 , itemHeight : 20 , itemsSpacing : 4 , symbolSize : 20 , symbolShape : 'circle' , itemDirection : 'left-to-right' , itemTextColor : theme . palette . text . secondary , effects : [ { on : 'hover' , style : { itemOpacity : 1 } } ] } ] } / > < /div> < /> < /StatBox>); } ) ; const NeedChardData = ( ) => { return < div />; } ; export class SpacedRepQueueChart extends React . Component < IProps , IState > { constructor ( props ) { super ( props ) ; this . state = { data : undefined } ; } public componentDidMount ( ) { const doAsync = async ( ) => { const data = await ReviewerStatistics . statistics ( this . props . mode , this . props . type ) ; this . setState ( { data } ) ; } ; doAsync ( ) . catch ( err => log . error ( "Could not fetch queue stats: " , err ) ) ; } public render ( ) { const { data } = this . state ; if ( ! data ) { return < LoadingProgress style = { { height : HEIGHT } } />; } const computeStats = ( ) => { const createDatapointsReducer = ( ) => { switch ( this . props . type ) { case "queue" : return minDatapointsReducer ; case "completed" : return sumDatapointsReducer ; } } ; const emptyDatapointFactory = ( created ) : SpacedRepStat => { return { created , type : this . props . type , mode : this . props . mode , nrNew : 0 , nrLapsed : 0 , nrLearning : 0 , nrReview : 0 } ; } ; return Statistics . compute ( data , createDatapointsReducer ( ) , emptyDatapointFactory ) ; } ; const stats = computeStats ( ) ; const computeLineData = ( ) => { type StatID = 'nrLearning' | 'nrReview' | 'nrLapsed' ; const toDataPoint = ( spacedRepStat , id ) : Datum => { const x = ISODateTimeStrings . parse ( spacedRepStat . created ) ; const y = ( spacedRepStat as any ) [ id ] ; return { x , y } ; } ; const computeLine = ( id ) : Serie => { return { id , data : stats . map ( current => toDataPoint ( current , id ) ) } ; } ; return [ computeLine ( 'nrLearning' ) , computeLine ( 'nrReview' ) , computeLine ( 'nrLapsed' ) ] ; } ; const lineData = computeLineData ( ) ; const Main = ( ) => { if ( lineData [ 0 ] . data . length < 3 ) { return < NeedChardData />; } else { return < Chart type = { this . props . type } lineData = { lineData } / > ; } } ; return ( < div > < Main /> < /div>); } } export interface IProps { readonly mode : RepetitionMode ; readonly type : StatType ; } export interface IState { readonly data : ReadonlyArray < SpacedRepStat > | undefined ; }