diff --git a/frontend/src/component/admin/apiToken/ApiTokenTable/ApiTokenTable.tsx b/frontend/src/component/admin/apiToken/ApiTokenTable/ApiTokenTable.tsx
index 5a149e7886..4f833dd96a 100644
--- a/frontend/src/component/admin/apiToken/ApiTokenTable/ApiTokenTable.tsx
+++ b/frontend/src/component/admin/apiToken/ApiTokenTable/ApiTokenTable.tsx
@@ -28,12 +28,38 @@ import { useConditionallyHiddenColumns } from 'hooks/useConditionallyHiddenColum
import { TimeAgoCell } from 'component/common/Table/cells/TimeAgoCell/TimeAgoCell';
const hiddenColumnsSmall = ['Icon', 'createdAt'];
+const hiddenColumnsCompact = ['Icon', 'project', 'seenAt'];
-export const ApiTokenTable = () => {
+interface IApiTokenTableProps {
+ compact?: boolean;
+ filterForProject?: string;
+}
+export const ApiTokenTable = ({
+ compact = false,
+ filterForProject,
+}: IApiTokenTableProps) => {
const { tokens, loading } = useApiTokens();
const initialState = useMemo(() => ({ sortBy: [{ id: 'createdAt' }] }), []);
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
+ const filteredTokens = useMemo(() => {
+ if (Boolean(filterForProject)) {
+ return tokens.filter(token => {
+ if (token.projects) {
+ if (token.projects?.length > 1) return false;
+ if (
+ token.projects?.length === 1 &&
+ token.projects[0] === filterForProject
+ )
+ return true;
+ }
+
+ return token.project === filterForProject;
+ });
+ }
+ return tokens;
+ }, [tokens, filterForProject]);
+
const {
getTableProps,
getTableBodyProps,
@@ -46,7 +72,7 @@ export const ApiTokenTable = () => {
} = useTable(
{
columns: COLUMNS as any,
- data: tokens as any,
+ data: filteredTokens as any,
initialState,
sortTypes,
autoResetHiddenColumns: false,
@@ -62,6 +88,10 @@ export const ApiTokenTable = () => {
condition: isSmallScreen,
columns: hiddenColumnsSmall,
},
+ {
+ condition: compact,
+ columns: hiddenColumnsCompact,
+ },
],
setHiddenColumns,
COLUMNS
diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectApiAccess.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectApiAccess.tsx
new file mode 100644
index 0000000000..815ee3c90a
--- /dev/null
+++ b/frontend/src/component/project/Project/ProjectSettings/ProjectApiAccess.tsx
@@ -0,0 +1,35 @@
+import { useContext } from 'react';
+import { PageContent } from 'component/common/PageContent/PageContent';
+import { Alert } from '@mui/material';
+import { PageHeader } from 'component/common/PageHeader/PageHeader';
+import AccessContext from 'contexts/AccessContext';
+import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { usePageTitle } from 'hooks/usePageTitle';
+import { useProjectNameOrId } from 'hooks/api/getters/useProject/useProject';
+import { ApiTokenTable } from '../../../admin/apiToken/ApiTokenTable/ApiTokenTable';
+
+export const ProjectApiAccess = () => {
+ const projectId = useRequiredPathParam('projectId');
+ const projectName = useProjectNameOrId(projectId);
+ const { hasAccess } = useContext(AccessContext);
+
+ usePageTitle(`Project api access – ${projectName}`);
+
+ if (!hasAccess(UPDATE_PROJECT, projectId)) {
+ return (
+