Enable roles on api keys (#13334)
This commit is contained in:
+46
-32
@@ -4,66 +4,80 @@ import styled from '@emotion/styled';
|
||||
import { formatExpiration } from '@/settings/developers/utils/formatExpiration';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import {
|
||||
IconChevronRight,
|
||||
OverflowingTextWithTooltip,
|
||||
} from 'twenty-ui/display';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { IconChevronRight } from 'twenty-ui/display';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { ApiKey } from '~/generated-metadata/graphql';
|
||||
import { ApiKey, FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const StyledApisFieldTableRow = styled(TableRow)`
|
||||
grid-template-columns: 312px auto 28px;
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: 100%;
|
||||
grid-template-columns: 12fr 4fr;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledNameTableCell = styled(TableCell)`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
const StyledTruncatedCell = styled(TableCell)`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledIconTableCell = styled(TableCell)`
|
||||
justify-content: center;
|
||||
padding-right: ${({ theme }) => theme.spacing(1)};
|
||||
padding-left: 0;
|
||||
const StyledEllipsisLabel = styled.div`
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledIconChevronRight = styled(IconChevronRight)`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
type ApiKeyType = Pick<ApiKey, 'id' | 'name' | 'expiresAt' | 'revokedAt'> & {
|
||||
role?: { id: string; label: string; icon?: string | null } | null;
|
||||
};
|
||||
|
||||
type SettingsApiKeysFieldItemTableRowProps = {
|
||||
apiKey: ApiKeyType;
|
||||
to: string;
|
||||
};
|
||||
|
||||
export const SettingsApiKeysFieldItemTableRow = ({
|
||||
apiKey,
|
||||
to,
|
||||
}: {
|
||||
apiKey: Pick<ApiKey, 'id' | 'name' | 'expiresAt' | 'revokedAt'>;
|
||||
to: string;
|
||||
}) => {
|
||||
}: SettingsApiKeysFieldItemTableRowProps) => {
|
||||
const theme = useTheme();
|
||||
const formattedExpiration = formatExpiration(apiKey.expiresAt || null);
|
||||
|
||||
const isApiKeyRolesEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_API_KEY_ROLES_ENABLED,
|
||||
);
|
||||
|
||||
const gridColumns = isApiKeyRolesEnabled ? '5fr 2fr 3fr 1fr' : '5fr 3fr 1fr';
|
||||
|
||||
return (
|
||||
<StyledApisFieldTableRow to={to}>
|
||||
<StyledNameTableCell>
|
||||
<OverflowingTextWithTooltip text={apiKey.name} />
|
||||
</StyledNameTableCell>
|
||||
<TableCell
|
||||
<StyledApisFieldTableRow gridAutoColumns={gridColumns} to={to}>
|
||||
<StyledTruncatedCell color={theme.font.color.primary}>
|
||||
<StyledEllipsisLabel>{apiKey.name}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
|
||||
{isApiKeyRolesEnabled && (
|
||||
<StyledTruncatedCell color={theme.font.color.tertiary}>
|
||||
<StyledEllipsisLabel>{apiKey.role?.label || '-'}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
)}
|
||||
|
||||
<StyledTruncatedCell
|
||||
color={
|
||||
formattedExpiration === 'Expired'
|
||||
? theme.font.color.danger
|
||||
: theme.font.color.tertiary
|
||||
}
|
||||
>
|
||||
{formattedExpiration}
|
||||
</TableCell>
|
||||
<StyledIconTableCell>
|
||||
<StyledIconChevronRight
|
||||
<StyledEllipsisLabel>{formattedExpiration}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
|
||||
<TableCell align="right">
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
</StyledIconTableCell>
|
||||
</TableCell>
|
||||
</StyledApisFieldTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
+20
-18
@@ -4,46 +4,48 @@ import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableBody } from '@/ui/layout/table/components/TableBody';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import styled from '@emotion/styled';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
|
||||
import { useGetApiKeysQuery } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
useGetApiKeysQuery,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
const StyledTableBody = styled(TableBody)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
padding-top: ${({ theme }) => theme.spacing(3)};
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
grid-template-columns: 312px auto 28px;
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: 95%;
|
||||
grid-template-columns: 20fr 2fr;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SettingsApiKeysTable = () => {
|
||||
const isApiKeyRolesEnabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IS_API_KEY_ROLES_ENABLED,
|
||||
);
|
||||
|
||||
const { data: apiKeysData } = useGetApiKeysQuery();
|
||||
|
||||
const apiKeys = apiKeysData?.apiKeys;
|
||||
|
||||
const gridAutoColumns = isApiKeyRolesEnabled
|
||||
? '5fr 2fr 3fr 1fr'
|
||||
: '5fr 3fr 1fr';
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<StyledTableRow>
|
||||
<TableRow gridAutoColumns={gridAutoColumns}>
|
||||
<TableHeader>
|
||||
<Trans>Name</Trans>
|
||||
</TableHeader>
|
||||
{isApiKeyRolesEnabled && (
|
||||
<TableHeader>
|
||||
<Trans>Role</Trans>
|
||||
</TableHeader>
|
||||
)}
|
||||
<TableHeader>
|
||||
<Trans>Expiration</Trans>
|
||||
</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</StyledTableRow>
|
||||
</TableRow>
|
||||
{!!apiKeys?.length && (
|
||||
<StyledTableBody>
|
||||
{apiKeys.map((apiKey) => (
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useIcons } from 'twenty-ui/display';
|
||||
import { Role } from '~/generated-metadata/graphql';
|
||||
|
||||
type SettingsDevelopersRoleSelectorProps = {
|
||||
value?: string | null | undefined;
|
||||
onChange: (roleId: string) => void;
|
||||
label?: string;
|
||||
description?: string;
|
||||
roles: Role[];
|
||||
};
|
||||
|
||||
export const SettingsDevelopersRoleSelector = ({
|
||||
value,
|
||||
onChange,
|
||||
label,
|
||||
description,
|
||||
roles,
|
||||
}: SettingsDevelopersRoleSelectorProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
let doesDefaultRoleExistInRoles = false;
|
||||
|
||||
const defaultRole = currentWorkspace?.defaultRole;
|
||||
try {
|
||||
doesDefaultRoleExistInRoles = roles.some(
|
||||
(role) => role.id === defaultRole?.id,
|
||||
);
|
||||
} catch {
|
||||
throw new Error(
|
||||
'Default role does not exist in roles, this should not happen',
|
||||
);
|
||||
}
|
||||
|
||||
if (roles.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const options = roles.map((role) => ({
|
||||
label: role.label,
|
||||
value: role.id,
|
||||
Icon: getIcon(role.icon) ?? undefined,
|
||||
}));
|
||||
|
||||
const selectValue =
|
||||
value || (doesDefaultRoleExistInRoles ? defaultRole?.id : roles[0].id);
|
||||
|
||||
return (
|
||||
<Select
|
||||
dropdownId="role-selector"
|
||||
options={options}
|
||||
value={selectValue}
|
||||
onChange={onChange}
|
||||
label={label}
|
||||
description={description}
|
||||
withSearchInput
|
||||
fullWidth
|
||||
/>
|
||||
);
|
||||
};
|
||||
+5
@@ -13,6 +13,11 @@ const meta: Meta<typeof SettingsApiKeysFieldItemTableRow> = {
|
||||
name: 'Zapier Api Key',
|
||||
expiresAt: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString(), // 3 days from now
|
||||
revokedAt: null,
|
||||
role: {
|
||||
id: '1',
|
||||
label: 'Admin',
|
||||
icon: 'admin',
|
||||
},
|
||||
},
|
||||
to: '/settings/developers/api-keys/3f4a42e8-b81f-4f8c-9c20-1602e6b34791',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user