integrationExternalMappings.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {Component, Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import capitalize from 'lodash/capitalize';
  4. import Access from 'sentry/components/acl/access';
  5. import Button from 'sentry/components/button';
  6. import Confirm from 'sentry/components/confirm';
  7. import Pagination from 'sentry/components/pagination';
  8. import {Panel, PanelBody, PanelHeader, PanelItem} from 'sentry/components/panels';
  9. import Tooltip from 'sentry/components/tooltip';
  10. import {IconAdd, IconDelete, IconEdit} from 'sentry/icons';
  11. import {t, tct} from 'sentry/locale';
  12. import space from 'sentry/styles/space';
  13. import {ExternalActorMapping, Integration} from 'sentry/types';
  14. import {getIntegrationIcon} from 'sentry/utils/integrationUtil';
  15. import EmptyMessage from 'sentry/views/settings/components/emptyMessage';
  16. type Props = {
  17. integration: Integration;
  18. mappings: {id: string; externalName: string; sentryName: string}[];
  19. type: 'team' | 'user';
  20. onCreateOrEdit: (mapping?: ExternalActorMapping) => void;
  21. onDelete: (mapping: ExternalActorMapping) => void;
  22. pageLinks?: string;
  23. };
  24. type State = {};
  25. class IntegrationExternalMappings extends Component<Props, State> {
  26. render() {
  27. const {integration, mappings, type, onCreateOrEdit, onDelete, pageLinks} = this.props;
  28. return (
  29. <Fragment>
  30. <Panel>
  31. <PanelHeader disablePadding hasButtons>
  32. <HeaderLayout>
  33. <ExternalNameColumn>{tct('External [type]', {type})}</ExternalNameColumn>
  34. <SentryNameColumn>{tct('Sentry [type]', {type})}</SentryNameColumn>
  35. <Access access={['org:integrations']}>
  36. {({hasAccess}) => (
  37. <ButtonColumn>
  38. <Tooltip
  39. title={tct(
  40. 'You must be an organization owner, manager or admin to edit or remove a [type] mapping.',
  41. {type}
  42. )}
  43. disabled={hasAccess}
  44. >
  45. <AddButton
  46. data-test-id="add-mapping-button"
  47. onClick={() => onCreateOrEdit()}
  48. size="xsmall"
  49. icon={<IconAdd size="xs" isCircled />}
  50. disabled={!hasAccess}
  51. >
  52. {tct('Add [type] Mapping', {type})}
  53. </AddButton>
  54. </Tooltip>
  55. </ButtonColumn>
  56. )}
  57. </Access>
  58. </HeaderLayout>
  59. </PanelHeader>
  60. <PanelBody>
  61. {!mappings.length && (
  62. <EmptyMessage icon={getIntegrationIcon(integration.provider.key, 'lg')}>
  63. {tct('Set up External [type] Mappings.', {type: capitalize(type)})}
  64. </EmptyMessage>
  65. )}
  66. {mappings.map(item => (
  67. <Access access={['org:integrations']} key={item.id}>
  68. {({hasAccess}) => (
  69. <ConfigPanelItem>
  70. <Layout>
  71. <ExternalNameColumn>{item.externalName}</ExternalNameColumn>
  72. <SentryNameColumn>{item.sentryName}</SentryNameColumn>
  73. <ButtonColumn>
  74. <Tooltip
  75. title={t(
  76. 'You must be an organization owner, manager or admin to edit or remove an external user mapping.'
  77. )}
  78. disabled={hasAccess}
  79. >
  80. <StyledButton
  81. size="small"
  82. icon={<IconEdit size="sm" />}
  83. label={t('edit')}
  84. disabled={!hasAccess}
  85. onClick={() => onCreateOrEdit(item)}
  86. />
  87. <Confirm
  88. disabled={!hasAccess}
  89. onConfirm={() => onDelete(item)}
  90. message={t(
  91. 'Are you sure you want to remove this external user mapping?'
  92. )}
  93. >
  94. <StyledButton
  95. size="small"
  96. icon={<IconDelete size="sm" />}
  97. label={t('delete')}
  98. disabled={!hasAccess}
  99. />
  100. </Confirm>
  101. </Tooltip>
  102. </ButtonColumn>
  103. </Layout>
  104. </ConfigPanelItem>
  105. )}
  106. </Access>
  107. ))}
  108. </PanelBody>
  109. </Panel>
  110. <Pagination pageLinks={pageLinks} />
  111. </Fragment>
  112. );
  113. }
  114. }
  115. export default IntegrationExternalMappings;
  116. const AddButton = styled(Button)`
  117. text-transform: capitalize;
  118. `;
  119. const Layout = styled('div')`
  120. display: grid;
  121. grid-column-gap: ${space(1)};
  122. width: 100%;
  123. align-items: center;
  124. grid-template-columns: 2.5fr 2.5fr 1fr;
  125. grid-template-areas: 'external-name sentry-name button';
  126. `;
  127. const HeaderLayout = styled(Layout)`
  128. align-items: center;
  129. margin: 0;
  130. margin-left: ${space(2)};
  131. text-transform: uppercase;
  132. `;
  133. const ConfigPanelItem = styled(PanelItem)``;
  134. const StyledButton = styled(Button)`
  135. margin: ${space(0.5)};
  136. `;
  137. // Columns below
  138. const Column = styled('span')`
  139. overflow: hidden;
  140. overflow-wrap: break-word;
  141. `;
  142. const ExternalNameColumn = styled(Column)`
  143. grid-area: external-name;
  144. `;
  145. const SentryNameColumn = styled(Column)`
  146. grid-area: sentry-name;
  147. `;
  148. const ButtonColumn = styled(Column)`
  149. grid-area: button;
  150. text-align: right;
  151. `;