teamMembersRow.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import styled from '@emotion/styled';
  2. import {Button} from 'sentry/components/button';
  3. import IdBadge from 'sentry/components/idBadge';
  4. import PanelItem from 'sentry/components/panels/panelItem';
  5. import TeamRoleSelect from 'sentry/components/teamRoleSelect';
  6. import {IconSubtract} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import {Member, Organization, Team, TeamMember, User} from 'sentry/types';
  10. import {getButtonHelpText} from 'sentry/views/settings/organizationTeams/utils';
  11. interface Props {
  12. hasWriteAccess: boolean;
  13. isOrgOwner: boolean;
  14. member: TeamMember;
  15. organization: Organization;
  16. removeMember: (member: Member) => void;
  17. team: Team;
  18. updateMemberRole: (member: Member, newRole: string) => void;
  19. user: User;
  20. }
  21. function TeamMembersRow({
  22. organization,
  23. team,
  24. member,
  25. user,
  26. hasWriteAccess,
  27. isOrgOwner,
  28. removeMember,
  29. updateMemberRole,
  30. }: Props) {
  31. const isSelf = user.email === member.email;
  32. return (
  33. <TeamRolesPanelItem key={member.id}>
  34. <div>
  35. <IdBadge avatarSize={36} member={member} useLink orgId={organization.slug} />
  36. </div>
  37. <RoleSelectWrapper>
  38. <TeamRoleSelect
  39. disabled={isSelf || !hasWriteAccess}
  40. organization={organization}
  41. team={team}
  42. member={member}
  43. onChangeTeamRole={newRole => updateMemberRole(member, newRole)}
  44. />
  45. </RoleSelectWrapper>
  46. <div>
  47. <RemoveButton
  48. hasWriteAccess={hasWriteAccess}
  49. hasOrgRoleFromTeam={!!team.orgRole}
  50. isOrgOwner={isOrgOwner}
  51. isSelf={isSelf}
  52. onClick={() => removeMember(member)}
  53. member={member}
  54. />
  55. </div>
  56. </TeamRolesPanelItem>
  57. );
  58. }
  59. function RemoveButton(props: {
  60. hasOrgRoleFromTeam: boolean;
  61. hasWriteAccess: boolean;
  62. isOrgOwner: boolean;
  63. isSelf: boolean;
  64. member: TeamMember;
  65. onClick: () => void;
  66. }) {
  67. const {member, hasWriteAccess, isOrgOwner, isSelf, hasOrgRoleFromTeam, onClick} = props;
  68. const canRemoveMember = hasWriteAccess || isSelf;
  69. if (!canRemoveMember) {
  70. return (
  71. <Button
  72. size="xs"
  73. disabled
  74. icon={<IconSubtract size="xs" isCircled />}
  75. aria-label={t('Remove')}
  76. title={t('You do not have permission to remove a member from this team.')}
  77. >
  78. {t('Remove')}
  79. </Button>
  80. );
  81. }
  82. const isIdpProvisioned = member.flags['idp:provisioned'];
  83. const isPermissionGroup = hasOrgRoleFromTeam && !isOrgOwner;
  84. const buttonHelpText = getButtonHelpText(isIdpProvisioned, isPermissionGroup);
  85. if (isIdpProvisioned || isPermissionGroup) {
  86. return (
  87. <Button
  88. size="xs"
  89. disabled
  90. icon={<IconSubtract size="xs" isCircled />}
  91. aria-label={t('Remove')}
  92. title={buttonHelpText}
  93. >
  94. {t('Remove')}
  95. </Button>
  96. );
  97. }
  98. const buttonRemoveText = isSelf ? t('Leave') : t('Remove');
  99. return (
  100. <Button
  101. data-test-id={`button-remove-${member.id}`}
  102. size="xs"
  103. disabled={!canRemoveMember}
  104. icon={<IconSubtract size="xs" isCircled />}
  105. onClick={onClick}
  106. aria-label={buttonRemoveText}
  107. >
  108. {buttonRemoveText}
  109. </Button>
  110. );
  111. }
  112. const RoleSelectWrapper = styled('div')`
  113. display: flex;
  114. flex-direction: row;
  115. align-items: center;
  116. > div:first-child {
  117. flex-grow: 1;
  118. }
  119. `;
  120. export const GRID_TEMPLATE = `
  121. display: grid;
  122. grid-template-columns: minmax(100px, 1fr) 200px 150px;
  123. gap: ${space(1)};
  124. `;
  125. const TeamRolesPanelItem = styled(PanelItem)`
  126. ${GRID_TEMPLATE};
  127. align-items: center;
  128. > div:last-child {
  129. margin-left: auto;
  130. }
  131. `;
  132. export default TeamMembersRow;