AlertError.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { SupportCategories } from '@supabase/shared-types/out/constants'
  2. import { PropsWithChildren, useEffect, useRef } from 'react'
  3. import { Button } from 'ui'
  4. import { Admonition } from 'ui-patterns/admonition'
  5. import { SupportLink } from '@/components/interfaces/Support/SupportLink'
  6. import { useTrack } from '@/lib/telemetry/track'
  7. export interface AlertErrorProps {
  8. projectRef?: string
  9. subject?: string
  10. description?: string
  11. error?: { message: string } | null
  12. layout?: 'vertical' | 'horizontal' | 'responsive'
  13. className?: string
  14. showIcon?: boolean
  15. showInstructions?: boolean
  16. showErrorPrefix?: boolean
  17. additionalActions?: React.ReactNode
  18. }
  19. export const ContactSupportButton = ({
  20. projectRef,
  21. subject,
  22. error,
  23. }: {
  24. projectRef?: string
  25. subject?: string
  26. error?: { message: string } | null
  27. }) => {
  28. return (
  29. <Button asChild type="default" className="w-min">
  30. <SupportLink
  31. queryParams={{
  32. category: SupportCategories.DASHBOARD_BUG,
  33. projectRef,
  34. subject,
  35. error: error?.message,
  36. }}
  37. >
  38. Contact support
  39. </SupportLink>
  40. </Button>
  41. )
  42. }
  43. // [Joshen] To standardize the language for all error UIs
  44. export const AlertError = ({
  45. projectRef,
  46. subject,
  47. description = 'Try refreshing your browser, but if the issue persists for more than a few minutes, please reach out to us via support.',
  48. error,
  49. className,
  50. showIcon = true,
  51. layout = 'responsive',
  52. showInstructions = true,
  53. showErrorPrefix = true,
  54. children,
  55. additionalActions,
  56. }: PropsWithChildren<AlertErrorProps>) => {
  57. const track = useTrack()
  58. const hasTrackedRef = useRef(false)
  59. const formattedErrorMessage = error?.message?.includes('503')
  60. ? '503 Service Temporarily Unavailable'
  61. : error?.message
  62. useEffect(() => {
  63. if (!hasTrackedRef.current) {
  64. hasTrackedRef.current = true
  65. if (Math.random() < 0.1) {
  66. track('dashboard_error_created', {
  67. source: 'admonition',
  68. })
  69. }
  70. }
  71. }, [track])
  72. return (
  73. <Admonition
  74. type="warning"
  75. layout={additionalActions ? 'vertical' : layout}
  76. showIcon={showIcon}
  77. title={subject}
  78. description={
  79. <>
  80. {error?.message && (
  81. <p>
  82. {showErrorPrefix && 'Error: '}
  83. {formattedErrorMessage}
  84. </p>
  85. )}
  86. {showInstructions && <p>{description}</p>}
  87. {children}
  88. </>
  89. }
  90. actions={
  91. additionalActions ? (
  92. <>
  93. {additionalActions}
  94. <ContactSupportButton projectRef={projectRef} subject={subject} error={error} />
  95. </>
  96. ) : (
  97. <ContactSupportButton projectRef={projectRef} subject={subject} error={error} />
  98. )
  99. }
  100. className={className}
  101. />
  102. )
  103. }
  104. export default AlertError