123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- import React from 'react';
- import styled from '@emotion/styled';
- import startCase from 'lodash/startCase';
- import {Button} from 'sentry/components/button';
- import EmptyMessage from 'sentry/components/emptyMessage';
- import {TeamBadge} from 'sentry/components/idBadge/teamBadge';
- import Link from 'sentry/components/links/link';
- import LoadingIndicator from 'sentry/components/loadingIndicator';
- import {Panel, PanelBody, PanelHeader, PanelItem} from 'sentry/components/panels';
- import TeamRoleSelect from 'sentry/components/teamRoleSelect';
- import {IconSubtract} from 'sentry/icons';
- import {t} from 'sentry/locale';
- import {space} from 'sentry/styles/space';
- import {Member, Organization, Team} from 'sentry/types';
- import {getEffectiveOrgRole} from 'sentry/utils/orgRole';
- import {useTeams} from 'sentry/utils/useTeams';
- import {RoleOverwritePanelAlert} from 'sentry/views/settings/organizationTeams/roleOverwriteWarning';
- import {getButtonHelpText} from 'sentry/views/settings/organizationTeams/utils';
- import {DropdownAddTeam, TeamSelectProps} from './utils';
- type Props = TeamSelectProps & {
- /**
- * Member that this component is acting upon
- */
- member: Member;
- /**
- * Used when showing Teams for a Member
- */
- onChangeTeamRole: (teamSlug: string, teamRole: string) => void;
- /**
- * Used when showing Teams for a Member
- */
- selectedOrgRole: Member['orgRole'];
- /**
- * Used when showing Teams for a Member
- */
- selectedTeamRoles: Member['teamRoles'];
- };
- function TeamSelect({
- disabled,
- loadingTeams,
- member,
- selectedOrgRole,
- selectedTeamRoles,
- organization,
- onAddTeam,
- onRemoveTeam,
- onCreateTeam,
- onChangeTeamRole,
- }: Props) {
- const {teams, onSearch, fetching: isLoadingTeams} = useTeams();
- const {orgRoleList, teamRoleList} = organization;
- const selectedTeamSlugs = new Set(selectedTeamRoles.map(tm => tm.teamSlug));
- const selectedTeams = teams.filter(tm => selectedTeamSlugs.has(tm.slug));
- // Determine if adding a team changes the minimum team-role
- // Get org-roles from team membership, if any
- const groupOrgRoles = selectedTeams
- .filter(team => team.orgRole)
- .map(team => team.orgRole as string);
- if (selectedOrgRole) {
- groupOrgRoles.push(selectedOrgRole);
- }
- // Sort them and to get the highest priority role
- // Highest priority role may change minimum team role
- const effectiveOrgRole = getEffectiveOrgRole(groupOrgRoles, orgRoleList);
- const renderBody = () => {
- if (selectedTeams.length === 0) {
- return <EmptyMessage>{t('No Teams assigned')}</EmptyMessage>;
- }
- return (
- <React.Fragment>
- {organization.features.includes('team-roles') && effectiveOrgRole && (
- <RoleOverwritePanelAlert
- orgRole={effectiveOrgRole?.id}
- orgRoleList={orgRoleList}
- teamRoleList={teamRoleList}
- />
- )}
- {selectedTeams.map(team => (
- <TeamRow
- key={team.slug}
- disabled={disabled}
- organization={organization}
- team={team}
- member={{
- ...member,
- groupOrgRoles: [{role: effectiveOrgRole, teamSlug: ''}],
- orgRole: selectedOrgRole,
- teamRoles: selectedTeamRoles,
- }}
- onChangeTeamRole={onChangeTeamRole}
- onRemoveTeam={slug => onRemoveTeam(slug)}
- />
- ))}
- </React.Fragment>
- );
- };
- return (
- <Panel>
- <PanelHeader hasButtons>
- {t('Team')}
- <DropdownAddTeam
- disabled={disabled}
- isLoadingTeams={isLoadingTeams}
- isAddingTeamToMember
- canCreateTeam={false}
- onSearch={onSearch}
- onSelect={onAddTeam}
- onCreateTeam={onCreateTeam}
- organization={organization}
- selectedTeams={selectedTeams.map(tm => tm.slug)}
- teams={teams}
- />
- </PanelHeader>
- <PanelBody>{loadingTeams ? <LoadingIndicator /> : renderBody()}</PanelBody>
- </Panel>
- );
- }
- function TeamRow({
- disabled,
- organization,
- team,
- member,
- onRemoveTeam,
- onChangeTeamRole,
- }: {
- disabled: boolean;
- member: Member;
- onChangeTeamRole: Props['onChangeTeamRole'];
- onRemoveTeam: Props['onRemoveTeam'];
- organization: Organization;
- team: Team;
- }) {
- const hasOrgAdmin = organization.access.includes('org:admin');
- const isIdpProvisioned = team.flags['idp:provisioned'];
- const isPermissionGroup = team.orgRole !== null && !hasOrgAdmin;
- const isRemoveDisabled = disabled || isIdpProvisioned || isPermissionGroup;
- const buttonHelpText = getButtonHelpText(isIdpProvisioned, isPermissionGroup);
- const orgRoleFromTeam = team.orgRole ? `${startCase(team.orgRole)} Team` : null;
- return (
- <TeamPanelItem data-test-id="team-row-for-member">
- <TeamPanelItemLeft>
- <Link to={`/settings/${organization.slug}/teams/${team.slug}/`}>
- <TeamBadge team={team} />
- </Link>
- </TeamPanelItemLeft>
- <TeamOrgRole>{orgRoleFromTeam}</TeamOrgRole>
- {organization.features.includes('team-roles') && (
- <RoleSelectWrapper>
- <TeamRoleSelect
- disabled={disabled}
- size="xs"
- organization={organization}
- team={team}
- member={member}
- onChangeTeamRole={newRole => onChangeTeamRole(team.slug, newRole)}
- />
- </RoleSelectWrapper>
- )}
- <Button
- size="xs"
- icon={<IconSubtract isCircled size="xs" />}
- title={buttonHelpText}
- disabled={isRemoveDisabled}
- onClick={() => onRemoveTeam(team.slug)}
- >
- {t('Remove')}
- </Button>
- </TeamPanelItem>
- );
- }
- const TeamPanelItem = styled(PanelItem)`
- padding: ${space(2)};
- align-items: center;
- justify-content: space-between;
- `;
- const TeamPanelItemLeft = styled('div')`
- flex-grow: 4;
- `;
- const TeamOrgRole = styled('div')`
- min-width: 90px;
- flex-grow: 1;
- display: flex;
- justify-content: center;
- `;
- const RoleSelectWrapper = styled('div')`
- min-width: 200px;
- margin-right: ${space(2)};
- `;
- export default TeamSelect;
|