mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: connect project overview table to search api (#5237)
This commit is contained in:
		
							parent
							
								
									598d022a5a
								
							
						
					
					
						commit
						d074254b61
					
				@ -72,6 +72,7 @@ interface IProjectFeatureTogglesProps {
 | 
			
		||||
    features: IProject['features'];
 | 
			
		||||
    environments: IProject['environments'];
 | 
			
		||||
    loading: boolean;
 | 
			
		||||
    onChange: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const staticColumns = ['Select', 'Actions', 'name', 'favorite'];
 | 
			
		||||
@ -84,6 +85,7 @@ export const ProjectFeatureToggles = ({
 | 
			
		||||
    features,
 | 
			
		||||
    loading,
 | 
			
		||||
    environments: newEnvironments = [],
 | 
			
		||||
    onChange,
 | 
			
		||||
}: IProjectFeatureTogglesProps) => {
 | 
			
		||||
    const { classes: styles } = useStyles();
 | 
			
		||||
    const theme = useTheme();
 | 
			
		||||
@ -118,7 +120,6 @@ export const ProjectFeatureToggles = ({
 | 
			
		||||
            ? [{ environment: 'a' }, { environment: 'b' }, { environment: 'c' }]
 | 
			
		||||
            : newEnvironments,
 | 
			
		||||
    );
 | 
			
		||||
    const { refetch } = useProject(projectId);
 | 
			
		||||
    const { isFavoritesPinned, sortTypes, onChangeIsFavoritePinned } =
 | 
			
		||||
        usePinnedFavorites(
 | 
			
		||||
            searchParams.has('favorites')
 | 
			
		||||
@ -140,9 +141,9 @@ export const ProjectFeatureToggles = ({
 | 
			
		||||
            } else {
 | 
			
		||||
                await favorite(projectId, feature.name);
 | 
			
		||||
            }
 | 
			
		||||
            refetch();
 | 
			
		||||
            onChange();
 | 
			
		||||
        },
 | 
			
		||||
        [projectId, refetch],
 | 
			
		||||
        [projectId, onChange],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const showTagsColumn = useMemo(
 | 
			
		||||
@ -263,7 +264,7 @@ export const ProjectFeatureToggles = ({
 | 
			
		||||
                    projectId,
 | 
			
		||||
                    name,
 | 
			
		||||
                    isChangeRequestEnabled,
 | 
			
		||||
                    refetch,
 | 
			
		||||
                    onChange,
 | 
			
		||||
                    onFeatureToggle,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
@ -617,16 +618,14 @@ export const ProjectFeatureToggles = ({
 | 
			
		||||
                    isOpen={Boolean(featureStaleDialogState.featureId)}
 | 
			
		||||
                    onClose={() => {
 | 
			
		||||
                        setFeatureStaleDialogState({});
 | 
			
		||||
                        refetch();
 | 
			
		||||
                        onChange();
 | 
			
		||||
                    }}
 | 
			
		||||
                    featureId={featureStaleDialogState.featureId || ''}
 | 
			
		||||
                    projectId={projectId}
 | 
			
		||||
                />
 | 
			
		||||
                <FeatureArchiveDialog
 | 
			
		||||
                    isOpen={Boolean(featureArchiveState)}
 | 
			
		||||
                    onConfirm={() => {
 | 
			
		||||
                        refetch();
 | 
			
		||||
                    }}
 | 
			
		||||
                    onConfirm={onChange}
 | 
			
		||||
                    onClose={() => {
 | 
			
		||||
                        setFeatureArchiveState(undefined);
 | 
			
		||||
                    }}
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { useEffect } from 'react';
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
import useProject, {
 | 
			
		||||
    useProjectNameOrId,
 | 
			
		||||
} from 'hooks/api/getters/useProject/useProject';
 | 
			
		||||
@ -12,6 +12,7 @@ import { useLastViewedProject } from 'hooks/useLastViewedProject';
 | 
			
		||||
import { ProjectStats } from './ProjectStats/ProjectStats';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag';
 | 
			
		||||
import { useFeatureSearch } from '../../../hooks/api/getters/useFeatureSearch/useFeatureSearch';
 | 
			
		||||
 | 
			
		||||
const refreshInterval = 15 * 1000;
 | 
			
		||||
 | 
			
		||||
@ -34,10 +35,50 @@ const StyledContentContainer = styled(Box)(() => ({
 | 
			
		||||
    minWidth: 0,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const InfiniteProjectOverview = () => {
 | 
			
		||||
    const projectId = useRequiredPathParam('projectId');
 | 
			
		||||
    const { project, loading: projectLoading } = useProject(projectId, {
 | 
			
		||||
        refreshInterval,
 | 
			
		||||
    });
 | 
			
		||||
    const [nextCursor, setNextCursor] = useState('');
 | 
			
		||||
    const {
 | 
			
		||||
        features: searchFeatures,
 | 
			
		||||
        refetch,
 | 
			
		||||
        loading,
 | 
			
		||||
    } = useFeatureSearch(nextCursor, projectId, { refreshInterval });
 | 
			
		||||
    const { members, features, health, description, environments, stats } =
 | 
			
		||||
        project;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledContainer>
 | 
			
		||||
            <ProjectInfo
 | 
			
		||||
                id={projectId}
 | 
			
		||||
                description={description}
 | 
			
		||||
                memberCount={members}
 | 
			
		||||
                health={health}
 | 
			
		||||
                features={features}
 | 
			
		||||
                stats={stats}
 | 
			
		||||
            />
 | 
			
		||||
            <StyledContentContainer>
 | 
			
		||||
                <ProjectStats stats={project.stats} />
 | 
			
		||||
                <StyledProjectToggles>
 | 
			
		||||
                    <ProjectFeatureToggles
 | 
			
		||||
                        key={loading ? 'loading' : 'ready'}
 | 
			
		||||
                        features={searchFeatures.features}
 | 
			
		||||
                        environments={environments}
 | 
			
		||||
                        loading={loading}
 | 
			
		||||
                        onChange={refetch}
 | 
			
		||||
                    />
 | 
			
		||||
                </StyledProjectToggles>
 | 
			
		||||
            </StyledContentContainer>
 | 
			
		||||
        </StyledContainer>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ProjectOverview = () => {
 | 
			
		||||
    const projectId = useRequiredPathParam('projectId');
 | 
			
		||||
    const projectName = useProjectNameOrId(projectId);
 | 
			
		||||
    const { project, loading } = useProject(projectId, {
 | 
			
		||||
    const { project, loading, refetch } = useProject(projectId, {
 | 
			
		||||
        refreshInterval,
 | 
			
		||||
    });
 | 
			
		||||
    const { members, features, health, description, environments, stats } =
 | 
			
		||||
@ -45,11 +86,14 @@ const ProjectOverview = () => {
 | 
			
		||||
    usePageTitle(`Project overview – ${projectName}`);
 | 
			
		||||
    const { setLastViewed } = useLastViewedProject();
 | 
			
		||||
    const featureSwitchRefactor = useUiFlag('featureSwitchRefactor');
 | 
			
		||||
    const featureSearchFrontend = useUiFlag('featureSearchFrontend');
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        setLastViewed(projectId);
 | 
			
		||||
    }, [projectId, setLastViewed]);
 | 
			
		||||
 | 
			
		||||
    if (featureSearchFrontend) return <InfiniteProjectOverview />;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledContainer>
 | 
			
		||||
            <ProjectInfo
 | 
			
		||||
@ -71,6 +115,7 @@ const ProjectOverview = () => {
 | 
			
		||||
                                features={features}
 | 
			
		||||
                                environments={environments}
 | 
			
		||||
                                loading={loading}
 | 
			
		||||
                                onChange={refetch}
 | 
			
		||||
                            />
 | 
			
		||||
                        )}
 | 
			
		||||
                        elseShow={() => (
 | 
			
		||||
 | 
			
		||||
@ -13,14 +13,18 @@ interface IUseFeatureSearchOutput {
 | 
			
		||||
    refetch: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const fallbackFeatures: { features: IFeatureToggleListItem[] } = {
 | 
			
		||||
    features: [],
 | 
			
		||||
};
 | 
			
		||||
const fallbackFeatures: { features: IFeatureToggleListItem[]; total: number } =
 | 
			
		||||
    {
 | 
			
		||||
        features: [],
 | 
			
		||||
        total: 0,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
export const useFeatureSearch = (
 | 
			
		||||
    cursor: string,
 | 
			
		||||
    projectId = '',
 | 
			
		||||
    options: SWRConfiguration = {},
 | 
			
		||||
): IUseFeatureSearchOutput => {
 | 
			
		||||
    const { KEY, fetcher } = getFeatureSearchFetcher();
 | 
			
		||||
    const { KEY, fetcher } = getFeatureSearchFetcher(projectId, cursor);
 | 
			
		||||
    const { data, error, mutate } = useSWR<IFeatureSearchResponse>(
 | 
			
		||||
        KEY,
 | 
			
		||||
        fetcher,
 | 
			
		||||
@ -39,9 +43,11 @@ export const useFeatureSearch = (
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const getFeatureSearchFetcher = () => {
 | 
			
		||||
const getFeatureSearchFetcher = (projectId: string, cursor: string) => {
 | 
			
		||||
    const KEY = `api/admin/search/features?projectId=${projectId}&cursor=${cursor}`;
 | 
			
		||||
 | 
			
		||||
    const fetcher = () => {
 | 
			
		||||
        const path = formatApiPath(`api/admin/search/features`);
 | 
			
		||||
        const path = formatApiPath(KEY);
 | 
			
		||||
        return fetch(path, {
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
        })
 | 
			
		||||
@ -49,8 +55,6 @@ const getFeatureSearchFetcher = () => {
 | 
			
		||||
            .then((res) => res.json());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const KEY = `api/admin/search/features`;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        fetcher,
 | 
			
		||||
        KEY,
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,8 @@ export type UiFlags = {
 | 
			
		||||
    playgroundImprovements?: boolean;
 | 
			
		||||
    featureSwitchRefactor?: boolean;
 | 
			
		||||
    scheduledConfigurationChanges?: boolean;
 | 
			
		||||
    featureSearchAPI?: boolean;
 | 
			
		||||
    featureSearchFrontend?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export interface IVersionInfo {
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,7 @@ exports[`should create default config 1`] = `
 | 
			
		||||
      "embedProxyFrontend": true,
 | 
			
		||||
      "featureNamingPattern": false,
 | 
			
		||||
      "featureSearchAPI": false,
 | 
			
		||||
      "featureSearchFrontend": false,
 | 
			
		||||
      "featureSwitchRefactor": false,
 | 
			
		||||
      "featuresExportImport": true,
 | 
			
		||||
      "filterInvalidClientMetrics": false,
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ export type IFlagKey =
 | 
			
		||||
    | 'playgroundImprovements'
 | 
			
		||||
    | 'featureSwitchRefactor'
 | 
			
		||||
    | 'featureSearchAPI'
 | 
			
		||||
    | 'featureSearchFrontend'
 | 
			
		||||
    | 'scheduledConfigurationChanges';
 | 
			
		||||
 | 
			
		||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
 | 
			
		||||
@ -168,6 +169,10 @@ const flags: IFlags = {
 | 
			
		||||
        process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_API,
 | 
			
		||||
        false,
 | 
			
		||||
    ),
 | 
			
		||||
    featureSearchFrontend: parseEnvVarBoolean(
 | 
			
		||||
        process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_FRONTEND,
 | 
			
		||||
        false,
 | 
			
		||||
    ),
 | 
			
		||||
    scheduledConfigurationChanges: parseEnvVarBoolean(
 | 
			
		||||
        process.env.UNLEASH_EXPERIMENTAL_SCHEDULED_CONFIGURATION_CHANGES,
 | 
			
		||||
        false,
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,7 @@ process.nextTick(async () => {
 | 
			
		||||
                        playgroundImprovements: true,
 | 
			
		||||
                        featureSwitchRefactor: true,
 | 
			
		||||
                        featureSearchAPI: true,
 | 
			
		||||
                        featureSearchFrontend: false,
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
                authentication: {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user