diff --git a/frontend/src/component/segments/CreateSegmentButton/CreateSegmentButton.tsx b/frontend/src/component/segments/CreateSegmentButton/CreateSegmentButton.tsx index 7ca62fe951bb..74711b6a7880 100644 --- a/frontend/src/component/segments/CreateSegmentButton/CreateSegmentButton.tsx +++ b/frontend/src/component/segments/CreateSegmentButton/CreateSegmentButton.tsx @@ -6,8 +6,12 @@ import PermissionButton from 'component/common/PermissionButton/PermissionButton import { NAVIGATE_TO_CREATE_SEGMENT } from 'utils/testIds'; import { useNavigate } from 'react-router-dom'; import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; +import type { FC } from 'react'; -export const CreateSegmentButton = () => { +export const CreateSegmentButton: FC<{ + disabled: boolean; + tooltip?: string; +}> = ({ disabled, tooltip }) => { const projectId = useOptionalPathParam('projectId'); const navigate = useNavigate(); @@ -22,6 +26,10 @@ export const CreateSegmentButton = () => { }} permission={[CREATE_SEGMENT, UPDATE_PROJECT_SEGMENT]} projectId={projectId} + disabled={disabled} + tooltipProps={{ + title: tooltip, + }} data-testid={NAVIGATE_TO_CREATE_SEGMENT} > New segment diff --git a/frontend/src/component/segments/SegmentTable/SegmentTable.test.tsx b/frontend/src/component/segments/SegmentTable/SegmentTable.test.tsx index 8c4d4ba65be6..87b66b05d8a2 100644 --- a/frontend/src/component/segments/SegmentTable/SegmentTable.test.tsx +++ b/frontend/src/component/segments/SegmentTable/SegmentTable.test.tsx @@ -2,6 +2,7 @@ import { render } from 'utils/testRenderer'; import { screen } from '@testing-library/react'; import { SegmentTable } from './SegmentTable'; import { testServerRoute, testServerSetup } from 'utils/testServer'; +import { CREATE_SEGMENT } from '../../providers/AccessProvider/permissions'; const server = testServerSetup(); @@ -23,6 +24,10 @@ const setupRoutes = () => { testServerRoute(server, '/api/admin/ui-config', { flags: { SE: true, + resourceLimits: true, + }, + resourceLimits: { + segments: 2, }, }); }; @@ -30,8 +35,14 @@ const setupRoutes = () => { test('should show the count of projects and features used in', async () => { setupRoutes(); - render(); + render(, { permissions: [{ permission: CREATE_SEGMENT }] }); + + const loadingSegment = await screen.findByText('New segment'); + expect(loadingSegment).toBeDisabled(); await screen.findByText('2 feature flags'); await screen.findByText('3 projects'); + + const segment = await screen.findByText('New segment'); + expect(segment).not.toBeDisabled(); }); diff --git a/frontend/src/component/segments/SegmentTable/SegmentTable.tsx b/frontend/src/component/segments/SegmentTable/SegmentTable.tsx index c0d33139688e..c10d98446394 100644 --- a/frontend/src/component/segments/SegmentTable/SegmentTable.tsx +++ b/frontend/src/component/segments/SegmentTable/SegmentTable.tsx @@ -29,15 +29,41 @@ import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColum import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; import { UsedInCell } from 'component/context/ContextList/UsedInCell'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { useUiFlag } from 'hooks/useUiFlag'; + +const useSegmentLimit = (segmentsLimit: number, segmentsCount: number) => { + const resourceLimitsEnabled = useUiFlag('resourceLimits'); + const limitReached = + resourceLimitsEnabled && segmentsCount >= segmentsLimit; + + return { + limitReached, + limitMessage: limitReached + ? `Limit of ${segmentsCount} segments reached` + : undefined, + }; +}; export const SegmentTable = () => { const projectId = useOptionalPathParam('projectId'); - const { segments, loading } = useSegments(); + const { segments, loading: loadingSegments } = useSegments(); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); const [initialState] = useState({ sortBy: [{ id: 'createdAt' }], hiddenColumns: ['description'], }); + const { uiConfig, loading: loadingConfig } = useUiConfig(); + const segmentLimit = uiConfig.resourceLimits.segments; + const segmentCount = segments?.length || 0; + + const { limitReached, limitMessage } = useSegmentLimit( + segmentLimit, + segmentCount, + ); + + const createSegmentDisabled = + loadingSegments || loadingConfig || limitReached; const data = useMemo(() => { if (!segments) { @@ -112,15 +138,18 @@ export const SegmentTable = () => { onChange={setGlobalFilter} /> - + } /> } - isLoading={loading} + isLoading={loadingSegments} > diff --git a/frontend/src/hooks/api/getters/useUiConfig/defaultValue.tsx b/frontend/src/hooks/api/getters/useUiConfig/defaultValue.tsx index 033faefd9149..b81dcbd045e8 100644 --- a/frontend/src/hooks/api/getters/useUiConfig/defaultValue.tsx +++ b/frontend/src/hooks/api/getters/useUiConfig/defaultValue.tsx @@ -41,5 +41,6 @@ export const defaultValue: IUiConfig = { environments: 50, constraintValues: 250, projects: 500, + segments: 300, }, }; diff --git a/frontend/src/openapi/models/resourceLimitsSchema.ts b/frontend/src/openapi/models/resourceLimitsSchema.ts index f04845a529a4..5a5770852def 100644 --- a/frontend/src/openapi/models/resourceLimitsSchema.ts +++ b/frontend/src/openapi/models/resourceLimitsSchema.ts @@ -32,4 +32,6 @@ export interface ResourceLimitsSchema { constraintValues: number; /** The maximum number of projects allowed. */ projects: number; + /** The maximum number of segment allowed. */ + segments: number; }