From 64c10f9effd51b45e528e90b84fb67104e528a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivar=20Conradi=20=C3=98sthus?= Date: Wed, 8 May 2024 14:20:51 +0200 Subject: [PATCH] poc: many strategies pagination (#7011) This fixes the case when a customer have thousands of strategies causing the react UI to crash. We still consider it incorrect to use that amount of strategies and this is more a workaround to help the customer out of a crashing state. We put it behind a flag called `manyStrategiesPagination` and plan to only enable it for the customer in trouble. --- .../EnvironmentAccordionBody.tsx | 97 ++++++++++--- .../StrategyNonDraggableItem.tsx | 131 ++++++++++++++++++ frontend/src/hooks/usePagination.ts | 2 +- frontend/src/hooks/usePlausibleTracker.ts | 1 + frontend/src/interfaces/uiConfig.ts | 1 + .../__snapshots__/create-config.test.ts.snap | 1 + src/lib/types/experimental.ts | 5 + src/server-dev.ts | 1 + 8 files changed, 222 insertions(+), 17 deletions(-) create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyNonDraggableItem.tsx diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx index 11ef8e68bb..5f1b33c5ef 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx @@ -4,7 +4,7 @@ import { useEffect, useState, } from 'react'; -import { Alert, styled } from '@mui/material'; +import { Alert, Pagination, styled } from '@mui/material'; import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; import { formatUnknownError } from 'utils/formatUnknownError'; import useToast from 'hooks/useToast'; @@ -17,6 +17,11 @@ import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests'; +import usePagination from 'hooks/usePagination'; +import type { IFeatureStrategy } from 'interfaces/strategy'; +import { StrategyNonDraggableItem } from './StrategyDraggableItem/StrategyNonDraggableItem'; +import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import { useUiFlag } from 'hooks/useUiFlag'; interface IEnvironmentAccordionBodyProps { isDisabled: boolean; @@ -50,9 +55,12 @@ const EnvironmentAccordionBody = ({ usePendingChangeRequests(projectId); const { setToastData, setToastApiError } = useToast(); const { refetchFeature } = useFeature(projectId, featureId); + const manyStrategiesPagination = useUiFlag('manyStrategiesPagination'); const [strategies, setStrategies] = useState( featureEnvironment?.strategies || [], ); + const { trackEvent } = usePlausibleTracker(); + const [dragItem, setDragItem] = useState<{ id: string; index: number; @@ -63,10 +71,20 @@ const EnvironmentAccordionBody = ({ setStrategies(featureEnvironment?.strategies || []); }, [featureEnvironment?.strategies]); + useEffect(() => { + if (strategies.length > 50) { + trackEvent('many-strategies'); + } + }, []); + if (!featureEnvironment) { return null; } + const pageSize = 20; + const { page, pages, setPageIndex, pageIndex } = + usePagination(strategies, pageSize); + const onReorder = async (payload: { id: string; sortOrder: number }[]) => { try { await setStrategiesSortOrder( @@ -195,21 +213,68 @@ const EnvironmentAccordionBody = ({ 0} show={ - <> - {strategies.map((strategy, index) => ( - - ))} - + + {strategies.map((strategy, index) => ( + + ))} + + } + elseShow={ + <> + + We noticed you're using a high number of + activation strategies. To ensure a more + targeted approach, consider leveraging + constraints or segments. + +
+ {page.map((strategy, index) => ( + + ))} +
+ + setPageIndex(page - 1) + } + /> + + } + /> } elseShow={ { + const projectId = useRequiredPathParam('projectId'); + const featureId = useRequiredPathParam('featureId'); + const ref = useRef(null); + const strategyChangesFromRequest = useStrategyChangesFromRequest( + projectId, + featureId, + environmentName, + strategy.id, + ); + + const { changeRequests: scheduledChangesUsingStrategy } = + useScheduledChangeRequestsWithStrategy(projectId, strategy.id); + + return ( + + 0} + show={} + /> + + + + ); +}; + +const ChangeRequestStatusBadge = ({ + change, +}: { + change: IFeatureChange | undefined; +}) => { + const theme = useTheme(); + const isSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); + + if (isSmallScreen) { + return null; + } + + return ( + + Modified in draft} + /> + Deleted in draft} + /> + + ); +}; + +const renderHeaderChildren = ( + changes?: UseStrategyChangeFromRequestResult, + scheduledChanges?: ScheduledChangeRequestViewModel[], +): JSX.Element[] => { + const badges: JSX.Element[] = []; + if (changes?.length === 0 && scheduledChanges?.length === 0) { + return []; + } + + const draftChange = changes?.find( + ({ isScheduledChange }) => !isScheduledChange, + ); + + if (draftChange) { + badges.push( + , + ); + } + + if (scheduledChanges && scheduledChanges.length > 0) { + badges.push( + scheduledChange.id, + )} + />, + ); + } + + return badges; +}; diff --git a/frontend/src/hooks/usePagination.ts b/frontend/src/hooks/usePagination.ts index 26eb244377..0c68b1a300 100644 --- a/frontend/src/hooks/usePagination.ts +++ b/frontend/src/hooks/usePagination.ts @@ -20,7 +20,7 @@ const usePagination = ( } const result = paginate(dataToPaginate, limit); - setPageIndex(0); + // setPageIndex(0); setPaginatedData(result); /* eslint-disable-next-line */ }, [JSON.stringify(data), limit]); diff --git a/frontend/src/hooks/usePlausibleTracker.ts b/frontend/src/hooks/usePlausibleTracker.ts index 7bfd81c432..437907defd 100644 --- a/frontend/src/hooks/usePlausibleTracker.ts +++ b/frontend/src/hooks/usePlausibleTracker.ts @@ -59,6 +59,7 @@ export type CustomEvents = | 'search-bar' | 'sdk-reporting' | 'insights-share' + | 'many-strategies' | 'sdk-banner'; export const usePlausibleTracker = () => { diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index 0b7b0d6bd3..a4e1ab3957 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -84,6 +84,7 @@ export type UiFlags = { createProjectWithEnvironmentConfig?: boolean; projectsListNewCards?: boolean; newCreateProjectUI?: boolean; + manyStrategiesPagination?: boolean; }; export interface IVersionInfo { diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 9ee96f098d..1418f441c5 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -122,6 +122,7 @@ exports[`should create default config 1`] = ` "googleAuthEnabled": false, "killScheduledChangeRequestCache": false, "maintenanceMode": false, + "manyStrategiesPagination": false, "messageBanner": { "enabled": false, "name": "message-banner", diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index c203845baa..d9eec1c785 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -59,6 +59,7 @@ export type IFlagKey = | 'projectsListNewCards' | 'parseProjectFromSession' | 'createProjectWithEnvironmentConfig' + | 'manyStrategiesPagination' | 'newCreateProjectUI'; export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; @@ -284,6 +285,10 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_NEW_CREATE_PROJECT_UI, false, ), + manyStrategiesPagination: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_MANY_STRATEGIES_PAGINATION, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { diff --git a/src/server-dev.ts b/src/server-dev.ts index 58f781649d..dac91205ad 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -54,6 +54,7 @@ process.nextTick(async () => { projectsListNewCards: true, parseProjectFromSession: true, createProjectWithEnvironmentConfig: true, + manyStrategiesPagination: true, }, }, authentication: {