From bacb73667a7c2bbe7dc8e3a2eac8a48b4663d97c Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Mon, 8 May 2023 13:15:26 +0300 Subject: [PATCH] feat: add UI to variant metrics (#3697) --- .../groups/GroupsList/GroupCard/GroupCard.tsx | 1 - .../archive/ArchiveTable/ArchiveTable.tsx | 2 - .../ArchivedFeatureDeleteConfirm.tsx | 4 +- .../Changes/Change/ChangeActions.tsx | 4 +- .../ChangeRequestTitleCell.tsx | 1 - .../FeatureMetricsChart/createChartData.ts | 14 ++++--- ...ChartOptions.ts => createChartOptions.tsx} | 42 +++++++++++++++++-- frontend/src/interfaces/featureToggle.ts | 1 + 8 files changed, 50 insertions(+), 19 deletions(-) rename frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/{createChartOptions.ts => createChartOptions.tsx} (67%) diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx index cd9d0acee61..25ec159fa61 100644 --- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx +++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCard.tsx @@ -7,7 +7,6 @@ import { Badge } from 'component/common/Badge/Badge'; import { GroupCardActions } from './GroupCardActions/GroupCardActions'; import TopicOutlinedIcon from '@mui/icons-material/TopicOutlined'; import { IProjectRole } from 'interfaces/role'; -import { IProject } from 'interfaces/project'; const StyledLink = styled(Link)(({ theme }) => ({ textDecoration: 'none', diff --git a/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx b/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx index 6aede2799fa..8bf0e757f6b 100644 --- a/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx +++ b/frontend/src/component/archive/ArchiveTable/ArchiveTable.tsx @@ -34,7 +34,6 @@ import { ArchivedFeatureDeleteConfirm } from './ArchivedFeatureActionCell/Archiv import { IFeatureToggle } from 'interfaces/featureToggle'; import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColumns'; import { RowSelectCell } from '../../project/Project/ProjectFeatureToggles/RowSelectCell/RowSelectCell'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { BatchSelectionActionsBar } from '../../common/BatchSelectionActionsBar/BatchSelectionActionsBar'; import { ArchiveBatchActions } from './ArchiveBatchActions'; @@ -64,7 +63,6 @@ export const ArchiveTable = ({ const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg')); const { setToastData, setToastApiError } = useToast(); - const { uiConfig } = useUiConfig(); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [deletedFeature, setDeletedFeature] = useState(); diff --git a/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx b/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx index 8fb50babe25..f4b032ea924 100644 --- a/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx +++ b/frontend/src/component/archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureDeleteConfirm/ArchivedFeatureDeleteConfirm.tsx @@ -5,7 +5,6 @@ import Input from 'component/common/Input/Input'; import { formatUnknownError } from 'utils/formatUnknownError'; import useToast from 'hooks/useToast'; import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; interface IArchivedFeatureDeleteConfirmProps { deletedFeatures: string[]; @@ -35,8 +34,7 @@ export const ArchivedFeatureDeleteConfirm = ({ }: IArchivedFeatureDeleteConfirmProps) => { const [confirmName, setConfirmName] = useState(''); const { setToastData, setToastApiError } = useToast(); - const { deleteFeature, deleteFeatures } = useProjectApi(); - const { uiConfig } = useUiConfig(); + const { deleteFeatures } = useProjectApi(); const onDeleteFeatureToggle = async () => { try { diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ChangeActions.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ChangeActions.tsx index 6d6e3b7bd70..1b6d4136f46 100644 --- a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ChangeActions.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ChangeActions.tsx @@ -13,9 +13,7 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; import { changesCount } from '../../../changesCount'; import { - Box, IconButton, - Link, ListItemIcon, ListItemText, MenuItem, @@ -25,7 +23,7 @@ import { Tooltip, Typography, } from '@mui/material'; -import { Delete, Edit, GroupRounded, MoreVert } from '@mui/icons-material'; +import { Delete, Edit, MoreVert } from '@mui/icons-material'; import { EditChange } from './EditChange'; const useShowActions = (changeRequest: IChangeRequest, change: IChange) => { diff --git a/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestTitleCell.tsx b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestTitleCell.tsx index c915650f0f1..0cdd35484dc 100644 --- a/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestTitleCell.tsx +++ b/frontend/src/component/changeRequest/ProjectChangeRequests/ChangeRequestsTabs/ChangeRequestTitleCell.tsx @@ -1,7 +1,6 @@ import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { Link, styled, Typography } from '@mui/material'; import { Link as RouterLink } from 'react-router-dom'; -import { useTheme } from '@mui/system'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; interface IChangeRequestTitleCellProps { diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts index feee76a5100..3988f073306 100644 --- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts +++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts @@ -4,9 +4,10 @@ import { ILocationSettings } from 'hooks/useLocationSettings'; import 'chartjs-adapter-date-fns'; import { Theme } from '@mui/material/styles/createTheme'; -interface IPoint { +export interface IPoint { x: string; y: number; + variants: Record; } export const createChartData = ( @@ -15,7 +16,7 @@ export const createChartData = ( locationSettings: ILocationSettings ): ChartData<'line', IPoint[], string> => { const requestsSeries = { - label: 'total requests', + label: 'Total requests', borderColor: theme.palette.primary.main, backgroundColor: theme.palette.primary.main, data: createChartPoints(metrics, locationSettings, m => m.yes + m.no), @@ -31,7 +32,7 @@ export const createChartData = ( }; const yesSeries = { - label: 'exposed', + label: 'Exposed', borderColor: theme.palette.success.main, backgroundColor: theme.palette.success.main, data: createChartPoints(metrics, locationSettings, m => m.yes), @@ -44,7 +45,7 @@ export const createChartData = ( }; const noSeries = { - label: 'not exposed', + label: 'Not exposed', borderColor: theme.palette.error.main, backgroundColor: theme.palette.error.main, data: createChartPoints(metrics, locationSettings, m => m.no), @@ -57,7 +58,9 @@ export const createChartData = ( }, }; - return { datasets: [yesSeries, noSeries, requestsSeries] }; + return { + datasets: [yesSeries, noSeries, requestsSeries], + }; }; const createChartPoints = ( @@ -68,5 +71,6 @@ const createChartPoints = ( return metrics.map(metric => ({ x: metric.timestamp, y: y(metric), + variants: metric.variants, })); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.tsx similarity index 67% rename from frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts rename to frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.tsx index 29eb1e43348..2838bb230af 100644 --- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts +++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.tsx @@ -4,6 +4,17 @@ import { ChartOptions, defaults } from 'chart.js'; import { IFeatureMetricsRaw } from 'interfaces/featureToggle'; import { formatDateHM } from 'utils/formatDate'; import { Theme } from '@mui/material/styles/createTheme'; +import { IPoint } from './createChartData'; + +const formatVariantEntry = ( + variant: [string, number], + totalExposure: number +) => { + if (totalExposure === 0) return ''; + const [key, value] = variant; + const percentage = Math.floor((Number(value) / totalExposure) * 100); + return `${value} (${percentage}%) - ${key}`; +}; export const createChartOptions = ( theme: Theme, @@ -30,15 +41,38 @@ export const createChartOptions = ( padding: 10, boxPadding: 5, usePointStyle: true, + itemSort: (a, b) => { + const order = ['Total requests', 'Exposed', 'Not exposed']; + const aIndex = order.indexOf(a.dataset.label!); + const bIndex = order.indexOf(b.dataset.label!); + return aIndex - bIndex; + }, callbacks: { + label: item => { + return `${item.formattedValue} - ${item.dataset.label}`; + }, + afterLabel: item => { + const data = item.dataset.data[ + item.dataIndex + ] as unknown as IPoint; + + if ( + item.dataset.label !== 'Exposed' || + data.variants === undefined + ) { + return ''; + } + const { disabled, ...actualVariants } = data.variants; + return Object.entries(actualVariants) + .map(entry => formatVariantEntry(entry, data.y)) + .join('\n'); + }, title: items => - formatDateHM( + `Time: ${formatDateHM( items[0].parsed.x, locationSettings.locale - ), + )}`, }, - // Sort tooltip items in the same order as the lines in the chart. - itemSort: (a, b) => b.parsed.y - a.parsed.y, }, legend: { position: 'top', diff --git a/frontend/src/interfaces/featureToggle.ts b/frontend/src/interfaces/featureToggle.ts index b205ba89816..affb40cfbda 100644 --- a/frontend/src/interfaces/featureToggle.ts +++ b/frontend/src/interfaces/featureToggle.ts @@ -88,4 +88,5 @@ export interface IFeatureMetricsRaw { timestamp: string; yes: number; no: number; + variants: Record; }