apiTokenRow.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import styled from '@emotion/styled';
  2. import {Button} from 'sentry/components/button';
  3. import Confirm from 'sentry/components/confirm';
  4. import {DateTime} from 'sentry/components/dateTime';
  5. import Link from 'sentry/components/links/link';
  6. import PanelItem from 'sentry/components/panels/panelItem';
  7. import {IconSubtract} from 'sentry/icons';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {InternalAppApiToken} from 'sentry/types/user';
  11. import getDynamicText from 'sentry/utils/getDynamicText';
  12. import {tokenPreview} from 'sentry/views/settings/organizationAuthTokens';
  13. type Props = {
  14. onRemove: (token: InternalAppApiToken) => void;
  15. token: InternalAppApiToken;
  16. canEdit?: boolean;
  17. onRemoveConfirmMessage?: string;
  18. tokenPrefix?: string;
  19. };
  20. function ApiTokenRow({
  21. token,
  22. onRemove,
  23. tokenPrefix = '',
  24. canEdit = false,
  25. onRemoveConfirmMessage,
  26. }: Props) {
  27. return (
  28. <StyledPanelItem>
  29. <Controls>
  30. {canEdit ? (
  31. <LinkWrapper name={token.name}>
  32. <Link to={`/settings/account/api/auth-tokens/${token.id}/`}>
  33. {token.name ? token.name : 'Token created on '}
  34. <DateTime
  35. date={getDynamicText({
  36. value: token.dateCreated,
  37. fixed: new Date(1508208080000), // National Pasta Day
  38. })}
  39. hidden={!!token.name}
  40. />
  41. </Link>
  42. </LinkWrapper>
  43. ) : (
  44. <p>{token.name ? token.name : ''}</p>
  45. )}
  46. <ButtonWrapper>
  47. <Confirm
  48. onConfirm={() => onRemove(token)}
  49. message={
  50. onRemoveConfirmMessage ||
  51. t(
  52. 'Are you sure you want to revoke %s token? It will not be usable anymore, and this cannot be undone.',
  53. tokenPreview(token.tokenLastCharacters, tokenPrefix)
  54. )
  55. }
  56. >
  57. <Button
  58. data-test-id="token-delete"
  59. icon={<IconSubtract isCircled size="xs" />}
  60. >
  61. {t('Remove')}
  62. </Button>
  63. </Confirm>
  64. </ButtonWrapper>
  65. </Controls>
  66. <Details>
  67. <TokenWrapper>
  68. <Heading>{t('Token')}</Heading>
  69. <TokenPreview aria-label={t('Token preview')}>
  70. {tokenPreview(
  71. getDynamicText({
  72. value: token.tokenLastCharacters,
  73. fixed: 'ABCD',
  74. }),
  75. tokenPrefix
  76. )}
  77. </TokenPreview>
  78. </TokenWrapper>
  79. <ScopesWrapper>
  80. <Heading>{t('Scopes')}</Heading>
  81. <ScopeList>{token.scopes.join(', ')}</ScopeList>
  82. </ScopesWrapper>
  83. <div>
  84. <Heading>{t('Created')}</Heading>
  85. <Time>
  86. <DateTime
  87. date={getDynamicText({
  88. value: token.dateCreated,
  89. fixed: new Date(1508208080000), // National Pasta Day
  90. })}
  91. />
  92. </Time>
  93. </div>
  94. </Details>
  95. </StyledPanelItem>
  96. );
  97. }
  98. const StyledPanelItem = styled(PanelItem)`
  99. flex-direction: column;
  100. padding: ${space(2)};
  101. `;
  102. const Controls = styled('div')`
  103. display: flex;
  104. align-items: center;
  105. margin-bottom: ${space(1)};
  106. `;
  107. const Details = styled('div')`
  108. display: flex;
  109. margin-top: ${space(1)};
  110. `;
  111. const TokenWrapper = styled('div')`
  112. flex: 1;
  113. margin-right: ${space(1)};
  114. `;
  115. const ScopesWrapper = styled('div')`
  116. flex: 2;
  117. margin-right: ${space(4)};
  118. `;
  119. const ScopeList = styled('div')`
  120. font-size: ${p => p.theme.fontSizeRelativeSmall};
  121. line-height: 1.4;
  122. `;
  123. const Time = styled('time')`
  124. font-size: ${p => p.theme.fontSizeRelativeSmall};
  125. line-height: 1.4;
  126. `;
  127. const Heading = styled('div')`
  128. font-size: ${p => p.theme.fontSizeMedium};
  129. text-transform: uppercase;
  130. color: ${p => p.theme.subText};
  131. margin-bottom: ${space(1)};
  132. `;
  133. const TokenPreview = styled('div')`
  134. color: ${p => p.theme.gray300};
  135. `;
  136. const LinkWrapper = styled('div')<{name: string}>`
  137. font-style: ${p => (p.name ? 'normal' : 'italic')};
  138. `;
  139. const ButtonWrapper = styled('div')`
  140. margin-left: auto;
  141. display: flex;
  142. flex-direction: column;
  143. align-items: flex-end;
  144. justify-content: flex-end;
  145. font-size: ${p => p.theme.fontSizeSmall};
  146. gap: ${space(1)};
  147. `;
  148. export default ApiTokenRow;