apiTokenRow.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import styled from '@emotion/styled';
  2. import {Button} from 'sentry/components/button';
  3. import DateTime from 'sentry/components/dateTime';
  4. import PanelItem from 'sentry/components/panels/panelItem';
  5. import {IconSubtract} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import type {InternalAppApiToken} from 'sentry/types';
  9. import getDynamicText from 'sentry/utils/getDynamicText';
  10. import {tokenPreview} from 'sentry/views/settings/organizationAuthTokens';
  11. type Props = {
  12. onRemove: (token: InternalAppApiToken) => void;
  13. token: InternalAppApiToken;
  14. tokenPrefix?: string;
  15. };
  16. function ApiTokenRow({token, onRemove, tokenPrefix = ''}: Props) {
  17. return (
  18. <StyledPanelItem>
  19. <Controls>
  20. {token.name ? token.name : ''}
  21. <ButtonWrapper>
  22. <Button
  23. data-test-id="token-delete"
  24. onClick={() => onRemove(token)}
  25. icon={<IconSubtract isCircled size="xs" />}
  26. >
  27. {t('Remove')}
  28. </Button>
  29. </ButtonWrapper>
  30. </Controls>
  31. <Details>
  32. <TokenWrapper>
  33. <Heading>{t('Token')}</Heading>
  34. <TokenPreview aria-label={t('Token preview')}>
  35. {tokenPreview(
  36. getDynamicText({
  37. value: token.tokenLastCharacters,
  38. fixed: 'ABCD',
  39. }),
  40. tokenPrefix
  41. )}
  42. </TokenPreview>
  43. </TokenWrapper>
  44. <ScopesWrapper>
  45. <Heading>{t('Scopes')}</Heading>
  46. <ScopeList>{token.scopes.join(', ')}</ScopeList>
  47. </ScopesWrapper>
  48. <div>
  49. <Heading>{t('Created')}</Heading>
  50. <Time>
  51. <DateTime
  52. date={getDynamicText({
  53. value: token.dateCreated,
  54. fixed: new Date(1508208080000), // National Pasta Day
  55. })}
  56. />
  57. </Time>
  58. </div>
  59. </Details>
  60. </StyledPanelItem>
  61. );
  62. }
  63. const StyledPanelItem = styled(PanelItem)`
  64. flex-direction: column;
  65. padding: ${space(2)};
  66. `;
  67. const Controls = styled('div')`
  68. display: flex;
  69. align-items: center;
  70. margin-bottom: ${space(1)};
  71. `;
  72. const Details = styled('div')`
  73. display: flex;
  74. margin-top: ${space(1)};
  75. `;
  76. const TokenWrapper = styled('div')`
  77. flex: 1;
  78. margin-right: ${space(1)};
  79. `;
  80. const ScopesWrapper = styled('div')`
  81. flex: 2;
  82. margin-right: ${space(4)};
  83. `;
  84. const ScopeList = styled('div')`
  85. font-size: ${p => p.theme.fontSizeRelativeSmall};
  86. line-height: 1.4;
  87. `;
  88. const Time = styled('time')`
  89. font-size: ${p => p.theme.fontSizeRelativeSmall};
  90. line-height: 1.4;
  91. `;
  92. const Heading = styled('div')`
  93. font-size: ${p => p.theme.fontSizeMedium};
  94. text-transform: uppercase;
  95. color: ${p => p.theme.subText};
  96. margin-bottom: ${space(1)};
  97. `;
  98. const TokenPreview = styled('div')`
  99. color: ${p => p.theme.gray300};
  100. `;
  101. const ButtonWrapper = styled('div')`
  102. margin-left: auto;
  103. display: flex;
  104. flex-direction: column;
  105. align-items: flex-end;
  106. justify-content: flex-end;
  107. font-size: ${p => p.theme.fontSizeSmall};
  108. gap: ${space(1)};
  109. `;
  110. export default ApiTokenRow;