footer.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import Hook from 'sentry/components/hook';
  4. import ExternalLink from 'sentry/components/links/externalLink';
  5. import {IconSentry} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import ConfigStore from 'sentry/stores/configStore';
  8. import {useLegacyStore} from 'sentry/stores/useLegacyStore';
  9. import space from 'sentry/styles/space';
  10. import getDynamicText from 'sentry/utils/getDynamicText';
  11. type Props = {
  12. className?: string;
  13. };
  14. function BaseFooter({className}: Props) {
  15. const {isSelfHosted, version, privacyUrl, termsUrl, demoMode} =
  16. useLegacyStore(ConfigStore);
  17. return (
  18. <footer className={className}>
  19. <LeftLinks>
  20. {isSelfHosted && (
  21. <Fragment>
  22. {'Sentry '}
  23. {getDynamicText({
  24. fixed: 'Acceptance Test',
  25. value: version.current,
  26. })}
  27. <Build>
  28. {getDynamicText({
  29. fixed: 'test',
  30. value: version.build.substring(0, 7),
  31. })}
  32. </Build>
  33. </Fragment>
  34. )}
  35. {privacyUrl && <FooterLink href={privacyUrl}>{t('Privacy Policy')}</FooterLink>}
  36. {termsUrl && <FooterLink href={termsUrl}>{t('Terms of Use')}</FooterLink>}
  37. </LeftLinks>
  38. <LogoLink />
  39. <RightLinks>
  40. {!isSelfHosted && (
  41. <FooterLink href="https://status.sentry.io/">{t('Service Status')}</FooterLink>
  42. )}
  43. <FooterLink href="/api/">{t('API')}</FooterLink>
  44. <FooterLink href="/docs/">{t('Docs')}</FooterLink>
  45. <FooterLink href="https://github.com/getsentry/sentry">
  46. {t('Contribute')}
  47. </FooterLink>
  48. {isSelfHosted && !demoMode && (
  49. <FooterLink href="/out/">{t('Migrate to SaaS')}</FooterLink>
  50. )}
  51. </RightLinks>
  52. <Hook name="footer" />
  53. </footer>
  54. );
  55. }
  56. const LeftLinks = styled('div')`
  57. display: grid;
  58. grid-auto-flow: column;
  59. grid-auto-columns: max-content;
  60. align-items: center;
  61. justify-self: flex-start;
  62. gap: ${space(2)};
  63. `;
  64. const RightLinks = styled('div')`
  65. display: grid;
  66. grid-auto-flow: column;
  67. grid-auto-columns: max-content;
  68. align-items: center;
  69. justify-self: flex-end;
  70. gap: ${space(2)};
  71. `;
  72. const FooterLink = styled(ExternalLink)`
  73. color: ${p => p.theme.subText};
  74. &.focus-visible {
  75. outline: none;
  76. box-shadow: ${p => p.theme.blue300} 0 2px 0;
  77. }
  78. `;
  79. const LogoLink = styled(props => (
  80. <ExternalLink href="https://sentry.io/welcome/" tabIndex={-1} {...props}>
  81. <IconSentry size="lg" />
  82. </ExternalLink>
  83. ))`
  84. display: flex;
  85. align-items: center;
  86. margin: 0 auto;
  87. color: ${p => p.theme.subText};
  88. `;
  89. const Build = styled('span')`
  90. font-size: ${p => p.theme.fontSizeRelativeSmall};
  91. color: ${p => p.theme.subText};
  92. font-weight: bold;
  93. margin-left: ${space(1)};
  94. `;
  95. const Footer = styled(BaseFooter)`
  96. display: grid;
  97. grid-template-columns: 1fr 1fr 1fr;
  98. color: ${p => p.theme.subText};
  99. font-size: ${p => p.theme.fontSizeMedium};
  100. border-top: 1px solid ${p => p.theme.border};
  101. align-content: center;
  102. padding: ${space(2)} ${space(4)};
  103. margin-top: auto; /* pushes footer to the bottom of the page when loading */
  104. @media (max-width: ${p => p.theme.breakpoints.medium}) {
  105. padding: ${space(2)};
  106. }
  107. @media (max-width: ${p => p.theme.breakpoints.small}) {
  108. display: none;
  109. }
  110. `;
  111. export default Footer;