From 584b9323ec07bc8feadd0dbbdd9f463a04ac9a74 Mon Sep 17 00:00:00 2001 From: kattoczko <50657366+kattoczko@users.noreply.github.com> Date: Thu, 5 Oct 2023 09:55:00 +0100 Subject: [PATCH] feat: introduce admin apps page (#1296) * feat: introduce admin apps page * feat: add access restriction and fix incorrectly placed key prop --- packages/web/src/adminSettingsRoutes.tsx | 12 ++++ .../components/AdminSettingsLayout/index.tsx | 13 ++++ packages/web/src/components/AppRow/index.tsx | 10 +-- packages/web/src/config/urls.ts | 2 + packages/web/src/graphql/queries/get-apps.ts | 1 + packages/web/src/locales/en.json | 4 +- .../web/src/pages/AdminApplications/index.tsx | 63 +++++++++++++++++++ packages/web/src/pages/Applications/index.tsx | 4 +- 8 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 packages/web/src/pages/AdminApplications/index.tsx diff --git a/packages/web/src/adminSettingsRoutes.tsx b/packages/web/src/adminSettingsRoutes.tsx index fadcab61..de392567 100644 --- a/packages/web/src/adminSettingsRoutes.tsx +++ b/packages/web/src/adminSettingsRoutes.tsx @@ -11,6 +11,7 @@ import UserInterface from 'pages/UserInterface'; import * as URLS from 'config/urls'; import Can from 'components/Can'; +import AdminApplications from 'pages/AdminApplications'; // TODO: consider introducing redirections to `/` as fallback export default ( @@ -107,6 +108,17 @@ export default ( } /> + + + + + + } + /> + } diff --git a/packages/web/src/components/AdminSettingsLayout/index.tsx b/packages/web/src/components/AdminSettingsLayout/index.tsx index c56adf84..e24fe743 100644 --- a/packages/web/src/components/AdminSettingsLayout/index.tsx +++ b/packages/web/src/components/AdminSettingsLayout/index.tsx @@ -3,6 +3,8 @@ import GroupIcon from '@mui/icons-material/Group'; import GroupsIcon from '@mui/icons-material/Groups'; import LockIcon from '@mui/icons-material/LockPerson'; import BrushIcon from '@mui/icons-material/Brush'; +import AppsIcon from '@mui/icons-material/Apps'; + import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; import { useTheme } from '@mui/material/styles'; @@ -30,11 +32,13 @@ function createDrawerLinks({ canReadUser, canUpdateConfig, canManageSamlAuthProvider, + canUpdateApp, }: { canReadRole: boolean; canReadUser: boolean; canUpdateConfig: boolean; canManageSamlAuthProvider: boolean; + canUpdateApp: boolean; }) { const items = [ canReadUser @@ -69,6 +73,14 @@ function createDrawerLinks({ dataTest: 'authentication-drawer-link', } : null, + canUpdateApp + ? { + Icon: AppsIcon, + primary: 'adminSettingsDrawer.apps', + to: URLS.ADMIN_APPS, + dataTest: 'apps-drawer-link', + } + : null, ].filter(Boolean) as DrawerLink[]; return items; @@ -101,6 +113,7 @@ export default function SettingsLayout({ currentUserAbility.can('read', 'SamlAuthProvider') && currentUserAbility.can('update', 'SamlAuthProvider') && currentUserAbility.can('create', 'SamlAuthProvider'), + canUpdateApp: currentUserAbility.can('update', 'App'), }); return ( diff --git a/packages/web/src/components/AppRow/index.tsx b/packages/web/src/components/AppRow/index.tsx index 0ac728d1..e92064ae 100644 --- a/packages/web/src/components/AppRow/index.tsx +++ b/packages/web/src/components/AppRow/index.tsx @@ -7,13 +7,13 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import useFormatMessage from 'hooks/useFormatMessage'; import AppIcon from 'components/AppIcon'; -import * as URLS from 'config/urls'; import type { IApp } from '@automatisch/types'; import { CardContent, Typography } from './style'; type AppRowProps = { application: IApp; + url: string; }; const countTranslation = (value: React.ReactNode) => ( @@ -25,11 +25,11 @@ const countTranslation = (value: React.ReactNode) => ( function AppRow(props: AppRowProps): React.ReactElement { const formatMessage = useFormatMessage(); - const { name, key, primaryColor, iconUrl, connectionCount, flowCount } = + const { name, primaryColor, iconUrl, connectionCount, flowCount } = props.application; return ( - + @@ -48,7 +48,7 @@ function AppRow(props: AppRowProps): React.ReactElement { sx={{ display: ['none', 'inline-block'] }} > {formatMessage('app.connectionCount', { - count: countTranslation(connectionCount), + count: countTranslation(connectionCount || '-'), })} @@ -60,7 +60,7 @@ function AppRow(props: AppRowProps): React.ReactElement { sx={{ display: ['none', 'inline-block'] }} > {formatMessage('app.flowCount', { - count: countTranslation(flowCount), + count: countTranslation(flowCount || '-'), })} diff --git a/packages/web/src/config/urls.ts b/packages/web/src/config/urls.ts index b11d12be..1fd7d978 100644 --- a/packages/web/src/config/urls.ts +++ b/packages/web/src/config/urls.ts @@ -95,6 +95,8 @@ export const ROLE_PATTERN = `${ROLES}/:roleId`; export const CREATE_ROLE = `${ROLES}/create`; export const USER_INTERFACE = `${ADMIN_SETTINGS}/user-interface`; export const AUTHENTICATION = `${ADMIN_SETTINGS}/authentication`; +export const ADMIN_APPS = `${ADMIN_SETTINGS}/apps`; +export const ADMIN_APP = (appKey: string) => `${ADMIN_SETTINGS}/apps/${appKey}`; export const DASHBOARD = FLOWS; diff --git a/packages/web/src/graphql/queries/get-apps.ts b/packages/web/src/graphql/queries/get-apps.ts index 62f9ed4a..de417dac 100644 --- a/packages/web/src/graphql/queries/get-apps.ts +++ b/packages/web/src/graphql/queries/get-apps.ts @@ -18,6 +18,7 @@ export const GET_APPS = gql` authDocUrl primaryColor connectionCount + flowCount supportsConnections auth { fields { diff --git a/packages/web/src/locales/en.json b/packages/web/src/locales/en.json index a5299731..b9f9ec93 100644 --- a/packages/web/src/locales/en.json +++ b/packages/web/src/locales/en.json @@ -18,6 +18,7 @@ "adminSettingsDrawer.authentication": "Authentication", "adminSettingsDrawer.userInterface": "User Interface", "adminSettingsDrawer.goBack": "Go to the dashboard", + "adminSettingsDrawer.apps": "Applications", "app.connectionCount": "{count} connections", "app.flowCount": "{count} flows", "app.addConnection": "Add connection", @@ -246,5 +247,6 @@ "roleMappingsForm.appendRoleMapping": "Append", "roleMappingsForm.save": "Save", "roleMappingsForm.notFound": "No role mappings have found.", - "roleMappingsForm.successfullySaved": "Role mappings have been saved." + "roleMappingsForm.successfullySaved": "Role mappings have been saved.", + "adminApps.title": "Apps" } diff --git a/packages/web/src/pages/AdminApplications/index.tsx b/packages/web/src/pages/AdminApplications/index.tsx new file mode 100644 index 00000000..b4ecb328 --- /dev/null +++ b/packages/web/src/pages/AdminApplications/index.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import Grid from '@mui/material/Grid'; +import CircularProgress from '@mui/material/CircularProgress'; +import Divider from '@mui/material/Divider'; +import { useQuery } from '@apollo/client'; +import { IApp } from '@automatisch/types'; + +import PageTitle from 'components/PageTitle'; +import Container from 'components/Container'; +import SearchInput from 'components/SearchInput'; +import AppRow from 'components/AppRow'; + +import * as URLS from 'config/urls'; +import useFormatMessage from 'hooks/useFormatMessage'; +import { GET_APPS } from 'graphql/queries/get-apps'; + +function AdminApplications() { + const formatMessage = useFormatMessage(); + const [appName, setAppName] = React.useState(null); + const { data, loading: appsLoading } = useQuery(GET_APPS, { + variables: { name: appName }, + }); + const apps = data?.getApps; + + const onSearchChange = React.useCallback((event) => { + setAppName(event.target.value); + }, []); + + return ( + + + + + {formatMessage('adminApps.title')} + + + + + + + + + + + {appsLoading && ( + + )} + + {!appsLoading && + apps?.map((app: IApp) => ( + + + + ))} + + + ); +} + +export default AdminApplications; diff --git a/packages/web/src/pages/Applications/index.tsx b/packages/web/src/pages/Applications/index.tsx index fcb08cf6..e6b6749b 100644 --- a/packages/web/src/pages/Applications/index.tsx +++ b/packages/web/src/pages/Applications/index.tsx @@ -97,7 +97,9 @@ export default function Applications(): React.ReactElement { )} {!loading && - apps?.map((app: IApp) => )} + apps?.map((app: IApp) => ( + + ))}