diff --git a/frontend/src/component/admin/users/UsersHeader/LicensedUsersChart.tsx b/frontend/src/component/admin/users/UsersHeader/LicensedUsersChart.tsx new file mode 100644 index 0000000000..378f5f08ba --- /dev/null +++ b/frontend/src/component/admin/users/UsersHeader/LicensedUsersChart.tsx @@ -0,0 +1,54 @@ +import type { FC } from 'react'; +import 'chartjs-adapter-date-fns'; +import type { LicensedUsersSchema } from 'openapi'; +import { LineChart } from 'component/insights/components/LineChart/LineChart'; +import { useTheme } from '@mui/material'; + +interface ILicensedUsersChartProps { + licensedUsers: LicensedUsersSchema['licensedUsers']['history']; +} + +export const LicensedUsersChart: FC = ({ + licensedUsers, +}) => { + const theme = useTheme(); + + const data = { + datasets: [ + { + label: 'Licensed users', + data: licensedUsers, + borderColor: theme.palette.primary.main, + backgroundColor: theme.palette.primary.main, + fill: false, + }, + ], + }; + return ( + + ); +}; diff --git a/frontend/src/component/admin/users/UsersHeader/LicensedUsersSidebar.tsx b/frontend/src/component/admin/users/UsersHeader/LicensedUsersSidebar.tsx index c87a0bf4bb..82a1f61f77 100644 --- a/frontend/src/component/admin/users/UsersHeader/LicensedUsersSidebar.tsx +++ b/frontend/src/component/admin/users/UsersHeader/LicensedUsersSidebar.tsx @@ -1,6 +1,8 @@ import { Alert, Button, styled, Typography } from '@mui/material'; import { DynamicSidebarModal } from 'component/common/SidebarModal/SidebarModal'; import type React from 'react'; +import { LicensedUsersChart } from './LicensedUsersChart'; +import { useLicensedUsers } from 'hooks/useLicensedUsers'; const ModalContentContainer = styled('section')(({ theme }) => ({ minHeight: '100vh', maxWidth: 700, @@ -74,6 +76,7 @@ export const LicensedUsersSidebar = ({ open, close, }: LicensedUsersSidebarProps) => { + const { data } = useLicensedUsers(); return ( Last 30 days - 11/25 + + {data.licensedUsers.current}/ + {data.seatCount} + Used seats last 30 days @@ -108,7 +114,9 @@ export const LicensedUsersSidebar = ({ Last year -
this will be great grid
+
diff --git a/frontend/src/component/insights/components/LineChart/LineChartComponent.tsx b/frontend/src/component/insights/components/LineChart/LineChartComponent.tsx index bf0d46090b..b5508b2ae5 100644 --- a/frontend/src/component/insights/components/LineChart/LineChartComponent.tsx +++ b/frontend/src/component/insights/components/LineChart/LineChartComponent.tsx @@ -24,6 +24,7 @@ import { import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { styled } from '@mui/material'; import { createOptions } from './createChartOptions'; +import merge from 'deepmerge'; const StyledContainer = styled('div')(({ theme }) => ({ position: 'relative', @@ -81,6 +82,10 @@ const customHighlightPlugin = { }, }; +function mergeAll(objects: Partial[]): T { + return merge.all(objects.filter((i) => i)); +} + const LineChartComponent: FC<{ data: ChartData<'line', unknown>; aspectRatio?: number; @@ -100,16 +105,18 @@ const LineChartComponent: FC<{ const { locationSettings } = useLocationSettings(); const [tooltip, setTooltip] = useState(null); + const options = useMemo( - () => ({ - ...createOptions( - theme, - locationSettings, - setTooltip, - Boolean(cover), - ), - ...overrideOptions, - }), + () => + mergeAll([ + createOptions( + theme, + locationSettings, + setTooltip, + Boolean(cover), + ), + overrideOptions ?? {}, + ]), [theme, locationSettings, overrideOptions, cover], ); diff --git a/frontend/src/component/insights/components/LineChart/createChartOptions.ts b/frontend/src/component/insights/components/LineChart/createChartOptions.ts index 6cc51ce033..3340fd0793 100644 --- a/frontend/src/component/insights/components/LineChart/createChartOptions.ts +++ b/frontend/src/component/insights/components/LineChart/createChartOptions.ts @@ -3,13 +3,14 @@ import type { ILocationSettings } from 'hooks/useLocationSettings'; import type { TooltipState } from './ChartTooltip/ChartTooltip'; import { createTooltip } from './createTooltip'; import { legendOptions } from './legendOptions'; +import type { ChartOptions } from 'chart.js'; export const createOptions = ( theme: Theme, locationSettings: ILocationSettings, setTooltip: React.Dispatch>, isPlaceholder?: boolean, -) => +): ChartOptions<'line'> => ({ responsive: true, ...(isPlaceholder @@ -27,10 +28,6 @@ export const createOptions = ( tooltip: { enabled: false, position: 'nearest', - interaction: { - axis: 'xy', - mode: 'nearest', - }, external: createTooltip(setTooltip), }, }, @@ -46,8 +43,6 @@ export const createOptions = ( hitRadius: 15, }, }, - // cubicInterpolationMode: 'monotone', - tension: 0.1, color: theme.palette.text.secondary, scales: { y: { diff --git a/frontend/src/hooks/useLicensedUsers.ts b/frontend/src/hooks/useLicensedUsers.ts new file mode 100644 index 0000000000..45f2411fcc --- /dev/null +++ b/frontend/src/hooks/useLicensedUsers.ts @@ -0,0 +1,22 @@ +import type { LicensedUsersSchema } from '../openapi'; +import { useApiGetter, fetcher } from './api/getters/useApiGetter/useApiGetter'; +import { formatApiPath } from '../utils/formatPath'; + +const path = `api/admin/licensed-users`; + +const placeholderData: LicensedUsersSchema = { + seatCount: 0, + licensedUsers: { + current: 0, + history: [], + }, +}; + +export const useLicensedUsers = () => { + const { data, refetch, loading, error } = useApiGetter( + formatApiPath(path), + () => fetcher(formatApiPath(path), 'Licensed users'), + ); + + return { data: data || placeholderData, refetch, loading, error }; +};