apiTokenRow.tsx 4.1 KB

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