actions.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ActionButton from 'sentry/components/actions/button';
  4. import MenuItemActionLink from 'sentry/components/actions/menuItemActionLink';
  5. import {Button} from 'sentry/components/button';
  6. import ButtonBar from 'sentry/components/buttonBar';
  7. import ConfirmDelete from 'sentry/components/confirmDelete';
  8. import DropdownLink from 'sentry/components/dropdownLink';
  9. import {Tooltip} from 'sentry/components/tooltip';
  10. import {IconEllipsis} from 'sentry/icons/iconEllipsis';
  11. import {t} from 'sentry/locale';
  12. import {space} from 'sentry/styles/space';
  13. import {CustomRepoType} from 'sentry/types/debugFiles';
  14. import TextBlock from 'sentry/views/settings/components/text/textBlock';
  15. type Props = {
  16. hasAccess: boolean;
  17. hasFeature: boolean;
  18. onDelete: () => void;
  19. onEdit: () => void;
  20. repositoryName: string;
  21. repositoryType: string;
  22. disabled?: boolean;
  23. syncNowButton?: React.ReactElement;
  24. };
  25. function Actions({
  26. repositoryName,
  27. repositoryType,
  28. disabled,
  29. onEdit,
  30. onDelete,
  31. hasFeature,
  32. hasAccess,
  33. syncNowButton,
  34. }: Props) {
  35. function renderConfirmDelete(element: React.ReactElement) {
  36. return (
  37. <ConfirmDelete
  38. confirmText={t('Delete Repository')}
  39. message={
  40. repositoryType === CustomRepoType.APP_STORE_CONNECT ? (
  41. <Fragment>
  42. <TextBlock>
  43. <strong>
  44. {t(
  45. 'Removing App Store Connect symbol source does not remove current dSYMs.'
  46. )}
  47. </strong>
  48. </TextBlock>
  49. <TextBlock>
  50. {t(
  51. 'The App Store Connect symbol source periodically imports dSYMs into the "Uploaded debug information files". Removing this symbol source does not delete those files and they will remain available for symbolication until deleted directly.'
  52. )}
  53. </TextBlock>
  54. </Fragment>
  55. ) : (
  56. <Fragment>
  57. <TextBlock>
  58. <strong>
  59. {t('Removing this repository applies instantly to new events.')}
  60. </strong>
  61. </TextBlock>
  62. <TextBlock>
  63. {t(
  64. 'Debug files from this repository will not be used to symbolicate future events. This may create new issues and alert members in your organization.'
  65. )}
  66. </TextBlock>
  67. </Fragment>
  68. )
  69. }
  70. confirmInput={repositoryName}
  71. priority="danger"
  72. onConfirm={onDelete}
  73. >
  74. {element}
  75. </ConfirmDelete>
  76. );
  77. }
  78. const actionsDisabled = !hasAccess || !hasFeature || disabled;
  79. return (
  80. <StyledButtonBar gap={1}>
  81. {syncNowButton}
  82. <ButtonTooltip
  83. title={
  84. !hasFeature
  85. ? undefined
  86. : !hasAccess
  87. ? t('You do not have permission to edit custom repositories configurations.')
  88. : undefined
  89. }
  90. disabled={actionsDisabled}
  91. >
  92. <ActionBtn disabled={actionsDisabled} onClick={onEdit} size="sm">
  93. {t('Configure')}
  94. </ActionBtn>
  95. </ButtonTooltip>
  96. {actionsDisabled ? (
  97. <ButtonTooltip
  98. title={
  99. !hasFeature
  100. ? undefined
  101. : !hasAccess
  102. ? t(
  103. 'You do not have permission to delete custom repositories configurations.'
  104. )
  105. : undefined
  106. }
  107. disabled={actionsDisabled}
  108. >
  109. <ActionBtn size="sm" disabled>
  110. {t('Delete')}
  111. </ActionBtn>
  112. </ButtonTooltip>
  113. ) : (
  114. renderConfirmDelete(<ActionBtn size="sm">{t('Delete')}</ActionBtn>)
  115. )}
  116. <DropDownWrapper>
  117. <DropdownLink
  118. caret={false}
  119. disabled={actionsDisabled}
  120. customTitle={
  121. <StyledActionButton
  122. aria-label={t('Actions')}
  123. disabled={actionsDisabled}
  124. title={
  125. !hasFeature
  126. ? undefined
  127. : !hasAccess
  128. ? t(
  129. 'You do not have permission to edit and delete custom repositories configurations.'
  130. )
  131. : undefined
  132. }
  133. icon={<IconEllipsis />}
  134. />
  135. }
  136. anchorRight
  137. >
  138. <MenuItemActionLink onClick={onEdit}>{t('Configure')}</MenuItemActionLink>
  139. {renderConfirmDelete(<MenuItemActionLink>{t('Delete')}</MenuItemActionLink>)}
  140. </DropdownLink>
  141. </DropDownWrapper>
  142. </StyledButtonBar>
  143. );
  144. }
  145. export default Actions;
  146. const StyledActionButton = styled(ActionButton)`
  147. height: 32px;
  148. `;
  149. const StyledButtonBar = styled(ButtonBar)`
  150. gap: ${space(1)};
  151. grid-column: 2/2;
  152. grid-row: 4/4;
  153. grid-auto-flow: row;
  154. margin-top: ${space(0.5)};
  155. @media (min-width: ${p => p.theme.breakpoints.small}) {
  156. grid-column: 3/3;
  157. grid-row: 1/3;
  158. grid-auto-flow: column;
  159. margin-top: 0;
  160. }
  161. `;
  162. const ButtonTooltip = styled(Tooltip)`
  163. @media (min-width: ${p => p.theme.breakpoints.small}) {
  164. display: none;
  165. }
  166. `;
  167. const ActionBtn = styled(Button)`
  168. width: 100%;
  169. @media (min-width: ${p => p.theme.breakpoints.small}) {
  170. display: none;
  171. }
  172. `;
  173. const DropDownWrapper = styled('div')`
  174. display: none;
  175. @media (min-width: ${p => p.theme.breakpoints.small}) {
  176. display: block;
  177. }
  178. `;