teamMembersRow.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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 {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. isOrgOwner={isOrgOwner}
  50. isSelf={isSelf}
  51. onClick={() => removeMember(member)}
  52. member={member}
  53. />
  54. </div>
  55. </TeamRolesPanelItem>
  56. );
  57. }
  58. function RemoveButton(props: {
  59. hasWriteAccess: boolean;
  60. isOrgOwner: boolean;
  61. isSelf: boolean;
  62. member: TeamMember;
  63. onClick: () => void;
  64. }) {
  65. const {member, hasWriteAccess, isOrgOwner, isSelf, onClick} = props;
  66. const canRemoveMember = hasWriteAccess || isSelf;
  67. if (!canRemoveMember) {
  68. return (
  69. <Button
  70. size="xs"
  71. disabled
  72. icon={<IconSubtract isCircled />}
  73. aria-label={t('Remove')}
  74. title={t('You do not have permission to remove a member from this team.')}
  75. >
  76. {t('Remove')}
  77. </Button>
  78. );
  79. }
  80. const isIdpProvisioned = member.flags['idp:provisioned'];
  81. const buttonHelpText = getButtonHelpText(isIdpProvisioned, !isOrgOwner);
  82. const buttonRemoveText = isSelf ? t('Leave') : t('Remove');
  83. return (
  84. <Button
  85. data-test-id={`button-remove-${member.id}`}
  86. size="xs"
  87. disabled={!canRemoveMember || isIdpProvisioned}
  88. icon={<IconSubtract isCircled />}
  89. onClick={onClick}
  90. aria-label={buttonRemoveText}
  91. title={buttonHelpText}
  92. >
  93. {buttonRemoveText}
  94. </Button>
  95. );
  96. }
  97. const RoleSelectWrapper = styled('div')`
  98. display: flex;
  99. flex-direction: row;
  100. align-items: center;
  101. > div:first-child {
  102. flex-grow: 1;
  103. }
  104. `;
  105. export const GRID_TEMPLATE = `
  106. display: grid;
  107. grid-template-columns: minmax(100px, 1fr) 200px 150px;
  108. gap: ${space(1)};
  109. `;
  110. const TeamRolesPanelItem = styled(PanelItem)`
  111. ${GRID_TEMPLATE};
  112. align-items: center;
  113. > div:last-child {
  114. margin-left: auto;
  115. }
  116. `;
  117. export default TeamMembersRow;