import { css, cx } from '@emotion/css';
import { memo } from 'react';
import Skeleton from 'react-loading-skeleton';
import { GrafanaTheme2, OrgRole } from '@grafana/data';
import { Button, Icon, IconButton, Stack, useStyles2 } from '@grafana/ui';
import { SkeletonComponent, attachSkeleton } from '@grafana/ui/src/unstable';
import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';
import { contextSrv } from 'app/core/core';
import { OrgRolePicker } from 'app/features/admin/OrgRolePicker';
import { AccessControlAction, Role, ServiceAccountDTO } from 'app/types';
type ServiceAccountListItemProps = {
serviceAccount: ServiceAccountDTO;
onRoleChange: (role: OrgRole, serviceAccount: ServiceAccountDTO) => void;
roleOptions: Role[];
onRemoveButtonClick: (serviceAccount: ServiceAccountDTO) => void;
onDisable: (serviceAccount: ServiceAccountDTO) => void;
onEnable: (serviceAccount: ServiceAccountDTO) => void;
onAddTokenClick: (serviceAccount: ServiceAccountDTO) => void;
};
const getServiceAccountsAriaLabel = (name: string) => {
return `Edit service account's ${name} details`;
};
const ServiceAccountListItemComponent = memo(
({
serviceAccount,
onRoleChange,
roleOptions,
onRemoveButtonClick,
onDisable,
onEnable,
onAddTokenClick,
}: ServiceAccountListItemProps) => {
const editUrl = `org/serviceaccounts/${serviceAccount.id}`;
const styles = useStyles2(getStyles);
const canUpdateRole = contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsWrite, serviceAccount);
const displayRolePicker =
contextSrv.hasPermission(AccessControlAction.ActionRolesList) &&
contextSrv.hasPermission(AccessControlAction.ActionUserRolesList);
return (
|
{serviceAccount.name}
|
{serviceAccount.login}
|
{contextSrv.licensedAccessControlEnabled() ? (
{displayRolePicker && (
onRoleChange(newRole, serviceAccount)}
roleOptions={roleOptions}
basicRoleDisabled={!canUpdateRole}
disabled={serviceAccount.isExternal || serviceAccount.isDisabled}
width={40}
/>
)}
|
) : (
onRoleChange(newRole, serviceAccount)}
/>
|
)}
{serviceAccount.tokens || 'No tokens'}
|
{!serviceAccount.isExternal && (
{contextSrv.hasPermission(AccessControlAction.ServiceAccountsWrite) && !serviceAccount.tokens && (
)}
{contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsWrite, serviceAccount) &&
(serviceAccount.isDisabled ? (
) : (
))}
{contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsDelete, serviceAccount) && (
onRemoveButtonClick(serviceAccount)}
tooltip={`Delete service account ${serviceAccount.name}`}
/>
)}
)}
{serviceAccount.isExternal && (
)}
|
);
}
);
ServiceAccountListItemComponent.displayName = 'ServiceAccountListItem';
const ServiceAccountsListItemSkeleton: SkeletonComponent = ({ rootProps }) => {
const styles = useStyles2(getSkeletonStyles);
return (
|
|
|
|
|
|
|
);
};
const ServiceAccountListItem = attachSkeleton(ServiceAccountListItemComponent, ServiceAccountsListItemSkeleton);
const getSkeletonStyles = (theme: GrafanaTheme2) => ({
blockSkeleton: css({
display: 'block',
lineHeight: 1,
}),
deleteButton: css({
marginRight: theme.spacing(0.5),
}),
});
const getStyles = (theme: GrafanaTheme2) => {
return {
iconRow: css({
svg: {
marginLeft: theme.spacing(0.5),
},
}),
accountId: cx(
'ellipsis',
css({
color: theme.colors.text.secondary,
})
),
deleteButton: css({
color: theme.colors.text.secondary,
}),
tokensInfo: css({
span: {
marginRight: theme.spacing(1),
},
}),
tokensInfoSecondary: css({
color: theme.colors.text.secondary,
}),
disabled: css({
'td a': {
color: theme.colors.text.secondary,
},
}),
actionButton: css({
minWidth: 85,
}),
};
};
export default ServiceAccountListItem;