diff --git a/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx b/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx index 2ef753f437f..f7e017e2864 100644 --- a/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx +++ b/public/app/features/serviceaccounts/ServiceAccountsListPage.tsx @@ -1,11 +1,17 @@ import React, { memo, useEffect } from 'react'; import { connect, ConnectedProps } from 'react-redux'; -import { Icon, LinkButton, useStyles2 } from '@grafana/ui'; +import { Button, ConfirmModal, Icon, LinkButton, useStyles2 } from '@grafana/ui'; import { css, cx } from '@emotion/css'; import Page from 'app/core/components/Page/Page'; import { StoreState, ServiceAccountDTO, AccessControlAction, Role } from 'app/types'; -import { fetchACOptions, loadServiceAccounts, removeServiceAccount, updateServiceAccount } from './state/actions'; +import { + fetchACOptions, + loadServiceAccounts, + removeServiceAccount, + updateServiceAccount, + setServiceAccountToRemove, +} from './state/actions'; import { getNavModel } from 'app/core/selectors/navModel'; import { getServiceAccounts, getServiceAccountsSearchPage, getServiceAccountsSearchQuery } from './state/selectors'; import PageLoader from 'app/core/components/PageLoader/PageLoader'; @@ -13,6 +19,7 @@ import { GrafanaTheme2, OrgRole } from '@grafana/data'; import { contextSrv } from 'app/core/core'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { OrgRolePicker } from '../admin/OrgRolePicker'; +import pluralize from 'pluralize'; export type Props = ConnectedProps; function mapStateToProps(state: StoreState) { @@ -24,6 +31,7 @@ function mapStateToProps(state: StoreState) { isLoading: state.serviceAccounts.isLoading, roleOptions: state.serviceAccounts.roleOptions, builtInRoles: state.serviceAccounts.builtInRoles, + toRemove: state.serviceAccounts.serviceAccountToRemove, }; } @@ -32,21 +40,26 @@ const mapDispatchToProps = { fetchACOptions, updateServiceAccount, removeServiceAccount, + setServiceAccountToRemove, }; const connector = connect(mapStateToProps, mapDispatchToProps); const ServiceAccountsListPage = ({ loadServiceAccounts, + removeServiceAccount, fetchACOptions, updateServiceAccount, + setServiceAccountToRemove, navModel, serviceAccounts, isLoading, roleOptions, builtInRoles, + toRemove, }: Props) => { const styles = useStyles2(getStyles); + useEffect(() => { loadServiceAccounts(); if (contextSrv.accessControlEnabled()) { @@ -84,6 +97,7 @@ const ServiceAccountsListPage = ({ ID Roles Tokens + @@ -94,6 +108,7 @@ const ServiceAccountsListPage = ({ builtInRoles={builtInRoles} roleOptions={roleOptions} onRoleChange={onRoleChange} + onSetToRemove={setServiceAccountToRemove} /> ))} @@ -101,6 +116,28 @@ const ServiceAccountsListPage = ({ )} + {toRemove && ( + + Are you sure you want to delete '{toRemove.name}' + {Boolean(toRemove.tokens) && + ` and ${toRemove.tokens} accompanying ${pluralize('token', toRemove.tokens)}`} + ? + + } + confirmText="Delete" + title="Delete service account" + onDismiss={() => { + setServiceAccountToRemove(null); + }} + isOpen={true} + onConfirm={() => { + removeServiceAccount(toRemove.id); + setServiceAccountToRemove(null); + }} + /> + )} ); @@ -111,6 +148,7 @@ type ServiceAccountListItemProps = { onRoleChange: (role: OrgRole, serviceAccount: ServiceAccountDTO) => void; roleOptions: Role[]; builtInRoles: Record; + onSetToRemove: (serviceAccount: ServiceAccountDTO) => void; }; const getServiceAccountsAriaLabel = (name: string) => { @@ -118,7 +156,7 @@ const getServiceAccountsAriaLabel = (name: string) => { }; const ServiceAccountListItem = memo( - ({ serviceAccount, onRoleChange, roleOptions, builtInRoles }: ServiceAccountListItemProps) => { + ({ serviceAccount, onRoleChange, roleOptions, builtInRoles, onSetToRemove }: ServiceAccountListItemProps) => { const editUrl = `org/serviceAccounts/${serviceAccount.id}`; const styles = useStyles2(getStyles); const canUpdateRole = contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsWrite, serviceAccount); @@ -188,6 +226,19 @@ const ServiceAccountListItem = memo( {serviceAccount.tokens} + {contextSrv.hasPermissionInMetadata(AccessControlAction.ServiceAccountsDelete, serviceAccount) && ( + +