import { css } from '@emotion/css'; import { useEffect, useMemo, useState } from 'react'; import Skeleton from 'react-loading-skeleton'; import { connect, ConnectedProps } from 'react-redux'; import { GrafanaTheme2 } from '@grafana/data'; import { Avatar, CellProps, Column, DeleteButton, EmptyState, FilterInput, InlineField, InteractiveTable, LinkButton, Pagination, Stack, TextLink, useStyles2, } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { fetchRoleOptions } from 'app/core/components/RolePicker/api'; import { Trans, t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; import { AccessControlAction, Role, StoreState, TeamWithRoles } from 'app/types'; import { TeamRolePicker } from '../../core/components/RolePicker/TeamRolePicker'; import { deleteTeam, loadTeams, changePage, changeQuery, changeSort } from './state/actions'; type Cell = CellProps; export interface OwnProps {} export interface State { roleOptions: Role[]; } // this is dummy data to pass to the table while the real data is loading const skeletonData: TeamWithRoles[] = new Array(3).fill(null).map((_, index) => ({ id: index, uid: '', memberCount: 0, name: '', orgId: 0, })); export const TeamList = ({ teams, query, noTeams, hasFetched, loadTeams, deleteTeam, changeQuery, totalPages, page, rolesLoading, changePage, changeSort, }: Props) => { const [roleOptions, setRoleOptions] = useState([]); const styles = useStyles2(getStyles); useEffect(() => { loadTeams(true); }, [loadTeams]); useEffect(() => { if (contextSrv.licensedAccessControlEnabled() && contextSrv.hasPermission(AccessControlAction.ActionRolesList)) { fetchRoleOptions().then((roles) => setRoleOptions(roles)); } }, []); const canCreate = contextSrv.hasPermission(AccessControlAction.ActionTeamsCreate); const displayRolePicker = shouldDisplayRolePicker(); const columns: Array> = useMemo( () => [ { id: 'avatarUrl', header: '', disableGrow: true, cell: ({ cell: { value } }: Cell<'avatarUrl'>) => { if (!hasFetched) { return ; } return value && ; }, }, { id: 'name', header: 'Name', cell: ({ cell: { value }, row: { original } }: Cell<'name'>) => { if (!hasFetched) { return ; } const canReadTeam = contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsRead, original); if (!canReadTeam) { return value; } return ( {value} ); }, sortType: 'string', }, { id: 'email', header: 'Email', cell: ({ cell: { value } }: Cell<'email'>) => { if (!hasFetched) { return ; } return value; }, sortType: 'string', }, { id: 'memberCount', header: 'Members', disableGrow: true, cell: ({ cell: { value } }: Cell<'memberCount'>) => { if (!hasFetched) { return ; } return value; }, sortType: 'number', }, ...(displayRolePicker ? [ { id: 'role', header: 'Role', cell: ({ cell: { value }, row: { original } }: Cell<'memberCount'>) => { if (!hasFetched) { return ; } const canSeeTeamRoles = contextSrv.hasPermissionInMetadata( AccessControlAction.ActionTeamsRolesList, original ); return ( canSeeTeamRoles && ( ) ); }, }, ] : []), { id: 'actions', header: '', disableGrow: true, cell: ({ row: { original } }: Cell) => { if (!hasFetched) { return ( ); } const canReadTeam = contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsRead, original); const canDelete = contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsDelete, original); return ( {canReadTeam && ( )} deleteTeam(original.uid)} /> ); }, }, ], [displayRolePicker, hasFetched, rolesLoading, roleOptions, deleteTeam, styles] ); return ( New Team ) : undefined } > {noTeams ? ( New team } message={t('teams.empty-state.title', "You haven't created any teams yet")} > Assign folder and dashboard permissions to teams instead of users to ease administration.{' '} Learn more ) : ( <>
{hasFetched && teams.length === 0 ? ( ) : ( String(team.id)} fetchData={changeSort} /> )} )}
); }; function shouldDisplayRolePicker(): boolean { return ( contextSrv.licensedAccessControlEnabled() && contextSrv.hasPermission(AccessControlAction.ActionTeamsRolesList) && contextSrv.hasPermission(AccessControlAction.ActionRolesList) ); } function mapStateToProps(state: StoreState) { return { teams: state.teams.teams, query: state.teams.query, perPage: state.teams.perPage, page: state.teams.page, noTeams: state.teams.noTeams, totalPages: state.teams.totalPages, hasFetched: state.teams.hasFetched, rolesLoading: state.teams.rolesLoading, }; } const mapDispatchToProps = { loadTeams, deleteTeam, changePage, changeQuery, changeSort, }; const connector = connect(mapStateToProps, mapDispatchToProps); export type Props = OwnProps & ConnectedProps; export default connector(TeamList); const getStyles = (theme: GrafanaTheme2) => ({ blockSkeleton: css({ lineHeight: 1, // needed for things to align properly in the table display: 'flex', }), });