1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/frontend/src/component/project/Project/Project.tsx

301 lines
11 KiB
TypeScript
Raw Normal View History

import { useNavigate } from 'react-router';
2022-03-28 10:49:59 +02:00
import useProject from 'hooks/api/getters/useProject/useProject';
import useLoading from 'hooks/useLoading';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import {
StyledDiv,
StyledFavoriteIconButton,
StyledHeader,
StyledInnerContainer,
StyledName,
StyledProjectTitle,
StyledSeparator,
StyledTab,
StyledTabContainer,
StyledTopRow,
} from './Project.styles';
import { Box, Paper, Tabs, Typography, styled } from '@mui/material';
2023-09-08 09:21:11 +02:00
import { FileUpload } from '@mui/icons-material';
2022-03-28 10:49:59 +02:00
import useToast from 'hooks/useToast';
import useQueryParams from 'hooks/useQueryParams';
2022-12-15 13:43:06 +01:00
import { useEffect, useState } from 'react';
import ProjectEnvironment from '../ProjectEnvironment/ProjectEnvironment';
import { ProjectFeaturesArchive } from './ProjectFeaturesArchive/ProjectFeaturesArchive';
import ProjectOverview from './ProjectOverview';
import ProjectHealth from './ProjectHealth/ProjectHealth';
2022-03-28 10:49:59 +02:00
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
2023-09-08 09:21:11 +02:00
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import { DeleteProjectDialogue } from './DeleteProject/DeleteProjectDialogue';
2022-10-12 14:40:37 +02:00
import { ProjectLog } from './ProjectLog/ProjectLog';
import { ChangeRequestOverview } from 'component/changeRequest/ChangeRequestOverview/ChangeRequestOverview';
import { ProjectChangeRequests } from '../../changeRequest/ProjectChangeRequests/ProjectChangeRequests';
import { ProjectSettings } from './ProjectSettings/ProjectSettings';
import { useFavoriteProjectsApi } from 'hooks/api/actions/useFavoriteProjectsApi/useFavoriteProjectsApi';
import { ImportModal } from './Import/ImportModal';
2023-02-08 10:16:38 +01:00
import { IMPORT_BUTTON } from 'utils/testIds';
import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
import { Badge } from 'component/common/Badge/Badge';
import { ProjectDoraMetrics } from './ProjectDoraMetrics/ProjectDoraMetrics';
import { UiFlags } from 'interfaces/uiConfig';
const StyledBadge = styled(Badge)(({ theme }) => ({
position: 'absolute',
top: 5,
right: 20,
[theme.breakpoints.down('md')]: {
top: 2,
},
}));
interface ITab {
title: string;
path: string;
name: string;
flag?: keyof UiFlags;
new?: boolean;
isEnterprise?: boolean;
}
export const Project = () => {
const projectId = useRequiredPathParam('projectId');
const params = useQueryParams();
const { project, loading, error, refetch } = useProject(projectId);
const ref = useLoading(loading);
feat/rbac roles (#562) * feat: create screen * fix: import accordion summary * feat: add accordions * fix: add codebox * feat: select permissions * fix: permission checker * fix: update permission checker * feat: wire up role list * fix: change icon color in project roles list * fix: add color to icon in project roles * add confirm dialog on role deletion * feat: add created screen * fix: cleanup * fix: update access permissions * fix: update admin panel * feat: add edit screen * fix: use color from palette and show toast when fails * fix: refactor * feat: validation * feat: implement checked all * fix: experimental toast * fix: error handling * fix: toast * feat: unique name validation * fix: update toasts * fix: remove toast * fix: reset flag * fix: remove unused vars * fix: update tests * feat: add error icon for toast * fix: replace wrong import for setToastData * feat: Patch keying on ui to handle uniqueness for permissions across multiple envs * fix: hasAccess handles * * fix: update permission switch * fix: use flag for environments rbac * fix: do not include check all keys in payload * fix: filter roles * fix: account for new permissions in variants list * fix: use effect on length property * fix: set polling interval on user * 4.5.0-beta.0 * fix: set initial permissions correctly to avoid race condition * fix: handle activeEnvironment when it is null * fix: remove unused imports * fix: unused imports * fix: Include missing project in hasAccess for deleteinng a tag * fix: Move add/delete tag to use update feature permissions * fix: use rest parameter * fix: remove sandbox from scripts * 4.6.0-beta.1 * fix: remove loading deduping * fix: disable editing on builtin roles * fix: check all * fix: feature overview environment * fix: refetch user on project create * fix: update snaphots * fix: frontend permissions * fix: delete create confirm * fix: remove unused permission * 4.6.0-beta.4 * fix: update permissions * fix: permissions * fix: set error to string * 4.6.0-beta.5 * fix: add permissions for project view * fix: add permissions to useEffect deps * fix: update permission for move feature toggle * fix: add permissions data to useEffect * fix: move settings * fix: key on confetti * fix: refetch project permissions on environment create/delete * fix: optional coalescing error object * fix: remove logging error * fix: reorder disable importance in permissionbutton * fix: add project roles to menu * fix: add disabled check to revive * fix: update snapshots * fix: change text to select all * fix: change text to select * 4.6.0-beta.6 Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: sighphyre <liquidwicked64@gmail.com>
2022-01-14 15:50:02 +01:00
const { setToastData } = useToast();
const [modalOpen, setModalOpen] = useState(false);
const navigate = useNavigate();
const { pathname } = useLocation();
const { isOss, uiConfig, isPro } = useUiConfig();
const basePath = `/projects/${projectId}`;
const projectName = project?.name || projectId;
const { favorite, unfavorite } = useFavoriteProjectsApi();
const [showDelDialog, setShowDelDialog] = useState(false);
const tabs: ITab[] = [
2022-12-15 13:43:06 +01:00
{
title: 'Overview',
path: basePath,
name: 'overview',
},
{
title: 'Health',
path: `${basePath}/health`,
name: 'health',
},
{
title: 'Archive',
path: `${basePath}/archive`,
name: 'archive',
},
{
title: 'Change requests',
path: `${basePath}/change-requests`,
name: 'change-request',
isEnterprise: true,
},
{
title: 'Metrics',
path: `${basePath}/metrics`,
name: 'dora',
flag: 'doraMetrics',
new: true,
2022-12-15 13:43:06 +01:00
},
{
title: 'Event log',
path: `${basePath}/logs`,
name: 'logs',
},
{
title: 'Project settings',
path: `${basePath}/settings${isOss() ? '/environments' : ''}`,
name: 'settings',
},
];
const filteredTabs = tabs
.filter((tab) => {
if (tab.flag) {
return uiConfig.flags[tab.flag];
}
return true;
})
.filter((tab) => !(isOss() && tab.isEnterprise));
const activeTab = [...filteredTabs]
.reverse()
.find((tab) => pathname.startsWith(tab.path));
useEffect(() => {
const created = params.get('created');
const edited = params.get('edited');
if (created || edited) {
const text = created ? 'Project created' : 'Project updated';
setToastData({
type: 'success',
feat/rbac roles (#562) * feat: create screen * fix: import accordion summary * feat: add accordions * fix: add codebox * feat: select permissions * fix: permission checker * fix: update permission checker * feat: wire up role list * fix: change icon color in project roles list * fix: add color to icon in project roles * add confirm dialog on role deletion * feat: add created screen * fix: cleanup * fix: update access permissions * fix: update admin panel * feat: add edit screen * fix: use color from palette and show toast when fails * fix: refactor * feat: validation * feat: implement checked all * fix: experimental toast * fix: error handling * fix: toast * feat: unique name validation * fix: update toasts * fix: remove toast * fix: reset flag * fix: remove unused vars * fix: update tests * feat: add error icon for toast * fix: replace wrong import for setToastData * feat: Patch keying on ui to handle uniqueness for permissions across multiple envs * fix: hasAccess handles * * fix: update permission switch * fix: use flag for environments rbac * fix: do not include check all keys in payload * fix: filter roles * fix: account for new permissions in variants list * fix: use effect on length property * fix: set polling interval on user * 4.5.0-beta.0 * fix: set initial permissions correctly to avoid race condition * fix: handle activeEnvironment when it is null * fix: remove unused imports * fix: unused imports * fix: Include missing project in hasAccess for deleteinng a tag * fix: Move add/delete tag to use update feature permissions * fix: use rest parameter * fix: remove sandbox from scripts * 4.6.0-beta.1 * fix: remove loading deduping * fix: disable editing on builtin roles * fix: check all * fix: feature overview environment * fix: refetch user on project create * fix: update snaphots * fix: frontend permissions * fix: delete create confirm * fix: remove unused permission * 4.6.0-beta.4 * fix: update permissions * fix: permissions * fix: set error to string * 4.6.0-beta.5 * fix: add permissions for project view * fix: add permissions to useEffect deps * fix: update permission for move feature toggle * fix: add permissions data to useEffect * fix: move settings * fix: key on confetti * fix: refetch project permissions on environment create/delete * fix: optional coalescing error object * fix: remove logging error * fix: reorder disable importance in permissionbutton * fix: add project roles to menu * fix: add disabled check to revive * fix: update snapshots * fix: change text to select all * fix: change text to select * 4.6.0-beta.6 Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: sighphyre <liquidwicked64@gmail.com>
2022-01-14 15:50:02 +01:00
title: text,
});
}
/* eslint-disable-next-line */
}, []);
if (error?.status === 404) {
return (
<Paper sx={(theme) => ({ padding: theme.spacing(2, 4, 4) })}>
<Typography variant='h1'>404 Not Found</Typography>
<Typography>
Project <strong>{projectId}</strong> does not exist.
</Typography>
</Paper>
);
}
const onFavorite = async () => {
if (project?.favorite) {
await unfavorite(projectId);
} else {
await favorite(projectId);
}
refetch();
};
const enterpriseIcon = (
<Box
sx={(theme) => ({
marginLeft: theme.spacing(1),
display: 'flex',
})}
>
<EnterpriseBadge />
</Box>
);
return (
2023-01-04 10:24:39 +01:00
<div ref={ref}>
<StyledHeader>
<StyledInnerContainer>
<StyledTopRow>
<StyledDiv>
2022-12-21 13:03:06 +01:00
<StyledFavoriteIconButton
onClick={onFavorite}
isFavorite={project?.favorite}
/>
<StyledProjectTitle>
<StyledName data-loading>
{projectName}
</StyledName>
</StyledProjectTitle>
</StyledDiv>
<StyledDiv>
2023-03-29 08:19:33 +02:00
<ConditionallyRender
condition={Boolean(
uiConfig?.flags?.featuresExportImport,
2023-03-29 08:19:33 +02:00
)}
show={
<PermissionIconButton
permission={UPDATE_FEATURE}
projectId={projectId}
onClick={() => setModalOpen(true)}
tooltipProps={{ title: 'Import' }}
data-testid={IMPORT_BUTTON}
data-loading
>
<FileUpload />
</PermissionIconButton>
}
2023-02-16 08:08:51 +01:00
/>
</StyledDiv>
</StyledTopRow>
</StyledInnerContainer>
<StyledSeparator />
<StyledTabContainer>
<Tabs
value={activeTab?.path}
indicatorColor='primary'
textColor='primary'
variant='scrollable'
2022-12-07 12:52:17 +01:00
allowScrollButtonsMobile
>
{filteredTabs.map((tab) => {
return (
<StyledTab
key={tab.title}
label={tab.title}
value={tab.path}
onClick={() => navigate(tab.path)}
data-testid={`TAB_${tab.title}`}
iconPosition={
tab.isEnterprise ? 'end' : undefined
}
icon={
<>
<ConditionallyRender
condition={Boolean(tab.new)}
show={
<StyledBadge color='success'>
New
</StyledBadge>
}
/>
{(tab.isEnterprise &&
isPro() &&
enterpriseIcon) ||
undefined}
</>
}
/>
);
})}
</Tabs>
</StyledTabContainer>
</StyledHeader>
<DeleteProjectDialogue
project={projectId}
open={showDelDialog}
onClose={() => {
setShowDelDialog(false);
}}
onSuccess={() => {
navigate('/projects');
}}
/>
<Routes>
<Route path='health' element={<ProjectHealth />} />
<Route
path='access/*'
element={
<Navigate
replace
to={`/projects/${projectId}/settings/access`}
/>
}
/>
<Route path='environments' element={<ProjectEnvironment />} />
<Route path='archive' element={<ProjectFeaturesArchive />} />
<Route path='logs' element={<ProjectLog />} />
<Route
path='change-requests'
2022-12-15 13:43:06 +01:00
element={<ProjectChangeRequests />}
/>
<Route
path='change-requests/:id'
2022-12-15 13:43:06 +01:00
element={<ChangeRequestOverview />}
/>
<Route path='settings/*' element={<ProjectSettings />} />
<Route path='metrics' element={<ProjectDoraMetrics />} />
<Route path='*' element={<ProjectOverview />} />
</Routes>
<ImportModal
open={modalOpen}
setOpen={setModalOpen}
project={projectId}
/>
2023-01-04 10:24:39 +01:00
</div>
);
};