diff --git a/frontend/src/component/App.tsx b/frontend/src/component/App.tsx index 44d2e60917..3313833635 100644 --- a/frontend/src/component/App.tsx +++ b/frontend/src/component/App.tsx @@ -13,15 +13,21 @@ import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; import { SplashPageRedirect } from 'component/splash/SplashPageRedirect/SplashPageRedirect'; import { useStyles } from './App.styles'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; export const App = () => { const { classes: styles } = useStyles(); const { authDetails } = useAuthDetails(); const { user } = useAuthUser(); + const { isOss } = useUiConfig(); const isLoggedIn = Boolean(user?.id); const hasFetchedAuth = Boolean(authDetails || user); usePlausibleTracker(); + const availableRoutes = isOss() + ? routes.filter(route => !route.enterprise) + : routes; + return ( { - {routes.map(route => ( + {availableRoutes.map(route => ( ; + sx?: IconButtonProps['sx']; size?: string; } diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index c969705c34..6c49570e44 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -11,6 +11,7 @@ exports[`returns all baseRoutes 1`] = ` }, { "component": [Function], + "enterprise": true, "menu": {}, "parent": "/projects", "path": "/projects/create", @@ -19,6 +20,7 @@ exports[`returns all baseRoutes 1`] = ` }, { "component": [Function], + "enterprise": true, "menu": {}, "parent": "/projects", "path": "/projects/:projectId/edit", diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index eda5e0027b..1e51189519 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -70,6 +70,7 @@ export const routes: IRoute[] = [ title: 'Create', component: CreateProject, type: 'protected', + enterprise: true, menu: {}, }, { @@ -78,6 +79,7 @@ export const routes: IRoute[] = [ title: ':projectId', component: EditProject, type: 'protected', + enterprise: true, menu: {}, }, { diff --git a/frontend/src/component/project/Project/EditProject/EditProject.tsx b/frontend/src/component/project/Project/EditProject/EditProject.tsx index 4fbc658526..149c6b4369 100644 --- a/frontend/src/component/project/Project/EditProject/EditProject.tsx +++ b/frontend/src/component/project/Project/EditProject/EditProject.tsx @@ -10,13 +10,18 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; +import { useContext } from 'react'; +import AccessContext from 'contexts/AccessContext'; +import { Alert } from '@mui/material'; const EditProject = () => { const { uiConfig } = useUiConfig(); const { setToastData, setToastApiError } = useToast(); + const { hasAccess } = useContext(AccessContext); const id = useRequiredPathParam('projectId'); const { project } = useProject(id); const navigate = useNavigate(); + const { projectId, projectName, @@ -68,6 +73,12 @@ const EditProject = () => { navigate(-1); }; + const accessDeniedAlert = !hasAccess(UPDATE_PROJECT, projectId) && ( + + You do not have the required permissions to edit this project. + + ); + return ( { documentationLinkLabel="Projects documentation" formatApiCode={formatApiCode} > + {accessDeniedAlert} { clearErrors={clearErrors} validateProjectId={validateProjectId} > - + ); diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx index 91554d4183..b29e033c5a 100644 --- a/frontend/src/component/project/Project/Project.tsx +++ b/frontend/src/component/project/Project/Project.tsx @@ -19,6 +19,7 @@ import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions'; import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; const Project = () => { const projectId = useRequiredPathParam('projectId'); @@ -29,6 +30,7 @@ const Project = () => { const { setToastData } = useToast(); const { classes: styles } = useStyles(); const navigate = useNavigate(); + const { isOss } = useUiConfig(); const basePath = `/projects/${projectId}`; const tabData = [ @@ -118,7 +120,8 @@ const Project = () => { navigate(`/projects/${projectId}/edit`) } diff --git a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx index 5824fb3d99..5c031eeed8 100644 --- a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx +++ b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx @@ -33,7 +33,7 @@ const ProjectInfo = ({ }: IProjectInfoProps) => { const { classes: themeStyles } = useThemeStyles(); const { classes: styles } = useStyles(); - const { uiConfig } = useUiConfig(); + const { uiConfig, isOss } = useUiConfig(); let link = `/admin/users`; @@ -52,6 +52,7 @@ const ProjectInfo = ({
{ + onClose={event => { + event.preventDefault(); setAnchorEl(null); setShowDelDialog(false); }} diff --git a/frontend/src/component/project/ProjectList/ProjectList.tsx b/frontend/src/component/project/ProjectList/ProjectList.tsx index f635212353..43deaecebb 100644 --- a/frontend/src/component/project/ProjectList/ProjectList.tsx +++ b/frontend/src/component/project/ProjectList/ProjectList.tsx @@ -118,12 +118,12 @@ export const ProjectListNew = () => { className={styles.cardLink} > handleHover(project?.id)} - name={project?.name} - memberCount={project?.memberCount ?? 0} - health={project?.health} - id={project?.id} - featureCount={project?.featureCount} + onHover={() => handleHover(project.id)} + name={project.name} + memberCount={project.memberCount ?? 0} + health={project.health} + id={project.id} + featureCount={project.featureCount} /> ); diff --git a/frontend/src/component/providers/AccessProvider/permissions.ts b/frontend/src/component/providers/AccessProvider/permissions.ts index b96a67c12e..6dee17bf48 100644 --- a/frontend/src/component/providers/AccessProvider/permissions.ts +++ b/frontend/src/component/providers/AccessProvider/permissions.ts @@ -11,6 +11,7 @@ export const UPDATE_CONTEXT_FIELD = 'UPDATE_CONTEXT_FIELD'; export const DELETE_CONTEXT_FIELD = 'DELETE_CONTEXT_FIELD'; export const CREATE_PROJECT = 'CREATE_PROJECT'; export const UPDATE_PROJECT = 'UPDATE_PROJECT'; +export const DELETE_PROJECT = 'DELETE_PROJECT'; export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE'; export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE'; export const CREATE_ADDON = 'CREATE_ADDON'; diff --git a/frontend/src/hooks/api/getters/useDefaultProject/useDefaultProjectId.ts b/frontend/src/hooks/api/getters/useDefaultProject/useDefaultProjectId.ts index 1a5542f178..55304f6110 100644 --- a/frontend/src/hooks/api/getters/useDefaultProject/useDefaultProjectId.ts +++ b/frontend/src/hooks/api/getters/useDefaultProject/useDefaultProjectId.ts @@ -1,7 +1,8 @@ import useProjects from 'hooks/api/getters/useProjects/useProjects'; -const DEFAULT_PROJECT_ID = 'default'; +export const DEFAULT_PROJECT_ID = 'default'; +// Get the default project ID, or the first ID if there is no default project. export const useDefaultProjectId = (): string | undefined => { const { projects = [] } = useProjects(); diff --git a/frontend/src/interfaces/route.ts b/frontend/src/interfaces/route.ts index c924eb31c4..213bec4a51 100644 --- a/frontend/src/interfaces/route.ts +++ b/frontend/src/interfaces/route.ts @@ -8,6 +8,7 @@ export interface IRoute { parent?: string; flag?: string; hidden?: boolean; + enterprise?: boolean; component: VoidFunctionComponent; menu: IRouteMenu; } diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts index 25031048cd..9c7e0a476f 100644 --- a/frontend/src/themes/theme.ts +++ b/frontend/src/themes/theme.ts @@ -265,5 +265,14 @@ export default createTheme({ }, }, }, + MuiMenuItem: { + styleOverrides: { + root: { + '&.Mui-disabled': { + opacity: 0.66, + }, + }, + }, + }, }, });