teamMembersRow.tsx 3.1 KB

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