mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Refactor flag filters (#10703)
Refactored and simplified code around flag filters, in preparation for UI improvements. It's split into 2 PRs in order to simplify what needs to be behind a flag and what doesn't. - `ExperimentalColumnsMenu` moved to `ColumnsMenu`, old unused `ColumnsMenu` removed - Parts of the code moved to `ProjectFeaturesColumnsMenu` - Moved `FlagCreationButton` to a separate file - Removed part behind archived flag (`projectOverviewRefactorFeedback`)
This commit is contained in:
		
							parent
							
								
									e46f8881d1
								
							
						
					
					
						commit
						c12aca72db
					
				@ -4,7 +4,7 @@ import {
 | 
				
			|||||||
    UPDATE_PROJECT,
 | 
					    UPDATE_PROJECT,
 | 
				
			||||||
    CREATE_PROJECT_API_TOKEN,
 | 
					    CREATE_PROJECT_API_TOKEN,
 | 
				
			||||||
} from 'component/providers/AccessProvider/permissions';
 | 
					} from 'component/providers/AccessProvider/permissions';
 | 
				
			||||||
import { FlagCreationButton } from '../../project/Project/PaginatedProjectFeatureToggles/ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader.tsx';
 | 
					import { FlagCreationButton } from '../../project/Project/PaginatedProjectFeatureToggles/ProjectFeatureTogglesHeader/FlagCreationButton/FlagCreationButton.tsx';
 | 
				
			||||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
 | 
					import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
 | 
				
			||||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
 | 
					import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
 | 
				
			||||||
import { SdkExample } from './SdkExample.tsx';
 | 
					import { SdkExample } from './SdkExample.tsx';
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { useState, type VFC } from 'react';
 | 
					import { useState, type FC } from 'react';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    IconButton,
 | 
					    IconButton,
 | 
				
			||||||
    ListItemIcon,
 | 
					    ListItemIcon,
 | 
				
			||||||
@ -17,7 +17,7 @@ import {
 | 
				
			|||||||
    StyledDivider,
 | 
					    StyledDivider,
 | 
				
			||||||
    StyledIconButton,
 | 
					    StyledIconButton,
 | 
				
			||||||
    StyledMenuItem,
 | 
					    StyledMenuItem,
 | 
				
			||||||
} from './ExperimentalColumnsMenu.styles';
 | 
					} from './ColumnsMenu.styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IColumnsMenuProps {
 | 
					interface IColumnsMenuProps {
 | 
				
			||||||
    columns: {
 | 
					    columns: {
 | 
				
			||||||
@ -29,10 +29,7 @@ interface IColumnsMenuProps {
 | 
				
			|||||||
    onToggle?: (id: string) => void;
 | 
					    onToggle?: (id: string) => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ExperimentalColumnsMenu: VFC<IColumnsMenuProps> = ({
 | 
					export const ColumnsMenu: FC<IColumnsMenuProps> = ({ columns, onToggle }) => {
 | 
				
			||||||
    columns,
 | 
					 | 
				
			||||||
    onToggle,
 | 
					 | 
				
			||||||
}) => {
 | 
					 | 
				
			||||||
    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 | 
					    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const onIconClick = (event: React.MouseEvent<HTMLButtonElement>) => {
 | 
					    const onIconClick = (event: React.MouseEvent<HTMLButtonElement>) => {
 | 
				
			||||||
@ -1,41 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
    Box,
 | 
					 | 
				
			||||||
    Checkbox,
 | 
					 | 
				
			||||||
    Divider,
 | 
					 | 
				
			||||||
    IconButton,
 | 
					 | 
				
			||||||
    MenuItem,
 | 
					 | 
				
			||||||
    styled,
 | 
					 | 
				
			||||||
} from '@mui/material';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { flexRow } from 'themes/themeStyles';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StyledBoxContainer = styled(Box)(() => ({
 | 
					 | 
				
			||||||
    ...flexRow,
 | 
					 | 
				
			||||||
    justifyContent: 'center',
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StyledIconButton = styled(IconButton)(({ theme }) => ({
 | 
					 | 
				
			||||||
    margin: theme.spacing(-1, 0),
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StyledBoxMenuHeader = styled(Box)(({ theme }) => ({
 | 
					 | 
				
			||||||
    ...flexRow,
 | 
					 | 
				
			||||||
    justifyContent: 'space-between',
 | 
					 | 
				
			||||||
    padding: theme.spacing(1, 1, 0, 4),
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StyledMenuItem = styled(MenuItem)(({ theme }) => ({
 | 
					 | 
				
			||||||
    padding: theme.spacing(0, 2),
 | 
					 | 
				
			||||||
    margin: theme.spacing(0, 2),
 | 
					 | 
				
			||||||
    borderRadius: theme.shape.borderRadius,
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StyledDivider = styled(Divider)(({ theme }) => ({
 | 
					 | 
				
			||||||
    '&.MuiDivider-root.MuiDivider-fullWidth': {
 | 
					 | 
				
			||||||
        margin: theme.spacing(0.75, 0),
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StyledCheckbox = styled(Checkbox)(({ theme }) => ({
 | 
					 | 
				
			||||||
    padding: theme.spacing(0.75, 1),
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
@ -9,7 +9,6 @@ import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightC
 | 
				
			|||||||
import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/FavoriteIconHeader';
 | 
					import { FavoriteIconHeader } from 'component/common/Table/FavoriteIconHeader/FavoriteIconHeader';
 | 
				
			||||||
import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell';
 | 
					import { FavoriteIconCell } from 'component/common/Table/cells/FavoriteIconCell/FavoriteIconCell';
 | 
				
			||||||
import { ActionsCell } from '../ProjectFeatureToggles/ActionsCell/ActionsCell.tsx';
 | 
					import { ActionsCell } from '../ProjectFeatureToggles/ActionsCell/ActionsCell.tsx';
 | 
				
			||||||
import { ExperimentalColumnsMenu as ColumnsMenu } from './ExperimentalColumnsMenu/ExperimentalColumnsMenu.tsx';
 | 
					 | 
				
			||||||
import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
 | 
					import { useFavoriteFeaturesApi } from 'hooks/api/actions/useFavoriteFeaturesApi/useFavoriteFeaturesApi';
 | 
				
			||||||
import { MemoizedRowSelectCell } from '../ProjectFeatureToggles/RowSelectCell/RowSelectCell.tsx';
 | 
					import { MemoizedRowSelectCell } from '../ProjectFeatureToggles/RowSelectCell/RowSelectCell.tsx';
 | 
				
			||||||
import { BatchSelectionActionsBar } from 'component/common/BatchSelectionActionsBar/BatchSelectionActionsBar';
 | 
					import { BatchSelectionActionsBar } from 'component/common/BatchSelectionActionsBar/BatchSelectionActionsBar';
 | 
				
			||||||
@ -56,14 +55,13 @@ import { UPDATE_FEATURE } from '@server/types/permissions';
 | 
				
			|||||||
import { ImportModal } from '../Import/ImportModal.tsx';
 | 
					import { ImportModal } from '../Import/ImportModal.tsx';
 | 
				
			||||||
import { IMPORT_BUTTON } from 'utils/testIds';
 | 
					import { IMPORT_BUTTON } from 'utils/testIds';
 | 
				
			||||||
import { ProjectCleanupReminder } from './ProjectCleanupReminder/ProjectCleanupReminder.tsx';
 | 
					import { ProjectCleanupReminder } from './ProjectCleanupReminder/ProjectCleanupReminder.tsx';
 | 
				
			||||||
 | 
					import { formatEnvironmentColumnId } from './formatEnvironmentColumnId.ts';
 | 
				
			||||||
 | 
					import { ProjectFeaturesColumnsMenu } from './ProjectFeaturesColumnsMenu/ProjectFeaturesColumnsMenu.tsx';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ProjectFeatureTogglesProps = {
 | 
					type ProjectFeatureTogglesProps = {
 | 
				
			||||||
    environments: string[];
 | 
					    environments: string[];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const formatEnvironmentColumnId = (environment: string) =>
 | 
					 | 
				
			||||||
    `environment:${environment}`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const columnHelper = createColumnHelper<FeatureSearchResponseSchema>();
 | 
					const columnHelper = createColumnHelper<FeatureSearchResponseSchema>();
 | 
				
			||||||
const getRowId = (row: { name: string }) => row.name;
 | 
					const getRowId = (row: { name: string }) => row.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -514,50 +512,9 @@ export const ProjectFeatureToggles = ({
 | 
				
			|||||||
                        dataToExport={data}
 | 
					                        dataToExport={data}
 | 
				
			||||||
                        environmentsToExport={environments}
 | 
					                        environmentsToExport={environments}
 | 
				
			||||||
                        actions={
 | 
					                        actions={
 | 
				
			||||||
                            <ColumnsMenu
 | 
					                            <ProjectFeaturesColumnsMenu
 | 
				
			||||||
                                columns={[
 | 
					                                columnVisibility={columnVisibility}
 | 
				
			||||||
                                    {
 | 
					                                environments={environments}
 | 
				
			||||||
                                        header: 'Name',
 | 
					 | 
				
			||||||
                                        id: 'name',
 | 
					 | 
				
			||||||
                                        isVisible: columnVisibility.name,
 | 
					 | 
				
			||||||
                                        isStatic: true,
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        header: 'Created',
 | 
					 | 
				
			||||||
                                        id: 'createdAt',
 | 
					 | 
				
			||||||
                                        isVisible: columnVisibility.createdAt,
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        header: 'By',
 | 
					 | 
				
			||||||
                                        id: 'createdBy',
 | 
					 | 
				
			||||||
                                        isVisible: columnVisibility.createdBy,
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        header: 'Last seen',
 | 
					 | 
				
			||||||
                                        id: 'lastSeenAt',
 | 
					 | 
				
			||||||
                                        isVisible: columnVisibility.lastSeenAt,
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        header: 'Lifecycle',
 | 
					 | 
				
			||||||
                                        id: 'lifecycle',
 | 
					 | 
				
			||||||
                                        isVisible: columnVisibility.lifecycle,
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        id: 'divider',
 | 
					 | 
				
			||||||
                                    },
 | 
					 | 
				
			||||||
                                    ...environments.map((environment) => ({
 | 
					 | 
				
			||||||
                                        header: environment,
 | 
					 | 
				
			||||||
                                        id: formatEnvironmentColumnId(
 | 
					 | 
				
			||||||
                                            environment,
 | 
					 | 
				
			||||||
                                        ),
 | 
					 | 
				
			||||||
                                        isVisible:
 | 
					 | 
				
			||||||
                                            columnVisibility[
 | 
					 | 
				
			||||||
                                                formatEnvironmentColumnId(
 | 
					 | 
				
			||||||
                                                    environment,
 | 
					 | 
				
			||||||
                                                )
 | 
					 | 
				
			||||||
                                            ],
 | 
					 | 
				
			||||||
                                    })),
 | 
					 | 
				
			||||||
                                ]}
 | 
					 | 
				
			||||||
                                onToggle={onToggleColumnVisibility}
 | 
					                                onToggle={onToggleColumnVisibility}
 | 
				
			||||||
                            />
 | 
					                            />
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,66 @@
 | 
				
			|||||||
 | 
					import { useState } from 'react';
 | 
				
			||||||
 | 
					import Add from '@mui/icons-material/Add';
 | 
				
			||||||
 | 
					import { styled } from '@mui/material';
 | 
				
			||||||
 | 
					import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
 | 
				
			||||||
 | 
					import { useSearchParams } from 'react-router-dom';
 | 
				
			||||||
 | 
					import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
				
			||||||
 | 
					import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
 | 
				
			||||||
 | 
					import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
				
			||||||
 | 
					import { CreateFeatureDialog } from '../CreateFeatureDialog.tsx';
 | 
				
			||||||
 | 
					import type { OverridableStringUnion } from '@mui/types';
 | 
				
			||||||
 | 
					import type { ButtonPropsVariantOverrides } from '@mui/material/Button/Button';
 | 
				
			||||||
 | 
					import { NAVIGATE_TO_CREATE_FEATURE } from 'utils/testIds';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IFlagCreationButtonProps {
 | 
				
			||||||
 | 
					    text?: string;
 | 
				
			||||||
 | 
					    variant?: OverridableStringUnion<
 | 
				
			||||||
 | 
					        'text' | 'outlined' | 'contained',
 | 
				
			||||||
 | 
					        ButtonPropsVariantOverrides
 | 
				
			||||||
 | 
					    >;
 | 
				
			||||||
 | 
					    skipNavigationOnComplete?: boolean;
 | 
				
			||||||
 | 
					    isLoading?: boolean;
 | 
				
			||||||
 | 
					    onSuccess?: () => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledResponsiveButton = styled(ResponsiveButton)(() => ({
 | 
				
			||||||
 | 
					    whiteSpace: 'nowrap',
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const FlagCreationButton = ({
 | 
				
			||||||
 | 
					    variant,
 | 
				
			||||||
 | 
					    text = 'New feature flag',
 | 
				
			||||||
 | 
					    skipNavigationOnComplete,
 | 
				
			||||||
 | 
					    isLoading,
 | 
				
			||||||
 | 
					    onSuccess,
 | 
				
			||||||
 | 
					}: IFlagCreationButtonProps) => {
 | 
				
			||||||
 | 
					    const { loading } = useUiConfig();
 | 
				
			||||||
 | 
					    const [searchParams] = useSearchParams();
 | 
				
			||||||
 | 
					    const projectId = useRequiredPathParam('projectId');
 | 
				
			||||||
 | 
					    const showCreateDialog = Boolean(searchParams.get('create'));
 | 
				
			||||||
 | 
					    const [openCreateDialog, setOpenCreateDialog] = useState(showCreateDialog);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					            <StyledResponsiveButton
 | 
				
			||||||
 | 
					                onClick={() => setOpenCreateDialog(true)}
 | 
				
			||||||
 | 
					                maxWidth='960px'
 | 
				
			||||||
 | 
					                Icon={Add}
 | 
				
			||||||
 | 
					                projectId={projectId}
 | 
				
			||||||
 | 
					                disabled={loading || isLoading}
 | 
				
			||||||
 | 
					                variant={variant}
 | 
				
			||||||
 | 
					                permission={CREATE_FEATURE}
 | 
				
			||||||
 | 
					                data-testid={
 | 
				
			||||||
 | 
					                    loading || isLoading ? '' : NAVIGATE_TO_CREATE_FEATURE
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                {text}
 | 
				
			||||||
 | 
					            </StyledResponsiveButton>
 | 
				
			||||||
 | 
					            <CreateFeatureDialog
 | 
				
			||||||
 | 
					                open={openCreateDialog}
 | 
				
			||||||
 | 
					                onClose={() => setOpenCreateDialog(false)}
 | 
				
			||||||
 | 
					                skipNavigationOnComplete={skipNavigationOnComplete}
 | 
				
			||||||
 | 
					                onSuccess={onSuccess}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import { type FC, useState } from 'react';
 | 
				
			||||||
 | 
					import { ReactComponent as ImportSvg } from 'assets/icons/import.svg';
 | 
				
			||||||
 | 
					import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
				
			||||||
 | 
					import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
 | 
				
			||||||
 | 
					import { UPDATE_FEATURE } from '@server/types/permissions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { ImportModal } from '../../../Import/ImportModal.tsx';
 | 
				
			||||||
 | 
					import { IMPORT_BUTTON } from 'utils/testIds';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ImportButtonProps = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ImportButton: FC<ImportButtonProps> = () => {
 | 
				
			||||||
 | 
					    const projectId = useRequiredPathParam('projectId');
 | 
				
			||||||
 | 
					    const [modalOpen, setModalOpen] = useState(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					            <PermissionIconButton
 | 
				
			||||||
 | 
					                permission={UPDATE_FEATURE}
 | 
				
			||||||
 | 
					                projectId={projectId}
 | 
				
			||||||
 | 
					                onClick={() => setModalOpen(true)}
 | 
				
			||||||
 | 
					                tooltipProps={{ title: 'Import' }}
 | 
				
			||||||
 | 
					                data-testid={IMPORT_BUTTON}
 | 
				
			||||||
 | 
					                data-loading-project
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <ImportSvg />
 | 
				
			||||||
 | 
					            </PermissionIconButton>
 | 
				
			||||||
 | 
					            <ImportModal
 | 
				
			||||||
 | 
					                open={modalOpen}
 | 
				
			||||||
 | 
					                setOpen={setModalOpen}
 | 
				
			||||||
 | 
					                project={projectId}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
import { type ReactNode, type FC, useState } from 'react';
 | 
					import { type ReactNode, type FC, useState } from 'react';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    Box,
 | 
					    Box,
 | 
				
			||||||
    Button,
 | 
					 | 
				
			||||||
    IconButton,
 | 
					    IconButton,
 | 
				
			||||||
    Tooltip,
 | 
					    Tooltip,
 | 
				
			||||||
    useMediaQuery,
 | 
					    useMediaQuery,
 | 
				
			||||||
@ -11,24 +10,12 @@ import useLoading from 'hooks/useLoading';
 | 
				
			|||||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
 | 
					import { PageHeader } from 'component/common/PageHeader/PageHeader';
 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { Search } from 'component/common/Search/Search';
 | 
					import { Search } from 'component/common/Search/Search';
 | 
				
			||||||
import { useUiFlag } from 'hooks/useUiFlag';
 | 
					 | 
				
			||||||
import Add from '@mui/icons-material/Add';
 | 
					 | 
				
			||||||
import { styled } from '@mui/material';
 | 
					 | 
				
			||||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
 | 
					 | 
				
			||||||
import { useSearchParams } from 'react-router-dom';
 | 
					 | 
				
			||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
					import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
 | 
				
			||||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
 | 
					 | 
				
			||||||
import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog';
 | 
					import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog';
 | 
				
			||||||
import type { FeatureSchema } from 'openapi';
 | 
					import type { FeatureSchema } from 'openapi';
 | 
				
			||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
					import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
				
			||||||
import ReviewsOutlined from '@mui/icons-material/ReviewsOutlined';
 | 
					 | 
				
			||||||
import { useFeedback } from 'component/feedbackNew/useFeedback';
 | 
					 | 
				
			||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
 | 
					 | 
				
			||||||
import { CreateFeatureDialog } from './CreateFeatureDialog.tsx';
 | 
					 | 
				
			||||||
import IosShare from '@mui/icons-material/IosShare';
 | 
					import IosShare from '@mui/icons-material/IosShare';
 | 
				
			||||||
import type { OverridableStringUnion } from '@mui/types';
 | 
					import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx';
 | 
				
			||||||
import type { ButtonPropsVariantOverrides } from '@mui/material/Button/Button';
 | 
					 | 
				
			||||||
import { NAVIGATE_TO_CREATE_FEATURE } from 'utils/testIds';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IProjectFeatureTogglesHeaderProps {
 | 
					interface IProjectFeatureTogglesHeaderProps {
 | 
				
			||||||
    isLoading?: boolean;
 | 
					    isLoading?: boolean;
 | 
				
			||||||
@ -40,60 +27,6 @@ interface IProjectFeatureTogglesHeaderProps {
 | 
				
			|||||||
    actions?: ReactNode;
 | 
					    actions?: ReactNode;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IFlagCreationButtonProps {
 | 
					 | 
				
			||||||
    text?: string;
 | 
					 | 
				
			||||||
    variant?: OverridableStringUnion<
 | 
					 | 
				
			||||||
        'text' | 'outlined' | 'contained',
 | 
					 | 
				
			||||||
        ButtonPropsVariantOverrides
 | 
					 | 
				
			||||||
    >;
 | 
					 | 
				
			||||||
    skipNavigationOnComplete?: boolean;
 | 
					 | 
				
			||||||
    isLoading?: boolean;
 | 
					 | 
				
			||||||
    onSuccess?: () => void;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const StyledResponsiveButton = styled(ResponsiveButton)(() => ({
 | 
					 | 
				
			||||||
    whiteSpace: 'nowrap',
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const FlagCreationButton = ({
 | 
					 | 
				
			||||||
    variant,
 | 
					 | 
				
			||||||
    text = 'New feature flag',
 | 
					 | 
				
			||||||
    skipNavigationOnComplete,
 | 
					 | 
				
			||||||
    isLoading,
 | 
					 | 
				
			||||||
    onSuccess,
 | 
					 | 
				
			||||||
}: IFlagCreationButtonProps) => {
 | 
					 | 
				
			||||||
    const { loading } = useUiConfig();
 | 
					 | 
				
			||||||
    const [searchParams] = useSearchParams();
 | 
					 | 
				
			||||||
    const projectId = useRequiredPathParam('projectId');
 | 
					 | 
				
			||||||
    const showCreateDialog = Boolean(searchParams.get('create'));
 | 
					 | 
				
			||||||
    const [openCreateDialog, setOpenCreateDialog] = useState(showCreateDialog);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <>
 | 
					 | 
				
			||||||
            <StyledResponsiveButton
 | 
					 | 
				
			||||||
                onClick={() => setOpenCreateDialog(true)}
 | 
					 | 
				
			||||||
                maxWidth='960px'
 | 
					 | 
				
			||||||
                Icon={Add}
 | 
					 | 
				
			||||||
                projectId={projectId}
 | 
					 | 
				
			||||||
                disabled={loading || isLoading}
 | 
					 | 
				
			||||||
                variant={variant}
 | 
					 | 
				
			||||||
                permission={CREATE_FEATURE}
 | 
					 | 
				
			||||||
                data-testid={
 | 
					 | 
				
			||||||
                    loading || isLoading ? '' : NAVIGATE_TO_CREATE_FEATURE
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                {text}
 | 
					 | 
				
			||||||
            </StyledResponsiveButton>
 | 
					 | 
				
			||||||
            <CreateFeatureDialog
 | 
					 | 
				
			||||||
                open={openCreateDialog}
 | 
					 | 
				
			||||||
                onClose={() => setOpenCreateDialog(false)}
 | 
					 | 
				
			||||||
                skipNavigationOnComplete={skipNavigationOnComplete}
 | 
					 | 
				
			||||||
                onSuccess={onSuccess}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
        </>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ProjectFeatureTogglesHeader: FC<
 | 
					export const ProjectFeatureTogglesHeader: FC<
 | 
				
			||||||
    IProjectFeatureTogglesHeaderProps
 | 
					    IProjectFeatureTogglesHeaderProps
 | 
				
			||||||
> = ({
 | 
					> = ({
 | 
				
			||||||
@ -111,10 +44,6 @@ export const ProjectFeatureTogglesHeader: FC<
 | 
				
			|||||||
    const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
 | 
					    const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
 | 
				
			||||||
    const [showExportDialog, setShowExportDialog] = useState(false);
 | 
					    const [showExportDialog, setShowExportDialog] = useState(false);
 | 
				
			||||||
    const { trackEvent } = usePlausibleTracker();
 | 
					    const { trackEvent } = usePlausibleTracker();
 | 
				
			||||||
    const projectOverviewRefactorFeedback = useUiFlag(
 | 
					 | 
				
			||||||
        'projectOverviewRefactorFeedback',
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    const { openFeedback } = useFeedback('newProjectOverview', 'automatic');
 | 
					 | 
				
			||||||
    const handleSearch = (query: string) => {
 | 
					    const handleSearch = (query: string) => {
 | 
				
			||||||
        onChangeSearchQuery?.(query);
 | 
					        onChangeSearchQuery?.(query);
 | 
				
			||||||
        trackEvent('search-bar', {
 | 
					        trackEvent('search-bar', {
 | 
				
			||||||
@ -125,16 +54,6 @@ export const ProjectFeatureTogglesHeader: FC<
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const createFeedbackContext = () => {
 | 
					 | 
				
			||||||
        openFeedback({
 | 
					 | 
				
			||||||
            title: 'How easy was it to work with the project overview in Unleash?',
 | 
					 | 
				
			||||||
            positiveLabel:
 | 
					 | 
				
			||||||
                'What do you like most about the updated project overview?',
 | 
					 | 
				
			||||||
            areasForImprovementsLabel:
 | 
					 | 
				
			||||||
                'What improvements are needed in the project overview?',
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Box
 | 
					        <Box
 | 
				
			||||||
            ref={headerLoadingRef}
 | 
					            ref={headerLoadingRef}
 | 
				
			||||||
@ -196,22 +115,6 @@ export const ProjectFeatureTogglesHeader: FC<
 | 
				
			|||||||
                                />
 | 
					                                />
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                        <ConditionallyRender
 | 
					 | 
				
			||||||
                            condition={
 | 
					 | 
				
			||||||
                                projectOverviewRefactorFeedback &&
 | 
					 | 
				
			||||||
                                !isSmallScreen
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            show={
 | 
					 | 
				
			||||||
                                <Button
 | 
					 | 
				
			||||||
                                    startIcon={<ReviewsOutlined />}
 | 
					 | 
				
			||||||
                                    onClick={createFeedbackContext}
 | 
					 | 
				
			||||||
                                    variant='outlined'
 | 
					 | 
				
			||||||
                                    data-loading
 | 
					 | 
				
			||||||
                                >
 | 
					 | 
				
			||||||
                                    Provide feedback
 | 
					 | 
				
			||||||
                                </Button>
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        />
 | 
					 | 
				
			||||||
                        <FlagCreationButton isLoading={isLoading} />
 | 
					                        <FlagCreationButton isLoading={isLoading} />
 | 
				
			||||||
                    </>
 | 
					                    </>
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					import type { FC } from 'react';
 | 
				
			||||||
 | 
					import { ColumnsMenu } from '../ColumnsMenu/ColumnsMenu.tsx';
 | 
				
			||||||
 | 
					import { formatEnvironmentColumnId } from '../formatEnvironmentColumnId.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ProjectFeaturesColumnsMenuProps = {
 | 
				
			||||||
 | 
					    columnVisibility: Record<string, boolean>;
 | 
				
			||||||
 | 
					    environments: string[];
 | 
				
			||||||
 | 
					    onToggle: (id: string) => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProjectFeaturesColumnsMenu: FC<
 | 
				
			||||||
 | 
					    ProjectFeaturesColumnsMenuProps
 | 
				
			||||||
 | 
					> = ({ columnVisibility, environments, onToggle }) => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <ColumnsMenu
 | 
				
			||||||
 | 
					            columns={[
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    header: 'Name',
 | 
				
			||||||
 | 
					                    id: 'name',
 | 
				
			||||||
 | 
					                    isVisible: columnVisibility.name,
 | 
				
			||||||
 | 
					                    isStatic: true,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    header: 'Created',
 | 
				
			||||||
 | 
					                    id: 'createdAt',
 | 
				
			||||||
 | 
					                    isVisible: columnVisibility.createdAt,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    header: 'By',
 | 
				
			||||||
 | 
					                    id: 'createdBy',
 | 
				
			||||||
 | 
					                    isVisible: columnVisibility.createdBy,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    header: 'Last seen',
 | 
				
			||||||
 | 
					                    id: 'lastSeenAt',
 | 
				
			||||||
 | 
					                    isVisible: columnVisibility.lastSeenAt,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    header: 'Lifecycle',
 | 
				
			||||||
 | 
					                    id: 'lifecycle',
 | 
				
			||||||
 | 
					                    isVisible: columnVisibility.lifecycle,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    id: 'divider',
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                ...environments.map((environment) => ({
 | 
				
			||||||
 | 
					                    header: environment,
 | 
				
			||||||
 | 
					                    id: formatEnvironmentColumnId(environment),
 | 
				
			||||||
 | 
					                    isVisible:
 | 
				
			||||||
 | 
					                        columnVisibility[
 | 
				
			||||||
 | 
					                            formatEnvironmentColumnId(environment)
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                })),
 | 
				
			||||||
 | 
					            ]}
 | 
				
			||||||
 | 
					            onToggle={onToggle}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					import type { FC } from 'react';
 | 
				
			||||||
 | 
					import { Box } from '@mui/material';
 | 
				
			||||||
 | 
					import { Search } from 'component/common/Search/Search';
 | 
				
			||||||
 | 
					import useLoading from 'hooks/useLoading';
 | 
				
			||||||
 | 
					import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IProjectFlagsSearchProps {
 | 
				
			||||||
 | 
					    isLoading?: boolean;
 | 
				
			||||||
 | 
					    searchQuery?: string;
 | 
				
			||||||
 | 
					    onChangeSearchQuery?: (query: string) => void;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ProjectFlagsSearch: FC<IProjectFlagsSearchProps> = ({
 | 
				
			||||||
 | 
					    isLoading,
 | 
				
			||||||
 | 
					    searchQuery,
 | 
				
			||||||
 | 
					    onChangeSearchQuery,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					    const headerLoadingRef = useLoading(isLoading || false);
 | 
				
			||||||
 | 
					    const { trackEvent } = usePlausibleTracker();
 | 
				
			||||||
 | 
					    const handleSearch = (query: string) => {
 | 
				
			||||||
 | 
					        onChangeSearchQuery?.(query);
 | 
				
			||||||
 | 
					        trackEvent('search-bar', {
 | 
				
			||||||
 | 
					            props: {
 | 
				
			||||||
 | 
					                screen: 'project',
 | 
				
			||||||
 | 
					                length: query.length,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <Box ref={headerLoadingRef} aria-busy={isLoading} aria-live='polite'>
 | 
				
			||||||
 | 
					            <Search
 | 
				
			||||||
 | 
					                placeholder='Search'
 | 
				
			||||||
 | 
					                expandable
 | 
				
			||||||
 | 
					                initialValue={searchQuery || ''}
 | 
				
			||||||
 | 
					                onChange={handleSearch}
 | 
				
			||||||
 | 
					                hasFilters
 | 
				
			||||||
 | 
					                id='projectFeatureFlags'
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </Box>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					export const formatEnvironmentColumnId = (environment: string) =>
 | 
				
			||||||
 | 
					    `environment:${environment}`;
 | 
				
			||||||
@ -1,212 +0,0 @@
 | 
				
			|||||||
import { useEffect, useState, type VFC } from 'react';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    IconButton,
 | 
					 | 
				
			||||||
    ListItemIcon,
 | 
					 | 
				
			||||||
    ListItemText,
 | 
					 | 
				
			||||||
    MenuList,
 | 
					 | 
				
			||||||
    Popover,
 | 
					 | 
				
			||||||
    Tooltip,
 | 
					 | 
				
			||||||
    Typography,
 | 
					 | 
				
			||||||
    useMediaQuery,
 | 
					 | 
				
			||||||
    useTheme,
 | 
					 | 
				
			||||||
} from '@mui/material';
 | 
					 | 
				
			||||||
import ColumnIcon from '@mui/icons-material/ViewWeek';
 | 
					 | 
				
			||||||
import CloseIcon from '@mui/icons-material/Close';
 | 
					 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    StyledBoxContainer,
 | 
					 | 
				
			||||||
    StyledBoxMenuHeader,
 | 
					 | 
				
			||||||
    StyledCheckbox,
 | 
					 | 
				
			||||||
    StyledDivider,
 | 
					 | 
				
			||||||
    StyledIconButton,
 | 
					 | 
				
			||||||
    StyledMenuItem,
 | 
					 | 
				
			||||||
} from './ColumnsMenu.styles';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
interface IColumnsMenuProps {
 | 
					 | 
				
			||||||
    allColumns: {
 | 
					 | 
				
			||||||
        Header?: string | any;
 | 
					 | 
				
			||||||
        id: string;
 | 
					 | 
				
			||||||
        isVisible: boolean;
 | 
					 | 
				
			||||||
        toggleHidden: (state: boolean) => void;
 | 
					 | 
				
			||||||
        hideInMenu?: boolean;
 | 
					 | 
				
			||||||
    }[];
 | 
					 | 
				
			||||||
    staticColumns?: string[];
 | 
					 | 
				
			||||||
    dividerBefore?: string[];
 | 
					 | 
				
			||||||
    dividerAfter?: string[];
 | 
					 | 
				
			||||||
    isCustomized?: boolean;
 | 
					 | 
				
			||||||
    setHiddenColumns: (hiddenColumns: string[]) => void;
 | 
					 | 
				
			||||||
    onCustomize?: () => void;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const columnNameMap: Record<string, string> = {
 | 
					 | 
				
			||||||
    favorite: 'Favorite',
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
 | 
					 | 
				
			||||||
    allColumns,
 | 
					 | 
				
			||||||
    staticColumns = [],
 | 
					 | 
				
			||||||
    dividerBefore = [],
 | 
					 | 
				
			||||||
    dividerAfter = [],
 | 
					 | 
				
			||||||
    isCustomized = false,
 | 
					 | 
				
			||||||
    onCustomize,
 | 
					 | 
				
			||||||
    setHiddenColumns,
 | 
					 | 
				
			||||||
}) => {
 | 
					 | 
				
			||||||
    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 | 
					 | 
				
			||||||
    const theme = useTheme();
 | 
					 | 
				
			||||||
    const isTinyScreen = useMediaQuery(theme.breakpoints.down('sm'));
 | 
					 | 
				
			||||||
    const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
 | 
					 | 
				
			||||||
    const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg'));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    useEffect(() => {
 | 
					 | 
				
			||||||
        if (isCustomized) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const setVisibleColumns = (
 | 
					 | 
				
			||||||
            columns: string[],
 | 
					 | 
				
			||||||
            environmentsToShow: number = 0,
 | 
					 | 
				
			||||||
        ) => {
 | 
					 | 
				
			||||||
            const visibleEnvColumns = allColumns
 | 
					 | 
				
			||||||
                .filter(({ id }) => id.startsWith('environment:') !== false)
 | 
					 | 
				
			||||||
                .map(({ id }) => id)
 | 
					 | 
				
			||||||
                .slice(0, environmentsToShow);
 | 
					 | 
				
			||||||
            const hiddenColumns = allColumns
 | 
					 | 
				
			||||||
                .map(({ id }) => id)
 | 
					 | 
				
			||||||
                .filter((id) => !columns.includes(id))
 | 
					 | 
				
			||||||
                .filter((id) => !staticColumns.includes(id))
 | 
					 | 
				
			||||||
                .filter((id) => !visibleEnvColumns.includes(id));
 | 
					 | 
				
			||||||
            setHiddenColumns(hiddenColumns);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (isTinyScreen) {
 | 
					 | 
				
			||||||
            return setVisibleColumns(['createdAt']);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (isSmallScreen) {
 | 
					 | 
				
			||||||
            return setVisibleColumns(['createdAt'], 1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (isMediumScreen) {
 | 
					 | 
				
			||||||
            return setVisibleColumns(['type', 'createdAt'], 1);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        setVisibleColumns(['lastSeenAt', 'type', 'createdAt'], 3);
 | 
					 | 
				
			||||||
        // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
					 | 
				
			||||||
    }, [isTinyScreen, isSmallScreen, isMediumScreen]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
 | 
					 | 
				
			||||||
        setAnchorEl(event.currentTarget);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const handleClose = () => {
 | 
					 | 
				
			||||||
        setAnchorEl(null);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const isOpen = Boolean(anchorEl);
 | 
					 | 
				
			||||||
    const id = `columns-menu`;
 | 
					 | 
				
			||||||
    const menuId = `columns-menu-list-${id}`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <StyledBoxContainer>
 | 
					 | 
				
			||||||
            <Tooltip title='Select columns' arrow describeChild>
 | 
					 | 
				
			||||||
                <StyledIconButton
 | 
					 | 
				
			||||||
                    id={id}
 | 
					 | 
				
			||||||
                    aria-controls={isOpen ? menuId : undefined}
 | 
					 | 
				
			||||||
                    aria-haspopup='true'
 | 
					 | 
				
			||||||
                    aria-expanded={isOpen ? 'true' : undefined}
 | 
					 | 
				
			||||||
                    onClick={handleClick}
 | 
					 | 
				
			||||||
                    type='button'
 | 
					 | 
				
			||||||
                    size='large'
 | 
					 | 
				
			||||||
                    data-loading
 | 
					 | 
				
			||||||
                >
 | 
					 | 
				
			||||||
                    <ColumnIcon />
 | 
					 | 
				
			||||||
                </StyledIconButton>
 | 
					 | 
				
			||||||
            </Tooltip>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <Popover
 | 
					 | 
				
			||||||
                id={menuId}
 | 
					 | 
				
			||||||
                open={isOpen}
 | 
					 | 
				
			||||||
                anchorEl={anchorEl}
 | 
					 | 
				
			||||||
                onClose={handleClose}
 | 
					 | 
				
			||||||
                anchorOrigin={{
 | 
					 | 
				
			||||||
                    vertical: 'top',
 | 
					 | 
				
			||||||
                    horizontal: 'right',
 | 
					 | 
				
			||||||
                }}
 | 
					 | 
				
			||||||
                transformOrigin={{
 | 
					 | 
				
			||||||
                    vertical: 'top',
 | 
					 | 
				
			||||||
                    horizontal: 'right',
 | 
					 | 
				
			||||||
                }}
 | 
					 | 
				
			||||||
                disableScrollLock={true}
 | 
					 | 
				
			||||||
                PaperProps={{
 | 
					 | 
				
			||||||
                    sx: (theme) => ({
 | 
					 | 
				
			||||||
                        borderRadius: theme.shape.borderRadius,
 | 
					 | 
				
			||||||
                        paddingBottom: theme.spacing(2),
 | 
					 | 
				
			||||||
                    }),
 | 
					 | 
				
			||||||
                }}
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
                <StyledBoxMenuHeader>
 | 
					 | 
				
			||||||
                    <Typography variant='body2'>
 | 
					 | 
				
			||||||
                        <strong>Columns</strong>
 | 
					 | 
				
			||||||
                    </Typography>
 | 
					 | 
				
			||||||
                    <IconButton onClick={handleClose}>
 | 
					 | 
				
			||||||
                        <CloseIcon />
 | 
					 | 
				
			||||||
                    </IconButton>
 | 
					 | 
				
			||||||
                </StyledBoxMenuHeader>
 | 
					 | 
				
			||||||
                <MenuList>
 | 
					 | 
				
			||||||
                    {allColumns
 | 
					 | 
				
			||||||
                        .filter(({ hideInMenu }) => !hideInMenu)
 | 
					 | 
				
			||||||
                        .map((column) => [
 | 
					 | 
				
			||||||
                            <ConditionallyRender
 | 
					 | 
				
			||||||
                                condition={dividerBefore.includes(column.id)}
 | 
					 | 
				
			||||||
                                show={<StyledDivider />}
 | 
					 | 
				
			||||||
                            />,
 | 
					 | 
				
			||||||
                            <StyledMenuItem
 | 
					 | 
				
			||||||
                                onClick={() => {
 | 
					 | 
				
			||||||
                                    column.toggleHidden(column.isVisible);
 | 
					 | 
				
			||||||
                                    onCustomize?.();
 | 
					 | 
				
			||||||
                                }}
 | 
					 | 
				
			||||||
                                disabled={staticColumns.includes(column.id)}
 | 
					 | 
				
			||||||
                            >
 | 
					 | 
				
			||||||
                                <ListItemIcon>
 | 
					 | 
				
			||||||
                                    <StyledCheckbox
 | 
					 | 
				
			||||||
                                        edge='start'
 | 
					 | 
				
			||||||
                                        checked={column.isVisible}
 | 
					 | 
				
			||||||
                                        disableRipple
 | 
					 | 
				
			||||||
                                        inputProps={{
 | 
					 | 
				
			||||||
                                            'aria-labelledby': column.id,
 | 
					 | 
				
			||||||
                                        }}
 | 
					 | 
				
			||||||
                                        size='medium'
 | 
					 | 
				
			||||||
                                    />
 | 
					 | 
				
			||||||
                                </ListItemIcon>
 | 
					 | 
				
			||||||
                                <ListItemText
 | 
					 | 
				
			||||||
                                    id={column.id}
 | 
					 | 
				
			||||||
                                    primary={
 | 
					 | 
				
			||||||
                                        <Typography variant='body2'>
 | 
					 | 
				
			||||||
                                            <ConditionallyRender
 | 
					 | 
				
			||||||
                                                condition={Boolean(
 | 
					 | 
				
			||||||
                                                    typeof column.Header ===
 | 
					 | 
				
			||||||
                                                        'string' &&
 | 
					 | 
				
			||||||
                                                        column.Header,
 | 
					 | 
				
			||||||
                                                )}
 | 
					 | 
				
			||||||
                                                show={() => (
 | 
					 | 
				
			||||||
                                                    <>{column.Header}</>
 | 
					 | 
				
			||||||
                                                )}
 | 
					 | 
				
			||||||
                                                elseShow={() => (
 | 
					 | 
				
			||||||
                                                    <>
 | 
					 | 
				
			||||||
                                                        {columnNameMap[
 | 
					 | 
				
			||||||
                                                            column.id
 | 
					 | 
				
			||||||
                                                        ] || column.id}
 | 
					 | 
				
			||||||
                                                    </>
 | 
					 | 
				
			||||||
                                                )}
 | 
					 | 
				
			||||||
                                            />
 | 
					 | 
				
			||||||
                                        </Typography>
 | 
					 | 
				
			||||||
                                    }
 | 
					 | 
				
			||||||
                                />
 | 
					 | 
				
			||||||
                            </StyledMenuItem>,
 | 
					 | 
				
			||||||
                            <ConditionallyRender
 | 
					 | 
				
			||||||
                                condition={dividerAfter.includes(column.id)}
 | 
					 | 
				
			||||||
                                show={<StyledDivider />}
 | 
					 | 
				
			||||||
                            />,
 | 
					 | 
				
			||||||
                        ])}
 | 
					 | 
				
			||||||
                </MenuList>
 | 
					 | 
				
			||||||
            </Popover>
 | 
					 | 
				
			||||||
        </StyledBoxContainer>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -74,7 +74,6 @@ export type UiFlags = {
 | 
				
			|||||||
    outdatedSdksBanner?: boolean;
 | 
					    outdatedSdksBanner?: boolean;
 | 
				
			||||||
    estimateTrafficDataCost?: boolean;
 | 
					    estimateTrafficDataCost?: boolean;
 | 
				
			||||||
    disableShowContextFieldSelectionValues?: boolean;
 | 
					    disableShowContextFieldSelectionValues?: boolean;
 | 
				
			||||||
    projectOverviewRefactorFeedback?: boolean;
 | 
					 | 
				
			||||||
    featureLifecycle?: boolean;
 | 
					    featureLifecycle?: boolean;
 | 
				
			||||||
    manyStrategiesPagination?: boolean;
 | 
					    manyStrategiesPagination?: boolean;
 | 
				
			||||||
    enableLegacyVariants?: boolean;
 | 
					    enableLegacyVariants?: boolean;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user