GridResize.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // @ts-nocheck
  2. import RGL, { WidthProvider } from 'react-grid-layout'
  3. import 'react-grid-layout/css/styles.css'
  4. import 'react-resizable/css/styles.css'
  5. import { useParams } from 'common'
  6. import { toast } from 'sonner'
  7. import { createSqlSnippetSkeletonV2 } from '../SQLEditor/SQLEditor.utils'
  8. import { ChartConfig } from '../SQLEditor/UtilityPanel/ChartConfig'
  9. import { ReportBlock } from './ReportBlock/ReportBlock'
  10. import { LAYOUT_COLUMN_COUNT } from './Reports.constants'
  11. import { DEFAULT_CHART_CONFIG } from '@/components/ui/QueryBlock/QueryBlock'
  12. import { AnalyticsInterval } from '@/data/analytics/constants'
  13. import {
  14. UpsertContentPayload,
  15. useContentUpsertMutation,
  16. } from '@/data/content/content-upsert-mutation'
  17. import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
  18. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  19. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  20. import { useProfile } from '@/lib/profile'
  21. import type { Dashboards } from '@/types'
  22. const ReactGridLayout = WidthProvider(RGL)
  23. interface GridResizeProps {
  24. startDate: string
  25. endDate: string
  26. interval: AnalyticsInterval
  27. editableReport: Dashboards.Content
  28. disableUpdate: boolean
  29. isRefreshing: boolean
  30. onRemoveChart: ({ metric }: { metric: { key: string } }) => void
  31. onUpdateChart: (
  32. id: string,
  33. {
  34. chart,
  35. chartConfig,
  36. }: { chart?: Partial<Dashboards.Chart>; chartConfig?: Partial<ChartConfig> }
  37. ) => void
  38. setEditableReport: (payload: any) => void
  39. }
  40. export const GridResize = ({
  41. startDate,
  42. endDate,
  43. interval,
  44. editableReport,
  45. disableUpdate,
  46. isRefreshing,
  47. onRemoveChart,
  48. onUpdateChart,
  49. setEditableReport,
  50. }: GridResizeProps) => {
  51. const { ref } = useParams()
  52. const { profile } = useProfile()
  53. const { data: project } = useSelectedProjectQuery()
  54. const { data: selectedOrg } = useSelectedOrganizationQuery()
  55. const { mutate: sendEvent } = useSendEventMutation()
  56. const { mutate: upsertContent } = useContentUpsertMutation()
  57. const onUpdateLayout = (layout: RGL.Layout[]) => {
  58. const updatedLayout = [...editableReport.layout]
  59. layout.forEach((chart) => {
  60. const chartIdx = updatedLayout.findIndex((y) => chart.i === y.id)
  61. if (chartIdx !== undefined && chartIdx >= 0) {
  62. updatedLayout[chartIdx] = {
  63. ...updatedLayout[chartIdx],
  64. w: chart.w,
  65. h: chart.h,
  66. x: chart.x,
  67. y: chart.y,
  68. }
  69. }
  70. })
  71. setEditableReport({ ...editableReport, layout: updatedLayout })
  72. }
  73. const onDropBlock = async (layout: RGL.Layout[], layoutItem: RGL.Layout, e: any) => {
  74. if (!ref) return console.error('Project ref is required')
  75. if (!profile) return console.error('Profile is required')
  76. if (!project) return console.error('Project is required')
  77. const data = e.dataTransfer.getData('application/json')
  78. if (!data) return
  79. const queryData = JSON.parse(data)
  80. const { label, sql, config } = queryData
  81. if (!label || !sql) return console.error('SQL and Label required')
  82. const toastId = toast.loading(`Creating new query: ${label}`)
  83. const payload = createSqlSnippetSkeletonV2({
  84. name: label,
  85. sql,
  86. owner_id: profile?.id,
  87. project_id: project?.id,
  88. }) as UpsertContentPayload
  89. const updatedLayout = layout.map((x) => {
  90. const existingBlock = editableReport.layout.find((y) => x.i === y.id)
  91. if (existingBlock) {
  92. return { ...existingBlock, x: x.x, y: x.y }
  93. } else {
  94. return {
  95. id: payload.id,
  96. attribute: `new_snippet_${payload.id}`,
  97. chartConfig: { ...DEFAULT_CHART_CONFIG, ...(config ?? {}) },
  98. label,
  99. chart_type: 'bar',
  100. h: layoutItem.h,
  101. w: layoutItem.w,
  102. x: layoutItem.x,
  103. y: layoutItem.y,
  104. }
  105. }
  106. })
  107. setEditableReport({ ...editableReport, layout: updatedLayout })
  108. upsertContent(
  109. { projectRef: ref, payload },
  110. {
  111. onSuccess: () => {
  112. toast.success(`Successfully created new query: ${label}`, { id: toastId })
  113. const finalLayout = updatedLayout.map((x) => {
  114. if (x.id === payload.id) {
  115. return { ...x, attribute: `snippet_${payload.id}` }
  116. } else return x
  117. })
  118. setEditableReport({ ...editableReport, layout: finalLayout })
  119. },
  120. }
  121. )
  122. sendEvent({
  123. action: 'custom_report_assistant_sql_block_added',
  124. groups: { project: ref ?? 'Unknown', organization: selectedOrg?.slug ?? 'Unknown' },
  125. })
  126. }
  127. if (!editableReport) return null
  128. return (
  129. <ReactGridLayout
  130. autoSize
  131. isDraggable
  132. isDroppable
  133. isResizable
  134. rowHeight={270}
  135. cols={LAYOUT_COLUMN_COUNT}
  136. containerPadding={[0, 0]}
  137. resizeHandles={['sw', 'se']}
  138. compactType="vertical"
  139. onDrop={onDropBlock}
  140. onDragStop={onUpdateLayout}
  141. onResizeStop={onUpdateLayout}
  142. draggableHandle=".grid-item-drag-handle"
  143. >
  144. {editableReport.layout.map((item) => {
  145. return (
  146. <div
  147. key={item.id}
  148. data-grid={{ ...item, h: 1, minH: 1, maxH: 1, minW: 1, maxW: LAYOUT_COLUMN_COUNT }}
  149. >
  150. <ReportBlock
  151. key={item.id}
  152. item={item}
  153. startDate={startDate}
  154. endDate={endDate}
  155. interval={interval}
  156. disableUpdate={disableUpdate}
  157. isRefreshing={isRefreshing}
  158. onRemoveChart={onRemoveChart}
  159. onUpdateChart={(config) => onUpdateChart(item.id, config)}
  160. />
  161. </div>
  162. )
  163. })}
  164. </ReactGridLayout>
  165. )
  166. }