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