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