From 4f80ddd6bf93f643f08ba0c60a010c1965d72f05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Mod=C3=A9e?= <bjomo323@student.liu.se>
Date: Wed, 14 Apr 2021 08:41:44 +0000
Subject: [PATCH] Resolve "Create a dashboard"

---
 client/package-lock.json                      | 115 ++++++++++++++++++
 client/package.json                           |   3 +
 client/src/pages/admin/AdminPage.tsx          |   9 +-
 .../src/pages/admin/dashboard/Dashboard.tsx   |  58 +++++++++
 .../dashboard/components/CurrentUser.tsx      |  29 +++++
 .../components/NumberOfCompetitions.tsx       |  33 +++++
 .../dashboard/components/NumberOfRegions.tsx  |  33 +++++
 .../dashboard/components/NumberOfUsers.tsx    |  33 +++++
 8 files changed, 309 insertions(+), 4 deletions(-)
 create mode 100644 client/src/pages/admin/dashboard/Dashboard.tsx
 create mode 100644 client/src/pages/admin/dashboard/components/CurrentUser.tsx
 create mode 100644 client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx
 create mode 100644 client/src/pages/admin/dashboard/components/NumberOfRegions.tsx
 create mode 100644 client/src/pages/admin/dashboard/components/NumberOfUsers.tsx

diff --git a/client/package-lock.json b/client/package-lock.json
index b08413fa..7e73859f 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -1125,6 +1125,49 @@
       "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz",
       "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg=="
     },
+    "@devexpress/dx-chart-core": {
+      "version": "2.7.5",
+      "resolved": "https://registry.npmjs.org/@devexpress/dx-chart-core/-/dx-chart-core-2.7.5.tgz",
+      "integrity": "sha512-ZSpBN7SjnOhBOcvmuYZ5U+XyUYKlsTrYXCYMfFH2qXwvqh0e0UbhzW1FPfDXdWqx0Y52MtPX0HJrBC4QqH7Bcg==",
+      "requires": {
+        "d3-array": "^2.4.0",
+        "d3-scale": "^3.2.0",
+        "d3-shape": "^1.3.7"
+      }
+    },
+    "@devexpress/dx-core": {
+      "version": "2.7.5",
+      "resolved": "https://registry.npmjs.org/@devexpress/dx-core/-/dx-core-2.7.5.tgz",
+      "integrity": "sha512-VQQkz0uUqQ7YuVZeBEx1JFqpSSe5Bz9Qy2T0XAbilBQ8IItj74xjzaFMCNjuASAKXOJGtG0RUh+BOKrMGV7jlg=="
+    },
+    "@devexpress/dx-react-chart": {
+      "version": "2.7.5",
+      "resolved": "https://registry.npmjs.org/@devexpress/dx-react-chart/-/dx-react-chart-2.7.5.tgz",
+      "integrity": "sha512-j3nPsHrMiCbgm6olZCykxHOxxJ2XXG0CVihMYGOoyenM74Ed+NIaLi1bk2VtxKZdEPB5UrWgU10rd9SLH45BsQ==",
+      "requires": {
+        "@devexpress/dx-chart-core": "2.7.5",
+        "d3-scale": "^3.2.0",
+        "d3-shape": "^1.3.7"
+      }
+    },
+    "@devexpress/dx-react-chart-material-ui": {
+      "version": "2.7.5",
+      "resolved": "https://registry.npmjs.org/@devexpress/dx-react-chart-material-ui/-/dx-react-chart-material-ui-2.7.5.tgz",
+      "integrity": "sha512-uS450uSP1D6mZ2jgtueShqaATNRVqJEwqhgyRQUXL/gQyTrTCvM61TC5NLyEYCOW6RkiHK1BabWbhJlVATZbeg==",
+      "requires": {
+        "clsx": "^1.0.4",
+        "prop-types": "^15.7.2"
+      }
+    },
+    "@devexpress/dx-react-core": {
+      "version": "2.7.5",
+      "resolved": "https://registry.npmjs.org/@devexpress/dx-react-core/-/dx-react-core-2.7.5.tgz",
+      "integrity": "sha512-YwJ4l8nnMs/vghqamo8OzirDnrbT1ZNIcMP5xJFJJQIyfYAXvGGDS4yLeYI4cwitBg/d2O4jMOReXDB8tUaDDQ==",
+      "requires": {
+        "@devexpress/dx-core": "2.7.5",
+        "prop-types": "^15.7.2"
+      }
+    },
     "@emotion/hash": {
       "version": "0.8.0",
       "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
@@ -5416,6 +5459,73 @@
         "type": "^1.0.1"
       }
     },
+    "d3-array": {
+      "version": "2.12.1",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+      "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+      "requires": {
+        "internmap": "^1.0.0"
+      }
+    },
+    "d3-color": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
+      "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
+    },
+    "d3-format": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
+      "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
+    },
+    "d3-interpolate": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
+      "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==",
+      "requires": {
+        "d3-color": "1 - 2"
+      }
+    },
+    "d3-path": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+      "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
+    },
+    "d3-scale": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.4.tgz",
+      "integrity": "sha512-PG6gtpbPCFqKbvdBEswQcJcTzHC8VEd/XzezF5e68KlkT4/ggELw/nR1tv863jY6ufKTvDlzCMZvhe06codbbA==",
+      "requires": {
+        "d3-array": "^2.3.0",
+        "d3-format": "1 - 2",
+        "d3-interpolate": "1.2.0 - 2",
+        "d3-time": "1 - 2",
+        "d3-time-format": "2 - 3"
+      }
+    },
+    "d3-shape": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+      "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+      "requires": {
+        "d3-path": "1"
+      }
+    },
+    "d3-time": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+      "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+      "requires": {
+        "d3-array": "2"
+      }
+    },
+    "d3-time-format": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
+      "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
+      "requires": {
+        "d3-time": "1 - 2"
+      }
+    },
     "damerau-levenshtein": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
@@ -8492,6 +8602,11 @@
         "side-channel": "^1.0.4"
       }
     },
+    "internmap": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+      "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
+    },
     "ip": {
       "version": "1.1.5",
       "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
diff --git a/client/package.json b/client/package.json
index f41350a6..2d393d3d 100644
--- a/client/package.json
+++ b/client/package.json
@@ -3,6 +3,9 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@devexpress/dx-react-chart": "^2.7.5",
+    "@devexpress/dx-react-chart-material-ui": "^2.7.5",
+    "@devexpress/dx-react-core": "^2.7.5",
     "@material-ui/core": "^4.11.3",
     "@material-ui/icons": "^4.11.2",
     "@material-ui/lab": "^4.0.0-alpha.57",
diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx
index 921f721d..a0dbe2bd 100644
--- a/client/src/pages/admin/AdminPage.tsx
+++ b/client/src/pages/admin/AdminPage.tsx
@@ -23,6 +23,7 @@ import { getRoles } from '../../actions/roles'
 import { logoutUser } from '../../actions/user'
 import { useAppDispatch, useAppSelector } from '../../hooks'
 import CompetitionManager from './competitions/CompetitionManager'
+import Dashboard from './dashboard/Dashboard'
 import RegionManager from './regions/Regions'
 import { LeftDrawer } from './styled'
 import UserManager from './users/UserManager'
@@ -45,7 +46,9 @@ const useStyles = makeStyles((theme: Theme) =>
     content: {
       flexGrow: 1,
       backgroundColor: theme.palette.background.default,
-      paddingLeft: theme.spacing(31),
+      paddingTop: theme.spacing(2),
+      paddingLeft: theme.spacing(35),
+      paddingRight: theme.spacing(5),
     },
   })
 )
@@ -138,9 +141,7 @@ const AdminView: React.FC = () => {
         <div className={classes.toolbar} />
         <Switch>
           <Route exact path={[path, `${path}/startsida`]}>
-            <Typography variant="h1" noWrap>
-              Startsida
-            </Typography>
+            <Dashboard />
           </Route>
           <Route path={`${path}/regioner`}>
             <RegionManager />
diff --git a/client/src/pages/admin/dashboard/Dashboard.tsx b/client/src/pages/admin/dashboard/Dashboard.tsx
new file mode 100644
index 00000000..a0f569b0
--- /dev/null
+++ b/client/src/pages/admin/dashboard/Dashboard.tsx
@@ -0,0 +1,58 @@
+import { createStyles, makeStyles, Paper, Theme, Typography } from '@material-ui/core'
+import Grid from '@material-ui/core/Grid'
+import React from 'react'
+import CurrentUser from './components/CurrentUser'
+import NumberOfCompetitions from './components/NumberOfCompetitions'
+import NumberOfRegions from './components/NumberOfRegions'
+import NumberOfUsers from './components/NumberOfUsers'
+
+const useStyles = makeStyles((theme: Theme) =>
+  createStyles({
+    root: {
+      flexGrow: 1,
+    },
+    paper: {
+      padding: theme.spacing(2),
+      textAlign: 'center',
+      color: theme.palette.text.secondary,
+    },
+  })
+)
+
+const Dashboard: React.FC = () => {
+  const classes = useStyles()
+  return (
+    <div className={classes.root}>
+      <div>
+        <Grid container spacing={3}>
+          <Grid item xs={4}>
+            <Paper className={classes.paper}>
+              <CurrentUser />
+            </Paper>
+          </Grid>
+
+          <Grid item xs>
+            <Paper className={classes.paper}>
+              <Typography variant="h4">Antal Användare:</Typography>
+              <NumberOfUsers />
+            </Paper>
+          </Grid>
+          <Grid item xs>
+            <Paper className={classes.paper}>
+              <Typography variant="h4">Antal Regioner:</Typography>
+              <NumberOfRegions />
+            </Paper>
+          </Grid>
+          <Grid item xs>
+            <Paper className={classes.paper}>
+              <Typography variant="h4">Antal Tävlingar:</Typography>
+              <NumberOfCompetitions />
+            </Paper>
+          </Grid>
+        </Grid>
+      </div>
+    </div>
+  )
+}
+
+export default Dashboard
diff --git a/client/src/pages/admin/dashboard/components/CurrentUser.tsx b/client/src/pages/admin/dashboard/components/CurrentUser.tsx
new file mode 100644
index 00000000..0dfdf557
--- /dev/null
+++ b/client/src/pages/admin/dashboard/components/CurrentUser.tsx
@@ -0,0 +1,29 @@
+import { Box, Typography } from '@material-ui/core'
+import React from 'react'
+import { useAppSelector } from '../../../../hooks'
+
+const CurrentUser: React.FC = () => {
+  const currentUser = useAppSelector((state: { user: { userInfo: any } }) => state.user.userInfo)
+  return (
+    <div>
+      <Box display="flex" flexDirection="column" alignContent="flex-start">
+        <div>
+          <Typography variant="h2">
+            Välkommen{currentUser && currentUser.name ? `, ${currentUser.name}` : ''}!
+          </Typography>
+        </div>
+        <div>
+          <Typography variant="h6">Email: {currentUser && currentUser.email}</Typography>
+        </div>
+        <div>
+          <Typography variant="h6">Region: {currentUser && currentUser.city && currentUser.city.name}</Typography>
+        </div>
+        <div>
+          <Typography variant="h6">Roll: {currentUser && currentUser.role && currentUser.role.name}</Typography>
+        </div>
+      </Box>
+    </div>
+  )
+}
+
+export default CurrentUser
diff --git a/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx
new file mode 100644
index 00000000..da5d015f
--- /dev/null
+++ b/client/src/pages/admin/dashboard/components/NumberOfCompetitions.tsx
@@ -0,0 +1,33 @@
+import { Box, Typography } from '@material-ui/core'
+import React, { useEffect } from 'react'
+import { getSearchUsers } from '../../../../actions/searchUser'
+import { useAppDispatch, useAppSelector } from '../../../../hooks'
+
+const NumberOfCompetitions: React.FC = () => {
+  const cities = useAppSelector((state) => state.cities.cities)
+  const dispatch = useAppDispatch()
+
+  const handleCount = () => {
+    if (cities.length >= 1000000) {
+      ;<div>{cities.length / 1000000 + 'M'}</div>
+    } else if (cities.length >= 1000) {
+      ;<div>{cities.length / 1000 + 'K'}</div>
+    }
+    return <div>{cities.length}</div>
+  }
+
+  useEffect(() => {
+    dispatch(getSearchUsers())
+  }, [])
+  return (
+    <div>
+      <Box width="100%" height="100%">
+        <div>
+          <Typography variant="h4">{handleCount()}</Typography>
+        </div>
+      </Box>
+    </div>
+  )
+}
+
+export default NumberOfCompetitions
diff --git a/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx
new file mode 100644
index 00000000..a48b41a6
--- /dev/null
+++ b/client/src/pages/admin/dashboard/components/NumberOfRegions.tsx
@@ -0,0 +1,33 @@
+import { Box, Typography } from '@material-ui/core'
+import React, { useEffect } from 'react'
+import { getSearchUsers } from '../../../../actions/searchUser'
+import { useAppDispatch, useAppSelector } from '../../../../hooks'
+
+const NumberOfRegions: React.FC = () => {
+  const competitionTotal = useAppSelector((state) => state.competitions.total)
+  const dispatch = useAppDispatch()
+
+  const handleCount = () => {
+    if (competitionTotal >= 1000000) {
+      ;<div>{competitionTotal / 1000000 + 'M'}</div>
+    } else if (competitionTotal >= 1000) {
+      ;<div>{competitionTotal / 1000 + 'K'}</div>
+    }
+    return <div>{competitionTotal}</div>
+  }
+
+  useEffect(() => {
+    dispatch(getSearchUsers())
+  }, [])
+  return (
+    <div>
+      <Box width="100%" height="100%">
+        <div>
+          <Typography variant="h4">{handleCount()}</Typography>
+        </div>
+      </Box>
+    </div>
+  )
+}
+
+export default NumberOfRegions
diff --git a/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx
new file mode 100644
index 00000000..af0f9767
--- /dev/null
+++ b/client/src/pages/admin/dashboard/components/NumberOfUsers.tsx
@@ -0,0 +1,33 @@
+import { Box, Typography } from '@material-ui/core'
+import React, { useEffect } from 'react'
+import { getSearchUsers } from '../../../../actions/searchUser'
+import { useAppDispatch, useAppSelector } from '../../../../hooks'
+
+const NumberOfUsers: React.FC = () => {
+  const usersTotal = useAppSelector((state) => state.searchUsers.total)
+  const dispatch = useAppDispatch()
+
+  const handleCount = () => {
+    if (usersTotal >= 1000000) {
+      ;<div>{usersTotal / 1000000 + 'M'}</div>
+    } else if (usersTotal >= 1000) {
+      ;<div>{usersTotal / 1000 + 'K'}</div>
+    }
+    return <div>{usersTotal}</div>
+  }
+
+  useEffect(() => {
+    dispatch(getSearchUsers())
+  }, [])
+  return (
+    <div>
+      <Box width="100%" height="100%">
+        <div>
+          <Typography variant="h4">{handleCount()}</Typography>
+        </div>
+      </Box>
+    </div>
+  )
+}
+
+export default NumberOfUsers
-- 
GitLab