apiTokenRow.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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 TextCopyInput from 'sentry/components/textCopyInput';
  6. import {IconSubtract} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. import {InternalAppApiToken} from 'sentry/types';
  10. import getDynamicText from 'sentry/utils/getDynamicText';
  11. import {tokenPreview} from 'sentry/views/settings/organizationAuthTokens';
  12. type Props = {
  13. onRemove: (token: InternalAppApiToken) => void;
  14. token: InternalAppApiToken;
  15. };
  16. // TODO: After the BE portion of code changes have been released, remove the conditional rendering of the token.
  17. // We are currently doing the conditional logic to do safe blue/green deploys and handle contract changes.
  18. function ApiTokenRow({token, onRemove}: Props) {
  19. return (
  20. <StyledPanelItem>
  21. <Controls>
  22. {token.tokenLastCharacters ? (
  23. <TokenPreview aria-label={t('Token preview')}>
  24. {tokenPreview(
  25. getDynamicText({
  26. value: token.tokenLastCharacters,
  27. fixed: 'ABCD',
  28. })
  29. )}
  30. </TokenPreview>
  31. ) : (
  32. <InputWrapper>
  33. <TextCopyInput>
  34. {getDynamicText({value: token.token, fixed: 'CI_AUTH_TOKEN'})}
  35. </TextCopyInput>
  36. </InputWrapper>
  37. )}
  38. <ButtonWrapper>
  39. <Button
  40. onClick={() => onRemove(token)}
  41. icon={<IconSubtract isCircled size="xs" />}
  42. >
  43. {t('Remove')}
  44. </Button>
  45. </ButtonWrapper>
  46. </Controls>
  47. <Details>
  48. <ScopesWrapper>
  49. <Heading>{t('Scopes')}</Heading>
  50. <ScopeList>{token.scopes.join(', ')}</ScopeList>
  51. </ScopesWrapper>
  52. <div>
  53. <Heading>{t('Created')}</Heading>
  54. <Time>
  55. <DateTime
  56. date={getDynamicText({
  57. value: token.dateCreated,
  58. fixed: new Date(1508208080000), // National Pasta Day
  59. })}
  60. />
  61. </Time>
  62. </div>
  63. </Details>
  64. </StyledPanelItem>
  65. );
  66. }
  67. const StyledPanelItem = styled(PanelItem)`
  68. flex-direction: column;
  69. padding: ${space(2)};
  70. `;
  71. const Controls = styled('div')`
  72. display: flex;
  73. align-items: center;
  74. margin-bottom: ${space(1)};
  75. `;
  76. const InputWrapper = styled('div')`
  77. font-size: ${p => p.theme.fontSizeRelativeSmall};
  78. flex: 1;
  79. margin-right: ${space(1)};
  80. `;
  81. const Details = styled('div')`
  82. display: flex;
  83. margin-top: ${space(1)};
  84. `;
  85. const ScopesWrapper = styled('div')`
  86. flex: 1;
  87. `;
  88. const ScopeList = styled('div')`
  89. font-size: ${p => p.theme.fontSizeRelativeSmall};
  90. line-height: 1.4;
  91. `;
  92. const Time = styled('time')`
  93. font-size: ${p => p.theme.fontSizeRelativeSmall};
  94. line-height: 1.4;
  95. `;
  96. const Heading = styled('div')`
  97. font-size: ${p => p.theme.fontSizeMedium};
  98. text-transform: uppercase;
  99. color: ${p => p.theme.subText};
  100. margin-bottom: ${space(1)};
  101. `;
  102. const TokenPreview = styled('div')`
  103. color: ${p => p.theme.gray300};
  104. `;
  105. const ButtonWrapper = styled('div')`
  106. margin-left: auto;
  107. display: flex;
  108. flex-direction: column;
  109. align-items: flex-end;
  110. justify-content: flex-end;
  111. font-size: ${p => p.theme.fontSizeSmall};
  112. gap: ${space(1)};
  113. `;
  114. export default ApiTokenRow;