teamMembersRow.tsx 3.1 KB

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