From f22387ecd60142f0a11a46b37026d66e0c2a4d9d Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:19:32 +0100 Subject: [PATCH] active users tooltip --- .../executiveDashboard/ExecutiveDashboard.tsx | 25 ++- .../UsersChart/UsersChartComponent.tsx | 144 +++++++++++++++--- 2 files changed, 144 insertions(+), 25 deletions(-) diff --git a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx index 1576b1ebe93f..f623492e07cf 100644 --- a/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx +++ b/frontend/src/component/executiveDashboard/ExecutiveDashboard.tsx @@ -1,10 +1,33 @@ +import { Box, Paper, styled, Typography } from '@mui/material'; +import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { VFC } from 'react'; import { UsersChart } from './UsersChart/UsersChart'; +const StyledGrid = styled(Box)(({ theme }) => ({ + display: 'grid', + gridTemplateColumns: `repeat(auto-fill, minmax(600px, 1fr))`, + gridAutoRows: '1fr', + gap: theme.spacing(2), +})); + export const ExecutiveDashboard: VFC = () => { return ( <> - + ({ paddingBottom: theme.spacing(4) })}> + + Dashboard + + } + // subtitle='Succesfully synchronized: 01 Sep 2023 - 07:05:07' + /> + + {/* Dashboard */} + + Stats + + ); }; diff --git a/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx b/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx index 1ee325e9172f..c006bfe516bb 100644 --- a/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx +++ b/frontend/src/component/executiveDashboard/UsersChart/UsersChartComponent.tsx @@ -8,58 +8,153 @@ import { Title, Tooltip, Legend, + TimeScale, } from 'chart.js'; import { Line } from 'react-chartjs-2'; +import 'chartjs-adapter-date-fns'; import faker from 'faker'; import { Paper, Theme, useTheme } from '@mui/material'; import { useLocationSettings, type ILocationSettings, } from 'hooks/useLocationSettings'; +import { formatDateYMD } from 'utils/formatDate'; -const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; +type Data = { + date: string | Date; + total?: number; + active?: number; + inactive?: number; +}[]; + +const now = new Date(); +const yearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate()); +const mockStart = new Date( + yearAgo.getFullYear(), + yearAgo.getMonth() + 3, + yearAgo.getDate(), +); +const mockLabels = Array.from({ length: 52 }, (_, i) => { + const date = new Date(yearAgo.getTime() + i * 7 * 24 * 60 * 60 * 1000); + + return date.toISOString(); +}); + +const mockData: Data = mockLabels.reduce((prev, curr) => { + const date = new Date(curr); + const key = date.toISOString().slice(0, 10); + const lastValues = prev[prev.length - 1]; + + if (date < mockStart) { + return [...prev, { date: key }]; + } + + if (lastValues.total === undefined) { + return [ + ...prev, + { + date: key, + total: faker.datatype.number({ min: 15, max: 50 }), + active: 0, + inactive: 0, + }, + ]; + } + + const total = + lastValues.total + faker.datatype.number({ min: -10, max: 20 }); + + const inactive = + date < new Date(mockStart.getTime() + 2 * 30 * 24 * 60 * 60 * 1000) + ? 0 + : Math.max( + 0, + (lastValues.inactive || 0) + + faker.datatype.number({ min: -3, max: 5 }), + ); + + const active = total - inactive; + + return [...prev, { date: key, total, active, inactive }]; +}, [] as Data); const createData = (theme: Theme) => ({ - labels, + labels: mockData.map((item) => item.date), datasets: [ { label: 'Active users', - data: labels.map(() => - faker.datatype.number({ min: 150, max: 200 }), - ), + data: mockData.map((item) => item.total), borderColor: theme.palette.primary.main, backgroundColor: theme.palette.primary.main, fill: true, }, { label: 'Inactive users', - data: labels.map(() => faker.datatype.number({ min: 10, max: 50 })), + data: mockData.map((item) => item.inactive), borderColor: theme.palette.error.main, backgroundColor: theme.palette.error.main, fill: true, }, + { + label: 'Active users', + data: mockData.map((item) => item.active), + borderColor: theme.palette.success.main, + backgroundColor: theme.palette.success.main, + fill: true, + }, ], }); -const createOptions = (theme: Theme, locationSettings: ILocationSettings) => ({ - responsive: true, - plugins: { - legend: { - position: 'bottom' as const, +const createOptions = (theme: Theme, locationSettings: ILocationSettings) => + ({ + responsive: true, + plugins: { + legend: { + position: 'bottom', + }, + tooltip: { + callbacks: { + title: (tooltipItems: any) => { + const item = tooltipItems?.[0]; + const date = + item?.chart?.data?.labels?.[item.dataIndex]; + return date + ? formatDateYMD(date, locationSettings.locale) + : ''; + }, + }, + }, }, - // title: { - // display: true, - // text: 'Chart.js Line Chart', - // }, - }, - locale: locationSettings.locale, - // maintainAspectRatio: false, - // interaction: { - // mode: 'index', - // intersect: false, - // }, - color: theme.palette.text.secondary, -}); + locale: locationSettings.locale, + interaction: { + intersect: false, + axis: 'x', + }, + color: theme.palette.text.secondary, + scales: { + y: { + type: 'linear', + grid: { + color: theme.palette.divider, + borderColor: theme.palette.divider, + }, + ticks: { color: theme.palette.text.secondary }, + }, + x: { + type: 'time', + time: { + unit: 'month', + }, + grid: { + color: theme.palette.divider, + borderColor: theme.palette.divider, + }, + ticks: { + color: theme.palette.text.secondary, + }, + }, + }, + }) as const; const UsersChartComponent: VFC = () => { const theme = useTheme(); @@ -80,6 +175,7 @@ ChartJS.register( LinearScale, PointElement, LineElement, + TimeScale, Title, Tooltip, Legend,