mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Ensures that the first date of the data is also shown. Before: <img width="1144" height="441" alt="image" src="https://github.com/user-attachments/assets/33da968a-4f44-4ca9-825e-d19471cff00a" /> After: <img width="1132" height="435" alt="image" src="https://github.com/user-attachments/assets/65d009d4-5bcb-40d0-abb6-05be81c9a361" />
		
			
				
	
	
		
			211 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import 'chartjs-adapter-date-fns';
 | |
| import { type FC, useMemo, useState } from 'react';
 | |
| import type { InstanceInsightsSchema } from 'openapi';
 | |
| import { useProjectChartData } from 'component/insights/hooks/useProjectChartData';
 | |
| import { useTheme } from '@mui/material';
 | |
| import type { GroupedDataByProject } from 'component/insights/hooks/useGroupedProjectTrends';
 | |
| import { usePlaceholderData } from 'component/insights/hooks/usePlaceholderData';
 | |
| import {
 | |
|     CategoryScale,
 | |
|     LinearScale,
 | |
|     PointElement,
 | |
|     LineElement,
 | |
|     BarElement,
 | |
|     Tooltip,
 | |
|     Legend,
 | |
|     TimeScale,
 | |
|     Chart as ChartJS,
 | |
|     Filler,
 | |
| } from 'chart.js';
 | |
| import { useLocationSettings } from 'hooks/useLocationSettings';
 | |
| import type { TooltipState } from 'component/insights/components/LineChart/ChartTooltip/ChartTooltip';
 | |
| import type { WeekData, RawWeekData } from './types.ts';
 | |
| import { createTooltip } from 'component/insights/components/LineChart/createTooltip.ts';
 | |
| import { CreationArchiveRatioTooltip } from './CreationArchiveRatioTooltip.tsx';
 | |
| import { Chart } from 'react-chartjs-2';
 | |
| import { getDateFnsLocale } from '../../getDateFnsLocale.ts';
 | |
| 
 | |
| ChartJS.register(
 | |
|     CategoryScale,
 | |
|     LinearScale,
 | |
|     PointElement,
 | |
|     LineElement,
 | |
|     BarElement,
 | |
|     TimeScale,
 | |
|     Tooltip,
 | |
|     Legend,
 | |
|     Filler,
 | |
| );
 | |
| 
 | |
| interface ICreationArchiveChartProps {
 | |
|     creationArchiveTrends: GroupedDataByProject<
 | |
|         InstanceInsightsSchema['creationArchiveTrends']
 | |
|     >;
 | |
|     isLoading?: boolean;
 | |
| }
 | |
| 
 | |
| export const CreationArchiveChart: FC<ICreationArchiveChartProps> = ({
 | |
|     creationArchiveTrends,
 | |
|     isLoading,
 | |
| }) => {
 | |
|     const creationVsArchivedChart = useProjectChartData(creationArchiveTrends);
 | |
|     const theme = useTheme();
 | |
|     const placeholderData = usePlaceholderData();
 | |
|     const { locationSettings } = useLocationSettings();
 | |
|     const [tooltip, setTooltip] = useState<null | TooltipState>(null);
 | |
| 
 | |
|     const aggregateOrProjectData = useMemo(() => {
 | |
|         const labels: string[] = Array.from(
 | |
|             new Set(
 | |
|                 creationVsArchivedChart.datasets.flatMap((d) =>
 | |
|                     d.data.map((item) => item.week),
 | |
|                 ),
 | |
|             ),
 | |
|         );
 | |
| 
 | |
|         const aggregateWeekData = (acc: WeekData, item: RawWeekData) => {
 | |
|             if (item) {
 | |
|                 acc.archivedFlags += item.archivedFlags || 0;
 | |
| 
 | |
|                 if (item.createdFlags) {
 | |
|                     Object.entries(item.createdFlags).forEach(([_, count]) => {
 | |
|                         acc.totalCreatedFlags += count;
 | |
|                     });
 | |
|                 }
 | |
|             }
 | |
|             if (!acc.date) {
 | |
|                 acc.date = item?.date;
 | |
|             }
 | |
|             return acc;
 | |
|         };
 | |
| 
 | |
|         const createInitialWeekData = (label: string): WeekData => ({
 | |
|             archivedFlags: 0,
 | |
|             totalCreatedFlags: 0,
 | |
|             archivePercentage: 0,
 | |
|             week: label,
 | |
|         });
 | |
| 
 | |
|         const weeks: WeekData[] = labels
 | |
|             .map((label) => {
 | |
|                 return creationVsArchivedChart.datasets
 | |
|                     .map((d) => d.data.find((item) => item.week === label))
 | |
|                     .reduce(aggregateWeekData, createInitialWeekData(label));
 | |
|             })
 | |
|             .map((week) => ({
 | |
|                 ...week,
 | |
|                 archivePercentage:
 | |
|                     week.totalCreatedFlags > 0
 | |
|                         ? (week.archivedFlags / week.totalCreatedFlags) * 100
 | |
|                         : 0,
 | |
|             }))
 | |
|             .sort((a, b) => (a.week > b.week ? 1 : -1));
 | |
| 
 | |
|         return {
 | |
|             datasets: [
 | |
|                 {
 | |
|                     label: 'Flags archived',
 | |
|                     data: weeks,
 | |
|                     backgroundColor: theme.palette.charts.A2,
 | |
|                     borderColor: theme.palette.charts.A2,
 | |
|                     parsing: { yAxisKey: 'archivedFlags', xAxisKey: 'date' },
 | |
|                     order: 1,
 | |
|                 },
 | |
|                 {
 | |
|                     label: 'Flags created',
 | |
|                     data: weeks,
 | |
|                     backgroundColor: theme.palette.charts.A1,
 | |
|                     borderColor: theme.palette.charts.A1,
 | |
|                     parsing: {
 | |
|                         yAxisKey: 'totalCreatedFlags',
 | |
|                         xAxisKey: 'date',
 | |
|                     },
 | |
|                     order: 2,
 | |
|                 },
 | |
|             ],
 | |
|         };
 | |
|     }, [creationVsArchivedChart, theme]);
 | |
| 
 | |
|     const notEnoughData = useMemo(
 | |
|         () =>
 | |
|             !isLoading &&
 | |
|             !creationVsArchivedChart.datasets.some((d) => d.data.length > 1),
 | |
|         [creationVsArchivedChart, isLoading],
 | |
|     );
 | |
|     const data =
 | |
|         notEnoughData || isLoading ? placeholderData : aggregateOrProjectData;
 | |
| 
 | |
|     const options = useMemo(
 | |
|         () => ({
 | |
|             responsive: true,
 | |
|             interaction: {
 | |
|                 mode: 'index' as const,
 | |
|                 intersect: false,
 | |
|             },
 | |
|             datasets: { bar: { borderRadius: 4 } },
 | |
|             plugins: {
 | |
|                 legend: {
 | |
|                     position: 'bottom' as const,
 | |
|                     labels: {
 | |
|                         color: theme.palette.text.secondary,
 | |
|                         usePointStyle: true,
 | |
|                         padding: 21,
 | |
|                         boxHeight: 8,
 | |
|                     },
 | |
|                 },
 | |
|                 tooltip: {
 | |
|                     enabled: false,
 | |
|                     position: 'average' as const,
 | |
|                     external: createTooltip(setTooltip),
 | |
|                 },
 | |
|             },
 | |
|             locale: locationSettings.locale,
 | |
|             scales: {
 | |
|                 x: {
 | |
|                     adapters: {
 | |
|                         date: {
 | |
|                             locale: getDateFnsLocale(locationSettings.locale),
 | |
|                         },
 | |
|                     },
 | |
|                     type: 'time' as const,
 | |
|                     display: true,
 | |
|                     time: {
 | |
|                         unit: 'week' as const,
 | |
|                         tooltipFormat: 'P',
 | |
|                     },
 | |
|                     grid: {
 | |
|                         display: false,
 | |
|                     },
 | |
|                     ticks: { source: 'data' },
 | |
|                 },
 | |
|                 y: {
 | |
|                     type: 'linear' as const,
 | |
|                     position: 'left' as const,
 | |
|                     beginAtZero: true,
 | |
|                     title: {
 | |
|                         display: true,
 | |
|                         text: 'Number of flags',
 | |
|                     },
 | |
|                     ticks: {
 | |
|                         stepSize: 1,
 | |
|                     },
 | |
|                 },
 | |
|             },
 | |
|         }),
 | |
|         [theme, locationSettings, setTooltip],
 | |
|     );
 | |
| 
 | |
|     return (
 | |
|         <>
 | |
|             <Chart
 | |
|                 type='bar'
 | |
|                 data={data}
 | |
|                 options={options}
 | |
|                 height={100}
 | |
|                 width={250}
 | |
|             />
 | |
|             <CreationArchiveRatioTooltip tooltip={tooltip} />
 | |
|         </>
 | |
|     );
 | |
| };
 |