ReportWidget.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { useParams } from 'common'
  2. import { ExternalLink, HelpCircle } from 'lucide-react'
  3. import { NextRouter, useRouter } from 'next/router'
  4. import { ReactNode } from 'react'
  5. import { Button, cn, Loading, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
  6. import type { LogsEndpointParams } from '../Settings/Logs/Logs.types'
  7. import type { BaseReportParams, ReportQueryType } from './Reports.types'
  8. import Panel from '@/components/ui/Panel'
  9. export interface ReportWidgetProps<T = any> {
  10. data: T[]
  11. title: string
  12. description?: string
  13. error?: string | Object | null
  14. tooltip?: string | ReactNode
  15. className?: string
  16. contentClassName?: string
  17. headerClassName?: string
  18. renderer: (props: ReportWidgetRendererProps) => ReactNode
  19. append?: (props: ReportWidgetRendererProps) => ReactNode
  20. // for overriding props, such as data
  21. appendProps?: Partial<ReportWidgetRendererProps>
  22. // omitting params will hide the "View in logs explorer" button
  23. params?: BaseReportParams | LogsEndpointParams
  24. queryType?: ReportQueryType
  25. isLoading: boolean
  26. resolvedSql?: string
  27. }
  28. export interface ReportWidgetRendererProps<T = any> extends ReportWidgetProps<T> {
  29. router: NextRouter
  30. projectRef: string
  31. }
  32. const ReportWidget = (props: ReportWidgetProps) => {
  33. const router = useRouter()
  34. const { ref } = useParams()
  35. const projectRef = ref as string
  36. return (
  37. <Panel noMargin noHideOverflow className={cn('pb-0', props.className)} wrapWithLoading={false}>
  38. <Panel.Content className={cn('space-y-4', props.contentClassName)}>
  39. <div className={cn('flex flex-row items-start justify-between', props.headerClassName)}>
  40. <div className="gap-2">
  41. <div className="flex flex-row gap-2">
  42. <h3 className="w-full h-6">{props.title}</h3>{' '}
  43. {props?.tooltip && (
  44. <Tooltip>
  45. <TooltipTrigger>
  46. <HelpCircle className="text-foreground-light" size={14} strokeWidth={1.5} />
  47. </TooltipTrigger>
  48. <TooltipContent side="bottom">{props.tooltip}</TooltipContent>
  49. </Tooltip>
  50. )}
  51. </div>
  52. <p className="text-sm text-foreground-light">{props.description}</p>
  53. </div>
  54. {props.params && (
  55. <Tooltip>
  56. <TooltipTrigger asChild>
  57. <Button
  58. type="default"
  59. icon={<ExternalLink />}
  60. className="px-1"
  61. onClick={() => {
  62. const isDbQueryType = props.queryType === 'db'
  63. const pathname = isDbQueryType
  64. ? `/project/${projectRef}/sql/new`
  65. : `/project/${projectRef}/logs/explorer`
  66. const query: Record<string, string | undefined> = {}
  67. if (isDbQueryType) {
  68. query.content = props.resolvedSql
  69. } else {
  70. query.q = props.params?.sql
  71. query.its = props.params?.iso_timestamp_start || ''
  72. query.ite = props.params?.iso_timestamp_end || ''
  73. }
  74. router.push({ pathname, query })
  75. }}
  76. />
  77. </TooltipTrigger>
  78. <TooltipContent side="left">
  79. {props.queryType === 'db' ? 'Open in SQL Editor' : 'Open in Logs Explorer'}
  80. </TooltipContent>
  81. </Tooltip>
  82. )}
  83. </div>
  84. <Loading active={props.isLoading}>
  85. {props.data === undefined
  86. ? null
  87. : props.renderer({ ...props, router, projectRef: projectRef as string })}
  88. </Loading>
  89. </Panel.Content>
  90. {props.append && (
  91. <>
  92. {props.append({
  93. ...props,
  94. ...(props.appendProps || {}),
  95. router,
  96. projectRef: projectRef as string,
  97. })}
  98. </>
  99. )}
  100. </Panel>
  101. )
  102. }
  103. export default ReportWidget