ErrorCodeDialog.tsx113 lines · main
| 1 | import { AlertTriangle } from 'lucide-react' |
| 2 | import { |
| 3 | Alert, |
| 4 | AlertDescription, |
| 5 | AlertTitle, |
| 6 | Badge, |
| 7 | Button_Shadcn_, |
| 8 | Dialog, |
| 9 | DialogContent, |
| 10 | DialogDescription, |
| 11 | DialogHeader, |
| 12 | DialogTitle, |
| 13 | } from 'ui' |
| 14 | import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' |
| 15 | |
| 16 | import { useErrorCodesQuery } from '@/data/content-api/docs-error-codes-query' |
| 17 | import { Service, type ErrorCodeQueryQuery } from '@/data/graphql/graphql' |
| 18 | |
| 19 | interface ErrorCodeDialogProps { |
| 20 | open: boolean |
| 21 | onOpenChange: (open: boolean) => void |
| 22 | errorCode: string |
| 23 | service?: Service |
| 24 | } |
| 25 | |
| 26 | export 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 | |
| 57 | const LoadingState = () => ( |
| 58 | <> |
| 59 | <ShimmeringLoader className="w-3/4 mb-2" /> |
| 60 | <ShimmeringLoader className="w-1/2" /> |
| 61 | </> |
| 62 | ) |
| 63 | |
| 64 | const 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 | |
| 82 | const 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 | |
| 100 | const 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 | ) |