github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/dashboard/frontend/src/pages/Dashboard.tsx (about)

     1  import React, { FC, useCallback, useEffect, useState, useMemo } from 'react'
     2  import bluebird from 'bluebird'
     3  import {
     4    ComposedChart,
     5    Line,
     6    Area,
     7    Bar,
     8    XAxis,
     9    YAxis,
    10    CartesianGrid,
    11    Tooltip,
    12    Legend,
    13    ResponsiveContainer,
    14    PieChart,
    15    Pie,
    16    Sector,
    17    Cell,
    18    RadialBarChart,
    19    RadialBar,
    20  } from 'recharts'
    21  
    22  import Box from '@mui/material/Box'
    23  import Grid from '@mui/material/Grid'
    24  import Container from '@mui/material/Container'
    25  import Paper from '@mui/material/Paper'
    26  
    27  import NumberHighlight from '../components/dashboard/NumberHighlight'
    28  import AutoAwesomeMotionIcon from '@mui/icons-material/AutoAwesomeMotion'
    29  import TimelineIcon from '@mui/icons-material/Timeline'
    30  import PersonIcon from '@mui/icons-material/Person'
    31  import CodeIcon from '@mui/icons-material/Code'
    32  
    33  import useLoadingErrorHandler from '../hooks/useLoadingErrorHandler'
    34  import useApi from '../hooks/useApi'
    35  
    36  import {
    37    DashboardSummary,
    38  } from '../types'
    39  
    40  const blue = '#0088FE'
    41  const green = '#00C49F'
    42  const yellow = '#FFBB28'
    43  const COLORS = [blue, green, yellow, '#FF8042'];
    44  
    45  const Dashboard: FC = () => {
    46    const api = useApi()
    47    const loadingErrorHandler = useLoadingErrorHandler()
    48  
    49    const [ data, setData ] = useState<DashboardSummary>()
    50  
    51    const loadData = useCallback(async () => {
    52      const handler = loadingErrorHandler(async () => {
    53        const data: DashboardSummary = await bluebird.props({
    54          annotations: await api.get('/api/v1/summary/annotations'),
    55          jobMonths: await api.get('/api/v1/summary/jobmonths'),
    56          jobExecutors: await api.get('/api/v1/summary/jobexecutors'),
    57          totalJobs: await api.get('/api/v1/summary/totaljobs'),
    58          totalEvents: await api.get('/api/v1/summary/totaljobevents'),
    59          totalUsers: await api.get('/api/v1/summary/totalusers'),
    60          totalExecutors: await api.get('/api/v1/summary/totalexecutors'),
    61        })
    62        setData(data)
    63      })
    64      await handler()
    65    }, [])
    66  
    67    const barGraphData = useMemo(() => {
    68      if(!data?.jobMonths) return []
    69      const mappedData = data?.jobMonths.map((month) => ({
    70        name: month.month,
    71        jobs: month.count,
    72      }))
    73      return [{
    74        name: '',
    75        jobs: 0,
    76      }].concat(mappedData)
    77    }, [
    78      data,
    79    ])
    80  
    81    const pieChartData = useMemo(() => {
    82      if(!data?.jobExecutors) return []
    83      return data.jobExecutors.map((executor, index) => ({
    84        name: executor.executor,
    85        value: executor.count,
    86      }))
    87    }, [
    88      data,
    89    ])
    90  
    91    useEffect(() => {
    92      loadData()
    93    }, [])
    94  
    95    if(!data) return null
    96  
    97    return (
    98      <Container maxWidth={ 'xl' } sx={{ mt: 4, mb: 4 }}>
    99        <Grid container spacing={3}>
   100          <Grid item xs={12} md={3}>
   101            <NumberHighlight
   102              headline={ `${data.totalJobs.count}` }
   103              subline="Total Jobs"
   104              backgroundColor="#D1E9FC"
   105              textColor="#061B64"
   106            >
   107              <AutoAwesomeMotionIcon
   108                sx={{
   109                  fontSize: '64px',
   110                  mb: 2,
   111                  color: '#061B64'
   112                }}
   113              />
   114            </NumberHighlight>
   115          </Grid>
   116          <Grid item xs={12} md={3}>
   117            <NumberHighlight
   118              headline={ `${data.totalEvents.count}` }
   119              subline="Total Events"
   120              backgroundColor="#D0F2FF"
   121              textColor="#264B90"
   122            >
   123              <TimelineIcon
   124                sx={{
   125                  fontSize: '64px',
   126                  mb: 2,
   127                  color: '#264B90'
   128                }}
   129              />
   130            </NumberHighlight>
   131          </Grid>
   132          <Grid item xs={12} md={3}>
   133            <NumberHighlight
   134              headline={ `${data.totalUsers.count}` }
   135              subline="Unique Users"
   136              backgroundColor="#FFF7CD"
   137              textColor="#7F5509"
   138            >
   139              <PersonIcon
   140                sx={{
   141                  fontSize: '64px',
   142                  mb: 2,
   143                  color: '#7F5509'
   144                }}
   145              />
   146            </NumberHighlight>
   147          </Grid>
   148  
   149          
   150          <Grid item xs={12} md={3}>
   151            <NumberHighlight
   152              headline={ `${data.totalExecutors.count}` }
   153              subline="Executors"
   154              backgroundColor="#FFE7D9"
   155              textColor="#7A0C2E"
   156            >
   157              <CodeIcon
   158                sx={{
   159                  fontSize: '64px',
   160                  mb: 2,
   161                  color: '#7A0C2E'
   162                }}
   163              />
   164            </NumberHighlight>
   165          </Grid>
   166  
   167          <Grid item xs={12} md={8}>
   168            <Box
   169              component="div"
   170              sx={{
   171                height: '400px',
   172                backgroundColor: '#fff',
   173                borderRadius: '15px',
   174                padding: '20px',
   175              }}
   176            >
   177              <ResponsiveContainer width="100%" height="100%">
   178                <ComposedChart
   179                  data={barGraphData}
   180                  margin={{
   181                    top: 20,
   182                    right: 20,
   183                    bottom: 20,
   184                    left: 20,
   185                  }}
   186                >
   187                  <CartesianGrid stroke="#f5f5f5" />
   188                  <XAxis dataKey="name" scale="band" />
   189                  <YAxis />
   190                  <Tooltip />
   191                  <Legend />
   192                  <Bar dataKey="jobs" barSize={20} fill={ blue } />
   193                  <Line type="monotone" dataKey="jobs" stroke={ green } />
   194                </ComposedChart>
   195              </ResponsiveContainer>
   196            </Box>
   197          </Grid>
   198  
   199          <Grid item xs={12} md={4}>
   200            <Box
   201              component="div"
   202              sx={{
   203                height: '400px',
   204                backgroundColor: '#fff',
   205                borderRadius: '15px',
   206                padding: '20px',
   207              }}
   208            >
   209              <ResponsiveContainer width="100%" height="100%">
   210                <PieChart>
   211                  <Pie
   212                    data={pieChartData}
   213                    innerRadius={40}
   214                    outerRadius={80}
   215                    fill="#8884d8"
   216                    paddingAngle={5}
   217                    dataKey="value"
   218                  >
   219                    {pieChartData.map((entry, index) => (
   220                      <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
   221                    ))}
   222                  </Pie>
   223                  <Legend
   224                    verticalAlign="bottom"
   225                    layout="vertical"
   226                    formatter={(value, entry: any, index) => {
   227                      return `${value} ${entry.payload?.value}`
   228                    }}
   229                  />
   230                </PieChart>
   231              </ResponsiveContainer>
   232            </Box>
   233          </Grid>
   234        </Grid>
   235      </Container>
   236    )
   237  }
   238  
   239  export default Dashboard