mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: licensed users chart (#8844)
Currently showing 2 lines, because backend is not sorting the data. 
This commit is contained in:
		
							parent
							
								
									f985cb1deb
								
							
						
					
					
						commit
						9a269e3597
					
				| @ -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<ILicensedUsersChartProps> = ({ | ||||
|     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 ( | ||||
|         <LineChart | ||||
|             data={data} | ||||
|             overrideOptions={{ | ||||
|                 parsing: { | ||||
|                     yAxisKey: 'count', | ||||
|                     xAxisKey: 'date', | ||||
|                 }, | ||||
|                 plugins: { | ||||
|                     legend: { | ||||
|                         display: false, | ||||
|                     }, | ||||
|                 }, | ||||
|                 scales: { | ||||
|                     x: { | ||||
|                         time: { | ||||
|                             unit: 'month', | ||||
|                             tooltipFormat: 'MMM yyyy', | ||||
|                             displayFormats: { | ||||
|                                 month: 'MMM', | ||||
|                             }, | ||||
|                         }, | ||||
|                     }, | ||||
|                 }, | ||||
|             }} | ||||
|         /> | ||||
|     ); | ||||
| }; | ||||
| @ -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 ( | ||||
|         <DynamicSidebarModal | ||||
|             open={open} | ||||
| @ -94,7 +97,10 @@ export const LicensedUsersSidebar = ({ | ||||
|                         <RowHeader>Last 30 days</RowHeader> | ||||
|                         <InfoRow> | ||||
|                             <LicenceBox> | ||||
|                                 <Typography fontWeight='bold'>11/25</Typography> | ||||
|                                 <Typography fontWeight='bold'> | ||||
|                                     {data.licensedUsers.current}/ | ||||
|                                     {data.seatCount} | ||||
|                                 </Typography> | ||||
|                                 <Typography variant='body2'> | ||||
|                                     Used seats last 30 days | ||||
|                                 </Typography> | ||||
| @ -108,7 +114,9 @@ export const LicensedUsersSidebar = ({ | ||||
|                     </Row> | ||||
|                     <Row> | ||||
|                         <RowHeader>Last year</RowHeader> | ||||
|                         <div>this will be great grid</div> | ||||
|                         <LicensedUsersChart | ||||
|                             licensedUsers={data.licensedUsers.history} | ||||
|                         /> | ||||
|                     </Row> | ||||
|                 </WidgetContainer> | ||||
|                 <CloseRow> | ||||
|  | ||||
| @ -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<T>(objects: Partial<T>[]): T { | ||||
|     return merge.all<T>(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 | TooltipState>(null); | ||||
| 
 | ||||
|     const options = useMemo( | ||||
|         () => ({ | ||||
|             ...createOptions( | ||||
|                 theme, | ||||
|                 locationSettings, | ||||
|                 setTooltip, | ||||
|                 Boolean(cover), | ||||
|             ), | ||||
|             ...overrideOptions, | ||||
|         }), | ||||
|         () => | ||||
|             mergeAll([ | ||||
|                 createOptions( | ||||
|                     theme, | ||||
|                     locationSettings, | ||||
|                     setTooltip, | ||||
|                     Boolean(cover), | ||||
|                 ), | ||||
|                 overrideOptions ?? {}, | ||||
|             ]), | ||||
|         [theme, locationSettings, overrideOptions, cover], | ||||
|     ); | ||||
| 
 | ||||
|  | ||||
| @ -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<React.SetStateAction<TooltipState | null>>, | ||||
|     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: { | ||||
|  | ||||
							
								
								
									
										22
									
								
								frontend/src/hooks/useLicensedUsers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								frontend/src/hooks/useLicensedUsers.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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<LicensedUsersSchema>( | ||||
|         formatApiPath(path), | ||||
|         () => fetcher(formatApiPath(path), 'Licensed users'), | ||||
|     ); | ||||
| 
 | ||||
|     return { data: data || placeholderData, refetch, loading, error }; | ||||
| }; | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user