QueryError.tsx105 lines · main
| 1 | import styles from '@ui/layout/ai-icon-animation/ai-icon-animation-style.module.css' |
| 2 | import { initial, last } from 'lodash' |
| 3 | import { Dispatch, SetStateAction } from 'react' |
| 4 | import { |
| 5 | Alert, |
| 6 | AlertTitle, |
| 7 | Button, |
| 8 | cn, |
| 9 | Collapsible, |
| 10 | CollapsibleContent, |
| 11 | CollapsibleTrigger, |
| 12 | } from 'ui' |
| 13 | |
| 14 | import { QueryResponseError } from '@/data/sql/execute-sql-mutation' |
| 15 | |
| 16 | export const QueryError = ({ |
| 17 | error, |
| 18 | open, |
| 19 | setOpen, |
| 20 | }: { |
| 21 | error: QueryResponseError |
| 22 | open: boolean |
| 23 | setOpen: Dispatch<SetStateAction<boolean>> |
| 24 | }) => { |
| 25 | const formattedError = |
| 26 | (error?.message?.split('\n') ?? [])?.filter((x: string) => x.length > 0) ?? [] |
| 27 | |
| 28 | return ( |
| 29 | <div className="flex flex-col gap-y-3 px-5"> |
| 30 | <Alert variant="destructive"> |
| 31 | <svg |
| 32 | xmlns="http://www.w3.org/2000/svg" |
| 33 | viewBox="0 0 20 20" |
| 34 | fill="currentColor" |
| 35 | className="w-5 h-5" |
| 36 | > |
| 37 | <path |
| 38 | fillRule="evenodd" |
| 39 | clipRule="evenodd" |
| 40 | d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" |
| 41 | /> |
| 42 | </svg> |
| 43 | <div className="flex flex-col gap-3"> |
| 44 | <AlertTitle className="m-0">Error running SQL query</AlertTitle> |
| 45 | |
| 46 | <Collapsible |
| 47 | defaultOpen |
| 48 | className="flex flex-col gap-3" |
| 49 | open={open} |
| 50 | onOpenChange={() => setOpen(!open)} |
| 51 | > |
| 52 | <div className="flex gap-2"> |
| 53 | <CollapsibleTrigger asChild> |
| 54 | <Button |
| 55 | size="tiny" |
| 56 | type="outline" |
| 57 | className={cn('group', styles['ai-icon__container--allow-hover-effect'])} |
| 58 | > |
| 59 | {open ? 'Hide error details' : 'Show error details'} |
| 60 | </Button> |
| 61 | </CollapsibleTrigger> |
| 62 | </div> |
| 63 | <CollapsibleContent className="overflow-auto"> |
| 64 | {formattedError.length > 0 ? ( |
| 65 | formattedError.map((x: string, i: number) => ( |
| 66 | <pre key={`error-${i}`} className="font-mono text-xs whitespace-pre-wrap"> |
| 67 | {x |
| 68 | .split(' ') |
| 69 | .reduce((arr, cur) => { |
| 70 | // Split the ERROR string so that it can be wrapped in a red span |
| 71 | const l = last(arr) |
| 72 | |
| 73 | if (l && l !== 'ERROR:') { |
| 74 | return initial(arr).concat([[l, cur].join(' ')]) |
| 75 | } |
| 76 | |
| 77 | if (l === '') { |
| 78 | return arr.concat([' ']) |
| 79 | } |
| 80 | |
| 81 | return arr.concat([cur]) |
| 82 | }, [] as string[]) |
| 83 | .map((str, index) => { |
| 84 | return ( |
| 85 | <span |
| 86 | key={index} |
| 87 | className={cn('break-all', str === 'ERROR:' && 'text-destructive')} |
| 88 | > |
| 89 | {str} |
| 90 | </span> |
| 91 | ) |
| 92 | })} |
| 93 | </pre> |
| 94 | )) |
| 95 | ) : ( |
| 96 | <p className="font-mono text-xs">{error.error}</p> |
| 97 | )} |
| 98 | </CollapsibleContent> |
| 99 | </Collapsible> |
| 100 | </div> |
| 101 | </Alert> |
| 102 | <div className="overflow-x-auto"></div> |
| 103 | </div> |
| 104 | ) |
| 105 | } |