mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
refactor: replace useProject with useProjectOverview (#7087)
This commit is contained in:
parent
ee92001bf5
commit
8a6daeee1e
@ -13,8 +13,10 @@ import { CF_CREATE_BTN_ID } from 'utils/testIds';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { GO_BACK } from 'constants/navigate';
|
||||
import { Alert, styled } from '@mui/material';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import useProjectOverview, {
|
||||
featuresCount,
|
||||
} from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
marginBottom: theme.spacing(2),
|
||||
@ -54,7 +56,7 @@ const CreateFeature = () => {
|
||||
errors,
|
||||
} = useFeatureForm();
|
||||
|
||||
const { project: projectInfo } = useProject(project);
|
||||
const { project: projectInfo } = useProjectOverview(project);
|
||||
|
||||
const { createFeatureToggle, loading } = useFeatureApi();
|
||||
|
||||
@ -98,7 +100,7 @@ const CreateFeature = () => {
|
||||
|
||||
const featureLimitReached = isFeatureLimitReached(
|
||||
projectInfo.featureLimit,
|
||||
projectInfo.features.length,
|
||||
featuresCount(projectInfo),
|
||||
);
|
||||
return (
|
||||
<FormTemplate
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import type { IFeatureToggle } from 'interfaces/featureToggle';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Dialogue } from 'component/common/Dialogue/Dialogue';
|
||||
@ -9,6 +8,7 @@ import { Link } from 'react-router-dom';
|
||||
import type { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
|
||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
display: 'grid',
|
||||
@ -41,7 +41,7 @@ const FeatureSettingsProjectConfirm = ({
|
||||
changeRequests,
|
||||
}: IFeatureSettingsProjectConfirm) => {
|
||||
const currentProjectId = useRequiredPathParam('projectId');
|
||||
const { project } = useProject(projectId);
|
||||
const { project } = useProjectOverview(projectId);
|
||||
|
||||
const { isChangeRequestConfiguredInAnyEnv } =
|
||||
useChangeRequestsEnabled(projectId);
|
||||
|
@ -2,7 +2,6 @@ import {
|
||||
PROJECT_SETTINGS_WRITE,
|
||||
UPDATE_PROJECT,
|
||||
} from 'component/providers/AccessProvider/permissions';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useContext } from 'react';
|
||||
@ -12,6 +11,9 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
||||
import { UpdateEnterpriseSettings } from './UpdateEnterpriseSettings';
|
||||
import { UpdateProject } from './UpdateProject';
|
||||
import { DeleteProjectForm } from './DeleteProjectForm';
|
||||
import useProjectOverview, {
|
||||
featuresCount,
|
||||
} from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const StyledFormContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -23,7 +25,7 @@ const EditProject = () => {
|
||||
const { isEnterprise } = useUiConfig();
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const id = useRequiredPathParam('projectId');
|
||||
const { project } = useProject(id);
|
||||
const { project } = useProjectOverview(id);
|
||||
|
||||
if (!project.name) {
|
||||
return null;
|
||||
@ -47,7 +49,7 @@ const EditProject = () => {
|
||||
condition={isEnterprise()}
|
||||
show={<UpdateEnterpriseSettings project={project} />}
|
||||
/>
|
||||
<DeleteProjectForm featureCount={project.features.length} />
|
||||
<DeleteProjectForm featureCount={featuresCount(project)} />
|
||||
</StyledFormContainer>
|
||||
</>
|
||||
);
|
||||
|
@ -3,16 +3,16 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useToast from 'hooks/useToast';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import useProjectEnterpriseSettingsForm from 'component/project/Project/hooks/useProjectEnterpriseSettingsForm';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||
import ProjectEnterpriseSettingsForm from 'component/project/Project/ProjectEnterpriseSettingsForm/ProjectEnterpriseSettingsForm';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import type { IProject } from 'component/../interfaces/project';
|
||||
import type { IProjectOverview } from 'component/../interfaces/project';
|
||||
import { styled } from '@mui/material';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
minHeight: 0,
|
||||
@ -34,7 +34,7 @@ const StyledFormContainer = styled('div')(({ theme }) => ({
|
||||
}));
|
||||
|
||||
interface IUpdateEnterpriseSettings {
|
||||
project: IProject;
|
||||
project: IProjectOverview;
|
||||
}
|
||||
const EDIT_PROJECT_SETTINGS_BTN = 'EDIT_PROJECT_SETTINGS_BTN';
|
||||
|
||||
@ -89,7 +89,7 @@ export const UpdateEnterpriseSettings = ({
|
||||
--data-raw '${JSON.stringify(getEnterpriseSettingsPayload(), undefined, 2)}'`;
|
||||
};
|
||||
|
||||
const { refetch } = useProject(id);
|
||||
const { refetch } = useProjectOverview(id);
|
||||
const { editProjectSettings, loading } = useProjectApi();
|
||||
|
||||
const useFeatureNamePatternTracking = () => {
|
||||
|
@ -14,10 +14,10 @@ import useToast from 'hooks/useToast';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import type { IProject } from 'interfaces/project';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import type { IProjectOverview } from 'interfaces/project';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { styled } from '@mui/material';
|
||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const StyledContainer = styled('div')<{ isPro: boolean }>(
|
||||
({ theme, isPro }) => ({
|
||||
@ -41,7 +41,7 @@ const StyledFormContainer = styled('div')(({ theme }) => ({
|
||||
}));
|
||||
|
||||
interface IUpdateProject {
|
||||
project: IProject;
|
||||
project: IProjectOverview;
|
||||
}
|
||||
const EDIT_PROJECT_BTN = 'EDIT_PROJECT_BTN';
|
||||
export const UpdateProject = ({ project }: IUpdateProject) => {
|
||||
@ -75,7 +75,7 @@ export const UpdateProject = ({ project }: IUpdateProject) => {
|
||||
);
|
||||
|
||||
const { editProject, loading } = useProjectApi();
|
||||
const { refetch } = useProject(id);
|
||||
const { refetch } = useProjectOverview(id);
|
||||
|
||||
const formatProjectApiCode = () => {
|
||||
return `curl --location --request PUT '${
|
||||
|
@ -6,7 +6,6 @@ import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import ApiError from 'component/common/ApiError/ApiError';
|
||||
import useToast from 'hooks/useToast';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import useProject from 'hooks/api/getters/useProject/useProject';
|
||||
import { Alert, styled, TableBody, TableRow } from '@mui/material';
|
||||
import useProjectApi from 'hooks/api/actions/useProjectApi/useProjectApi';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -30,7 +29,9 @@ import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
||||
import { EnvironmentHideDialog } from './EnvironmentHideDialog/EnvironmentHideDialog';
|
||||
import { useProjectEnvironments } from 'hooks/api/getters/useProjectEnvironments/useProjectEnvironments';
|
||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||
import { useProjectOverviewNameOrId } from '../../../hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
import useProjectOverview, {
|
||||
useProjectOverviewNameOrId,
|
||||
} from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
marginBottom: theme.spacing(4),
|
||||
@ -58,7 +59,7 @@ const ProjectEnvironmentList = () => {
|
||||
const { setToastData, setToastApiError } = useToast();
|
||||
const { environments, loading, error, refetchEnvironments } =
|
||||
useProjectEnvironments(projectId);
|
||||
const { project, refetch: refetchProject } = useProject(projectId);
|
||||
const { project, refetch: refetchProject } = useProjectOverview(projectId);
|
||||
const { removeEnvironmentFromProject, addEnvironmentToProject } =
|
||||
useProjectApi();
|
||||
|
||||
|
@ -40,8 +40,7 @@ export const ProjectGroup: React.FC<{
|
||||
projects: IProjectCard[];
|
||||
loading: boolean;
|
||||
searchValue: string;
|
||||
handleHover: (id: string) => void;
|
||||
}> = ({ sectionTitle, projects, loading, searchValue, handleHover }) => {
|
||||
}> = ({ sectionTitle, projects, loading, searchValue }) => {
|
||||
const useNewProjectCards = useUiFlag('projectsListNewCards');
|
||||
|
||||
const [StyledItemsContainer, ProjectCard] = useNewProjectCards
|
||||
@ -108,9 +107,7 @@ export const ProjectGroup: React.FC<{
|
||||
to={`/projects/${project.id}`}
|
||||
>
|
||||
<ProjectCard
|
||||
onHover={() =>
|
||||
handleHover(project.id)
|
||||
}
|
||||
onHover={() => {}}
|
||||
name={project.name}
|
||||
mode={project.mode}
|
||||
memberCount={
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { mutate } from 'swr';
|
||||
import { getProjectFetcher } from 'hooks/api/getters/useProject/getProjectFetcher';
|
||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import type { IProjectCard } from 'interfaces/project';
|
||||
@ -13,7 +11,7 @@ import { CREATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import Add from '@mui/icons-material/Add';
|
||||
import ApiError from 'component/common/ApiError/ApiError';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useMediaQuery, styled } from '@mui/material';
|
||||
import { styled, useMediaQuery } from '@mui/material';
|
||||
import theme from 'themes/theme';
|
||||
import { Search } from 'component/common/Search/Search';
|
||||
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
|
||||
@ -93,7 +91,6 @@ export const ProjectListNew = () => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const navigate = useNavigate();
|
||||
const { projects, loading, error, refetch } = useProjects();
|
||||
const [fetchedProjects, setFetchedProjects] = useState<projectMap>({});
|
||||
const { isOss } = useUiConfig();
|
||||
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
@ -144,16 +141,6 @@ export const ProjectListNew = () => {
|
||||
return groupProjects(myProjects, filteredProjects);
|
||||
}, [filteredProjects, myProjects, splitProjectList]);
|
||||
|
||||
const handleHover = (projectId: string) => {
|
||||
if (fetchedProjects[projectId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { KEY, fetcher } = getProjectFetcher(projectId);
|
||||
mutate(KEY, fetcher);
|
||||
setFetchedProjects((prev) => ({ ...prev, [projectId]: true }));
|
||||
};
|
||||
|
||||
const createButtonData = resolveCreateButtonData(
|
||||
isOss(),
|
||||
hasAccess(CREATE_PROJECT),
|
||||
@ -172,7 +159,6 @@ export const ProjectListNew = () => {
|
||||
<ProjectGroup
|
||||
loading={loading}
|
||||
searchValue={searchValue}
|
||||
handleHover={handleHover}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { formatApiPath } from 'utils/formatPath';
|
||||
import handleErrorResponses from '../httpErrorResponseHandler';
|
||||
|
||||
export const getProjectFetcher = (id: string) => {
|
||||
const fetcher = () => {
|
||||
const path = formatApiPath(`api/admin/projects/${id}`);
|
||||
return fetch(path, {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(handleErrorResponses('Project overview'))
|
||||
.then((res) => res.json());
|
||||
};
|
||||
|
||||
const KEY = `api/admin/projects/${id}`;
|
||||
|
||||
return {
|
||||
fetcher,
|
||||
KEY,
|
||||
};
|
||||
};
|
@ -1,55 +0,0 @@
|
||||
import useSWR, { type SWRConfiguration } from 'swr';
|
||||
import { useCallback } from 'react';
|
||||
import { getProjectFetcher } from './getProjectFetcher';
|
||||
import type { IProject } from 'interfaces/project';
|
||||
|
||||
const fallbackProject: IProject = {
|
||||
features: [],
|
||||
environments: [],
|
||||
name: '',
|
||||
health: 0,
|
||||
members: 0,
|
||||
version: '1',
|
||||
description: 'Default',
|
||||
favorite: false,
|
||||
mode: 'open',
|
||||
defaultStickiness: 'default',
|
||||
featureLimit: 0,
|
||||
featureNaming: {
|
||||
description: 'asd',
|
||||
example: 'A',
|
||||
pattern: '[A-z]',
|
||||
},
|
||||
stats: {
|
||||
archivedCurrentWindow: 0,
|
||||
archivedPastWindow: 0,
|
||||
avgTimeToProdCurrentWindow: 0,
|
||||
createdCurrentWindow: 0,
|
||||
createdPastWindow: 0,
|
||||
projectActivityCurrentWindow: 0,
|
||||
projectActivityPastWindow: 0,
|
||||
projectMembersAddedCurrentWindow: 0,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated It is recommended to use useProjectOverview instead, unless you need project features.
|
||||
* In that case, we should create a project features endpoint and use that instead if features needed.
|
||||
*/
|
||||
const useProject = (id: string, options: SWRConfiguration = {}) => {
|
||||
const { KEY, fetcher } = getProjectFetcher(id);
|
||||
const { data, error, mutate } = useSWR<IProject>(KEY, fetcher, options);
|
||||
|
||||
const refetch = useCallback(() => {
|
||||
mutate();
|
||||
}, [mutate]);
|
||||
|
||||
return {
|
||||
project: data || fallbackProject,
|
||||
loading: !error && !data,
|
||||
error,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
export default useProject;
|
@ -0,0 +1,16 @@
|
||||
import { featuresCount } from './useProjectOverview';
|
||||
|
||||
test('features count based on feature types', () => {
|
||||
expect(
|
||||
featuresCount({
|
||||
featureTypeCounts: [
|
||||
{ type: 'release', count: 10 },
|
||||
{ type: 'operational', count: 20 },
|
||||
],
|
||||
}),
|
||||
).toBe(30);
|
||||
expect(
|
||||
featuresCount({ featureTypeCounts: [{ type: 'release', count: 10 }] }),
|
||||
).toBe(10);
|
||||
expect(featuresCount({ featureTypeCounts: [] })).toBe(0);
|
||||
});
|
@ -50,4 +50,12 @@ export const useProjectOverviewNameOrId = (id: string): string => {
|
||||
return useProjectOverview(id).project.name || id;
|
||||
};
|
||||
|
||||
export const featuresCount = (
|
||||
project: Pick<IProjectOverview, 'featureTypeCounts'>,
|
||||
) => {
|
||||
return project.featureTypeCounts
|
||||
.map((count) => count.count)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
};
|
||||
|
||||
export default useProjectOverview;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import useProject from './api/getters/useProject/useProject';
|
||||
import useProjectOverview from './api/getters/useProjectOverview/useProjectOverview';
|
||||
|
||||
const DEFAULT_STICKINESS = 'default';
|
||||
export const useDefaultProjectSettings = (projectId: string) => {
|
||||
const { project, loading, error } = useProject(projectId);
|
||||
const { project, loading, error } = useProjectOverview(projectId);
|
||||
return {
|
||||
defaultStickiness: project.defaultStickiness
|
||||
? project.defaultStickiness
|
||||
|
Loading…
Reference in New Issue
Block a user