teamMembersRow.tsx 3.2 KB

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