ErrorCodeDialog.tsx113 lines · main
1import { AlertTriangle } from 'lucide-react'
2import {
3 Alert,
4 AlertDescription,
5 AlertTitle,
6 Badge,
7 Button_Shadcn_,
8 Dialog,
9 DialogContent,
10 DialogDescription,
11 DialogHeader,
12 DialogTitle,
13} from 'ui'
14import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
15
16import { useErrorCodesQuery } from '@/data/content-api/docs-error-codes-query'
17import { Service, type ErrorCodeQueryQuery } from '@/data/graphql/graphql'
18
19interface ErrorCodeDialogProps {
20 open: boolean
21 onOpenChange: (open: boolean) => void
22 errorCode: string
23 service?: Service
24}
25
26export const ErrorCodeDialog = ({
27 open,
28 onOpenChange,
29 errorCode,
30 service,
31}: ErrorCodeDialogProps) => {
32 const {
33 data,
34 isPending: isLoading,
35 isSuccess,
36 refetch,
37 } = useErrorCodesQuery({ code: errorCode, service }, { enabled: open })
38
39 return (
40 <Dialog open={open} onOpenChange={onOpenChange}>
41 <DialogContent>
42 <DialogHeader>
43 <DialogTitle className="mb-4">
44 Help for error code <code>{errorCode}</code>
45 </DialogTitle>
46 <DialogDescription>
47 {isLoading && <LoadingState />}
48 {isSuccess && <SuccessState data={data} />}
49 {!isLoading && !isSuccess && <ErrorState refetch={refetch} />}
50 </DialogDescription>
51 </DialogHeader>
52 </DialogContent>
53 </Dialog>
54 )
55}
56
57const LoadingState = () => (
58 <>
59 <ShimmeringLoader className="w-3/4 mb-2" />
60 <ShimmeringLoader className="w-1/2" />
61 </>
62)
63
64const SuccessState = ({ data }: { data: ErrorCodeQueryQuery | undefined }) => {
65 const errors = data?.errors?.nodes?.filter((error) => !!error.message)
66 if (!errors || errors.length === 0) {
67 return <>No information found for this error code.</>
68 }
69
70 return (
71 <>
72 <p className="mb-4">Possible explanations for this error:</p>
73 <div className="grid gap-2 grid-cols-[max-content_1fr]">
74 {errors.map((error) => (
75 <ErrorExplanation key={`${error.service}-${error.code}`} {...error} />
76 ))}
77 </div>
78 </>
79 )
80}
81
82const ErrorExplanation = ({
83 service,
84 message,
85}: {
86 code: string
87 service: Service
88 message?: string | null
89}) => {
90 if (!message) return null
91
92 return (
93 <>
94 <Badge className="h-fit">{service}</Badge>
95 <p>{message}</p>
96 </>
97 )
98}
99
100const ErrorState = ({ refetch }: { refetch?: () => void }) => (
101 <Alert variant="warning">
102 <AlertTriangle />
103 <AlertTitle>Lookup failed</AlertTitle>
104 <AlertDescription>
105 <p>Failed to look up error code help info</p>
106 {refetch && (
107 <Button_Shadcn_ variant="outline" size="sm" className="mt-2" onClick={refetch}>
108 Try again
109 </Button_Shadcn_>
110 )}
111 </AlertDescription>
112 </Alert>
113)