123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- import styled from '@emotion/styled';
- import debounce from 'lodash/debounce';
- import Button from 'sentry/components/button';
- import Confirm from 'sentry/components/confirm';
- import DropdownAutoComplete from 'sentry/components/dropdownAutoComplete';
- import {Item} from 'sentry/components/dropdownAutoComplete/types';
- import DropdownButton from 'sentry/components/dropdownButton';
- 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 {DEFAULT_DEBOUNCE_DURATION} from 'sentry/constants';
- import {IconSubtract} from 'sentry/icons';
- import {t} from 'sentry/locale';
- import space from 'sentry/styles/space';
- import {Organization, Team} from 'sentry/types';
- import useTeams from 'sentry/utils/useTeams';
- import EmptyMessage from 'sentry/views/settings/components/emptyMessage';
- type Props = {
- /**
- * Should button be disabled
- */
- disabled: boolean;
- /**
- * callback when teams are added
- */
- onAddTeam: (team: Team) => void;
- /**
- * Callback when teams are removed
- */
- onRemoveTeam: (teamSlug: string) => void;
- organization: Organization;
- /**
- * Teams that are already selected.
- */
- selectedTeams: Team[];
- /**
- * Message to display when the last team is removed
- * if empty no confirm will be displayed.
- */
- confirmLastTeamRemoveMessage?: string;
- /**
- * Used to determine whether we should show a loading state while waiting for teams
- */
- loadingTeams?: boolean;
- /**
- * Optional menu header.
- */
- menuHeader?: React.ReactElement;
- };
- function TeamSelect({
- disabled,
- selectedTeams,
- menuHeader,
- organization,
- onAddTeam,
- onRemoveTeam,
- confirmLastTeamRemoveMessage,
- loadingTeams,
- }: Props) {
- const {teams, onSearch, fetching} = useTeams();
- const handleAddTeam = (option: Item) => {
- const team = teams.find(tm => tm.slug === option.value);
- if (team) {
- onAddTeam(team);
- }
- };
- const renderBody = () => {
- if (selectedTeams.length === 0) {
- return <EmptyMessage>{t('No Teams assigned')}</EmptyMessage>;
- }
- const confirmMessage =
- selectedTeams.length === 1 && confirmLastTeamRemoveMessage
- ? confirmLastTeamRemoveMessage
- : null;
- return selectedTeams.map(team => (
- <TeamRow
- key={team.slug}
- orgId={organization.slug}
- team={team}
- onRemove={slug => onRemoveTeam(slug)}
- disabled={disabled}
- confirmMessage={confirmMessage}
- />
- ));
- };
- // Only show options that aren't selected in the dropdown
- const options = teams
- .filter(team => !selectedTeams.some(selectedTeam => selectedTeam.slug === team.slug))
- .map((team, index) => ({
- index,
- value: team.slug,
- searchKey: team.slug,
- label: <DropdownTeamBadge avatarSize={18} team={team} />,
- }));
- return (
- <Panel>
- <PanelHeader hasButtons>
- {t('Team')}
- <DropdownAutoComplete
- items={options}
- busyItemsStillVisible={fetching}
- onChange={debounce<(e: React.ChangeEvent<HTMLInputElement>) => void>(
- e => onSearch(e.target.value),
- DEFAULT_DEBOUNCE_DURATION
- )}
- onSelect={handleAddTeam}
- emptyMessage={t('No teams')}
- menuHeader={menuHeader}
- disabled={disabled}
- alignMenu="right"
- >
- {({isOpen}) => (
- <DropdownButton
- aria-label={t('Add Team')}
- isOpen={isOpen}
- size="xs"
- disabled={disabled}
- >
- {t('Add Team')}
- </DropdownButton>
- )}
- </DropdownAutoComplete>
- </PanelHeader>
- <PanelBody>{loadingTeams ? <LoadingIndicator /> : renderBody()}</PanelBody>
- </Panel>
- );
- }
- type TeamRowProps = {
- confirmMessage: string | null;
- disabled: boolean;
- onRemove: Props['onRemoveTeam'];
- orgId: string;
- team: Team;
- };
- const TeamRow = ({orgId, team, onRemove, disabled, confirmMessage}: TeamRowProps) => (
- <TeamPanelItem>
- <StyledLink to={`/settings/${orgId}/teams/${team.slug}/`}>
- <TeamBadge team={team} />
- </StyledLink>
- <Confirm
- message={confirmMessage}
- bypass={!confirmMessage}
- onConfirm={() => onRemove(team.slug)}
- disabled={disabled}
- >
- <Button size="xs" icon={<IconSubtract isCircled size="xs" />} disabled={disabled}>
- {t('Remove')}
- </Button>
- </Confirm>
- </TeamPanelItem>
- );
- const DropdownTeamBadge = styled(TeamBadge)`
- font-weight: normal;
- font-size: ${p => p.theme.fontSizeMedium};
- text-transform: none;
- `;
- const TeamPanelItem = styled(PanelItem)`
- padding: ${space(2)};
- align-items: center;
- `;
- const StyledLink = styled(Link)`
- flex: 1;
- margin-right: ${space(1)};
- `;
- export default TeamSelect;
|