detailedError.tsx 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import styled from '@emotion/styled';
  2. import * as Sentry from '@sentry/react';
  3. import Button from 'sentry/components/button';
  4. import ButtonBar from 'sentry/components/buttonBar';
  5. import {IconFlag} from 'sentry/icons';
  6. import {t} from 'sentry/locale';
  7. import space from 'sentry/styles/space';
  8. type Props = {
  9. /**
  10. * Error heading
  11. */
  12. heading: React.ReactNode;
  13. className?: string;
  14. /**
  15. * Hide support links in footer of error message
  16. */
  17. hideSupportLinks?: boolean;
  18. /**
  19. * Detailed error explanation
  20. */
  21. message?: React.ReactNode;
  22. /**
  23. * Retry callback
  24. */
  25. onRetry?: (e: React.MouseEvent) => void;
  26. };
  27. function openFeedback(e: React.MouseEvent) {
  28. e.preventDefault();
  29. Sentry.showReportDialog();
  30. }
  31. function DetailedError({className, heading, message, onRetry, hideSupportLinks}: Props) {
  32. const showFooter = !!onRetry || !hideSupportLinks;
  33. const hasLastEvent = !!Sentry.lastEventId();
  34. return (
  35. <Wrapper className={className}>
  36. <ErrorHeading>
  37. <IconFlag size="md" color="red300" />
  38. {heading}
  39. </ErrorHeading>
  40. {message}
  41. {showFooter && (
  42. <ErrorFooter>
  43. <div>{onRetry && <Button onClick={onRetry}>{t('Retry')}</Button>}</div>
  44. {!hideSupportLinks && (
  45. <ButtonBar gap={1.5}>
  46. {hasLastEvent && (
  47. <Button priority="link" onClick={openFeedback}>
  48. {t('Fill out a report')}
  49. </Button>
  50. )}
  51. <Button priority="link" external href="https://status.sentry.io/">
  52. {t('Service status')}
  53. </Button>
  54. <Button priority="link" external href="https://sentry.io/support/">
  55. {t('Contact support')}
  56. </Button>
  57. </ButtonBar>
  58. )}
  59. </ErrorFooter>
  60. )}
  61. </Wrapper>
  62. );
  63. }
  64. const Wrapper = styled('div')`
  65. margin: ${space(2)} auto 0 auto;
  66. padding: ${space(2)};
  67. width: max-content;
  68. `;
  69. const ErrorHeading = styled('h4')`
  70. display: flex;
  71. gap: ${space(1.5)};
  72. align-items: center;
  73. margin-left: calc(-1 * (${p => p.theme.iconSizes.md} + ${space(1.5)}));
  74. `;
  75. const ErrorFooter = styled('div')`
  76. display: flex;
  77. justify-content: space-between;
  78. align-items: center;
  79. margin-top: ${space(2)};
  80. border-top: 1px solid ${p => p.theme.innerBorder};
  81. padding-top: ${space(2)};
  82. `;
  83. export default DetailedError;