ErrorCodeDialog.tsx 2.7 KB

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